{"version":3,"file":"product.js","sources":["../../../node_modules/bootstrap.native/dist/components/tab-native.esm.js","../../../modules/add-to-cart/js/urls.js","../../utils/fetch.js","../../utils/template.js","../../utils/base-view.js","../../../modules/add-to-cart/js/views/sticky-footer.js","../../utils/debounce.js","../../../modules/add-to-cart/js/views/cart-modal.js","../../utils/modal-view.js","../../../modules/add-to-cart/js/views/message.js","../../../modules/add-to-cart/js/views/add-to-cart.js","../../../modules/add-to-cart/js/app.js","../../../modules/add-to-cart/js/views/bundles.js","../../../modules/add-to-quote/js/urls.js","../../../modules/add-to-quote/js/views/add-to-quote.js","../../../modules/add-to-quote/js/app.js","../../../modules/product-options/js/app.js","../../../modules/add-to-wishlist/js/urls.js","../../../modules/add-to-wishlist/js/views/add-to-wishlist.js","../../../modules/add-to-wishlist/js/app.js","../../../modules/product-ask-question/js/urls.js","../../../modules/product-ask-question/js/views/product-ask-question.js","../../../modules/product-ask-question/js/app.js","../../../modules/product-lease/js/urls.js","../../../modules/product-lease/js/views/app.js","../../../modules/product-lease/js/views/help.js","../../../modules/product-lease/js/app.js","../../../modules/product-in-stock/js/urls.js","../../../modules/product-in-stock/js/views/app.js","../../../modules/product-in-stock/js/views/help.js","../../../modules/product-in-stock/js/app.js","../../../modules/review-feedback/js/urls.js","../../../modules/review-feedback/js/app.js","../../../modules/product-map-pricing/js/app.js","../../../node_modules/tiny-slider/src/helpers/raf.js","../../../node_modules/tiny-slider/src/helpers/caf.js","../../../node_modules/tiny-slider/src/helpers/extend.js","../../../node_modules/tiny-slider/src/helpers/checkStorageValue.js","../../../node_modules/tiny-slider/src/helpers/setLocalStorage.js","../../../node_modules/tiny-slider/src/helpers/getBody.js","../../../node_modules/tiny-slider/src/helpers/docElement.js","../../../node_modules/tiny-slider/src/helpers/setFakeBody.js","../../../node_modules/tiny-slider/src/helpers/resetFakeBody.js","../../../node_modules/tiny-slider/src/helpers/addCSSRule.js","../../../node_modules/tiny-slider/src/helpers/getCssRulesLength.js","../../../node_modules/tiny-slider/src/helpers/forEach.js","../../../node_modules/tiny-slider/src/helpers/classListSupport.js","../../../node_modules/tiny-slider/src/helpers/hasClass.js","../../../node_modules/tiny-slider/src/helpers/addClass.js","../../../node_modules/tiny-slider/src/helpers/removeClass.js","../../../node_modules/tiny-slider/src/helpers/hasAttr.js","../../../node_modules/tiny-slider/src/helpers/getAttr.js","../../../node_modules/tiny-slider/src/helpers/isNodeList.js","../../../node_modules/tiny-slider/src/helpers/setAttrs.js","../../../node_modules/tiny-slider/src/helpers/removeAttrs.js","../../../node_modules/tiny-slider/src/helpers/arrayFromNodeList.js","../../../node_modules/tiny-slider/src/helpers/hideElement.js","../../../node_modules/tiny-slider/src/helpers/showElement.js","../../../node_modules/tiny-slider/src/helpers/isVisible.js","../../../node_modules/tiny-slider/src/helpers/whichProperty.js","../../../node_modules/tiny-slider/src/helpers/getEndProperty.js","../../../node_modules/tiny-slider/src/helpers/passiveOption.js","../../../node_modules/tiny-slider/src/helpers/addEvents.js","../../../node_modules/tiny-slider/src/helpers/removeEvents.js","../../../node_modules/tiny-slider/src/helpers/events.js","../../../node_modules/tiny-slider/src/tiny-slider.js","../../../node_modules/tiny-slider/src/helpers/calc.js","../../../node_modules/tiny-slider/src/helpers/percentageLayout.js","../../../node_modules/tiny-slider/src/helpers/mediaquerySupport.js","../../../node_modules/tiny-slider/src/helpers/has3DTransforms.js","../../../node_modules/tiny-slider/src/helpers/createStyleSheet.js","../../../node_modules/tiny-slider/src/helpers/getSlideId.js","../../../node_modules/tiny-slider/src/helpers/jsTransform.js","../../../node_modules/tiny-slider/src/helpers/removeCSSRule.js","../../../node_modules/tiny-slider/src/helpers/toDegree.js","../../../node_modules/tiny-slider/src/helpers/getTouchDirection.js","../../../modules/accessories/js/urls.js","../../../modules/accessories/js/views/product-with-accessory-modal.js","../../../modules/accessories/js/app.js","../../../modules/private-client-advisor/js/urls.js","../../../modules/private-client-advisor/js/views/private-client-advisor.js","../../../modules/private-client-advisor/js/app.js","../../store/product/utils.js","../../store/product/images.js","../../store/product/videos.js","../../store/product/related-products.js","../../../modules/popup-content/js/urls.js","../../../modules/popup-content/js/views/popup-content.js","../../store/product/mobile-tabs.js","../../store/common/login-for-price.js","../../utils/check-map-pricing.js","../../../modules/session/js/urls.js","../../../modules/session/js/models/contact-info.js","../../../modules/session/js/contact-info.js","../../store/product.js"],"sourcesContent":["/*!\n * Native JavaScript for Bootstrap - Tab v4.2.0 (https://thednp.github.io/bootstrap.native/)\n * Copyright 2015-2022 © dnp_theme\n * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE)\n */\n/**\n * A global namespace for aria-selected.\n * @type {string}\n */\nconst ariaSelected = 'aria-selected';\n\n/**\n * A global namespace for `click` event.\n * @type {string}\n */\nconst mouseclickEvent = 'click';\n\n/**\n * Shortcut for `HTMLElement.setAttribute()` method.\n * @param {HTMLElement} element target element\n * @param {string} attribute attribute name\n * @param {string} value attribute value\n * @returns {void}\n */\nconst setAttribute = (element, attribute, value) => element.setAttribute(attribute, value);\n\n/**\n * Shortcut for `HTMLElement.closest` method which also works\n * with children of `ShadowRoot`. The order of the parameters\n * is intentional since they're both required.\n *\n * @see https://stackoverflow.com/q/54520554/803358\n *\n * @param {HTMLElement} element Element to look into\n * @param {string} selector the selector name\n * @return {HTMLElement?} the query result\n */\nfunction closest(element, selector) {\n return element ? (element.closest(selector)\n // break out of `ShadowRoot`\n || closest(element.getRootNode().host, selector)) : null;\n}\n\n/**\n * Checks if an object is a `Node`.\n *\n * @param {any} node the target object\n * @returns {boolean} the query result\n */\nconst isNode = (element) => (element && [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n .some((x) => +element.nodeType === x)) || false;\n\n/**\n * Check if a target object is `Window`.\n * => equivalent to `object instanceof Window`\n *\n * @param {any} object the target object\n * @returns {boolean} the query result\n */\nconst isWindow = (object) => (object && object.constructor.name === 'Window') || false;\n\n/**\n * Checks if an object is a `Document`.\n * @see https://dom.spec.whatwg.org/#node\n *\n * @param {any} object the target object\n * @returns {boolean} the query result\n */\nconst isDocument = (object) => (object && object.nodeType === 9) || false;\n\n/**\n * Returns the `document` or the `#document` element.\n * @see https://github.com/floating-ui/floating-ui\n * @param {(Node | Window)=} node\n * @returns {Document}\n */\nfunction getDocument(node) {\n // node instanceof Document\n if (isDocument(node)) return node;\n // node instanceof Node\n if (isNode(node)) return node.ownerDocument;\n // node instanceof Window\n if (isWindow(node)) return node.document;\n // node is undefined | NULL\n return window.document;\n}\n\n/**\n * Shortcut for `HTMLElement.getElementsByClassName` method. Some `Node` elements\n * like `ShadowRoot` do not support `getElementsByClassName`.\n *\n * @param {string} selector the class name\n * @param {ParentNode=} parent optional Element to look into\n * @return {HTMLCollectionOf} the 'HTMLCollection'\n */\nfunction getElementsByClassName(selector, parent) {\n const lookUp = isNode(parent) ? parent : getDocument();\n return lookUp.getElementsByClassName(selector);\n}\n\n/**\n * Utility to check if target is typeof `HTMLElement`, `Element`, `Node`\n * or find one that matches a selector.\n *\n * @param {Node | string} selector the input selector or target element\n * @param {ParentNode=} parent optional node to look into\n * @return {HTMLElement?} the `HTMLElement` or `querySelector` result\n */\nfunction querySelector(selector, parent) {\n if (isNode(selector)) {\n return selector;\n }\n const lookUp = isNode(parent) ? parent : getDocument();\n\n return lookUp.querySelector(selector);\n}\n\n/**\n * Add class to `HTMLElement.classList`.\n *\n * @param {HTMLElement} element target\n * @param {string} classNAME to add\n * @returns {void}\n */\nfunction addClass(element, classNAME) {\n element.classList.add(classNAME);\n}\n\n/**\n * Check class in `HTMLElement.classList`.\n *\n * @param {HTMLElement} element target\n * @param {string} classNAME to check\n * @returns {boolean}\n */\nfunction hasClass(element, classNAME) {\n return element.classList.contains(classNAME);\n}\n\n/**\n * Remove class from `HTMLElement.classList`.\n *\n * @param {HTMLElement} element target\n * @param {string} classNAME to remove\n * @returns {void}\n */\nfunction removeClass(element, classNAME) {\n element.classList.remove(classNAME);\n}\n\n/**\n * Shortcut for the `Element.dispatchEvent(Event)` method.\n *\n * @param {HTMLElement} element is the target\n * @param {Event} event is the `Event` object\n */\nconst dispatchEvent = (element, event) => element.dispatchEvent(event);\n\n/**\n * A global namespace for 'transitionend' string.\n * @type {string}\n */\nconst transitionEndEvent = 'transitionend';\n\n/**\n * A global namespace for 'transitionDelay' string.\n * @type {string}\n */\nconst transitionDelay = 'transitionDelay';\n\n/**\n * A global namespace for `transitionProperty` string for modern browsers.\n *\n * @type {string}\n */\nconst transitionProperty = 'transitionProperty';\n\n/**\n * Shortcut for `window.getComputedStyle(element).propertyName`\n * static method.\n *\n * * If `element` parameter is not an `HTMLElement`, `getComputedStyle`\n * throws a `ReferenceError`.\n *\n * @param {HTMLElement} element target\n * @param {string} property the css property\n * @return {string} the css property value\n */\nfunction getElementStyle(element, property) {\n const computedStyle = getComputedStyle(element);\n\n // must use camelcase strings,\n // or non-camelcase strings with `getPropertyValue`\n return property.includes('--')\n ? computedStyle.getPropertyValue(property)\n : computedStyle[property];\n}\n\n/**\n * Utility to get the computed `transitionDelay`\n * from Element in miliseconds.\n *\n * @param {HTMLElement} element target\n * @return {number} the value in miliseconds\n */\nfunction getElementTransitionDelay(element) {\n const propertyValue = getElementStyle(element, transitionProperty);\n const delayValue = getElementStyle(element, transitionDelay);\n const delayScale = delayValue.includes('ms') ? /* istanbul ignore next */1 : 1000;\n const duration = propertyValue && propertyValue !== 'none'\n ? parseFloat(delayValue) * delayScale : 0;\n\n return !Number.isNaN(duration) ? duration : /* istanbul ignore next */0;\n}\n\n/**\n * A global namespace for 'transitionDuration' string.\n * @type {string}\n */\nconst transitionDuration = 'transitionDuration';\n\n/**\n * Utility to get the computed `transitionDuration`\n * from Element in miliseconds.\n *\n * @param {HTMLElement} element target\n * @return {number} the value in miliseconds\n */\nfunction getElementTransitionDuration(element) {\n const propertyValue = getElementStyle(element, transitionProperty);\n const durationValue = getElementStyle(element, transitionDuration);\n const durationScale = durationValue.includes('ms') ? /* istanbul ignore next */1 : 1000;\n const duration = propertyValue && propertyValue !== 'none'\n ? parseFloat(durationValue) * durationScale : 0;\n\n return !Number.isNaN(duration) ? duration : /* istanbul ignore next */0;\n}\n\n/**\n * Utility to make sure callbacks are consistently\n * called when transition ends.\n *\n * @param {HTMLElement} element target\n * @param {EventListener} handler `transitionend` callback\n */\nfunction emulateTransitionEnd(element, handler) {\n let called = 0;\n const endEvent = new Event(transitionEndEvent);\n const duration = getElementTransitionDuration(element);\n const delay = getElementTransitionDelay(element);\n\n if (duration) {\n /**\n * Wrap the handler in on -> off callback\n * @type {EventListener} e Event object\n */\n const transitionEndWrapper = (e) => {\n /* istanbul ignore else */\n if (e.target === element) {\n handler.apply(element, [e]);\n element.removeEventListener(transitionEndEvent, transitionEndWrapper);\n called = 1;\n }\n };\n element.addEventListener(transitionEndEvent, transitionEndWrapper);\n setTimeout(() => {\n /* istanbul ignore next */\n if (!called) dispatchEvent(element, endEvent);\n }, duration + delay + 17);\n } else {\n handler.apply(element, [endEvent]);\n }\n}\n\n/**\n * Utility to force re-paint of an `HTMLElement` target.\n *\n * @param {HTMLElement} element is the target\n * @return {number} the `Element.offsetHeight` value\n */\nconst reflow = (element) => element.offsetHeight;\n\n/**\n * Shortcut for `Object.assign()` static method.\n * @param {Record} obj a target object\n * @param {Record} source a source object\n */\nconst ObjectAssign = (obj, source) => Object.assign(obj, source);\n\n/**\n * Checks if an element is an `HTMLElement`.\n * @see https://dom.spec.whatwg.org/#node\n *\n * @param {any} element the target object\n * @returns {boolean} the query result\n */\nconst isHTMLElement = (element) => (element && element.nodeType === 1) || false;\n\n/** @type {Map>>} */\nconst componentData = new Map();\n/**\n * An interface for web components background data.\n * @see https://github.com/thednp/bootstrap.native/blob/master/src/components/base-component.js\n */\nconst Data = {\n /**\n * Sets web components data.\n * @param {HTMLElement} element target element\n * @param {string} component the component's name or a unique key\n * @param {Record} instance the component instance\n */\n set: (element, component, instance) => {\n if (!isHTMLElement(element)) return;\n\n /* istanbul ignore else */\n if (!componentData.has(component)) {\n componentData.set(component, new Map());\n }\n\n const instanceMap = componentData.get(component);\n // not undefined, but defined right above\n instanceMap.set(element, instance);\n },\n\n /**\n * Returns all instances for specified component.\n * @param {string} component the component's name or a unique key\n * @returns {Map>?} all the component instances\n */\n getAllFor: (component) => {\n const instanceMap = componentData.get(component);\n\n return instanceMap || null;\n },\n\n /**\n * Returns the instance associated with the target.\n * @param {HTMLElement} element target element\n * @param {string} component the component's name or a unique key\n * @returns {Record?} the instance\n */\n get: (element, component) => {\n if (!isHTMLElement(element) || !component) return null;\n const allForC = Data.getAllFor(component);\n const instance = element && allForC && allForC.get(element);\n\n return instance || null;\n },\n\n /**\n * Removes web components data.\n * @param {HTMLElement} element target element\n * @param {string} component the component's name or a unique key\n */\n remove: (element, component) => {\n const instanceMap = componentData.get(component);\n if (!instanceMap || !isHTMLElement(element)) return;\n\n instanceMap.delete(element);\n\n /* istanbul ignore else */\n if (instanceMap.size === 0) {\n componentData.delete(component);\n }\n },\n};\n\n/**\n * An alias for `Data.get()`.\n * @type {SHORTY.getInstance}\n */\nconst getInstance = (target, component) => Data.get(target, component);\n\n/** @type {Map} */\nconst TimeCache = new Map();\n/**\n * An interface for one or more `TimerHandler`s per `Element`.\n * @see https://github.com/thednp/navbar.js/\n */\nconst Timer = {\n /**\n * Sets a new timeout timer for an element, or element -> key association.\n * @param {HTMLElement} element target element\n * @param {ReturnType} callback the callback\n * @param {number} delay the execution delay\n * @param {string=} key a unique key\n */\n set: (element, callback, delay, key) => {\n if (!isHTMLElement(element)) return;\n\n /* istanbul ignore else */\n if (key && key.length) {\n /* istanbul ignore else */\n if (!TimeCache.has(element)) {\n TimeCache.set(element, new Map());\n }\n const keyTimers = TimeCache.get(element);\n keyTimers.set(key, setTimeout(callback, delay));\n } else {\n TimeCache.set(element, setTimeout(callback, delay));\n }\n },\n\n /**\n * Returns the timer associated with the target.\n * @param {HTMLElement} element target element\n * @param {string=} key a unique\n * @returns {number?} the timer\n */\n get: (element, key) => {\n if (!isHTMLElement(element)) return null;\n const keyTimers = TimeCache.get(element);\n\n if (key && key.length && keyTimers && keyTimers.get) {\n return keyTimers.get(key) || /* istanbul ignore next */null;\n }\n return keyTimers || null;\n },\n\n /**\n * Clears the element's timer.\n * @param {HTMLElement} element target element\n * @param {string=} key a unique key\n */\n clear: (element, key) => {\n if (!isHTMLElement(element)) return;\n\n if (key && key.length) {\n const keyTimers = TimeCache.get(element);\n /* istanbul ignore else */\n if (keyTimers && keyTimers.get) {\n clearTimeout(keyTimers.get(key));\n keyTimers.delete(key);\n /* istanbul ignore else */\n if (keyTimers.size === 0) {\n TimeCache.delete(element);\n }\n }\n } else {\n clearTimeout(TimeCache.get(element));\n TimeCache.delete(element);\n }\n },\n};\n\n/**\n * Checks if an object is an `Object`.\n *\n * @param {any} obj the target object\n * @returns {boolean} the query result\n */\nconst isObject = (obj) => (typeof obj === 'object') || false;\n\n/**\n * Returns a namespaced `CustomEvent` specific to each component.\n * @param {string} EventType Event.type\n * @param {Record=} config Event.options | Event.properties\n * @returns {SHORTY.OriginalEvent} a new namespaced event\n */\nfunction OriginalEvent(EventType, config) {\n const OriginalCustomEvent = new CustomEvent(EventType, {\n cancelable: true, bubbles: true,\n });\n\n /* istanbul ignore else */\n if (isObject(config)) {\n ObjectAssign(OriginalCustomEvent, config);\n }\n return OriginalCustomEvent;\n}\n\n/** @type {Record} */\nconst EventRegistry = {};\n\n/**\n * The global event listener.\n *\n * @type {EventListener}\n * @this {EventTarget}\n */\nfunction globalListener(e) {\n const that = this;\n const { type } = e;\n\n [...EventRegistry[type]].forEach((elementsMap) => {\n const [element, listenersMap] = elementsMap;\n /* istanbul ignore else */\n if (element === that) {\n [...listenersMap].forEach((listenerMap) => {\n const [listener, options] = listenerMap;\n listener.apply(element, [e]);\n\n if (options && options.once) {\n removeListener(element, type, listener, options);\n }\n });\n }\n });\n}\n\n/**\n * Register a new listener with its options and attach the `globalListener`\n * to the target if this is the first listener.\n *\n * @type {Listener.ListenerAction}\n */\nconst addListener = (element, eventType, listener, options) => {\n // get element listeners first\n if (!EventRegistry[eventType]) {\n EventRegistry[eventType] = new Map();\n }\n const oneEventMap = EventRegistry[eventType];\n\n if (!oneEventMap.has(element)) {\n oneEventMap.set(element, new Map());\n }\n const oneElementMap = oneEventMap.get(element);\n\n // get listeners size\n const { size } = oneElementMap;\n\n // register listener with its options\n oneElementMap.set(listener, options);\n\n // add listener last\n if (!size) {\n element.addEventListener(eventType, globalListener, options);\n }\n};\n\n/**\n * Remove a listener from registry and detach the `globalListener`\n * if no listeners are found in the registry.\n *\n * @type {Listener.ListenerAction}\n */\nconst removeListener = (element, eventType, listener, options) => {\n // get listener first\n const oneEventMap = EventRegistry[eventType];\n const oneElementMap = oneEventMap && oneEventMap.get(element);\n const savedOptions = oneElementMap && oneElementMap.get(listener);\n\n // also recover initial options\n const { options: eventOptions } = savedOptions !== undefined\n ? savedOptions\n : { options };\n\n // unsubscribe second, remove from registry\n if (oneElementMap && oneElementMap.has(listener)) oneElementMap.delete(listener);\n if (oneEventMap && (!oneElementMap || !oneElementMap.size)) oneEventMap.delete(element);\n if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType];\n\n // remove listener last\n /* istanbul ignore else */\n if (!oneElementMap || !oneElementMap.size) {\n element.removeEventListener(eventType, globalListener, eventOptions);\n }\n};\n\n/**\n * Global namespace for most components `collapsing` class.\n * As used by `Collapse` / `Tab`.\n */\nconst collapsingClass = 'collapsing';\n\n/**\n * Global namespace for most components active class.\n */\nconst activeClass = 'active';\n\n/**\n * Global namespace for most components `fade` class.\n */\nconst fadeClass = 'fade';\n\n/**\n * Global namespace for most components `show` class.\n */\nconst showClass = 'show';\n\n/**\n * Global namespace for `Dropdown` types / classes.\n */\nconst dropdownMenuClasses = ['dropdown', 'dropup', 'dropstart', 'dropend'];\n\n/**\n * Global namespace for most components `toggle` option.\n */\nconst dataBsToggle = 'data-bs-toggle';\n\n/** @type {string} */\nconst tabString = 'tab';\n\n/** @type {string} */\nconst tabComponent = 'Tab';\n\n/**\n * Shortcut for `HTMLElement.getAttribute()` method.\n * @param {HTMLElement} element target element\n * @param {string} attribute attribute name\n * @returns {string?} attribute value\n */\nconst getAttribute = (element, attribute) => element.getAttribute(attribute);\n\n/**\n * Global namespace for most components `target` option.\n */\nconst dataBsTarget = 'data-bs-target';\n\n/**\n * Global namespace for most components `parent` option.\n */\nconst dataBsParent = 'data-bs-parent';\n\n/**\n * Global namespace for most components `container` option.\n */\nconst dataBsContainer = 'data-bs-container';\n\n/**\n * Returns the `Element` that THIS one targets\n * via `data-bs-target`, `href`, `data-bs-parent` or `data-bs-container`.\n *\n * @param {HTMLElement} element the target element\n * @returns {HTMLElement?} the query result\n */\nfunction getTargetElement(element) {\n const targetAttr = [dataBsTarget, dataBsParent, dataBsContainer, 'href'];\n const doc = getDocument(element);\n\n return targetAttr.map((att) => {\n const attValue = getAttribute(element, att);\n if (attValue) {\n return att === dataBsParent ? closest(element, attValue) : querySelector(attValue, doc);\n }\n return null;\n }).filter((x) => x)[0];\n}\n\n/**\n * The raw value or a given component option.\n *\n * @typedef {string | HTMLElement | Function | number | boolean | null} niceValue\n */\n\n/**\n * Utility to normalize component options\n *\n * @param {any} value the input value\n * @return {niceValue} the normalized value\n */\nfunction normalizeValue(value) {\n if (['true', true].includes(value)) { // boolean\n // if ('true' === value) { // boolean\n return true;\n }\n\n if (['false', false].includes(value)) { // boolean\n // if ('false' === value) { // boolean\n return false;\n }\n\n if (value === '' || value === 'null') { // null\n return null;\n }\n\n if (value !== '' && !Number.isNaN(+value)) { // number\n return +value;\n }\n\n // string / function / HTMLElement / object\n return value;\n}\n\n/**\n * Shortcut for `Object.keys()` static method.\n * @param {Record} obj a target object\n * @returns {string[]}\n */\nconst ObjectKeys = (obj) => Object.keys(obj);\n\n/**\n * Shortcut for `String.toLowerCase()`.\n *\n * @param {string} source input string\n * @returns {string} lowercase output string\n */\nconst toLowerCase = (source) => source.toLowerCase();\n\n/**\n * Utility to normalize component options.\n *\n * @param {HTMLElement} element target\n * @param {Record} defaultOps component default options\n * @param {Record} inputOps component instance options\n * @param {string=} ns component namespace\n * @return {Record} normalized component options object\n */\nfunction normalizeOptions(element, defaultOps, inputOps, ns) {\n const data = { ...element.dataset };\n /** @type {Record} */\n const normalOps = {};\n /** @type {Record} */\n const dataOps = {};\n const title = 'title';\n\n ObjectKeys(data).forEach((k) => {\n const key = ns && k.includes(ns)\n ? k.replace(ns, '').replace(/[A-Z]/, (match) => toLowerCase(match))\n : k;\n\n dataOps[key] = normalizeValue(data[k]);\n });\n\n ObjectKeys(inputOps).forEach((k) => {\n inputOps[k] = normalizeValue(inputOps[k]);\n });\n\n ObjectKeys(defaultOps).forEach((k) => {\n /* istanbul ignore else */\n if (k in inputOps) {\n normalOps[k] = inputOps[k];\n } else if (k in dataOps) {\n normalOps[k] = dataOps[k];\n } else {\n normalOps[k] = k === title\n ? getAttribute(element, title)\n : defaultOps[k];\n }\n });\n\n return normalOps;\n}\n\nvar version = \"4.2.0\";\n\nconst Version = version;\n\n/* Native JavaScript for Bootstrap 5 | Base Component\n----------------------------------------------------- */\n\n/** Returns a new `BaseComponent` instance. */\nclass BaseComponent {\n /**\n * @param {HTMLElement | string} target `Element` or selector string\n * @param {BSN.ComponentOptions=} config component instance options\n */\n constructor(target, config) {\n const self = this;\n const element = querySelector(target);\n\n if (!element) {\n throw Error(`${self.name} Error: \"${target}\" is not a valid selector.`);\n }\n\n /** @static @type {BSN.ComponentOptions} */\n self.options = {};\n\n const prevInstance = Data.get(element, self.name);\n if (prevInstance) prevInstance.dispose();\n\n /** @type {HTMLElement} */\n self.element = element;\n\n /* istanbul ignore else */\n if (self.defaults && ObjectKeys(self.defaults).length) {\n self.options = normalizeOptions(element, self.defaults, (config || {}), 'bs');\n }\n\n Data.set(element, self.name, self);\n }\n\n /* eslint-disable */\n /* istanbul ignore next */\n /** @static */\n get version() { return Version; }\n\n /* eslint-enable */\n /* istanbul ignore next */\n /** @static */\n get name() { return this.constructor.name; }\n\n /* istanbul ignore next */\n /** @static */\n get defaults() { return this.constructor.defaults; }\n\n /**\n * Removes component from target element;\n */\n dispose() {\n const self = this;\n Data.remove(self.element, self.name);\n ObjectKeys(self).forEach((prop) => { self[prop] = null; });\n }\n}\n\n/* Native JavaScript for Bootstrap 5 | Tab\n------------------------------------------ */\n\n// TAB PRIVATE GC\n// ================\nconst tabSelector = `[${dataBsToggle}=\"${tabString}\"]`;\n\n/**\n * Static method which returns an existing `Tab` instance associated\n * to a target `Element`.\n *\n * @type {BSN.GetInstance}\n */\nconst getTabInstance = (element) => getInstance(element, tabComponent);\n\n/**\n * A `Tab` initialization callback.\n * @type {BSN.InitCallback}\n */\nconst tabInitCallback = (element) => new Tab(element);\n\n// TAB CUSTOM EVENTS\n// =================\nconst showTabEvent = OriginalEvent(`show.bs.${tabString}`);\nconst shownTabEvent = OriginalEvent(`shown.bs.${tabString}`);\nconst hideTabEvent = OriginalEvent(`hide.bs.${tabString}`);\nconst hiddenTabEvent = OriginalEvent(`hidden.bs.${tabString}`);\n\n/**\n * Stores the current active tab and its content\n * for a given `.nav` element.\n * @type {Map}\n */\nconst tabPrivate = new Map();\n\n// TAB PRIVATE METHODS\n// ===================\n/**\n * Executes after tab transition has finished.\n * @param {Tab} self the `Tab` instance\n */\nfunction triggerTabEnd(self) {\n const { tabContent, nav } = self;\n\n /* istanbul ignore else */\n if (tabContent && hasClass(tabContent, collapsingClass)) {\n tabContent.style.height = '';\n removeClass(tabContent, collapsingClass);\n }\n\n /* istanbul ignore else */\n if (nav) Timer.clear(nav);\n}\n\n/**\n * Executes before showing the tab content.\n * @param {Tab} self the `Tab` instance\n */\nfunction triggerTabShow(self) {\n const {\n element, tabContent, content: nextContent, nav,\n } = self;\n const { tab } = nav && tabPrivate.get(nav);\n\n /* istanbul ignore else */\n if (tabContent && hasClass(nextContent, fadeClass)) {\n const { currentHeight, nextHeight } = tabPrivate.get(element);\n if (currentHeight === nextHeight) {\n triggerTabEnd(self);\n } else {\n // enables height animation\n setTimeout(() => {\n tabContent.style.height = `${nextHeight}px`;\n reflow(tabContent);\n emulateTransitionEnd(tabContent, () => triggerTabEnd(self));\n }, 50);\n }\n } else if (nav) Timer.clear(nav);\n\n shownTabEvent.relatedTarget = tab;\n dispatchEvent(element, shownTabEvent);\n}\n\n/**\n * Executes before hiding the tab.\n * @param {Tab} self the `Tab` instance\n */\nfunction triggerTabHide(self) {\n const {\n element, content: nextContent, tabContent, nav,\n } = self;\n const { tab, content } = nav && tabPrivate.get(nav);\n let currentHeight = 0;\n\n /* istanbul ignore else */\n if (tabContent && hasClass(nextContent, fadeClass)) {\n [content, nextContent].forEach((c) => {\n addClass(c, 'overflow-hidden');\n });\n currentHeight = content.scrollHeight || /* istanbul ignore next */0;\n }\n\n // update relatedTarget and dispatch event\n showTabEvent.relatedTarget = tab;\n hiddenTabEvent.relatedTarget = element;\n dispatchEvent(element, showTabEvent);\n if (showTabEvent.defaultPrevented) return;\n\n addClass(nextContent, activeClass);\n removeClass(content, activeClass);\n\n /* istanbul ignore else */\n if (tabContent && hasClass(nextContent, fadeClass)) {\n const nextHeight = nextContent.scrollHeight;\n tabPrivate.set(element, { currentHeight, nextHeight });\n\n addClass(tabContent, collapsingClass);\n tabContent.style.height = `${currentHeight}px`;\n reflow(tabContent);\n [content, nextContent].forEach((c) => {\n removeClass(c, 'overflow-hidden');\n });\n }\n\n if (nextContent && hasClass(nextContent, fadeClass)) {\n setTimeout(() => {\n addClass(nextContent, showClass);\n emulateTransitionEnd(nextContent, () => {\n triggerTabShow(self);\n });\n }, 1);\n } else {\n addClass(nextContent, showClass);\n triggerTabShow(self);\n }\n\n dispatchEvent(tab, hiddenTabEvent);\n}\n\n/**\n * Returns the current active tab and its target content.\n * @param {Tab} self the `Tab` instance\n * @returns {Record} the query result\n */\nfunction getActiveTab(self) {\n const { nav } = self;\n\n const activeTabs = getElementsByClassName(activeClass, nav);\n /** @type {(HTMLElement)=} */\n let tab;\n /* istanbul ignore else */\n if (activeTabs.length === 1\n && !dropdownMenuClasses.some((c) => hasClass(activeTabs[0].parentElement, c))) {\n [tab] = activeTabs;\n } else if (activeTabs.length > 1) {\n tab = activeTabs[activeTabs.length - 1];\n }\n const content = tab ? getTargetElement(tab) : null;\n return { tab, content };\n}\n\n/**\n * Returns a parent dropdown.\n * @param {HTMLElement} element the `Tab` element\n * @returns {HTMLElement?} the parent dropdown\n */\nfunction getParentDropdown(element) {\n const dropdown = closest(element, `.${dropdownMenuClasses.join(',.')}`);\n return dropdown ? querySelector(`.${dropdownMenuClasses[0]}-toggle`, dropdown) : null;\n}\n\n/**\n * Toggles on/off the `click` event listener.\n * @param {Tab} self the `Tab` instance\n * @param {boolean=} add when `true`, event listener is added\n */\nfunction toggleTabHandler(self, add) {\n const action = add ? addListener : removeListener;\n action(self.element, mouseclickEvent, tabClickHandler);\n}\n\n// TAB EVENT HANDLER\n// =================\n/**\n * Handles the `click` event listener.\n * @this {HTMLElement}\n * @param {MouseEvent} e the `Event` object\n */\nfunction tabClickHandler(e) {\n const self = getTabInstance(this);\n /* istanbul ignore next: must filter */\n if (!self) return;\n e.preventDefault();\n\n self.show();\n}\n\n// TAB DEFINITION\n// ==============\n/** Creates a new `Tab` instance. */\nclass Tab extends BaseComponent {\n /**\n * @param {HTMLElement | string} target the target element\n */\n constructor(target) {\n super(target);\n // bind\n const self = this;\n\n // initialization element\n const { element } = self;\n const content = getTargetElement(element);\n\n // no point initializing a tab without a corresponding content\n if (!content) return;\n\n const nav = closest(element, '.nav');\n const container = closest(content, '.tab-content');\n\n /** @type {HTMLElement?} */\n self.nav = nav;\n /** @type {HTMLElement} */\n self.content = content;\n /** @type {HTMLElement?} */\n self.tabContent = container;\n\n // event targets\n /** @type {HTMLElement?} */\n self.dropdown = getParentDropdown(element);\n\n // show first Tab instance of none is shown\n // suggested on #432\n const { tab } = getActiveTab(self);\n if (nav && !tab) {\n const firstTab = querySelector(tabSelector, nav);\n const firstTabContent = firstTab && getTargetElement(firstTab);\n\n /* istanbul ignore else */\n if (firstTabContent) {\n addClass(firstTab, activeClass);\n addClass(firstTabContent, showClass);\n addClass(firstTabContent, activeClass);\n setAttribute(element, ariaSelected, 'true');\n }\n }\n\n // add event listener\n toggleTabHandler(self, true);\n }\n\n /* eslint-disable */\n /**\n * Returns component name string.\n */ \n get name() { return tabComponent; }\n /* eslint-enable */\n\n // TAB PUBLIC METHODS\n // ==================\n /** Shows the tab to the user. */\n show() {\n const self = this;\n const {\n element, content: nextContent, nav, dropdown,\n } = self;\n\n /* istanbul ignore else */\n if (!(nav && Timer.get(nav)) && !hasClass(element, activeClass)) {\n const { tab, content } = getActiveTab(self);\n\n /* istanbul ignore else */\n if (nav) tabPrivate.set(nav, { tab, content });\n\n // update relatedTarget and dispatch\n hideTabEvent.relatedTarget = element;\n\n dispatchEvent(tab, hideTabEvent);\n if (hideTabEvent.defaultPrevented) return;\n\n addClass(element, activeClass);\n setAttribute(element, ariaSelected, 'true');\n\n const activeDropdown = getParentDropdown(tab);\n if (activeDropdown && hasClass(activeDropdown, activeClass)) {\n removeClass(activeDropdown, activeClass);\n }\n\n /* istanbul ignore else */\n if (nav) {\n const toggleTab = () => {\n removeClass(tab, activeClass);\n setAttribute(tab, ariaSelected, 'false');\n if (dropdown && !hasClass(dropdown, activeClass)) addClass(dropdown, activeClass);\n };\n\n if (hasClass(content, fadeClass) || hasClass(nextContent, fadeClass)) {\n Timer.set(nav, toggleTab, 1);\n } else toggleTab();\n }\n\n removeClass(content, showClass);\n if (hasClass(content, fadeClass)) {\n emulateTransitionEnd(content, () => triggerTabHide(self));\n } else {\n triggerTabHide(self);\n }\n }\n }\n\n /** Removes the `Tab` component from the target element. */\n dispose() {\n toggleTabHandler(this);\n super.dispose();\n }\n}\n\nObjectAssign(Tab, {\n selector: tabSelector,\n init: tabInitCallback,\n getInstance: getTabInstance,\n});\n\nexport { Tab as default };\n","const Urls = {\r\n\tApi: {\r\n\t\tcart: '/my/cart',\r\n\t\tproduct: '/my/api/2/shopping-cart/active/product',\r\n\t\tproduct_options_manual: function( product_id ) {\r\n\t\t\treturn '/my/api/2/shopping-cart/active/product/' + product_id + '/option/manual';\r\n\t\t},\r\n\t\tproduct_related: '/my/api/2/shopping-cart/active/related',\r\n\t\tup_sale_promos_apply: function( promo_id ) {\r\n\t\t\treturn '/my/api/2/shopping-cart/active/upsale-promos/' + promo_id + '/apply';\r\n\t\t},\r\n\t\tget_template_url: function( template_url ) {\r\n\t\t\t//return '/my/api/2/templates?name=' + template_url;\r\n\t\t\treturn template_url;\r\n\t\t},\r\n\t\tbundle: '/my/api/2/shopping-cart/active/bundle'\r\n\t},\r\n\tTemplates: {\r\n\t\tapp: '/my/api/2/templates?name=modules/add-to-cart/app.html',\r\n\t\tmodal: '/my/api/2/templates?name=modules/add-to-cart/modal.html',\r\n\t\taccessory_modal: '/my/api/2/templates?name=modules/add-to-cart/accessory-modal.html',\r\n\t\tsticky_footer: '/my/api/2/templates?name=modules/add-to-cart/sticky-footer.html',\r\n\t\tmessage: '/my/api/2/templates?name=modules/add-to-cart/message.html'\r\n\t}\r\n};\r\n\r\nexport default Urls;","function fetch_status(response) {\r\n\tif (response.ok) {\r\n\t\treturn Promise.resolve(response);\r\n\t} else {\r\n\t\treturn Promise.reject(response);\r\n\t}\r\n}\r\n\r\nfunction fetch_json(response) {\r\n\treturn response.json();\r\n}\r\n\r\nfunction fetch_text(response) {\r\n\treturn response.text();\r\n}\r\n\r\nfunction fetch_error_message(error) {\r\n\tlet message = 'Something went wrong...';\r\n\tlet sessionErr = false;\r\n\r\n\tif (error.includes('Message')) {\r\n\t\terror = JSON.parse(error);\r\n\t}\r\n\r\n\tif (error.Type && error.Type === 'SessionVerificationRequiredException') {\r\n\t\tsessionErr = true;\r\n\t\tif (window.verifySession) {\r\n\t\t\twindow.verifySession();\r\n\t\t}\r\n\t} else if (!sessionErr) {\r\n\t\tif (error && typeof error === 'object') {\r\n\t\t\tif (error.exception && error.exception.Message) {\r\n\t\t\t\tmessage = error.exception.Message;\r\n\t\t\t} else if (error.responseJSON && error.responseJSON.Message) {\r\n\t\t\t\tmessage = error.responseJSON.Message;\r\n\t\t\t}\r\n\t\t}\r\n\t\tthrow new Error(message);\r\n\t}\r\n}\r\n\r\nfunction fetch_error(error, custom_error) {\r\n\tif (custom_error) {\r\n\t\treturn error.json();\r\n\t} else {\r\n\t\tif (error.name !== 'AbortError') {\r\n\t\t\tfetch_error_message(error);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nfunction fetcher(url = '', options = {}) {\r\n\tconst data = options.is_text ? fetch_text : fetch_json;\r\n\r\n\treturn options.custom_error ?\r\n\t\tfetch(url, options).then(fetch_status).then(data) :\r\n\t\tfetch(url, options).then(fetch_status).then(data).catch(error => {\r\n\t\t\tif (typeof error === 'string') {\r\n\t\t\t\tthrow new Error(error);\r\n\t\t\t} else {\r\n\t\t\t\tif (error.text && typeof error.text === 'function') {\r\n\t\t\t\t\terror.text()\r\n\t\t\t\t\t\t.then(error_text => {\r\n\t\t\t\t\t\t\tfetch_error_message(error_text);\r\n\t\t\t\t\t\t});\r\n\t\t\t\t} else if (options.signal) {\r\n\t\t\t\t\tif (!options.signal.aborted) {\r\n\t\t\t\t\t\tthrow new Error(error);\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new Error(error);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n}\r\n\r\nexport {fetch_status, fetch_json, fetch_text, fetch_error_message, fetch_error, fetcher};","// Do not use this on production.\r\n// All templates should be precompiled.\r\n\r\nimport {fetch_status, fetch_text, fetch_error, fetch_error_message} from './fetch.js';\r\n\r\nvar Template = {\r\n\tcompiled: {},\r\n\tload: function (template_url) {\r\n\t\tvar t = null;\r\n\t\tif (Object.prototype.hasOwnProperty.call(this.compiled, template_url)) {\r\n\t\t\tt = this.compiled[template_url];\r\n\t\t\treturn new Promise(function (resolve) {\r\n\t\t\t\treturn resolve(t);\r\n\t\t\t});\r\n\t\t} else {\r\n\t\t\treturn fetch(template_url)\r\n\t\t\t\t.then(fetch_status)\r\n\t\t\t\t.then(fetch_text)\r\n\t\t\t\t.then(function (text) {\r\n\t\t\t\t\tt = this.compile(text, template_url);\r\n\t\t\t\t\treturn t;\r\n\t\t\t\t}.bind(this))\r\n\t\t\t\t.catch(error => {\r\n\t\t\t\t\terror.text()\r\n\t\t\t\t\t\t.then(error_text => {\r\n\t\t\t\t\t\t\tfetch_error_message(error_text);\r\n\t\t\t\t\t\t});\r\n\t\t\t\t});\r\n\t\t}\r\n\t},\r\n\tcompile: function(template, template_name) {\r\n\t\tif (!this.compiled[template_name]) {\r\n\t\t\tthis.compiled[template_name] = Handlebars.compile(template);\r\n\t\t}\r\n\t\treturn this.compiled[template_name];\r\n\t},\r\n\trender: function(template, context) {\r\n\t\treturn template(context);\r\n\t}\r\n};\r\n\r\nexport default Template;","// Do not use this on production.\r\n// All templates should be precompiled.\r\n\r\nimport Template from './template.js';\r\n\r\nexport default class BaseView {\r\n\tconstructor (el) {\r\n\t\tif (el instanceof Element) {\r\n\t\t\tthis.el = el;\r\n\t\t} else {\r\n\t\t\tthis.el = document.querySelector(el);\r\n\t\t}\r\n\t}\r\n\r\n\tget_context () {\r\n\t\treturn {};\r\n\t}\r\n\r\n\tload_template () {\r\n\t\tif (this.template_url) {\r\n\t\t\treturn Template.load(this.template_url);\r\n\t\t} else {\r\n\t\t\tthrow new Error('BaseView.load_template require template_url');\r\n\t\t}\r\n\t}\r\n\r\n\trender () {\r\n\t\tthis.load_template()\r\n\t\t\t.then(function(template) {\r\n\t\t\t\tthis.render_template(template);\r\n\t\t\t}.bind(this));\r\n\t}\r\n\r\n\trender_template (template) {\r\n\t\tvar context = this.get_context();\r\n\t\tif (this.el) {\r\n\t\t\tthis.el.innerHTML = Template.render(template, context);\r\n\t\t\tthis.rendered();\r\n\t\t}\r\n\t}\r\n\r\n\trender_partial (template, context) {\r\n\t\tif (template && context) {\r\n\t\t\treturn Template.render(template, context);\r\n\t\t} else {\r\n\t\t\tthrow new Error('BaseView.render_partial require templates and context objects');\r\n\t\t}\r\n\t}\r\n\r\n\trendered () {\r\n\t\tconst e = new Event('rendered');\r\n\t\tthis.el.dispatchEvent(e);\r\n\t\tthis.after_render();\r\n\t}\r\n\r\n\tafter_render () {\r\n\t\tif (this.el) {\r\n\t\t\tthis.el.dispatchEvent(new CustomEvent('rendered'));\r\n\t\t}\r\n\t\tthis.bindSelectors();\r\n\t\treturn true;\r\n\t}\r\n\r\n\tbindSelectors() {\r\n\t\tif (this.selectors && !!Object.keys(this.selectors).length) {\r\n\t\t\tObject.keys(this.selectors).forEach(key => {\r\n\t\t\t\tthis[`${key}Element`] = this.el ?\r\n\t\t\t\t\tthis.el.querySelector(this.selectors[key]) :\r\n\t\t\t\t\tdocument.querySelector(this.selectors[key]) ;\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tparse_json (json) {\r\n\t\treturn JSON.parse(JSON.stringify(json));\r\n\t}\r\n\r\n\thtml_render (context) {\r\n\t\treturn this.load_template()\r\n\t\t\t.then(template => {\r\n\t\t\t\tif (template && context) {\r\n\t\t\t\t\tconst html = Template.render(template, context);\r\n\r\n\t\t\t\t\treturn Promise.resolve(html);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new Error('BaseView.html_render require templates and context objects');\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t}\r\n}\r\n","import Urls from '../urls.js';\r\nimport { debounce } from '../../../../scripts/utils/debounce.js';\r\nimport BaseView from '../../../../scripts/utils/base-view.js';\r\n\r\nexport default class AddToCartStickyFooterView extends BaseView {\r\n\tconstructor({\r\n\t\tcart_product,\r\n\t\tallow_duplicates,\r\n\t\tel,\r\n\t\tadd_to_cart_el,\r\n\t\tis_accessory\r\n\t}) {\r\n\t\tsuper(el);\r\n\t\tthis.template_url = Urls.Templates.sticky_footer;\r\n\t\tthis.parent_element = document.querySelector(el);\r\n\t\tthis.add_to_cart_element = document.querySelector(add_to_cart_el);\r\n\t\tthis.allow_duplicates = allow_duplicates;\r\n\t\tthis.in_cart = cart_product && !allow_duplicates;\r\n\t\tthis.is_accessory = is_accessory;\r\n\r\n\t\tthis.footer_element = null;\r\n\t\tthis.btn_add_to_cart = null;\r\n\r\n\t\tthis.debouncingFooterVisibility = debounce(() => this.handleFooterVisibility(), 100);\r\n\r\n\t\tif (this.add_to_cart_element && !this.is_accessory) {\r\n\t\t\tthis.render();\r\n\t\t\tthis.bindCartEvents();\r\n\t\t}\r\n\t}\r\n\r\n\tget_context () {\r\n\t\treturn {\r\n\t\t\tin_cart: this.in_cart,\r\n\t\t\tpath: window.location.pathname\r\n\t\t};\r\n\t}\r\n\r\n\tafter_render () {\r\n\t\tthis.initSelectors();\r\n\t}\r\n\r\n\tinitSelectors() {\r\n\t\tthis.removeListeners();\r\n\t\tthis.footer_element = document.querySelector('.cart-sticky-footer');\r\n\t\tthis.btn_add_to_cart = this.footer_element.querySelector('button[name=\"addtocart\"]');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners() {\r\n\t\tthis.addScrollResizeHandler();\r\n\r\n\t\tif (this.btn_add_to_cart) {\r\n\t\t\tthis.btn_add_to_cart.addEventListener('click', this.addToCart.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tremoveListeners() {\r\n\t\tthis.removeScrollResizeHandler();\r\n\r\n\t\tif (this.btn_add_to_cart) {\r\n\t\t\tthis.btn_add_to_cart.removeEventListener('click', this.addToCart.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\taddToCart() {\r\n\t\tthis.btn_add_to_cart.setAttribute('disabled', true);\r\n\t\tthis.btn_add_to_cart.classList.add('progress');\r\n\t\tconst add_product_event = new CustomEvent('[add-to-cart]:add-product');\r\n\t\tdocument.dispatchEvent(add_product_event);\r\n\t}\r\n\r\n\tbindCartEvents() {\r\n\t\tdocument.addEventListener('[add-to-cart]:product-added', () => {\r\n\t\t\tif (!this.allow_duplicates) {\r\n\t\t\t\tthis.in_cart = true;\r\n\t\t\t}\r\n\t\t\tthis.render();\r\n\t\t});\r\n\t}\r\n\r\n\taddScrollResizeHandler() {\r\n\t\t['scroll', 'resize'].forEach(event_name => {\r\n\t\t\twindow.addEventListener(event_name, this.debouncingFooterVisibility);\r\n\t\t});\r\n\t}\r\n\r\n\tremoveScrollResizeHandler() {\r\n\t\t['scroll', 'resize'].forEach(event_name => {\r\n\t\t\twindow.removeEventListener(event_name, this.debouncingFooterVisibility);\r\n\t\t});\r\n\t}\r\n\r\n\thandleFooterVisibility() {\r\n\t\tif (this.add_to_cart_element.getBoundingClientRect().top + window.pageYOffset + this.add_to_cart_element.clientHeight < window.pageYOffset) {\r\n\t\t\tif (this.footer_element.classList.contains('hide')) {\r\n\t\t\t\tthis.footer_element.classList.remove('hide');\r\n\t\t\t}\r\n\t\t\tthis.footer_element.classList.add('fixed-bottom');\r\n\t\t\tthis.footer_element.classList.remove('slide-down');\r\n\r\n\t\t\tif ((document.body.scrollHeight - window.innerHeight) === window.pageYOffset) {\r\n\t\t\t\tthis.footer_element.classList.add('slide-down');\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tthis.footer_element.classList.add('slide-down');\r\n\t\t\tthis.footer_element.classList.remove('fixed-bottom');\r\n\t\t}\r\n\t}\r\n}","export function debounce (func, wait, immediate) {\r\n\tvar timeout;\r\n\treturn function() {\r\n\t\tvar context = this, args = arguments;\r\n\t\tvar later = function() {\r\n\t\t\ttimeout = null;\r\n\t\t\tif (!immediate) func.apply(context, args);\r\n\t\t};\r\n\t\tvar callNow = immediate && !timeout;\r\n\t\tclearTimeout(timeout);\r\n\t\ttimeout = setTimeout(later, wait);\r\n\t\tif (callNow) func.apply(context, args);\r\n\t};\r\n}\r\n","import Urls from '../urls.js';\r\nimport { fetch_status, fetch_json, fetch_error, fetcher } from '../../../../scripts/utils/fetch.js';\r\nimport BaseView from '../../../../scripts/utils/base-view.js';\r\n\r\nexport default class AddToCartModalView extends BaseView {\r\n\tconstructor({ adv_source, cart_product, el }) {\r\n\t\tsuper(el);\r\n\t\tthis.template_url = Urls.Templates.modal;\r\n\t\tthis.adv_source = adv_source;\r\n\t\tthis.cart_product = cart_product;\r\n\t\tthis.modal_selector = '.app-product-cart-modal';\r\n\t\tthis.modal = null;\r\n\t\tthis.related = false;\r\n\t\tthis.recommended = null;\r\n\t\tthis.upsale_promos = null;\r\n\t\tthis.getRelatedProduct();\r\n\t}\r\n\r\n\tget_context () {\r\n\t\treturn {\r\n\t\t\trelated: this.related,\r\n\t\t\tsettings: window.SiteSettings || {},\r\n\t\t\tcart_url: Urls.Api.cart,\r\n\t\t\tcart_product: this.cart_product,\r\n\t\t\tupsale_promos: this.upsale_promos,\r\n\t\t\trecommended: this.recommended\r\n\t\t};\r\n\t}\r\n\r\n\tafter_render () {\r\n\t\tthis.initSelectors();\r\n\t\tthis.once_rendered();\r\n\t\tthis.renderRelatedElements();\r\n\t}\r\n\r\n\tonce_rendered () {}\r\n\r\n\tshow_modal (cart_product) {\r\n\t\tthis.cart_product = cart_product;\r\n\t\tthis.modal = document.querySelector(this.modal_selector);\r\n\t\tthis.modal.open();\r\n\t}\r\n\r\n\thide () {\r\n\t\tthis.el.classList.add('d-none');\r\n\t}\r\n\r\n\tgetRelatedProduct () {\r\n\t\tfetcher(Urls.Api.product_related)\r\n\t\t\t.then(data => {\r\n\t\t\t\tconst upsale_promos = data.UpSalePromos;\r\n\t\t\t\tlet product_upsale_promos = null;\r\n\t\t\t\tif (upsale_promos.length > 0) {\r\n\t\t\t\t\tproduct_upsale_promos = upsale_promos.filter((promo) => {\r\n\t\t\t\t\t\treturn promo.ShoppingCartProductId === this.cart_product.Id;\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t\tthis.upsale_promos = product_upsale_promos;\r\n\t\t\t\tthis.recommended = data.Recommendation;\r\n\t\t\t\tthis.render();\r\n\t\t\t});\r\n\t}\r\n\r\n\taddRecommended (event) {\r\n\t\tconst btn_element = event.currentTarget;\r\n\t\tif (!btn_element.classList.contains('in-cart') ) {\r\n\t\t\tconst product_data = {\r\n\t\t\t\tProductId: btn_element.getAttribute('data-id'),\r\n\t\t\t\tParentId: null,\r\n\t\t\t\tQty: 1,\r\n\t\t\t\tDefaultOptions: btn_element.getAttribute('data-default-options' ) ? true : false\r\n\t\t\t};\r\n\t\t\tif (this.adv_source) {\r\n\t\t\t\tproduct_data.Source = 'ad';\r\n\t\t\t}\r\n\t\t\tbtn_element.setAttribute('disabled', true);\r\n\t\t\tbtn_element.classList.add('progress');\r\n\t\t\tfetch(Urls.Api.product, {\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t\t},\r\n\t\t\t\tmethod: 'post',\r\n\t\t\t\tbody: JSON.stringify(product_data)\r\n\t\t\t})\r\n\t\t\t.then(fetch_status)\r\n\t\t\t.then(fetch_json)\r\n\t\t\t.then(response => {\r\n\t\t\t\tconst data = Object.assign(response, { source: 'Product Cart Modal', sku: this.cart_product.Sku });\r\n\t\t\t\tconst product_added_event = new CustomEvent('[add-to-cart]:product-added', { detail: data });\r\n\t\t\t\tbtn_element.classList.add('in-cart');\r\n\t\t\t\tbtn_element.textContent = 'In cart';\r\n\t\t\t\tdocument.dispatchEvent(product_added_event);\r\n\t\t\t})\r\n\t\t\t.catch(error => {\r\n\t\t\t\tfetch_error(error, true)\r\n\t\t\t\t\t.then(error_json => {\r\n\t\t\t\t\t\tthis.showError(error_json);\r\n\t\t\t\t\t});\r\n\t\t\t})\r\n\t\t\t.finally(() => {\r\n\t\t\t\tbtn_element.removeAttribute('disabled');\r\n\t\t\t\tbtn_element.classList.remove('progress');\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tsaveQty (qty) {\r\n\t\treturn fetch(Urls.Api.product, {\r\n\t\t\tmethod: 'post',\r\n\t\t\tbody: JSON.stringify(Object.assign(this.cart_product, { Qty: qty }))\r\n\t\t})\r\n\t\t.then(fetch_status)\r\n\t\t.catch(error => {\r\n\t\t\tfetch_error(error, true)\r\n\t\t\t\t.then(error_json => {\r\n\t\t\t\t\tthis.showError(error_json);\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t}\r\n\r\n\taddPromo (event) {\r\n\t\tconst btn_element = event.currentTarget;\r\n\t\tif (!btn_element.classList.contains('in-cart')) {\r\n\t\t\tconst cart_qty = btn_element.getAttribute('data-cart-qty');\r\n\t\t\tbtn_element.setAttribute('disabled', true);\r\n\t\t\tbtn_element.classList.add('progress');\r\n\t\t\tif (this.cart_product.Qty < cart_qty) {\r\n\t\t\t\tthis.saveQty(cart_qty)\r\n\t\t\t\t\t.then(() => {\r\n\t\t\t\t\t\tthis.savePromo(btn_element.getAttribute('data-id'))\r\n\t\t\t\t\t\t\t.then(() => {\r\n\t\t\t\t\t\t\t\tbtn_element.classList.remove('progress');\r\n\t\t\t\t\t\t\t\tbtn_element.classList.add('in-cart');\r\n\t\t\t\t\t\t\t\tbtn_element.textContent = 'In cart';\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t});\r\n\t\t\t} else {\r\n\t\t\t\tthis.savePromo(btn_element.getAttribute('data-id'))\r\n\t\t\t\t\t.then(fetch_status)\r\n\t\t\t\t\t.then(fetch_json)\r\n\t\t\t\t\t.then(response => {\r\n\t\t\t\t\t\tbtn_element.classList.remove('progress');\r\n\t\t\t\t\t\tbtn_element.classList.add('in-cart');\r\n\t\t\t\t\t\tbtn_element.textContent = 'In cart';\r\n\t\t\t\t\t\tconst data = Object.assign(response, { source: 'Product Cart Modal Upsale Autopromo' });\r\n\t\t\t\t\t\tconst product_added_event = new CustomEvent('[add-to-cart]:product-added', { detail: data });\r\n\t\t\t\t\t\tdocument.dispatchEvent(product_added_event);\r\n\t\t\t\t\t} );\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tsavePromo (promo_id) {\r\n\t\tconst url = Urls.Api.up_sale_promos_apply(promo_id);\r\n\t\treturn fetch(url, {\r\n\t\t\tmethod: 'post'\r\n\t\t\t})\r\n\t\t\t.catch(error => {\r\n\t\t\t\tfetch_error(error, true)\r\n\t\t\t\t\t.then(error_json => {\r\n\t\t\t\t\t\tthis.showError(error_json);\r\n\t\t\t\t\t});\r\n\t\t\t});\r\n\t}\r\n\r\n\tshowError (error) {\r\n\t\tlet message = 'Something went wrong...';\r\n\t\tif (error) {\r\n\t\t\tmessage = error.responseJSON.Message ? error.Message : error.responseJSON.Message;\r\n\t\t}\r\n\t\tif (this.message_element) {\r\n\t\t\tthis.message_element.querySelector('p').textContent = message;\r\n\t\t\tthis.message_element.classList.remove('hide');\r\n\t\t} else {\r\n\t\t\twindow.alert(message);\r\n\t\t}\r\n\t}\r\n\r\n\thideError () {\r\n\t\tthis.message_element.classList.add('hide');\r\n\t}\r\n\r\n\trenderRelatedElements () {\r\n\t\tthis.html_render({\r\n\t\t\trelated: true,\r\n\t\t\tupsale_promos: this.upsale_promos,\r\n\t\t\trecommended: this.recommended\r\n\t\t}).then(html => {\r\n\t\t\tconst root = document.createElement('div');\r\n\t\t\tlet accessories_element = null;\r\n\r\n\t\t\troot.innerHTML = html;\r\n\t\t\taccessories_element = root.querySelector('.cart-product-accessories');\r\n\t\t\tthis.related_element.innerHTML = '';\r\n\t\t\tif (accessories_element) {\r\n\t\t\t\tthis.related_element.append(accessories_element);\r\n\t\t\t}\r\n\r\n\t\t\tthis.initSelectors();\r\n\t\t});\r\n\t}\r\n\r\n\thideModal () {\r\n\t\tthis.modal.close();\r\n\t}\r\n\r\n\tinitSelectors () {\r\n\t\tthis.removeListeners();\r\n\t\tthis.related_element = document.querySelector('.related');\r\n\t\tthis.message_element = document.querySelector('.message');\r\n\t\tthis.add_promo_btns = document.querySelectorAll('.cart-product-upsales [name=\"add-promo\"]');\r\n\t\tthis.add_recommended_btns = document.querySelectorAll('.cart-product-accessories [name=\"add-recommended\"]');\r\n\t\tthis.modal_btn = document.querySelector('.modal-message .btn');\r\n\t\tthis.close_modal_btn = document.querySelector('.js-cart-popup-dismiss');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tif (this.add_promo_btns) {\r\n\t\t\tthis.add_promo_btns.forEach(element => element.addEventListener('click', this.addPromo.bind(this)));\r\n\t\t}\r\n\t\tif (this.add_recommended_btns) {\r\n\t\t\tthis.add_recommended_btns.forEach(element => element.addEventListener('click', this.addRecommended.bind(this)));\r\n\t\t}\r\n\t\tif (this.modal_btn) {\r\n\t\t\tthis.modal_btn.addEventListener('click', this.hideError);\r\n\t\t}\r\n\t\tif (this.close_modal_btn) {\r\n\t\t\tthis.close_modal_btn.addEventListener('click', this.hideModal.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.add_promo_btns) {\r\n\t\t\tthis.add_promo_btns.forEach(element => element.removeEventListener('click', this.addPromo.bind(this)));\r\n\t\t}\r\n\t\tif (this.add_recommended_btns) {\r\n\t\t\tthis.add_recommended_btns.forEach(element => element.removeEventListener('click', this.addRecommended.bind(this)));\r\n\t\t}\r\n\t\tif (this.modal_btn) {\r\n\t\t\tthis.modal_btn.removeEventListener('click', this.hideError);\r\n\t\t}\r\n\t\tif (this.close_modal_btn) {\r\n\t\t\tthis.close_modal_btn.removeEventListener('click', this.hideModal.bind(this));\r\n\t\t}\r\n\t}\r\n}","import BaseView from './base-view.js';\r\n\r\nexport default class ModalView extends BaseView {\r\n\tconstructor({ el, modal_selector, navigation, modal_content }) {\r\n\t\tsuper(el);\r\n\r\n\t\tif (!modal_selector) {\r\n\t\t\tthrow new Error('Missing \"modal_selector\"!');\r\n\t\t}\r\n\r\n\t\tthis.navigation = navigation;\r\n\t\tthis.modal_selector = modal_selector;\r\n\t\tthis.modal_content = modal_content || null;\r\n\t\tthis.modal = null;\r\n\t}\r\n\tget_context () {\r\n\t\treturn {\r\n\t\t\tnavigation: this.navigation,\r\n\t\t\tcontent: this.modal_content\r\n\t\t};\r\n\t}\r\n\tafter_render () {\r\n\t\tthis.once_rendered();\r\n\t}\r\n\tonce_rendered () {}\r\n\tisOpened() {}\r\n\tisClosed() {}\r\n\topen () {\r\n\t\tthis.modal.open();\r\n\t}\r\n\tclose () {\r\n\t\tthis.modal.close();\r\n\t}\r\n\tpopupOpened () {\r\n\t\tdocument.body.classList.add('popup-opened');\r\n\t\tthis.isOpened();\r\n\t}\r\n\tpopupClosed () {\r\n\t\tdocument.body.classList.remove('popup-opened');\r\n\t\tthis.isClosed();\r\n\t}\r\n\tinit_modal () {\r\n\t\tthis.modal = document.querySelector(this.modal_selector);\r\n\t}\r\n\tinit_popup_events () {\r\n\t\tthis.modal.addEventListener('open', this.popupOpened.bind(this));\r\n\t\tthis.modal.addEventListener('close', this.popupClosed.bind(this));\r\n\r\n\t\tthis.header = this.modal.querySelector('.modal-header');\r\n\t\tthis.content = this.modal.querySelector('.modal-content');\r\n\t\tthis.dismiss = this.modal.querySelector('[data-dismiss=\"modal\"]');\r\n\r\n\t\tthis.content.addEventListener('scroll', () => {\r\n\t\t\tthis.modal.dataset.scroll = this.content.scrollTop;\r\n\t\t});\r\n\r\n\t\tif( this.header ) {\r\n\t\t\tthis.header.addEventListener('click', this.close.bind(this));\r\n\t\t}\r\n\r\n\t\tif( this.dismiss ) {\r\n\t\t\tthis.dismiss.addEventListener('click', this.close.bind(this));\r\n\t\t}\r\n\t}\r\n}","import ModalView from '../../../../scripts/utils/modal-view.js';\r\nimport Urls from '../urls.js';\r\n\r\nexport default class AddToCartMsgView extends ModalView {\r\n\tconstructor({ el, modal_selector, navigation, modal_content }) {\r\n\t\tsuper({ el, modal_selector, navigation, modal_content });\r\n\t\tthis.template_url = Urls.Templates.message;\r\n\t\tthis.render();\r\n\t}\r\n}","import Urls from '../urls.js';\r\nimport { fetch_status, fetch_json, fetch_error } from '../../../../scripts/utils/fetch.js';\r\nimport BaseView from '../../../../scripts/utils/base-view.js';\r\nimport AddToCartModalView from './cart-modal.js';\r\nimport AddToCartMsgView from './message.js';\r\n\r\nexport default class AddToCartView extends BaseView {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tproduct_id,\r\n\t\tcart_product,\r\n\t\tin_stock_qty,\r\n\t\tallow_duplicates,\r\n\t\tshow_popup,\r\n\t\tadv_source,\r\n\t\tmap_policy,\r\n\t\tvariations,\r\n\t\ttrack_ga_events,\r\n\t\tis_accessory,\r\n\t\tcart_modal_placeholder,\r\n\t\toptions_active\r\n\t}) {\r\n\t\tsuper(el);\r\n\r\n\t\tthis.selectors = {\r\n\t\t\tqty: 'input[name=\"quantity\"]',\r\n\t\t\tbtnAddToCart: 'button[name=\"addtocart\"]',\r\n\t\t\tmessage: '.add-to-cart-message',\r\n\t\t\trewardPoints: '.reward-points'\r\n\t\t};\r\n\r\n\t\tthis.cart_modal_view = null;\r\n\t\tthis.valid_product_option = false;\r\n\t\tthis.product_data_options = null;\r\n\t\tthis.options_manual = null;\r\n\t\tthis.template_url = Urls.Templates.app;\r\n\r\n\t\tthis.product_id = product_id;\r\n\t\tthis.product_price = null;\r\n\t\tthis.cart_product = cart_product;\r\n\t\tthis.in_stock_qty = in_stock_qty;\r\n\t\tthis.allow_duplicates = allow_duplicates;\r\n\t\tthis.show_popup = show_popup;\r\n\t\tthis.adv_source = adv_source;\r\n\t\tthis.map_policy = map_policy;\r\n\t\tthis.variations = variations;\r\n\t\tthis.track_ga_events = track_ga_events;\r\n\t\tthis.is_accessory = is_accessory;\r\n\t\tthis.cart_modal_placeholder = cart_modal_placeholder;\r\n\t\tthis.options_active = options_active;\r\n\t\tthis.in_cart = cart_product && !allow_duplicates;\r\n\t\tthis.hasAddons = false;\r\n\r\n\t\tif (this.in_stock_qty < 0 || window.SiteSettings.STOCK_SELL_OUT_OF_STOCK) {\r\n\t\t\tthis.in_stock_qty = window.SiteSettings.SHOPPING_CART_MAX_QTY ? window.SiteSettings.SHOPPING_CART_MAX_QTY : 1000;\r\n\t\t}\r\n\r\n\t\tif( !this.is_accessory ) {\r\n\t\t\tthis.render();\r\n\t\t\tthis.bindCartEvents();\r\n\t\t}\r\n\r\n\t\tif (this.options_active) {\r\n\t\t\tthis.handleEvents();\r\n\t\t}\r\n\r\n\t\tthis.getProductPrice();\r\n\r\n\t\tif (window.ProductSettings && window.ProductSettings.AddWarranty) {\r\n\t\t\tthis.hasAddons = true;\r\n\t\t}\r\n\t}\r\n\r\n\tget_context () {\r\n\t\treturn {\r\n\t\t\tin_cart: this.in_cart,\r\n\t\t\tin_stock_qty: this.in_stock_qty,\r\n\t\t\tlabel: this.el ? this.el.getAttribute('data-label') : '',\r\n\t\t\tqty_step: window.ProductSettings.QtyStep ? window.ProductSettings.QtyStep : null,\r\n\t\t\tmin_qty: this.calculateMinQty(),\r\n\t\t\tproduct_price: this.product_price\r\n\t\t};\r\n\t}\r\n\r\n\tafter_render () {\r\n\t\tconst rendered_event = new CustomEvent('[add-to-cart]:rendered');\r\n\r\n\t\tthis.removeListeners();\r\n\t\tthis.bindSelectors();\r\n\t\tthis.addListeners();\r\n\t\tthis.calculateRewards();\r\n\r\n\t\tdocument.dispatchEvent(rendered_event);\r\n\t}\r\n\r\n\tbindCartEvents () {\r\n\t\tdocument.addEventListener('[add-to-cart]:add-product', () => {\r\n\t\t\tthis.btnAddToCartElement.click();\r\n\t\t} );\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tif (this.btnAddToCartElement) {\r\n\t\t\tthis.btnAddToCartElement.addEventListener('click', this.addToCart.bind(this));\r\n\t\t}\r\n\t\tif (this.qtyElement) {\r\n\t\t\tthis.qtyElement.addEventListener('input', this.qtyInput.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.btnAddToCartElement) {\r\n\t\t\tthis.btnAddToCartElement.removeEventListener('click', this.addToCart);\r\n\t\t}\r\n\t\tif (this.qtyElement) {\r\n\t\t\tthis.qtyElement.removeEventListener('input', this.qtyInput);\r\n\t\t}\r\n\t}\r\n\r\n\tqtyInput () {\r\n\t\tthis.validateQty();\r\n\t\tthis.calculateRewards();\r\n\t}\r\n\r\n\taddToCart (e) {\r\n\t\te.preventDefault();\r\n\t\tlet qty_error = this.validateQtyStep();\r\n\t\tif (!qty_error) {\r\n\t\t\tif (this.options_active) {\r\n\t\t\t\tdocument.dispatchEvent(new CustomEvent('product-options:collect-data'));\r\n\t\t\t\tif (this.valid_product_option) {\r\n\t\t\t\t\tthis.addToCartClicked();\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tthis.addToCartClicked();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\taddToCartClicked () {\r\n\t\tif (this.in_cart) {\r\n\t\t\tthis.goToCart();\r\n\t\t} else {\r\n\t\t\tthis.addProduct();\r\n\t\t}\r\n\t}\r\n\r\n\taddProduct () {\r\n\t\tconst product_data = {\r\n\t\t\tProductId: this.product_id,\r\n\t\t\tParentId: null,\r\n\t\t\tQty: parseInt(this.qtyElement.value),\r\n\t\t\tDefaultOptions: this.btnAddToCartElement.getAttribute('data-default-options')\r\n\t\t};\r\n\r\n\t\tconst upd_data = this.getExternalData();\r\n\t\tconst data = {...upd_data, ...product_data};\r\n\r\n\t\tthis.startProgress();\r\n\r\n\t\tfetch(Urls.Api.product, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify(data)\r\n\t\t})\r\n\t\t\t.then(fetch_status)\r\n\t\t\t.then(fetch_json)\r\n\t\t\t.then(function (json) {\r\n\t\t\t\tthis.cart_product = json.Cart.Products.find(product => product.Id === json.CartProductId);\r\n\t\t\t\tif (this.options_active && this.options_manual.length > 0) {\r\n\t\t\t\t\tthis.addManualOptions(this.options_manual);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.productAdded(json);\r\n\t\t\t\t}\r\n\t\t\t}.bind(this))\r\n\t\t\t.catch(error => {\r\n\t\t\t\tfetch_error(error, true)\r\n\t\t\t\t\t.then(error_json => {\r\n\t\t\t\t\t\tthis.processError(error_json);\r\n\t\t\t\t\t});\r\n\t\t\t});\r\n\t}\r\n\r\n\tstartProgress () {\r\n\t\tthis.btnAddToCartElement.setAttribute('disabled', true);\r\n\t\tthis.btnAddToCartElement.classList.add('progress');\r\n\t\tthis.qtyElement.setAttribute('disabled', true);\r\n\t}\r\n\r\n\tendProgress () {\r\n\t\tthis.btnAddToCartElement.removeAttribute('disabled');\r\n\t\tthis.btnAddToCartElement.classList.remove('progress');\r\n\t\tthis.qtyElement.removeAttribute('disabled');\r\n\t}\r\n\r\n\tprocessError (error) {\r\n\t\tlet message = 'Something went wrong...';\r\n\r\n\t\tif (error.Type && error.Type === 'SessionVerificationRequiredException') {\r\n\t\t\tif (window.verifySession) {\r\n\t\t\t\twindow.verifySession();\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tif (error && typeof error === 'object') {\r\n\t\t\t\tmessage = error.responseJSON && error.responseJSON.Message ? error.responseJSON.Message : error.Message;\r\n\t\t\t}\r\n\t\t\tthis.endProgress();\r\n\t\t\tthis.showError(message);\r\n\t\t}\r\n\t}\r\n\r\n\tshowError (message) {\r\n\t\tconst msgModalEl = document.querySelector('.add-to-cart-message-modal');\r\n\r\n\t\tif (msgModalEl) {\r\n\t\t\tconst defaultHeading = 'Can\\'t add product to shopping cart: ';\r\n\t\t\tif (message.includes(defaultHeading)) {\r\n\t\t\t\tmessage = message.replace(defaultHeading, '');\r\n\t\t\t\tmessage = `

${defaultHeading}

${message}

`;\r\n\t\t\t}\r\n\t\t\tconst msgView = new AddToCartMsgView({\r\n\t\t\t\tel: msgModalEl,\r\n\t\t\t\tmodal_selector: '.add-to-cart-message',\r\n\t\t\t\tnavigation: null,\r\n\t\t\t\tmodal_content: message\r\n\t\t\t});\r\n\r\n\t\t\tmsgView.once_rendered = () => {\r\n\t\t\t\tmsgView.init_modal();\r\n\t\t\t\tmsgView.init_popup_events();\r\n\t\t\t\tmsgView.open();\r\n\t\t\t};\r\n\t\t} else {\r\n\t\t\twindow.alert(message);\r\n\t\t}\r\n\t}\r\n\r\n\taddManualOptions (options) {\r\n\t\tconst url = Urls.Api.product_options_manual(this.cart_product.Id);\r\n\r\n\t\tfetch(url, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify(options)\r\n\t\t})\r\n\t\t.then(fetch_status)\r\n\t\t.then(fetch_json)\r\n\t\t.then(response => {\r\n\t\t\tthis.productAdded(response);\r\n\t\t})\r\n\t\t.catch(error => {\r\n\t\t\tfetch_error(error, true)\r\n\t\t\t\t.then(error_json => {\r\n\t\t\t\t\tthis.processError(error_json);\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t}\r\n\r\n\tproductAdded (cart_data) {\r\n\t\tconst data = Object.assign({}, cart_data, { source: 'Product', sku: this.cart_product.ProductSku, ProductId: this.cart_product.ProductId });\r\n\t\tconst product_added_event = new CustomEvent('[add-to-cart]:product-added', { detail: data });\r\n\r\n\t\tdocument.dispatchEvent(product_added_event);\r\n\r\n\t\tif (this.show_popup) {\r\n\t\t\tthis.showCartPopup()\r\n\t\t\t\t.then(() => {\r\n\t\t\t\t\tthis.endProgress();\r\n\t\t\t\t\tif (!this.is_accessory) {\r\n\t\t\t\t\t\tthis.set_state();\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t} else {\r\n\t\t\tthis.endProgress();\r\n\t\t\tif (!this.hasAddons) {\r\n\t\t\t\tthis.goToCart();\r\n\t\t\t} else {\r\n\t\t\t\tthis.set_state();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tset_state() {\r\n\t\tif (this.allow_duplicates) {\r\n\t\t\tthis.btnAddToCartElement.removeAttribute('disabled');\r\n\t\t\tthis.btnAddToCartElement.classList.remove('progress');\r\n\t\t\tthis.qtyElement.removeAttribute('disabled');\r\n\t\t} else {\r\n\t\t\tconst btn_state = this.btnAddToCartElement.querySelector('span');\r\n\r\n\t\t\tthis.btnAddToCartElement.classList.replace('progress', 'btn-success');\r\n\t\t\tbtn_state.textContent = btn_state.getAttribute('data-state');\r\n\t\t\tthis.qtyElement.classList.add('hide');\r\n\t\t\tthis.in_cart = true;\r\n\t\t}\r\n\t}\r\n\r\n\tshowCartPopup () {\r\n\t\tif (!this.cart_modal_view) {\r\n\t\t\tthis.cart_modal_view = new AddToCartModalView( {\r\n\t\t\t\tel: this.cart_modal_placeholder,\r\n\t\t\t\tadv_source: this.adv_source,\r\n\t\t\t\tcart_product: this.cart_product\r\n\t\t\t} );\r\n\t\t}\r\n\t\treturn new Promise(resolve => {\r\n\t\t\tthis.cart_modal_view.once_rendered = () => {\r\n\t\t\t\tthis.cart_modal_view.show_modal(this.cart_product);\r\n\t\t\t\tresolve(true);\r\n\t\t\t};\r\n\t\t});\r\n\t}\r\n\r\n\tgetExternalData () {\r\n\t\tconst product_data = {};\r\n\t\tconst skipUserData = window.ProductSettings.SkipUserCartData || false;\r\n\t\tconst userLocationCode = localStorage.getItem('userLocationCode');\r\n\t\tconst userLocationName = localStorage.getItem('userLocationName');\r\n\t\tconst userLocationAddress = localStorage.getItem('userLocationAddress');\r\n\t\tconst userLocationDelivery = localStorage.getItem('userLocationDelivery');\r\n\r\n\t\tif (this.adv_source) {\r\n\t\t\tproduct_data.Source = 'ad';\r\n\t\t}\r\n\t\tif (window.price_match_price) {\r\n\t\t\tproduct_data.PriceMatchPrice = window.price_match_price;\r\n\t\t}\r\n\t\tif (window.price_match_url) {\r\n\t\t\tproduct_data.PriceMatchUrl = window.price_match_url;\r\n\t\t}\r\n\t\tif (this.variations) {\r\n\t\t\tproduct_data.DefaultOptions = true;\r\n\t\t} else {\r\n\t\t\tproduct_data.Options = this.product_data_options;\r\n\t\t}\r\n\t\tif (this.map_policy === 'ADD_TO_CART') {\r\n\t\t\tproduct_data.UseListPrice = true;\r\n\t\t}\r\n\r\n\t\tif (userLocationCode && !skipUserData) {\r\n\t\t\tproduct_data.Data = [\r\n\t\t\t\t{\r\n\t\t\t\t\t'Name': 'LocationCode',\r\n\t\t\t\t\t'Value': userLocationCode,\r\n\t\t\t\t\t'Type': 'hidden_string'\r\n\t\t\t\t}\r\n\t\t\t];\r\n\t\t\tif (userLocationName) {\r\n\t\t\t\tproduct_data.Data.push({\r\n\t\t\t\t\t'Name': 'LocationName',\r\n\t\t\t\t\t'Value': userLocationName,\r\n\t\t\t\t\t'Type': 'hidden_string'\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t\tif (userLocationAddress) {\r\n\t\t\t\tproduct_data.Data.push({\r\n\t\t\t\t\t'Name': 'LocationAddress',\r\n\t\t\t\t\t'Value': userLocationAddress,\r\n\t\t\t\t\t'Type': 'hidden_string'\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t\tif (userLocationDelivery) {\r\n\t\t\t\tproduct_data.Data.push({\r\n\t\t\t\t\t'Name': 'Delivery',\r\n\t\t\t\t\t'Value': userLocationDelivery,\r\n\t\t\t\t\t'Type': 'hidden_string'\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn product_data;\r\n\t}\r\n\r\n\tgoToCart () {\r\n\t\twindow.location.href = Urls.Api.cart;\r\n\t}\r\n\r\n\tvalidateQty () {\r\n\t\tconst num = this.qtyElement.value.replace(/[^\\d]/,'');\r\n\t\tconst num_min = parseInt(this.qtyElement.min);\r\n\t\tconst num_max = parseInt(this.qtyElement.max);\r\n\r\n\t\tthis.qtyElement.value = num;\r\n\r\n\t\tif (num < num_min) {\r\n\t\t\tthis.qtyElement.value = num_min;\r\n\t\t}\r\n\t\tif (num > num_max) {\r\n\t\t\tthis.qtyElement.value = num_max;\r\n\t\t}\r\n\t\tif (this.messageElement && !this.messageElement.classList.contains('d-none') && !this.qty_error) {\r\n\t\t\tthis.messageElement.classList.add('d-none');\r\n\t\t}\r\n\t}\r\n\r\n\tvalidateQtyStep () {\r\n\t\tlet qty_error = false;\r\n\t\tif (this.qtyElement) {\r\n\t\t\tconst num = this.qtyElement.value.replace(/[^\\d]/,'');\r\n\t\t\tlet num_step = this.qtyElement.step;\r\n\r\n\t\t\tif (num_step) {\r\n\t\t\t\tnum_step = parseInt(num_step);\r\n\t\t\t\tif (num_step > 1) {\r\n\t\t\t\t\tif (num % num_step !== 0) {\r\n\t\t\t\t\t\tthis.showError('Please enter a quantity as a multiple of ' + num_step);\r\n\t\t\t\t\t\tqty_error = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn qty_error;\r\n\t}\r\n\r\n\thandleEvents () {\r\n\t\tdocument.addEventListener('product-options:is_valid', event => {\r\n\t\t\tconst detail = event.detail;\r\n\r\n\t\t\tif (detail) {\r\n\t\t\t\tthis.valid_product_option = true;\r\n\t\t\t}\r\n\t\t});\r\n\t\tdocument.addEventListener('product-options:manual', event => {\r\n\t\t\tthis.options_manual = event.detail || [];\r\n\t\t});\r\n\t\tdocument.addEventListener('product-options:selected', event => {\r\n\t\t\tconst detail = event.detail;\r\n\r\n\t\t\tif (detail) {\r\n\t\t\t\tthis.product_data_options = detail;\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\tcalculateMinQty () {\r\n\t\tlet min_qty = window.SiteSettings.SHOPPING_CART_MIN_QTY ? window.SiteSettings.SHOPPING_CART_MIN_QTY : 1;\r\n\t\tlet qty_step = window.ProductSettings.QtyStep ? window.ProductSettings.QtyStep : null;\r\n\t\tlet min_order = window.ProductSettings.MinOrder ? window.ProductSettings.MinOrder : null;\r\n\r\n\t\tif (qty_step) {\r\n\t\t\tmin_qty = qty_step;\r\n\t\t}\r\n\r\n\t\tif (qty_step && min_order) {\r\n\t\t\tif (this.product_price) {\r\n\t\t\t\tif (qty_step * this.product_price < min_order) {\r\n\t\t\t\t\tmin_qty = Math.ceil(Math.ceil(min_order / this.product_price) / qty_step) * qty_step;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn min_qty;\r\n\t}\r\n\r\n\tcalculateRewards () {\r\n\t\tif (this.qtyElement) {\r\n\t\t\tconst qty = parseInt(this.qtyElement.value);\r\n\r\n\t\t\tthis.rewardPointsElement = document.querySelector(this.selectors.rewardPoints);\r\n\t\t\tif (this.rewardPointsElement) {\r\n\t\t\t\tconst rewardPointsCountElement = this.rewardPointsElement.querySelector('.count');\r\n\t\t\t\tconst rewards = Math.round(qty * this.product_price);\r\n\r\n\t\t\t\tif (rewardPointsCountElement) {\r\n\t\t\t\t\trewardPointsCountElement.textContent = rewards;\r\n\t\t\t\t}\r\n\t\t\t\tif (this.rewardPointsElement.classList.contains('hide')) {\r\n\t\t\t\t\tthis.rewardPointsElement.classList.remove('hide');\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tgetProductPrice () {\r\n\t\tlet price_els = document.querySelectorAll('.js-product-price-label');\r\n\r\n\t\tif (price_els.length && price_els[0].dataset.price) {\r\n\t\t\tthis.product_price = parseFloat(price_els[0].dataset.price);\r\n\t\t}\r\n\t}\r\n}\r\n","import AddToCartStickyFooterView from './views/sticky-footer.js';\r\nimport AddToCartView from './views/add-to-cart.js';\r\n\r\nexport default class AddToCart {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tproduct_id,\r\n\t\tcart_product,\r\n\t\tin_stock_qty,\r\n\t\tallow_duplicates,\r\n\t\tshow_popup,\r\n\t\tadv_source,\r\n\t\tmap_policy,\r\n\t\tvariations,\r\n\t\tsticky_footer,\r\n\t\ttrack_ga_events,\r\n\t\tis_accessory,\r\n\t\tcart_modal_placeholder,\r\n\t\toptions_active\r\n\t}) {\r\n\t\tthis.view = new AddToCartView({\r\n\t\t\tel: el,\r\n\t\t\tproduct_id: product_id,\r\n\t\t\tcart_product: cart_product,\r\n\t\t\tin_stock_qty: in_stock_qty,\r\n\t\t\tallow_duplicates: allow_duplicates,\r\n\t\t\tshow_popup: show_popup,\r\n\t\t\tadv_source: adv_source,\r\n\t\t\tmap_policy: map_policy,\r\n\t\t\tvariations: variations,\r\n\t\t\ttrack_ga_events: track_ga_events,\r\n\t\t\tis_accessory: is_accessory,\r\n\t\t\tcart_modal_placeholder: cart_modal_placeholder,\r\n\t\t\toptions_active: options_active\r\n\t\t});\r\n\r\n\t\tif (typeof sticky_footer !== 'undefined') {\r\n\t\t\tthis.sticky_footer_view = new AddToCartStickyFooterView({\r\n\t\t\t\tel: sticky_footer,\r\n\t\t\t\tadd_to_cart_el: el,\r\n\t\t\t\tcart_product: cart_product,\r\n\t\t\t\tallow_duplicates: allow_duplicates,\r\n\t\t\t\tis_accessory: is_accessory\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n}","import { fetcher } from '../../../../scripts/utils/fetch.js';\r\nimport Urls from '../urls.js';\r\n\r\nexport default class AddToCartBundles {\r\n\tconstructor({ btnSelector }) {\r\n\t\tthis.btns = document.querySelectorAll(btnSelector);\r\n\r\n\t\tif (!!this.btns.length) {\r\n\t\t\tthis.addListeners();\r\n\t\t}\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tthis.btns.forEach(elem => elem.addEventListener('click', this.addToCart.bind(this)));\r\n\t}\r\n\r\n\taddToCart (event) {\r\n\t\tconst btn = event.currentTarget;\r\n\t\tconst BundleId = btn.getAttribute('data-bundle-id');\r\n\r\n\t\tbtn.classList.add('progress');\r\n\t\tbtn.setAttribute('disabled', true);\r\n\r\n\t\tfetcher(Urls.Api.bundle, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({ BundleId })\r\n\t\t})\r\n\t\t.then(response => {\r\n\t\t\tconst product_added_event = new CustomEvent('[add-to-cart]:product-added', { detail: response });\r\n\r\n\t\t\tthis.goToCart();\r\n\r\n\t\t\tdocument.dispatchEvent(product_added_event);\r\n\t\t});\r\n\t}\r\n\r\n\tgoToCart() {\r\n\t\twindow.location.href = Urls.Api.cart;\r\n\t}\r\n}","const Urls = {\r\n\tApi: {\r\n\t\tquote: '/my/quote-request',\r\n\t\tproduct: '/my/api/2/shopping-cart/quote-request/product'\r\n\t},\r\n\tTemplates: {\r\n\t\tapp: '/my/api/2/templates?name=modules/add-to-quote/app.html'\r\n\t}\r\n};\r\n\r\nexport default Urls;","import Urls from '../urls.js';\r\nimport { fetch_status, fetch_json, fetch_error } from '../../../../scripts/utils/fetch.js';\r\nimport BaseView from '../../../../scripts/utils/base-view.js';\r\n\r\nexport default class AddToCartView extends BaseView {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tproduct_id,\r\n\t\tcustomer,\r\n\t\tquote,\r\n\t\tis_accessory,\r\n\t\toptions_active\r\n\t}) {\r\n\t\tsuper(el);\r\n\t\tthis.btn_add_to_cart = null;\r\n\t\tthis.message_element = null;\r\n\t\tthis.valid_product_option = false;\r\n\t\tthis.product_data_options = null;\r\n\t\tthis.template_url = Urls.Templates.app;\r\n\r\n\t\tthis.product_id = product_id;\r\n\t\tthis.customer = customer;\r\n\t\tthis.quote = quote;\r\n\t\tthis.quote_product = null;\r\n\t\tthis.is_accessory = is_accessory;\r\n\t\tthis.options_active = options_active;\r\n\t\tthis.in_quote = false;\r\n\r\n\t\tthis.findProductInQuote();\r\n\r\n\t\tif( !this.is_accessory ) {\r\n\t\t\tthis.render();\r\n\t\t\tthis.bindQuoteEvents();\r\n\t\t}\r\n\r\n\t\tif (this.options_active) {\r\n\t\t\tthis.handleEvents();\r\n\t\t}\r\n\t}\r\n\r\n\tfindProductInQuote () {\r\n\t\tif (this.quote && this.quote.Products.length > 0) {\r\n\t\t\tthis.quote_product = this.quote.Products.find(product => product.ProductId === this.product_id);\r\n\t\t\tif (this.quote_product) {\r\n\t\t\t\tthis.in_quote = true;\r\n\t\t\t\tthis.productInQuoteEvent();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tget_context () {\r\n\t\treturn {\r\n\t\t\tin_quote: this.in_quote,\r\n\t\t\tlabel: this.el.getAttribute('data-label'),\r\n\t\t\tquote_exists: this.quote && this.quote.Products.length > 0\r\n\t\t};\r\n\t}\r\n\r\n\tafter_render () {\r\n\t\tthis.initSelectors();\r\n\t}\r\n\r\n\tbindQuoteEvents () {\r\n\t\tdocument.addEventListener('[add-to-quote]:add-product', () => {\r\n\t\t\tthis.btn_add_to_quote.click();\r\n\t\t} );\r\n\t}\r\n\r\n\tinitSelectors () {\r\n\t\tthis.removeListeners();\r\n\t\tthis.btn_add_to_quote = document.querySelector('button[name=\"addtoquote\"]');\r\n\t\tthis.message_element = document.querySelector('.message');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tif (this.btn_add_to_quote) {\r\n\t\t\tthis.btn_add_to_quote.addEventListener('click', this.addToQuote.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.btn_add_to_quote) {\r\n\t\t\tthis.btn_add_to_quote.removeEventListener('click', this.addToQuote);\r\n\t\t}\r\n\t}\r\n\r\n\taddToQuote (e) {\r\n\t\te.preventDefault();\r\n\t\tif (this.in_quote) {\r\n\t\t\tthis.goToQuote();\r\n\t\t} else {\r\n\t\t\tif (this.customer && this.customer.guest) {\r\n\t\t\t\twindow.location = '/my/signin?next=' + window.location.pathname;\r\n\t\t\t} else {\r\n\t\t\t\tif (this.options_active) {\r\n\t\t\t\t\tthis.getOptionsAndProceed();\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.addProduct();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tgetOptionsAndProceed () {\r\n\t\tdocument.dispatchEvent(new CustomEvent('product-options:collect-data'));\r\n\t\tif (this.valid_product_option) {\r\n\t\t\tthis.addProduct();\r\n\t\t}\r\n\t}\r\n\r\n\taddProduct () {\r\n\t\tconst product_data = {\r\n\t\t\tProductId: this.product_id,\r\n\t\t\tQty: window.ProductSettings.QtyStep ? window.ProductSettings.QtyStep : 1,\r\n\t\t\tDefaultOptions: this.btn_add_to_quote.getAttribute('data-default-options')\r\n\t\t};\r\n\r\n\t\tconst upd_data = this.getExternalData();\r\n\t\tconst data = {...upd_data, ...product_data};\r\n\r\n\t\tthis.startProgress();\r\n\r\n\t\tfetch(Urls.Api.product, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify(data)\r\n\t\t})\r\n\t\t\t.then(fetch_status)\r\n\t\t\t.then(fetch_json)\r\n\t\t\t.then(function (json) {\r\n\t\t\t\tif (json.Cart) {\r\n\t\t\t\t\tthis.quote = json.Cart;\r\n\t\t\t\t}\r\n\t\t\t\tthis.findProductInQuote();\r\n\t\t\t\tthis.productAdded();\r\n\t\t\t}.bind(this))\r\n\t\t\t.catch(error => {\r\n\t\t\t\tfetch_error(error, true)\r\n\t\t\t\t\t.then(error_json => {\r\n\t\t\t\t\t\tthis.processError(error_json);\r\n\t\t\t\t\t});\r\n\t\t\t});\r\n\t}\r\n\r\n\tstartProgress () {\r\n\t\tthis.btn_add_to_quote.setAttribute('disabled', true);\r\n\t\tthis.btn_add_to_quote.classList.add('progress');\r\n\t}\r\n\r\n\tendProgress () {\r\n\t\tthis.btn_add_to_quote.removeAttribute('disabled');\r\n\t\tthis.btn_add_to_quote.classList.remove('progress');\r\n\t}\r\n\r\n\tprocessError (error) {\r\n\t\tlet message = 'Something went wrong...';\r\n\r\n\t\tif (error.Type && error.Type === 'SessionVerificationRequiredException') {\r\n\t\t\tif (window.verifySession) {\r\n\t\t\t\twindow.verifySession();\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tif (error && typeof error === 'object') {\r\n\t\t\t\tmessage = error.responseJSON && error.responseJSON.Message ? error.responseJSON.Message : error.Message;\r\n\t\t\t}\r\n\t\t\tthis.endProgress();\r\n\t\t\tthis.showError(message);\r\n\t\t}\r\n\t}\r\n\r\n\tshowError (message) {\r\n\t\tif (this.message_element) {\r\n\t\t\tthis.message_element.textContent = message;\r\n\t\t\tthis.message_element.classList.remove('hide');\r\n\t\t} else {\r\n\t\t\twindow.alert(message);\r\n\t\t}\r\n\t}\r\n\r\n\tproductAdded () {\r\n\t\tconst product_added_event = new CustomEvent('[add-to-quote]:product-added', { detail: this.quote });\r\n\r\n\t\tdocument.dispatchEvent(product_added_event);\r\n\r\n\t\tif (!this.el.dataset.showPane) {\r\n\t\t\tthis.goToQuote();\r\n\t\t}\r\n\t}\r\n\r\n\tproductInQuoteEvent () {\r\n\t\tconst product_in_quote_event = new CustomEvent('[add-to-quote]:product-in-quote');\r\n\r\n\t\tdocument.dispatchEvent(product_in_quote_event);\r\n\t}\r\n\r\n\tset_state() {\r\n\t\tconst btn_state = this.btn_add_to_quote.querySelector('span');\r\n\r\n\t\tthis.btn_add_to_quote.classList.replace('progress', 'btn-success');\r\n\t\tbtn_state.textContent = btn_state.getAttribute('data-state');\r\n\t\tthis.in_quote = true;\r\n\t}\r\n\r\n\tgetExternalData () {\r\n\t\tconst product_data = {};\r\n\r\n\t\tif (this.adv_source) {\r\n\t\t\tproduct_data.Source = 'ad';\r\n\t\t}\r\n\t\tif (window.price_match_price) {\r\n\t\t\tproduct_data.PriceMatchPrice = window.price_match_price;\r\n\t\t}\r\n\t\tif (window.price_match_url) {\r\n\t\t\tproduct_data.PriceMatchUrl = window.price_match_url;\r\n\t\t}\r\n\t\tif (this.variations) {\r\n\t\t\tproduct_data.DefaultOptions = true;\r\n\t\t} else {\r\n\t\t\tproduct_data.Options = this.product_data_options;\r\n\t\t}\r\n\t\tif (this.map_policy === 'ADD_TO_CART') {\r\n\t\t\tproduct_data.UseListPrice = true;\r\n\t\t}\r\n\r\n\t\treturn product_data;\r\n\t}\r\n\r\n\tgoToQuote () {\r\n\t\twindow.location.href = Urls.Api.quote;\r\n\t}\r\n\r\n\thandleEvents () {\r\n\t\tdocument.addEventListener('product-options:is_valid', event => {\r\n\t\t\tconst detail = event.detail;\r\n\r\n\t\t\tif (detail) {\r\n\t\t\t\tthis.valid_product_option = true;\r\n\t\t\t}\r\n\t\t});\r\n\t\tdocument.addEventListener('product-options:selected', event => {\r\n\t\t\tconst detail = event.detail;\r\n\r\n\t\t\tif (detail) {\r\n\t\t\t\tthis.product_data_options = detail;\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n}\r\n","import AddToQuoteView from './views/add-to-quote.js';\r\n\r\nexport default class AddToQuote {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tproduct_id,\r\n\t\tquote,\r\n\t\tis_accessory,\r\n\t\toptions_active\r\n\t}) {\r\n\t\tthis.view = new AddToQuoteView({\r\n\t\t\tel: el,\r\n\t\t\tproduct_id: product_id,\r\n\t\t\tquote: quote,\r\n\t\t\tis_accessory: is_accessory,\r\n\t\t\toptions_active: options_active\r\n\t\t});\r\n\t}\r\n}","const forms_elements = document.querySelectorAll('.js-product-options-form');\r\nconst fields_elements = document.querySelectorAll('.js-product-options-field');\r\nconst values_elements = document.querySelectorAll('.js-product-options-value');\r\n\r\nexport default class ProductOptionsView {\r\n\tconstructor (options) {\r\n\t\tif (!options || typeof options !== 'object') {\r\n\t\t\tthrow new Error(`The parameter \"options\" should be of type object, but instead had ${typeof options} type`);\r\n\t\t}\r\n\t\tconst {\r\n\t\t\tproduct_id,\r\n\t\t\tel,\r\n\t\t\tel_price,\r\n\t\t\tel_price_original,\r\n\t\t\tdiscount\r\n\t\t} = options;\r\n\r\n\t\tthis.selectors = {\r\n\t\t\tel_price: el_price,\r\n\t\t\tel_price_original: el_price_original\r\n\t\t};\r\n\r\n\t\tthis.product_id = product_id;\r\n\t\tthis.el = el && document.querySelector(el);\r\n\t\tthis.el_price = el_price && document.querySelectorAll(el_price);\r\n\t\tthis.el_price_original = el_price_original && document.querySelector(el_price_original);\r\n\t\tthis.discount = discount;\r\n\t\tthis.forms_elements = forms_elements;\r\n\t\tthis.fields_elements = fields_elements;\r\n\t\tthis.values_elements = values_elements;\r\n\t\tthis.original_price = null;\r\n\r\n\t\tif (!this.el_price) {\r\n\t\t\tthrow 'Product options: product price element is undefined!';\r\n\t\t}\r\n\r\n\t\tif (this.el && this.el_price.length > 0) {\r\n\t\t\tthis.getPrice();\r\n\t\t\tthis.calculate();\r\n\t\t\tthis.setEventListeners();\r\n\t\t\tthis.cleanDescription();\r\n\t\t\tdocument.addEventListener('product-options:collect-data', () => this.dispatchEvents());\r\n\t\t\tdocument.addEventListener('[add-to-cart]:rendered', () => this.calculatePrice());\r\n\t\t}\r\n\t}\r\n\r\n\tsetEventListeners () {\r\n\t\tthis.values_elements.forEach(elem => {\r\n\t\t\tif (elem && elem.getAttribute('type') === 'checkbox' || elem.getAttribute('type') === 'radio') {\r\n\t\t\t\telem.addEventListener('change', this.calculate.bind(this, elem));\r\n\t\t\t}\r\n\t\t});\r\n\t\tthis.fields_elements.forEach(elem => {\r\n\t\t\tconst select = elem.querySelector('select');\r\n\r\n\t\t\tif (select) {\r\n\t\t\t\telem.addEventListener('change', this.calculate.bind(this, elem));\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\tcalculate (element) {\r\n\t\tthis.calculatePrice();\r\n\t\tthis.calculateDynamicPrices(element);\r\n\t}\r\n\r\n\tvalidate () {\r\n\t\tconst required_items = this.el.querySelectorAll('[required]');\r\n\t\trequired_items.forEach(value => {\r\n\t\t\tconst field = value.closest('.js-product-options-field');\r\n\r\n\t\t\tif (value.value.length === 0) {\r\n\t\t\t\tfield.classList.add('has-error');\r\n\t\t\t} else {\r\n\t\t\t\tfield.classList.remove('has-error');\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tif (this.el.classList.contains('has-error')) {\r\n\t\t\tconst error_field = this.el.querySelector('has-error');\r\n\r\n\t\t\terror_field.querySelector('.js-product-options-value').focus();\r\n\t\t\treturn false;\r\n\t\t} else {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\r\n\tgetSelectedOptionsData () {\r\n\t\treturn [].reduce.call(document.querySelectorAll('.js-product-options-value'), (result, element) => {\r\n\t\t\tif (element.checked || element.selected) {\r\n\t\t\t\tresult = [...result, {\r\n\t\t\t\t\tFormDefValId: element.getAttribute('data-form-def-val-id'),\r\n\t\t\t\t\tFormId: element.getAttribute('data-form-id'),\r\n\t\t\t\t\tValueId: element.getAttribute('data-val-id')\r\n\t\t\t\t}];\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t}, []);\r\n\t}\r\n\r\n\tcalculatePrice () {\r\n\t\tlet selected_values = [].filter.call(document.querySelectorAll('.js-product-options-field input'), element => element && element.checked);\r\n\t\tconst selected_options = [].map.call(document.querySelectorAll('.js-product-options-field select'), element => element.options[element.selectedIndex]);\r\n\t\tselected_values = [...selected_values, ...selected_options];\r\n\t\tconst adjustment = selected_values.reduce(function(memo, value) {\r\n\t\t\treturn memo + parseFloat(value.getAttribute('data-price-adjustment'));\r\n\t\t}, 0);\r\n\t\tlet adjustment_discounted = 0;\r\n\r\n\t\tif (this.discount > 0 && adjustment > 0) {\r\n\t\t\tadjustment_discounted = adjustment - ( ( adjustment / 100 ) * this.discount );\r\n\t\t}\r\n\t\tthis.setPrice(adjustment, adjustment_discounted);\r\n\t}\r\n\r\n\tvaluesInputText () {\r\n\t\treturn [].reduce.call(this.fields_elements, (result, current) => {\r\n\t\t\tif (current.getAttribute('data-type') === 'text') {\r\n\t\t\t\tlet val = current.value;\r\n\r\n\t\t\t\tif (!val) {\r\n\t\t\t\t\tif (current.querySelector('[data-value]')) {\r\n\t\t\t\t\t\tval = current.querySelector('[data-value]').getAttribute('data-value');\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tresult = [...result, {\r\n\t\t\t\t\tName: current.getAttribute('data-form-field-title'),\r\n\t\t\t\t\tValue: val,\r\n\t\t\t\t\tShowInCart: current.getAttribute('data-show-in-cart') > 0\r\n\t\t\t\t}];\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t},[]);\r\n\t}\r\n\r\n\tgetOriginalPrice () {\r\n\t\treturn window.Session.get('ProductInfo')\r\n\t\t\t.then(info => {\r\n\t\t\t\tconst { DynamicNumbers: { original_price }} = info;\r\n\t\t\t\treturn Promise.resolve(original_price);\r\n\t\t\t})\r\n\t\t\t.catch(error => {\r\n\t\t\t\tconsole.error(error);\r\n\t\t\t\treturn Promise.reject(error);\r\n\t\t\t});\r\n\t}\r\n\r\n\tmakeDynamicCalculations (element) {\r\n\t\tif (element && element.getAttribute('type') === 'radio') {\r\n\t\t\tconst parent_block = element.closest('.js-product-options-field');\r\n\t\t\tconst price = parseFloat(element.getAttribute('data-price-adjustment'));\r\n\r\n\t\t\tparent_block.querySelectorAll('input').forEach(item => {\r\n\t\t\t\tconst item_price = parseFloat(item.getAttribute('data-price-adjustment'));\r\n\t\t\t\tconst price_value = item.parentNode.querySelector('.j-price, .js-price-adjustment');\r\n\r\n\t\t\t\tif (price_value) {\r\n\t\t\t\t\tconst final_price = (item_price - price).money();\r\n\t\t\t\t\tprice_value.textContent = final_price[0] !== '-' ? `+${final_price}` : final_price;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tcalculateDynamicPrices (element) {\r\n\t\tif (!this.original_price) {\r\n\t\t\tthis.getOriginalPrice()\r\n\t\t\t\t.then(info => {\r\n\t\t\t\t\tthis.original_price = info;\r\n\t\t\t\t\tthis.makeDynamicCalculations(element);\r\n\t\t\t\t});\r\n\t\t} else {\r\n\t\t\tthis.makeDynamicCalculations(element);\r\n\t\t}\r\n\t}\r\n\r\n\tsetPrice (adjustment, adjustment_discounted) {\r\n\t\tconst el_price = document.querySelectorAll(this.selectors.el_price);\r\n\r\n\t\tthis.price_current = this.price + adjustment;\r\n\t\tif (adjustment_discounted > 0) {\r\n\t\t\tthis.price_current = this.price + adjustment_discounted;\r\n\t\t}\r\n\r\n\t\tel_price.forEach(el => {\r\n\t\t\tel.textContent = String(this.price_current.money());\r\n\t\t});\r\n\r\n\t\tif (this.el_price_original) {\r\n\t\t\tconst price_original = this.price_original + adjustment;\r\n\t\t\tthis.el_price_original.textContent(String(price_original.money()));\r\n\t\t}\r\n\t}\r\n\r\n\tgetPrice () {\r\n\t\tthis.price = parseFloat(this.el_price[0].getAttribute('data-price'));\r\n\t\tif (this.el_price_original) {\r\n\t\t\tthis.price_original = parseFloat(this.el_price_original[0].getAttribute('data-price'));\r\n\t\t}\r\n\t}\r\n\r\n\tdispatchSelectedOptions () {\r\n\t\tconst product_options_selected = new CustomEvent('product-options:selected', { detail: this.getSelectedOptionsData() });\r\n\t\tdocument.dispatchEvent(product_options_selected);\r\n\t}\r\n\r\n\tdispatchValidateOptions () {\r\n\t\tconst product_options_is_valid = new CustomEvent('product-options:is_valid', { detail: this.validate() });\r\n\t\tdocument.dispatchEvent(product_options_is_valid);\r\n\t}\r\n\r\n\tdispatchManualOptions () {\r\n\t\tconst product_manual_options = new CustomEvent('product-options:manual', { detail: this.valuesInputText() });\r\n\t\tdocument.dispatchEvent(product_manual_options);\r\n\t}\r\n\r\n\tdispatchEvents () {\r\n\t\tthis.dispatchSelectedOptions();\r\n\t\tthis.dispatchValidateOptions();\r\n\t\tthis.dispatchManualOptions();\r\n\t}\r\n\r\n\t// Hide empty descriptions\r\n\tcleanDescription () {\r\n\t\tconst descriptions = this.el.querySelectorAll('.input-group-description');\r\n\r\n\t\tif (descriptions) {\r\n\t\t\tdescriptions.forEach(description => {\r\n\t\t\t\tconst html = description.innerHTML = description.innerHTML.replace(/(?:\\r\\n|\\r|\\n)/g, '');\r\n\t\t\t\tif (html === '


') {\r\n\t\t\t\t\tdescription.classList.add('hide');\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n}","const Urls = {\r\n\tApi: {\r\n\t\tproduct: '/my/api/2/wishlist'\r\n\t},\r\n\tTemplates: {\r\n\t\tapp: '/my/api/2/templates?name=modules/add-to-wishlist/app.html'\r\n\t}\r\n};\r\n\r\nexport default Urls;","import Urls from '../urls.js';\r\nimport { fetcher } from '../../../../scripts/utils/fetch.js';\r\nimport BaseView from '../../../../scripts/utils/base-view.js';\r\n\r\nexport default class AddToWishListView extends BaseView {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tproduct_id,\r\n\t\tlogged_user,\r\n\t\tin_wishlist\r\n\t}) {\r\n\t\tsuper(el);\r\n\t\tthis.template_url = Urls.Templates.app;\r\n\t\tthis.parent_element = document.querySelector(el);\r\n\t\tthis.product_id = product_id;\r\n\t\tthis.logged_user = logged_user;\r\n\t\tthis.in_wishlist = in_wishlist;\r\n\t\tthis.render();\r\n\t}\r\n\r\n\tget_context () {\r\n\t\treturn {\r\n\t\t\tlogged_user: this.logged_user,\r\n\t\t\tin_wishlist: this.in_wishlist\r\n\t\t};\r\n\t}\r\n\r\n\tafter_render () {\r\n\t\tthis.initSelectors();\r\n\t}\r\n\r\n\tinitSelectors () {\r\n\t\tthis.removeListeners();\r\n\t\tthis.wishlist_element = this.el.querySelector('.wishlist');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tif (this.wishlist_element) {\r\n\t\t\tthis.wishlist_element.addEventListener('click', this.addToWishlist.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.wishlist_element) {\r\n\t\t\tthis.wishlist_element.removeEventListener('click', this.addToWishlist.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\taddToWishlist (event) {\r\n\t\tconst btn_add_to_wishlist = event.currentTarget;\r\n\r\n\t\tif (this.logged_user && !this.in_wishlist) {\r\n\t\t\tevent.preventDefault();\r\n\t\t\tbtn_add_to_wishlist.classList.add('progress');\r\n\r\n\t\t\tfetcher(Urls.Api.product, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t\t},\r\n\t\t\t\tbody: JSON.stringify({ ProductId: this.product_id })\r\n\t\t\t})\r\n\t\t\t.then(() => {\r\n\t\t\t\tthis.in_wishlist = true;\r\n\t\t\t\tthis.render();\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n}","import AddToWishListView from './views/add-to-wishlist.js';\r\n\r\nexport default class AddToWishList{\r\n\tconstructor({\r\n\t\tel,\r\n\t\tproduct_id,\r\n\t\tlogged_user,\r\n\t\tin_wishlist\r\n\t}) {\r\n\t\tthis.view = new AddToWishListView({\r\n\t\t\tel,\r\n\t\t\tproduct_id,\r\n\t\t\tlogged_user,\r\n\t\t\tin_wishlist\r\n\t\t});\r\n\t}\r\n}\r\n","const Urls = {\r\n\tApi: {\r\n\t\tquestion: function (product_id) {\r\n\t\t\treturn '/my/api/2/product/' + product_id + '/question';\r\n\t\t}\r\n\t},\r\n\tTemplates: {\r\n\t\tapp: '/my/api/2/templates?name=modules/product-ask-question/app.html'\r\n\t}\r\n};\r\n\r\nexport default Urls;","import Urls from '../urls.js';\r\nimport { fetch_status, fetch_json, fetch_error } from '../../../../scripts/utils/fetch.js';\r\nimport ModalView from '../../../../scripts/utils/modal-view.js';\r\n\r\nexport default class ProductAskQuestionView extends ModalView {\r\n\tconstructor({ el, modal_selector, customer, navigation }) {\r\n\t\tsuper({ el, modal_selector, navigation });\r\n\t\tthis.template_url = Urls.Templates.app;\r\n\t\tthis.customer = customer;\r\n\t\tthis.site_settings = window.SiteSettings || {};\r\n\t\tthis.render();\r\n\t}\r\n\r\n\tafter_render() {\r\n\t\tthis.once_rendered();\r\n\t\tthis.initSelectors();\r\n\t}\r\n\r\n\tget_context() {\r\n\t\treturn {\r\n\t\t\tcustomer: this.customer,\r\n\t\t\tnavigation: this.navigation\r\n\t\t};\r\n\t}\r\n\r\n\tinitSelectors() {\r\n\t\tthis.removeListeners();\r\n\t\tthis.submit_btn = this.modal.querySelector('[type=\"submit\"]');\r\n\t\tthis.cancel_btn = this.modal.querySelector('.js-cancel');\r\n\t\tthis.content = this.modal.querySelector('.modal-content');\r\n\t\tthis.fields = this.el.querySelectorAll('.form-control');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tthis.submit_btn.addEventListener('click', this.submit.bind(this));\r\n\t\tthis.cancel_btn.addEventListener('click', this.close.bind(this));\r\n\r\n\t\tthis.fields.forEach(input => {\r\n\t\t\t['change', 'paste', 'keyup'].forEach(evt_name => {\r\n\t\t\t\tinput.addEventListener(evt_name, this.validate.bind(this));\r\n\t\t\t});\r\n\t\t});\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.cancel_btn) {\r\n\t\t\tthis.cancel_btn.removeEventListener('click', this.close.bind(this));\r\n\t\t}\r\n\t\tif (this.fields) {\r\n\t\t\t['change', 'keyup', 'paste'].forEach(evt_name => {\r\n\t\t\t\tthis.fields.removeEventListener(evt_name, this.validate.bind(this));\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tget_data () {\r\n\t\treturn {\r\n\t\t\tcustomer_id: this.customer.cid,\r\n\t\t\tquestion_email: this.getElementValue('[name=\"question_email\"]'),\r\n\t\t\tquestion_body: this.getElementValue('[name=\"question_body\"]')\r\n\t\t};\r\n\t}\r\n\r\n\tgetElementValue (selector) {\r\n\t\tconst element = this.modal.querySelector(selector);\r\n\t\tif (element) {\r\n\t\t\treturn element.value;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tvalidate () {\r\n\t\tlet is_valid = true;\r\n\t\tconst data = this.get_data();\r\n\r\n\t\tis_valid = !!data.question_body.length &&\r\n\t\t\t( ( data.question_email && data.question_email.indexOf( '@' ) > 0 ) || data.customer_id );\r\n\r\n\t\tif (is_valid) {\r\n\t\t\tthis.submit_btn.removeAttribute('disabled');\r\n\t\t} else {\r\n\t\t\tthis.submit_btn.setAttribute('disabled', 'disabled');\r\n\t\t}\r\n\t}\r\n\r\n\tsubmit () {\r\n\t\tif (this.site_settings.USE_RECAPTCHA_FOR_FORMS && this.site_settings.RECAPTCHA_SITE_KEY.length && grecaptcha) {\r\n\t\t\tgrecaptcha.ready(function () {\r\n\t\t\t\tgrecaptcha.execute(this.site_settings.RECAPTCHA_SITE_KEY, {action: 'submit'}).then(function (token) {\r\n\t\t\t\t\tthis.submitQuestion(token);\r\n\t\t\t\t}.bind(this));\r\n\t\t\t}.bind(this));\r\n\t\t} else {\r\n\t\t\tthis.submitQuestion();\r\n\t\t}\r\n\t}\r\n\r\n\tsubmitQuestion (g_recaptcha_response) {\r\n\t\tconst url = Urls.Api.question(window.Session.product_id);\r\n\t\tconst data = this.get_data();\r\n\r\n\t\tif (g_recaptcha_response) {\r\n\t\t\tdata.g_recaptcha_response = g_recaptcha_response;\r\n\t\t}\r\n\r\n\t\tfetch(url, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify(data)\r\n\t\t})\r\n\t\t\t.then(fetch_status)\r\n\t\t\t.then(fetch_json)\r\n\t\t\t.then(json => {\r\n\t\t\t\tthis.content.innerHTML = '

Thank you! Your question has been sent.

';\r\n\t\t\t})\r\n\t\t\t.catch(error => {\r\n\t\t\t\tfetch_error(error, true)\r\n\t\t\t\t\t.then(error_json => {\r\n\t\t\t\t\t\tlet message = 'Something went wrong...';\r\n\t\t\t\t\t\tif (error_json && typeof error_json === 'object') {\r\n\t\t\t\t\t\t\tmessage = typeof error_json.responseJSON.Message === 'undefined' ? error_json.Message : error_json.responseJSON.Message;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tthis.content.innerHTML = `

Could not add question.
${message}

`;\r\n\t\t\t\t\t});\r\n\t\t\t\t});\r\n\t}\r\n\r\n}\r\n","import ProductAskQuestionView from './views/product-ask-question.js';\r\n\r\nexport default class ProductAskQuestionApp {\r\n\tconstructor({ placeholder, customer }) {\r\n\t\tthis.view = null;\r\n\t\tthis.btns = document.querySelectorAll('.js-ask-question');\r\n\r\n\t\tif ( this.btns.length ) {\r\n\t\t\tthis.placeholder = placeholder;\r\n\t\t\tthis.customer = customer;\r\n\t\t\tthis.initEventHandlers();\r\n\t\t}\r\n\t}\r\n\r\n\tinitView( event ) {\r\n\t\tconst element = event.target;\r\n\r\n\t\telement.classList.add('progress');\r\n\r\n\t\tthis.view = new ProductAskQuestionView({\r\n\t\t\tel: this.placeholder,\r\n\t\t\tcustomer: this.customer,\r\n\t\t\tmodal_selector: '.product-ask-question-app',\r\n\t\t\tnavigation: {\r\n\t\t\t\timage: window.ProductInfo.Image,\r\n\t\t\t\tcaption: window.ProductInfo.Name\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tthis.view.once_rendered = () => {\r\n\t\t\tthis.view.init_modal();\r\n\t\t\tthis.view.init_popup_events();\r\n\t\t\tthis.view.open();\r\n\t\t\telement.classList.remove('progress');\r\n\t\t};\r\n\t}\r\n\r\n\tinitEventHandlers() {\r\n\t\tthis.btns.forEach(button => {\r\n\t\t\tbutton.addEventListener('click', this.initView.bind(this));\r\n\t\t});\r\n\t}\r\n}\r\n","const Urls = {\r\n\tApi: {\r\n\t\tterms: function( product_id ) {\r\n\t\t\treturn '/my/api/2/product/' + product_id + '/lease-terms';\r\n\t\t},\r\n\t\tterm: '/my/api/2/shopping-cart/active/leasing',\r\n\t\tcontent_blocks: '/crm/json/global/content_blocks/get.aspx'\r\n\t},\r\n\tTemplates: {\r\n\t\tapp: '/my/api/2/templates?name=modules/product-lease/app.html',\r\n\t\thelp: '/my/api/2/templates?name=modules/product-lease/help.html'\r\n\t}\r\n};\r\n\r\nexport default Urls;","import Urls from '../urls.js';\r\nimport { fetch_status, fetch_json, fetch_error, fetcher } from '../../../../scripts/utils/fetch.js';\r\nimport ModalView from '../../../../scripts/utils/modal-view.js';\r\n\r\nexport default class ProductLeaseAppView extends ModalView {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tmodal_selector,\r\n\t\tproduct,\r\n\t\tnavigation\r\n\t}) {\r\n\t\tsuper({ el, modal_selector, navigation });\r\n\t\tthis.template_url = Urls.Templates.app;\r\n\t\tthis.product = product;\r\n\t\tthis.leasing_terms_data = null;\r\n\t\tthis.selected_term = null;\r\n\t\tthis.fetch_leasing_terms();\r\n\t}\r\n\r\n\tafter_render() {\r\n\t\tthis.once_rendered();\r\n\t\tthis.initSelectors();\r\n\t\tthis.select_term();\r\n\t}\r\n\r\n\tget_context() {\r\n\t\tvar settings = window.SiteSettings || {};\r\n\r\n\t\treturn {\r\n\t\t\tproduct: this.product,\r\n\t\t\tnavigation: this.navigation,\r\n\t\t\tterms: this.get_leasing_terms(),\r\n\t\t\ttypes: this.get_leasing_types(),\r\n\t\t\tphone: settings.COMPANY_PHONE ? settings.COMPANY_PHONE : null\r\n\t\t};\r\n\t}\r\n\r\n\tget_unique(collection, key) {\r\n\t\treturn collection.filter((item, index, self) => self.findIndex(t => t.place === item.place && t[key] === item[key]) === index);\r\n\t}\r\n\r\n\tget_leasing_terms() {\r\n\t\tconst leasing_terms = this.get_unique(this.leasing_terms_data, 'leasing_term_id');\r\n\r\n\t\treturn leasing_terms;\r\n\t}\r\n\r\n\tget_leasing_types() {\r\n\t\tconst leasing_types = this.get_unique(this.leasing_terms_data, 'leasing_type_id');\r\n\r\n\t\treturn leasing_types;\r\n\t}\r\n\r\n\tinitSelectors() {\r\n\t\tthis.removeListeners();\r\n\t\tthis.leasing_term_element = this.modal.querySelector('[name=\"leasing-term\"]');\r\n\t\tthis.leasing_type_element = this.modal.querySelector('[name=\"leasing-type\"]');\r\n\t\tthis.equipment_cost_element = this.modal.querySelector('[name=\"equipment-cost\"]');\r\n\t\tthis.monthly_payment_elements = this.modal.querySelectorAll('.js-monthly-payment');\r\n\t\tthis.btn_proceed_element = this.modal.querySelector('[name=\"proceed\"]');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tif (this.leasing_term_element) {\r\n\t\t\tthis.leasing_term_element.addEventListener('change', this.select_term.bind(this));\r\n\t\t}\r\n\t\tif (this.equipment_cost_element) {\r\n\t\t\t['change', 'keyup', 'paste'].forEach(evt_name => {\r\n\t\t\t\tthis.equipment_cost_element.addEventListener(evt_name, this.select_term.bind(this));\r\n\t\t\t});\r\n\t\t}\r\n\t\tif (this.btn_proceed_element) {\r\n\t\t\tthis.btn_proceed_element.addEventListener('click', this.save_term.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.leasing_term_element) {\r\n\t\t\tthis.leasing_term_element.removeEventListener('change', this.select_term.bind(this));\r\n\t\t}\r\n\t\tif (this.equipment_cost_element) {\r\n\t\t\t['change', 'keyup', 'paste'].forEach(evt_name => {\r\n\t\t\t\tthis.equipment_cost_element.removeEventListener(evt_name, this.select_term.bind(this));\r\n\t\t\t});\r\n\t\t}\r\n\t\tif (this.btn_proceed_element) {\r\n\t\t\tthis.btn_proceed_element.removeEventListener('click', this.save_term.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tfetch_leasing_terms() {\r\n\t\tconst url = Urls.Api.terms(this.product.id);\r\n\r\n\t\tfetcher(url)\r\n\t\t\t.then(data => {\r\n\t\t\t\tthis.leasing_terms_data = data;\r\n\r\n\t\t\t\tthis.render();\r\n\t\t\t});\r\n\t}\r\n\r\n\tselect_term() {\r\n\t\tconst terms = this.leasing_terms_data.filter(({\r\n\t\t\tleasing_term_id,\r\n\t\t\tleasing_type_id\r\n\t\t}) => {\r\n\t\t\treturn leasing_term_id === this.leasing_term_element.value && leasing_type_id === this.leasing_type_element.value;\r\n\t\t});\r\n\r\n\t\tthis.selected_term = terms.find(term => {\r\n\t\t\treturn this.product.price >= term.amount_from && this.product.price <= term.amount_to;\r\n\t\t});\r\n\r\n\t\tif (this.selected_term) {\r\n\t\t\tconst payment = parseFloat(this.selected_term.rate) * this.get_equipment_cost_value();\r\n\t\t\tconst payment_rounded = Math.round(payment * 100) / 100;\r\n\t\t\tconst payment_money = payment_rounded.money();\r\n\r\n\t\t\tthis.monthly_payment_elements.forEach(elm => elm.textContent = payment_money);\r\n\t\t\tthis.btn_proceed_element.removeAttribute('disabled');\r\n\t\t\tthis.equipment_cost_element.removeAttribute('disabled');\r\n\t\t} else {\r\n\t\t\t// show error?\r\n\t\t\tthis.btn_proceed_element.setAttribute('disabled', true);\r\n\t\t}\r\n\t}\r\n\r\n\tget_equipment_cost_value() {\r\n\t\tconst val = this.equipment_cost_element.dataset.price;\r\n\r\n\t\treturn val ? parseFloat(val) : 0;\r\n\t}\r\n\r\n\tsave_term() {\r\n\t\tif (this.selected_term) {\r\n\t\t\tconst term = this.get_parsed_values();\r\n\t\t\tconst url = Urls.Api.term;\r\n\r\n\t\t\tthis.btn_proceed_element.setAttribute('disabled', true);\r\n\t\t\tthis.btn_proceed_element.classList.add('progress');\r\n\r\n\t\t\tfetch(url, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t\t},\r\n\t\t\t\tbody: JSON.stringify(term)\r\n\t\t\t})\r\n\t\t\t\t.then(fetch_status)\r\n\t\t\t\t.then(fetch_json)\r\n\t\t\t\t.then(() => {\r\n\t\t\t\t\tconst add_product_event = new CustomEvent('[add-to-cart]:add-product');\r\n\r\n\t\t\t\t\tthis.btn_proceed_element.removeAttribute('disabled');\r\n\t\t\t\t\tthis.btn_proceed_element.classList.remove('progress');\r\n\r\n\t\t\t\t\tdocument.dispatchEvent(add_product_event);\r\n\t\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tget_parsed_values() {\r\n\t\treturn {\r\n\t\t\t...this.selected_term,\r\n\t\t\tLeaserId: this.selected_term.leaser_id,\r\n\t\t\tLeaserTermId: this.selected_term.leaser_term_id,\r\n\t\t\tLeasingTermId: this.selected_term.leasing_term_id,\r\n\t\t\tLeasingTypeId: this.selected_term.leasing_type_id\r\n\t\t};\r\n\t}\r\n\r\n}\r\n","import ModalView from '../../../../scripts/utils/modal-view.js';\r\nimport Urls from '../urls.js';\r\n\r\nexport default class ProductLeaseHelpView extends ModalView {\r\n\tconstructor({ el, modal_selector, navigation }) {\r\n\t\tsuper({ el, modal_selector, navigation });\r\n\t\tthis.template_url = Urls.Templates.help;\r\n\t\tthis.render();\r\n\t}\r\n\r\n\tget_context() {\r\n\t\tvar settings = window.SiteSettings || {};\r\n\r\n\t\treturn {\r\n\t\t\tnavigation: this.navigation,\r\n\t\t\tphone: settings.COMPANY_PHONE ? settings.COMPANY_PHONE : null\r\n\t\t};\r\n\t}\r\n}","import ProductLeaseAppView from './views/app.js';\r\nimport ProductLeaseHelpView from './views/help.js';\r\n\r\nexport default class ProductLeaseApp {\r\n\tconstructor({\r\n\t\tproduct_id,\r\n\t\tproduct_price,\r\n\t\tplaceholder\r\n\t}) {\r\n\t\tthis.view = null;\r\n\r\n\t\tthis.el = document.querySelector('.js-lease-calculate');\r\n\r\n\t\tif (this.el) {\r\n\t\t\tthis.el.classList.remove('d-none');\r\n\t\t\tthis.btn = this.el.querySelector('button');\r\n\t\t\tthis.btn_help = this.el.querySelector('.js-lease-calculate-help');\r\n\r\n\t\t\tthis.product = {\r\n\t\t\t\tid: product_id,\r\n\t\t\t\tname: document.querySelector('h1').textContent,\r\n\t\t\t\tprice: product_price,\r\n\t\t\t\tsku: document.querySelector('.js-sku').dataset.sku,\r\n\t\t\t\timage: document.querySelector('#product-main-image').src\r\n\t\t\t}\r\n\r\n\t\t\tthis.placeholder = placeholder;\r\n\t\t\tthis.help_popup = null;\r\n\r\n\t\t\tthis.init_event_handlers();\r\n\t\t}\r\n\t}\r\n\r\n\tinitLeaseView() {\r\n\t\tthis.btn.classList.add('progress');\r\n\t\tthis.view = new ProductLeaseAppView({\r\n\t\t\tel: this.placeholder,\r\n\t\t\tmodal_selector: '.product-lease-app',\r\n\t\t\tproduct: this.product,\r\n\t\t\tnavigation: {\r\n\t\t\t\timage: window.ProductInfo.Image,\r\n\t\t\t\tcaption: window.ProductInfo.Name\r\n\t\t\t}\r\n\t\t});\r\n\t\tthis.view.once_rendered = () => {\r\n\t\t\tthis.view.init_modal();\r\n\t\t\tthis.view.init_popup_events();\r\n\t\t\tthis.view.open();\r\n\t\t\tthis.btn.classList.remove('progress');\r\n\t\t};\r\n\t}\r\n\r\n\tshowHelpPopup (event) {\r\n\t\tevent.stopPropagation();\r\n\r\n\t\tthis.btn_help.classList.add('progress');\r\n\t\tthis.help_popup = new ProductLeaseHelpView({\r\n\t\t\tel: this.placeholder,\r\n\t\t\tmodal_selector: '.product-lease-app-help',\r\n\t\t\tnavigation: {\r\n\t\t\t\timage: window.ProductInfo.Image,\r\n\t\t\t\tcaption: window.ProductInfo.Name\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tthis.help_popup.once_rendered = () => {\r\n\t\t\tthis.help_popup.init_modal();\r\n\t\t\tthis.help_popup.init_popup_events();\r\n\t\t\tthis.help_popup.open();\r\n\t\t\tthis.btn_help.classList.remove('progress');\r\n\t\t};\r\n\t}\r\n\r\n\tinit_event_handlers() {\r\n\t\tthis.btn.addEventListener('click', this.initLeaseView.bind(this));\r\n\t\tthis.btn_help.addEventListener('click', this.showHelpPopup.bind(this));\r\n\t}\r\n}\r\n","const Urls = {\r\n\tApi: {\r\n\t\tform: '/my/api/2/order/request'\r\n\t},\r\n\tTemplates: {\r\n\t\tapp: '/my/api/2/templates?name=modules/product-in-stock/app-quote.html',\r\n\t\thelp: '/my/api/2/templates?name=modules/product-in-stock/help.html'\r\n\t}\r\n};\r\n\r\nexport default Urls;","import Urls from '../urls.js';\r\nimport { fetch_status, fetch_json, fetch_error } from '../../../../scripts/utils/fetch.js';\r\nimport ModalView from '../../../../scripts/utils/modal-view.js';\r\n\r\nexport default class ProductInStockAppView extends ModalView {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tmodal_selector,\r\n\t\tproduct,\r\n\t\tcustomer,\r\n\t\tnavigation\r\n\t}) {\r\n\t\tsuper({ el, modal_selector, navigation });\r\n\t\tthis.template_url = Urls.Templates.app;\r\n\t\tthis.product = product;\r\n\t\tthis.customer = customer;\r\n\t\tthis.is_submit_success = false;\r\n\t\tthis.site_settings = window.SiteSettings || {};\r\n\t\tthis.opts = {\r\n\t\t\tproduct_name: product.name\r\n\t\t};\r\n\t\tthis.render();\r\n\t}\r\n\r\n\tafter_render() {\r\n\t\tthis.once_rendered();\r\n\t\tthis.initSelectors();\r\n\t}\r\n\r\n\tget_context() {\r\n\t\tlet customerData = null;\r\n\r\n\t\tif (this.customer && !this.customer.guest) {\r\n\t\t\tcustomerData = {\r\n\t\t\t\tFirstName: this.customer.fname,\r\n\t\t\t\tLastName: this.customer.lname,\r\n\t\t\t\tEmailAddress: this.customer.emailaddress,\r\n\t\t\t\tPhoneNumber: this.customer.dtphone_body\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\treturn {\r\n\t\t\tis_submit_success: this.is_submit_success,\r\n\t\t\tproduct: this.product,\r\n\t\t\t// customer: this.customer,\r\n\t\t\tnavigation: this.navigation,\r\n\t\t\tcustomer: customerData\r\n\t\t};\r\n\t}\r\n\r\n\tinitSelectors() {\r\n\t\tthis.removeListeners();\r\n\t\tthis.submit_btn = this.modal.querySelector('[type=\"submit\"]');\r\n\t\tthis.cancel_btn = this.modal.querySelector('.js-cancel');\r\n\t\tthis.content = this.modal.querySelector('.modal-content');\r\n\t\tthis.form = this.content.querySelector('form');\r\n\t\tthis.fields = this.content.querySelectorAll('.form-control');\r\n\t\tthis.addListeners();\r\n\t}\r\n\r\n\taddListeners () {\r\n\t\tthis.submit_btn.addEventListener('click', this.submit.bind(this));\r\n\t\tthis.cancel_btn.addEventListener('click', this.close.bind(this));\r\n\r\n\t\tthis.fields.forEach(field => {\r\n\t\t\t['change', 'paste', 'keyup'].forEach(evt_name => {\r\n\t\t\t\tfield.addEventListener(evt_name, this.change_event.bind(this));\r\n\t\t\t});\r\n\t\t});\r\n\t}\r\n\r\n\tremoveListeners () {\r\n\t\tif (this.cancel_btn) {\r\n\t\t\tthis.cancel_btn.removeEventListener('click', this.close.bind(this));\r\n\t\t}\r\n\t\tif (this.fields) {\r\n\t\t\t['change', 'keyup', 'paste'].forEach(evt_name => {\r\n\t\t\t\tthis.fields.forEach(field => {\r\n\t\t\t\t\tfield.removeEventListener(evt_name, this.change_event.bind(this));\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tcollectFormData() {\r\n\t\tconst fd = new FormData(this.form);\r\n\t\tconst data = {};\r\n\r\n\t\tfor(let pair of fd.entries()) {\r\n\t\t\tconst [ key ] = pair;\r\n\t\t\tdata[key] = fd.getAll(key).length > 1 ? fd.getAll(key) : fd.get(key);\r\n\t\t}\r\n\r\n\t\tdata.Products = [{\r\n\t\t\tProductId: this.product.id,\r\n\t\t\tQuantity: data.qty\r\n\t\t}];\r\n\t\tdelete data.qty;\r\n\t\tdata.Source = 'Availability Check';\r\n\r\n\t\treturn data;\r\n\t}\r\n\r\n\tgetElementValue (selector) {\r\n\t\tconst element = this.modal.querySelector(selector);\r\n\t\tif (element) {\r\n\t\t\treturn element.value;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tisEmail (value) {\r\n\t\treturn /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i.test(value);\r\n\t}\r\n\r\n\tisUSZIP (value) {\r\n\t\treturn /^\\d{5}([ -]\\d{4})?$/.test(value);\r\n\t}\r\n\r\n\tisPhone (value) {\r\n\t\treturn /^[\\+]?[(]?[0-9]{3}[)]?[-\\s\\.]?[0-9]{3}[-\\s\\.]?[0-9]{4,6}$/.test(value);\r\n\t}\r\n\r\n\tchange_event (e) {\r\n\t\tif (this.is_valid()) {\r\n\t\t\tthis.submit_btn.removeAttribute('disabled');\r\n\t\t} else {\r\n\t\t\tthis.submit_btn.setAttribute('disabled', true);\r\n\t\t}\r\n\t}\r\n\r\n\tis_valid () {\r\n\t\tvar valid = true;\r\n\t\tconst data = this.collectFormData();\r\n\r\n\t\tfor (const [k, v] of Object.entries(data)) {\r\n\t\t\tif (valid && !v.length) {\r\n\t\t\t\tvalid = false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (valid) {\r\n\t\t\tif (!this.isEmail(data.EmailAddress)) {\r\n\t\t\t\tvalid = false;\r\n\t\t\t}\r\n\t\t\tif (!this.isUSZIP(data.ShipZipCode)) {\r\n\t\t\t\tvalid = false;\r\n\t\t\t}\r\n\t\t\tif (!this.isPhone(data.PhoneNumber)) {\r\n\t\t\t\tvalid = false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn valid;\r\n\t}\r\n\r\n\tsubmit () {\r\n\t\tif (this.site_settings.USE_RECAPTCHA_FOR_FORMS && this.site_settings.RECAPTCHA_SITE_KEY.length && grecaptcha) {\r\n\t\t\tgrecaptcha.ready(function () {\r\n\t\t\t\tgrecaptcha.execute(this.site_settings.RECAPTCHA_SITE_KEY, {action: 'submit'}).then(function (token) {\r\n\t\t\t\t\tthis.submitRequest(token);\r\n\t\t\t\t}.bind(this));\r\n\t\t\t}.bind(this));\r\n\t\t} else {\r\n\t\t\tthis.submitRequest();\r\n\t\t}\r\n\t}\r\n\r\n\tsubmitRequest (g_recaptcha_response) {\r\n\t\tconst data = this.collectFormData();\r\n\r\n\t\tif (g_recaptcha_response) {\r\n\t\t\tdata.g_recaptcha_response = g_recaptcha_response;\r\n\t\t}\r\n\r\n\t\tthis.submit_btn.classList.add('progress');\r\n\r\n\t\tfetch( Urls.Api.form, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify(data)\r\n\t\t})\r\n\t\t\t.then(fetch_status)\r\n\t\t\t.then(fetch_json)\r\n\t\t\t.then(() => {\r\n\t\t\t\tthis.is_submit_success = true;\r\n\t\t\t\tthis.render();\r\n\t\t\t})\r\n\t\t\t.catch(error => {\r\n\t\t\t\tfetch_error(error, true)\r\n\t\t\t\t\t.then(error_json => {\r\n\t\t\t\t\t\tlet message = 'Something went wrong...';\r\n\t\t\t\t\t\tif (error_json && typeof error_json === 'object') {\r\n\t\t\t\t\t\t\tmessage = typeof error_json.responseJSON.Message === 'undefined' ? error_json.Message : error_json.responseJSON.Message;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tthis.content.innerHTML = `

${message}

`;\r\n\t\t\t\t\t});\r\n\t\t\t})\r\n\t\t\t.finally(() => {\r\n\t\t\t\tthis.submit_btn.classList.remove('progress');\r\n\t\t\t});\r\n\t}\r\n\r\n}\r\n","import ModalView from '../../../../scripts/utils/modal-view.js';\r\nimport Urls from '../urls.js';\r\n\r\nexport default class ProductInStockHelpView extends ModalView {\r\n\tconstructor({ el, modal_selector, navigation }) {\r\n\t\tsuper({ el, modal_selector, navigation });\r\n\t\tthis.template_url = Urls.Templates.help;\r\n\t\tthis.render();\r\n\t}\r\n}","import ProductInStockAppView from './views/app.js';\r\nimport ProductInStockHelpView from './views/help.js';\r\n\r\nexport default class ProductInStockApp {\r\n\tconstructor({\r\n\t\tel,\r\n\t\tmodal_placeholder_selector,\r\n\t\tcustomer\r\n\t}) {\r\n\t\tthis.el = el;\r\n\t\tthis.modal_placeholder = modal_placeholder_selector;\r\n\t\tthis.view = null;\r\n\t\tthis.help_popup = null;\r\n\t\tthis.customer = customer;\r\n\r\n\t\tif (this.el) {\r\n\t\t\tthis.btn = this.el.querySelector('button');\r\n\t\t\tthis.btn_help = this.el.querySelector('.btn-help');\r\n\r\n\t\t\tthis.product = {\r\n\t\t\t\tid: this.el.dataset.productId,\r\n\t\t\t\tname: this.el.dataset.productName\r\n\t\t\t}\r\n\r\n\t\t\tthis.init_event_handlers();\r\n\t\t}\r\n\t}\r\n\r\n\tinitAppView() {\r\n\t\tthis.btn.classList.add('progress');\r\n\t\tthis.view = new ProductInStockAppView({\r\n\t\t\tel: this.modal_placeholder,\r\n\t\t\tmodal_selector: '.product-in-stock-app',\r\n\t\t\tproduct: this.product,\r\n\t\t\tcustomer: this.customer,\r\n\t\t\tnavigation: {\r\n\t\t\t\timage: window.ProductInfo.Image,\r\n\t\t\t\tcaption: window.ProductInfo.Name\r\n\t\t\t}\r\n\t\t});\r\n\t\tthis.view.once_rendered = () => {\r\n\t\t\tthis.view.init_modal();\r\n\t\t\tthis.view.init_popup_events();\r\n\t\t\tthis.view.open();\r\n\t\t\tthis.btn.classList.remove('progress');\r\n\t\t};\r\n\t}\r\n\r\n\tshowHelpPopup (event) {\r\n\t\tevent.stopPropagation();\r\n\r\n\t\tthis.btn_help.classList.add('progress');\r\n\t\tthis.help_popup = new ProductInStockHelpView({\r\n\t\t\tel: this.modal_placeholder,\r\n\t\t\tmodal_selector: '.product-in-stock-app-help',\r\n\t\t\tnavigation: {\r\n\t\t\t\timage: window.ProductInfo.Image,\r\n\t\t\t\tcaption: window.ProductInfo.Name\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tthis.help_popup.once_rendered = () => {\r\n\t\t\tthis.help_popup.init_modal();\r\n\t\t\tthis.help_popup.init_popup_events();\r\n\t\t\tthis.help_popup.open();\r\n\t\t\tthis.btn_help.classList.remove('progress');\r\n\t\t};\r\n\t}\r\n\r\n\tinit_event_handlers() {\r\n\t\tthis.btn.addEventListener('click', this.initAppView.bind(this));\r\n\t\tthis.btn_help.addEventListener('click', this.showHelpPopup.bind(this));\r\n\t}\r\n}\r\n","const Urls = {\r\n\tApi: {\r\n\t\tfeedback: function (review_id) {\r\n\t\t\treturn `/my/api/2/customer/reviews/${review_id}/feedback`;\r\n\t\t}\r\n\t}\r\n};\r\n\r\nexport default Urls;","import { fetcher } from '../../../scripts/utils/fetch.js';\r\nimport Urls from './urls.js';\r\n\r\nexport default class ReviewFeedbackApp {\r\n\tconstructor({ el }) {\r\n\t\tif (!el) {\r\n\t\t\tthrow new Error('\"el\" is required!');\r\n\t\t}\r\n\r\n\t\tthis.el = el;\r\n\t\tthis.review_id = this.el.getAttribute('data-review-id');\r\n\t\tthis.positive_feedback_btn = this.el.querySelector('.js-btn-review-feedback-positive');\r\n\t\tthis.negative_feedback_btn = this.el.querySelector('.js-btn-review-feedback-negative');\r\n\t\tthis.flag_btn = this.el.querySelector('.js-btn-review-feedback-flag');\r\n\t\tthis.controls = this.el.querySelectorAll('.feedback-control');\r\n\t\tthis.positive_count = this.el.querySelector('.js-review-feedback-positive-count');\r\n\t\tthis.negative_count = this.el.querySelector('.js-review-feedback-negative-count');\r\n\r\n\t\tthis.markVoted();\r\n\t\tthis.bindEvents();\r\n\t}\r\n\r\n\tbindEvents() {\r\n\t\tthis.positive_feedback_btn.addEventListener('click', this.sendPositive.bind(this));\r\n\t\tthis.negative_feedback_btn.addEventListener('click', this.sendNegative.bind(this));\r\n\t\tif( this.flag_btn ) {\r\n\t\t\tthis.flag_btn.addEventListener('click', this.sendFlag.bind(this));\r\n\t\t}\r\n\t}\r\n\r\n\tsendPositive(event) {\r\n\t\tconst feedback = {\r\n\t\t\treview_id: this.review_id,\r\n\t\t\tpositive: true\r\n\t\t};\r\n\r\n\t\tthis.send(feedback, event.currentTarget);\r\n\t}\r\n\r\n\tsendNegative(event) {\r\n\t\tconst feedback = {\r\n\t\t\treview_id: this.review_id,\r\n\t\t\tnegative: true\r\n\t\t};\r\n\r\n\t\tthis.send(feedback, event.currentTarget);\r\n\t}\r\n\r\n\tsendFlag(event) {\r\n\t\tconst feedback = {\r\n\t\t\treview_id: this.review_id,\r\n\t\t\tflag: true\r\n\t\t};\r\n\r\n\t\tthis.send(feedback, event.currentTarget);\r\n\t}\r\n\r\n\tsend(feedback, btn) {\r\n\t\tconst url = Urls.Api.feedback(this.review_id);\r\n\r\n\t\tbtn.classList.add('progress');\r\n\t\tfetcher(url, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Accept': 'application/json',\r\n\t\t\t\t'Content-Type': 'application/json'\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify(feedback)\r\n\t\t})\r\n\t\t\t.then(response => {\r\n\t\t\t\tconst comment = response.data;\r\n\r\n\t\t\t\tthis.render(comment);\r\n\t\t\t\tbtn.classList.add('selected');\r\n\t\t\t\tthis.saveVoteLocal(comment);\r\n\t\t\t})\r\n\t\t\t.finally(() => {\r\n\t\t\t\tbtn.classList.remove('progress');\r\n\t\t\t});\r\n\t}\r\n\r\n\tsaveVoteLocal(comment) {\r\n\t\tconst comment_id = comment.commentid;\r\n\r\n\t\tif (!this.isVoted(comment_id)) {\r\n\t\t\tif (comment_id && window.localStorage) {\r\n\t\t\t\tconst voted = this.getVotedLocal();\r\n\r\n\t\t\t\tvoted.push(comment_id);\r\n\t\t\t\tlocalStorage.setItem('voted_reviews', JSON.stringify(voted));\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tmarkVoted() {\r\n\t\tif (this.isVoted(this.review_id)) {\r\n\t\t\tthis.controls.forEach(elem => elem.classList.add('voted'));\r\n\t\t}\r\n\t}\r\n\r\n\tisVoted(comment_id) {\r\n\t\tif (comment_id && window.localStorage){\r\n\t\t\tconst voted = this.getVotedLocal();\r\n\r\n\t\t\treturn voted && voted.includes(comment_id);\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tgetVotedLocal() {\r\n\t\tif (window.localStorage) {\r\n\t\t\tconst voted = localStorage.getItem('voted_reviews');\r\n\r\n\t\t\tif (voted) {\r\n\t\t\t\treturn JSON.parse(voted);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn [];\r\n\t}\r\n\r\n\trender(review) {\r\n\t\tthis.controls.forEach(elem => elem.classList.add('voted'));\r\n\t\tthis.positive_count.textContent = review.feedback_positive_count || 0;\r\n\t\tthis.negative_count.textContent = review.feedback_negative_count || 0;\r\n\t}\r\n}","export default class ProductMapPricing {\r\n\tconstructor({\r\n\t\tproduct_info,\r\n\t\tcustomer_logged_in,\r\n\t\tcustomer_is_approved,\r\n\t\tproduct_in_cart,\r\n\t\tadv_source\r\n\t}) {\r\n\t\tthis.selectors = {\r\n\t\t\tprices: '.js-product-price-label',\r\n\t\t\tmap_prices: '.js-product-map-price',\r\n\t\t\tmap_show: '.js-map-show',\r\n\t\t\tmap_hide: '.js-map-hide',\r\n\t\t\tmap_remove: '.js-map-remove',\r\n\t\t\tapproved_show: '.js-map-approved-show',\r\n\t\t\tapproved_remove: '.js-map-approved-remove'\r\n\t\t};\r\n\r\n\t\tthis.settings = window.SiteSettings || {};\r\n\t\tthis.product_info = {\r\n\t\t\t...window.ProductSettings,\r\n\t\t\t...product_info,\r\n\t\t\tproduct_in_cart,\r\n\t\t\tadv_source\r\n\t\t};\r\n\t\tthis.customer_logged_in = customer_logged_in;\r\n\t\tthis.customer_is_approved = customer_is_approved;\r\n\t\tthis.list_price = this.product_info.DynamicNumbers.price;\r\n\t\tthis.is_list_price = false;\r\n\r\n\t\tthis.init();\r\n\r\n\t\tdocument.dispatchEvent(new CustomEvent('map-pricing:ready'));\r\n\t}\r\n\r\n\tinit () {\r\n\t\tif (!this.is_list_price) {\r\n\t\t\tthis.checkMap();\r\n\t\t\tif (this.is_list_price) {\r\n\t\t\t\tthis.updatePrice();\r\n\t\t\t\tthis.showPrices();\r\n\r\n\t\t\t\tconst map_pricing = new CustomEvent('show-retail-prices');\r\n\t\t\t\tdocument.dispatchEvent(map_pricing);\r\n\t\t\t\tlocalStorage.setItem('productPricing', 'retail');\r\n\t\t\t} else {\r\n\t\t\t\tlocalStorage.setItem('productPricing', 'map');\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tcheckMap() {\r\n\t\tlet show_list_for_approved = false;\r\n\r\n\t\tif (this.product_info.MapPolicy.ShowRetailPriceForApprovedCustomer && this.customer_is_approved) {\r\n\t\t\tshow_list_for_approved = true;\r\n\t\t}\r\n\r\n\t\tif (this.customer_logged_in) {\r\n\t\t\tthis.is_list_price = true;\r\n\t\t}\r\n\r\n\t\tif (\r\n\t\t\tthis.product_info.MapPolicy.ShowRetailPriceForCustomerAtProduct === false &&\r\n\t\t\t!show_list_for_approved\r\n\t\t) {\r\n\t\t\tthis.is_list_price = false;\r\n\t\t\tthis.list_price = this.product_info.DynamicNumbers.map_price;\r\n\t\t}\r\n\t}\r\n\r\n\tupdatePrice() {\r\n\t\tconst price = String(this.list_price.money());\r\n\t\tconst prices = document.querySelectorAll(this.selectors.prices);\r\n\r\n\t\tprices.forEach(elem => {\r\n\t\t\telem.textContent = price;\r\n\t\t\telem.setAttribute('data-price', this.list_price);\r\n\t\t});\r\n\t}\r\n\r\n\tshowPrices() {\r\n\t\tconst price = String(this.list_price.money());\r\n\r\n\t\tthis.changeElementsClass(this.selectors.map_show, 'd-none', 'remove');\r\n\t\tthis.changeElementsClass(this.selectors.map_hide, 'd-none', 'add');\r\n\t\tthis.removeElements(this.selectors.map_remove);\r\n\r\n\t\tif (this.customer_is_approved) {\r\n\t\t\tthis.changeElementsClass(this.selectors.approved_show, 'd-none', 'remove');\r\n\t\t\tthis.removeElements(this.selectors.approved_remove);\r\n\t\t}\r\n\r\n\t\tdocument.querySelectorAll(this.selectors.map_prices).forEach(el => {\r\n\t\t\tel.textContent = price;\r\n\t\t});\r\n\t}\r\n\r\n\trecalculatePrice() {\r\n\t\tconst retail_price = parseFloat(String(this.list_price).replace(',', ''));\r\n\r\n\t\tif (window.product_options) {\r\n\t\t\twindow.product_options.start_price = retail_price;\r\n\t\t\twindow.product_options.recalculate_total_price();\r\n\t\t}\r\n\t}\r\n\r\n\tchangeElementsClass (selector, css_class, action) {\r\n\t\tconst els = document.querySelectorAll(selector);\r\n\r\n\t\tif (els.length > 0) {\r\n\t\t\tels.forEach(el => {\r\n\t\t\t\tswitch (action) {\r\n\t\t\t\t\tcase 'add':\r\n\t\t\t\t\t\tel.classList.add(css_class);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase 'remove':\r\n\t\t\t\t\t\tel.classList.remove(css_class);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tremoveElements (selector) {\r\n\t\tconst els = document.querySelectorAll(selector);\r\n\r\n\t\tif (els.length > 0) {\r\n\t\t\tels.forEach(el => {\r\n\t\t\t\tel.remove();\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n}\r\n","var win = window;\n\nexport var raf = win.requestAnimationFrame\n || win.webkitRequestAnimationFrame\n || win.mozRequestAnimationFrame\n || win.msRequestAnimationFrame\n || function(cb) { return setTimeout(cb, 16); };\n","var win = window;\n\nexport var caf = win.cancelAnimationFrame\n || win.mozCancelAnimationFrame\n || function(id){ clearTimeout(id); };\n","export function extend() {\n var obj, name, copy,\n target = arguments[0] || {},\n i = 1,\n length = arguments.length;\n\n for (; i < length; i++) {\n if ((obj = arguments[i]) !== null) {\n for (name in obj) {\n copy = obj[name];\n\n if (target === copy) {\n continue;\n } else if (copy !== undefined) {\n target[name] = copy;\n }\n }\n }\n }\n return target;\n}","export function checkStorageValue (value) {\n return ['true', 'false'].indexOf(value) >= 0 ? JSON.parse(value) : value;\n}","export function setLocalStorage(storage, key, value, access) {\n if (access) {\n try { storage.setItem(key, value); } catch (e) {}\n }\n return value;\n}","export function getBody () {\n var doc = document,\n body = doc.body;\n\n if (!body) {\n body = doc.createElement('body');\n body.fake = true;\n }\n\n return body;\n}","export var docElement = document.documentElement;","import { docElement } from './docElement.js';\n\nexport function setFakeBody (body) {\n var docOverflow = '';\n if (body.fake) {\n docOverflow = docElement.style.overflow;\n //avoid crashing IE8, if background image is used\n body.style.background = '';\n //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible\n body.style.overflow = docElement.style.overflow = 'hidden';\n docElement.appendChild(body);\n }\n\n return docOverflow;\n}","import { docElement } from './docElement.js';\n\nexport function resetFakeBody (body, docOverflow) {\n if (body.fake) {\n body.remove();\n docElement.style.overflow = docOverflow;\n // Trigger layout so kinetic scrolling isn't disabled in iOS6+\n // eslint-disable-next-line\n docElement.offsetHeight;\n }\n}","// cross browsers addRule method\nimport { raf } from './raf.js';\nexport function addCSSRule(sheet, selector, rules, index) {\n // return raf(function() {\n 'insertRule' in sheet ?\n sheet.insertRule(selector + '{' + rules + '}', index) :\n sheet.addRule(selector, rules, index);\n // });\n}","export function getCssRulesLength(sheet) {\n var rule = ('insertRule' in sheet) ? sheet.cssRules : sheet.rules;\n return rule.length;\n}","// https://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/\nexport function forEach (arr, callback, scope) {\n for (var i = 0, l = arr.length; i < l; i++) {\n callback.call(scope, arr[i], i);\n }\n}","export var classListSupport = 'classList' in document.createElement('_');","import { classListSupport } from './classListSupport.js';\n\nvar hasClass = classListSupport ?\n function (el, str) { return el.classList.contains(str); } :\n function (el, str) { return el.className.indexOf(str) >= 0; };\n\nexport { classListSupport, hasClass };","import { classListSupport, hasClass } from './hasClass.js';\nvar addClass = classListSupport ?\n function (el, str) {\n if (!hasClass(el, str)) { el.classList.add(str); }\n } :\n function (el, str) {\n if (!hasClass(el, str)) { el.className += ' ' + str; }\n };\n\nexport { addClass };","import { classListSupport, hasClass } from './hasClass.js';\nvar removeClass = classListSupport ?\n function (el, str) {\n if (hasClass(el, str)) { el.classList.remove(str); }\n } :\n function (el, str) {\n if (hasClass(el, str)) { el.className = el.className.replace(str, ''); }\n };\n\nexport { removeClass };","export function hasAttr(el, attr) {\n return el.hasAttribute(attr);\n}","export function getAttr(el, attr) {\n return el.getAttribute(attr);\n}","export function isNodeList(el) {\n // Only NodeList has the \"item()\" function\n return typeof el.item !== \"undefined\"; \n}","import { isNodeList } from \"./isNodeList.js\";\n\nexport function setAttrs(els, attrs) {\n els = (isNodeList(els) || els instanceof Array) ? els : [els];\n if (Object.prototype.toString.call(attrs) !== '[object Object]') { return; }\n\n for (var i = els.length; i--;) {\n for(var key in attrs) {\n els[i].setAttribute(key, attrs[key]);\n }\n }\n}","import { isNodeList } from \"./isNodeList.js\";\n\nexport function removeAttrs(els, attrs) {\n els = (isNodeList(els) || els instanceof Array) ? els : [els];\n attrs = (attrs instanceof Array) ? attrs : [attrs];\n\n var attrLength = attrs.length;\n for (var i = els.length; i--;) {\n for (var j = attrLength; j--;) {\n els[i].removeAttribute(attrs[j]);\n }\n }\n}","export function arrayFromNodeList (nl) {\n var arr = [];\n for (var i = 0, l = nl.length; i < l; i++) {\n arr.push(nl[i]);\n }\n return arr;\n}","export function hideElement(el, forceHide) {\n if (el.style.display !== 'none') { el.style.display = 'none'; }\n}","export function showElement(el, forceHide) {\n if (el.style.display === 'none') { el.style.display = ''; }\n}","export function isVisible(el) {\n return window.getComputedStyle(el).display !== 'none';\n}","export function whichProperty(props){\n if (typeof props === 'string') {\n var arr = [props],\n Props = props.charAt(0).toUpperCase() + props.substr(1),\n prefixes = ['Webkit', 'Moz', 'ms', 'O'];\n \n prefixes.forEach(function(prefix) {\n if (prefix !== 'ms' || props === 'transform') {\n arr.push(prefix + Props);\n }\n });\n\n props = arr;\n }\n\n var el = document.createElement('fakeelement'),\n len = props.length;\n for(var i = 0; i < props.length; i++){\n var prop = props[i];\n if( el.style[prop] !== undefined ){ return prop; }\n }\n\n return false; // explicit for ie9-\n}\n","// get transitionend, animationend based on transitionDuration\n// @propin: string\n// @propOut: string, first-letter uppercase\n// Usage: getEndProperty('WebkitTransitionDuration', 'Transition') => webkitTransitionEnd\nexport function getEndProperty(propIn, propOut) {\n var endProp = false;\n if (/^Webkit/.test(propIn)) {\n endProp = 'webkit' + propOut + 'End';\n } else if (/^O/.test(propIn)) {\n endProp = 'o' + propOut + 'End';\n } else if (propIn) {\n endProp = propOut.toLowerCase() + 'end';\n }\n return endProp;\n}","// Test via a getter in the options object to see if the passive property is accessed\nvar supportsPassive = false;\ntry {\n var opts = Object.defineProperty({}, 'passive', {\n get: function() {\n supportsPassive = true;\n }\n });\n window.addEventListener(\"test\", null, opts);\n} catch (e) {}\nexport var passiveOption = supportsPassive ? { passive: true } : false;","import { passiveOption } from './passiveOption.js';\n\nexport function addEvents(el, obj, preventScrolling) {\n for (var prop in obj) {\n var option = ['touchstart', 'touchmove'].indexOf(prop) >= 0 && !preventScrolling ? passiveOption : false;\n el.addEventListener(prop, obj[prop], option);\n }\n}","import { passiveOption } from './passiveOption.js';\n\nexport function removeEvents(el, obj) {\n for (var prop in obj) {\n var option = ['touchstart', 'touchmove'].indexOf(prop) >= 0 ? passiveOption : false;\n el.removeEventListener(prop, obj[prop], option);\n }\n}","export function Events() {\n return {\n topics: {},\n on: function (eventName, fn) {\n this.topics[eventName] = this.topics[eventName] || [];\n this.topics[eventName].push(fn);\n },\n off: function(eventName, fn) {\n if (this.topics[eventName]) {\n for (var i = 0; i < this.topics[eventName].length; i++) {\n if (this.topics[eventName][i] === fn) {\n this.topics[eventName].splice(i, 1);\n break;\n }\n }\n }\n },\n emit: function (eventName, data) {\n data.type = eventName;\n if (this.topics[eventName]) {\n this.topics[eventName].forEach(function(fn) {\n fn(data, eventName);\n });\n }\n }\n };\n};","// Object.keys\nif (!Object.keys) {\n Object.keys = function(object) {\n var keys = [];\n for (var name in object) {\n if (Object.prototype.hasOwnProperty.call(object, name)) {\n keys.push(name);\n }\n }\n return keys;\n };\n}\n\n// ChildNode.remove\nif(!(\"remove\" in Element.prototype)){\n Element.prototype.remove = function(){\n if(this.parentNode) {\n this.parentNode.removeChild(this);\n }\n };\n}\n\nimport { raf } from './helpers/raf.js';\nimport { caf } from './helpers/caf.js';\nimport { extend } from './helpers/extend.js';\nimport { checkStorageValue } from './helpers/checkStorageValue.js';\nimport { setLocalStorage } from './helpers/setLocalStorage.js';\nimport { getSlideId } from './helpers/getSlideId.js';\nimport { calc } from './helpers/calc.js';\nimport { percentageLayout } from './helpers/percentageLayout.js';\nimport { mediaquerySupport } from './helpers/mediaquerySupport.js';\nimport { createStyleSheet } from './helpers/createStyleSheet.js';\nimport { addCSSRule } from './helpers/addCSSRule.js';\nimport { removeCSSRule } from './helpers/removeCSSRule.js';\nimport { getCssRulesLength } from './helpers/getCssRulesLength.js';\nimport { toDegree } from './helpers/toDegree.js';\nimport { getTouchDirection } from './helpers/getTouchDirection.js';\nimport { forEach } from './helpers/forEach.js';\nimport { hasClass } from './helpers/hasClass.js';\nimport { addClass } from './helpers/addClass.js';\nimport { removeClass } from './helpers/removeClass.js';\nimport { hasAttr } from './helpers/hasAttr.js';\nimport { getAttr } from './helpers/getAttr.js';\nimport { setAttrs } from './helpers/setAttrs.js';\nimport { removeAttrs } from './helpers/removeAttrs.js';\nimport { arrayFromNodeList } from './helpers/arrayFromNodeList.js';\nimport { hideElement } from './helpers/hideElement.js';\nimport { showElement } from './helpers/showElement.js';\nimport { isVisible } from './helpers/isVisible.js';\nimport { whichProperty } from './helpers/whichProperty.js';\nimport { has3DTransforms } from './helpers/has3DTransforms.js';\nimport { getEndProperty } from './helpers/getEndProperty.js';\nimport { addEvents } from './helpers/addEvents.js';\nimport { removeEvents } from './helpers/removeEvents.js';\nimport { Events } from './helpers/events.js';\nimport { jsTransform } from './helpers/jsTransform.js';\n\nexport var tns = function(options) {\n options = extend({\n container: '.slider',\n mode: 'carousel',\n axis: 'horizontal',\n items: 1,\n gutter: 0,\n edgePadding: 0,\n fixedWidth: false,\n autoWidth: false,\n viewportMax: false,\n slideBy: 1,\n center: false,\n controls: true,\n controlsPosition: 'top',\n controlsText: ['prev', 'next'],\n controlsContainer: false,\n prevButton: false,\n nextButton: false,\n nav: true,\n navPosition: 'top',\n navContainer: false,\n navAsThumbnails: false,\n arrowKeys: false,\n speed: 300,\n autoplay: false,\n autoplayPosition: 'top',\n autoplayTimeout: 5000,\n autoplayDirection: 'forward',\n autoplayText: ['start', 'stop'],\n autoplayHoverPause: false,\n autoplayButton: false,\n autoplayButtonOutput: true,\n autoplayResetOnVisibility: true,\n animateIn: 'tns-fadeIn',\n animateOut: 'tns-fadeOut',\n animateNormal: 'tns-normal',\n animateDelay: false,\n loop: true,\n rewind: false,\n autoHeight: false,\n responsive: false,\n lazyload: false,\n lazyloadSelector: '.tns-lazy-img',\n touch: true,\n mouseDrag: false,\n swipeAngle: 15,\n nested: false,\n preventActionWhenRunning: false,\n preventScrollOnTouch: false,\n freezable: true,\n onInit: false,\n useLocalStorage: true,\n nonce: false\n }, options || {});\n\n var doc = document,\n win = window,\n KEYS = {\n ENTER: 13,\n SPACE: 32,\n LEFT: 37,\n RIGHT: 39\n },\n tnsStorage = {},\n localStorageAccess = options.useLocalStorage;\n\n if (localStorageAccess) {\n // check browser version and local storage access\n var browserInfo = navigator.userAgent;\n var uid = new Date;\n\n try {\n tnsStorage = win.localStorage;\n if (tnsStorage) {\n tnsStorage.setItem(uid, uid);\n localStorageAccess = tnsStorage.getItem(uid) == uid;\n tnsStorage.removeItem(uid);\n } else {\n localStorageAccess = false;\n }\n if (!localStorageAccess) { tnsStorage = {}; }\n } catch(e) {\n localStorageAccess = false;\n }\n\n if (localStorageAccess) {\n // remove storage when browser version changes\n if (tnsStorage['tnsApp'] && tnsStorage['tnsApp'] !== browserInfo) {\n ['tC', 'tPL', 'tMQ', 'tTf', 't3D', 'tTDu', 'tTDe', 'tADu', 'tADe', 'tTE', 'tAE'].forEach(function(item) { tnsStorage.removeItem(item); });\n }\n // update browserInfo\n localStorage['tnsApp'] = browserInfo;\n }\n }\n\n var CALC = tnsStorage['tC'] ? checkStorageValue(tnsStorage['tC']) : setLocalStorage(tnsStorage, 'tC', calc(), localStorageAccess),\n PERCENTAGELAYOUT = tnsStorage['tPL'] ? checkStorageValue(tnsStorage['tPL']) : setLocalStorage(tnsStorage, 'tPL', percentageLayout(), localStorageAccess),\n CSSMQ = tnsStorage['tMQ'] ? checkStorageValue(tnsStorage['tMQ']) : setLocalStorage(tnsStorage, 'tMQ', mediaquerySupport(), localStorageAccess),\n TRANSFORM = tnsStorage['tTf'] ? checkStorageValue(tnsStorage['tTf']) : setLocalStorage(tnsStorage, 'tTf', whichProperty('transform'), localStorageAccess),\n HAS3DTRANSFORMS = tnsStorage['t3D'] ? checkStorageValue(tnsStorage['t3D']) : setLocalStorage(tnsStorage, 't3D', has3DTransforms(TRANSFORM), localStorageAccess),\n TRANSITIONDURATION = tnsStorage['tTDu'] ? checkStorageValue(tnsStorage['tTDu']) : setLocalStorage(tnsStorage, 'tTDu', whichProperty('transitionDuration'), localStorageAccess),\n TRANSITIONDELAY = tnsStorage['tTDe'] ? checkStorageValue(tnsStorage['tTDe']) : setLocalStorage(tnsStorage, 'tTDe', whichProperty('transitionDelay'), localStorageAccess),\n ANIMATIONDURATION = tnsStorage['tADu'] ? checkStorageValue(tnsStorage['tADu']) : setLocalStorage(tnsStorage, 'tADu', whichProperty('animationDuration'), localStorageAccess),\n ANIMATIONDELAY = tnsStorage['tADe'] ? checkStorageValue(tnsStorage['tADe']) : setLocalStorage(tnsStorage, 'tADe', whichProperty('animationDelay'), localStorageAccess),\n TRANSITIONEND = tnsStorage['tTE'] ? checkStorageValue(tnsStorage['tTE']) : setLocalStorage(tnsStorage, 'tTE', getEndProperty(TRANSITIONDURATION, 'Transition'), localStorageAccess),\n ANIMATIONEND = tnsStorage['tAE'] ? checkStorageValue(tnsStorage['tAE']) : setLocalStorage(tnsStorage, 'tAE', getEndProperty(ANIMATIONDURATION, 'Animation'), localStorageAccess);\n\n // get element nodes from selectors\n var supportConsoleWarn = win.console && typeof win.console.warn === \"function\",\n tnsList = ['container', 'controlsContainer', 'prevButton', 'nextButton', 'navContainer', 'autoplayButton'],\n optionsElements = {};\n\n tnsList.forEach(function(item) {\n if (typeof options[item] === 'string') {\n var str = options[item],\n el = doc.querySelector(str);\n optionsElements[item] = str;\n\n if (el && el.nodeName) {\n options[item] = el;\n } else {\n if (supportConsoleWarn) { console.warn('Can\\'t find', options[item]); }\n return;\n }\n }\n });\n\n // make sure at least 1 slide\n if (options.container.children.length < 1) {\n if (supportConsoleWarn) { console.warn('No slides found in', options.container); }\n return;\n }\n\n // update options\n var responsive = options.responsive,\n nested = options.nested,\n carousel = options.mode === 'carousel' ? true : false;\n\n if (responsive) {\n // apply responsive[0] to options and remove it\n if (0 in responsive) {\n options = extend(options, responsive[0]);\n delete responsive[0];\n }\n\n var responsiveTem = {};\n for (var key in responsive) {\n var val = responsive[key];\n // update responsive\n // from: 300: 2\n // to:\n // 300: {\n // items: 2\n // }\n val = typeof val === 'number' ? {items: val} : val;\n responsiveTem[key] = val;\n }\n responsive = responsiveTem;\n responsiveTem = null;\n }\n\n // update options\n function updateOptions (obj) {\n for (var key in obj) {\n if (!carousel) {\n if (key === 'slideBy') { obj[key] = 'page'; }\n if (key === 'edgePadding') { obj[key] = false; }\n if (key === 'autoHeight') { obj[key] = false; }\n }\n\n // update responsive options\n if (key === 'responsive') { updateOptions(obj[key]); }\n }\n }\n if (!carousel) { updateOptions(options); }\n\n\n // === define and set variables ===\n if (!carousel) {\n options.axis = 'horizontal';\n options.slideBy = 'page';\n options.edgePadding = false;\n\n var animateIn = options.animateIn,\n animateOut = options.animateOut,\n animateDelay = options.animateDelay,\n animateNormal = options.animateNormal;\n }\n\n var horizontal = options.axis === 'horizontal' ? true : false,\n outerWrapper = doc.createElement('div'),\n innerWrapper = doc.createElement('div'),\n middleWrapper,\n container = options.container,\n containerParent = container.parentNode,\n containerHTML = container.outerHTML,\n slideItems = container.children,\n slideCount = slideItems.length,\n breakpointZone,\n windowWidth = getWindowWidth(),\n isOn = false;\n if (responsive) { setBreakpointZone(); }\n if (carousel) { container.className += ' tns-vpfix'; }\n\n // fixedWidth: viewport > rightBoundary > indexMax\n var autoWidth = options.autoWidth,\n fixedWidth = getOption('fixedWidth'),\n edgePadding = getOption('edgePadding'),\n gutter = getOption('gutter'),\n viewport = getViewportWidth(),\n center = getOption('center'),\n items = !autoWidth ? Math.floor(getOption('items')) : 1,\n slideBy = getOption('slideBy'),\n viewportMax = options.viewportMax || options.fixedWidthViewportWidth,\n arrowKeys = getOption('arrowKeys'),\n speed = getOption('speed'),\n rewind = options.rewind,\n loop = rewind ? false : options.loop,\n autoHeight = getOption('autoHeight'),\n controls = getOption('controls'),\n controlsText = getOption('controlsText'),\n nav = getOption('nav'),\n touch = getOption('touch'),\n mouseDrag = getOption('mouseDrag'),\n autoplay = getOption('autoplay'),\n autoplayTimeout = getOption('autoplayTimeout'),\n autoplayText = getOption('autoplayText'),\n autoplayHoverPause = getOption('autoplayHoverPause'),\n autoplayResetOnVisibility = getOption('autoplayResetOnVisibility'),\n sheet = createStyleSheet(null, getOption('nonce')),\n lazyload = options.lazyload,\n lazyloadSelector = options.lazyloadSelector,\n slidePositions, // collection of slide positions\n slideItemsOut = [],\n cloneCount = loop ? getCloneCountForLoop() : 0,\n slideCountNew = !carousel ? slideCount + cloneCount : slideCount + cloneCount * 2,\n hasRightDeadZone = (fixedWidth || autoWidth) && !loop ? true : false,\n rightBoundary = fixedWidth ? getRightBoundary() : null,\n updateIndexBeforeTransform = (!carousel || !loop) ? true : false,\n // transform\n transformAttr = horizontal ? 'left' : 'top',\n transformPrefix = '',\n transformPostfix = '',\n // index\n getIndexMax = (function () {\n if (fixedWidth) {\n return function() { return center && !loop ? slideCount - 1 : Math.ceil(- rightBoundary / (fixedWidth + gutter)); };\n } else if (autoWidth) {\n return function() {\n for (var i = 0; i < slideCountNew; i++) {\n if (slidePositions[i] >= - rightBoundary) { return i; }\n }\n };\n } else {\n return function() {\n if (center && carousel && !loop) {\n return slideCount - 1;\n } else {\n return loop || carousel ? Math.max(0, slideCountNew - Math.ceil(items)) : slideCountNew - 1;\n }\n };\n }\n })(),\n index = getStartIndex(getOption('startIndex')),\n indexCached = index,\n displayIndex = getCurrentSlide(),\n indexMin = 0,\n indexMax = !autoWidth ? getIndexMax() : null,\n // resize\n resizeTimer,\n preventActionWhenRunning = options.preventActionWhenRunning,\n swipeAngle = options.swipeAngle,\n moveDirectionExpected = swipeAngle ? '?' : true,\n running = false,\n onInit = options.onInit,\n events = new Events(),\n // id, class\n newContainerClasses = ' tns-slider tns-' + options.mode,\n slideId = container.id || getSlideId(),\n disable = getOption('disable'),\n disabled = false,\n freezable = options.freezable,\n freeze = freezable && !autoWidth ? getFreeze() : false,\n frozen = false,\n controlsEvents = {\n 'click': onControlsClick,\n 'keydown': onControlsKeydown\n },\n navEvents = {\n 'click': onNavClick,\n 'keydown': onNavKeydown\n },\n hoverEvents = {\n 'mouseover': mouseoverPause,\n 'mouseout': mouseoutRestart\n },\n visibilityEvent = {'visibilitychange': onVisibilityChange},\n docmentKeydownEvent = {'keydown': onDocumentKeydown},\n touchEvents = {\n 'touchstart': onPanStart,\n 'touchmove': onPanMove,\n 'touchend': onPanEnd,\n 'touchcancel': onPanEnd\n }, dragEvents = {\n 'mousedown': onPanStart,\n 'mousemove': onPanMove,\n 'mouseup': onPanEnd,\n 'mouseleave': onPanEnd\n },\n hasControls = hasOption('controls'),\n hasNav = hasOption('nav'),\n navAsThumbnails = autoWidth ? true : options.navAsThumbnails,\n hasAutoplay = hasOption('autoplay'),\n hasTouch = hasOption('touch'),\n hasMouseDrag = hasOption('mouseDrag'),\n slideActiveClass = 'tns-slide-active',\n slideClonedClass = 'tns-slide-cloned',\n imgCompleteClass = 'tns-complete',\n imgEvents = {\n 'load': onImgLoaded,\n 'error': onImgFailed\n },\n imgsComplete,\n liveregionCurrent,\n preventScroll = options.preventScrollOnTouch === 'force' ? true : false;\n\n // controls\n if (hasControls) {\n var controlsContainer = options.controlsContainer,\n controlsContainerHTML = options.controlsContainer ? options.controlsContainer.outerHTML : '',\n prevButton = options.prevButton,\n nextButton = options.nextButton,\n prevButtonHTML = options.prevButton ? options.prevButton.outerHTML : '',\n nextButtonHTML = options.nextButton ? options.nextButton.outerHTML : '',\n prevIsButton,\n nextIsButton;\n }\n\n // nav\n if (hasNav) {\n var navContainer = options.navContainer,\n navContainerHTML = options.navContainer ? options.navContainer.outerHTML : '',\n navItems,\n pages = autoWidth ? slideCount : getPages(),\n pagesCached = 0,\n navClicked = -1,\n navCurrentIndex = getCurrentNavIndex(),\n navCurrentIndexCached = navCurrentIndex,\n navActiveClass = 'tns-nav-active',\n navStr = 'Carousel Page ',\n navStrCurrent = ' (Current Slide)';\n }\n\n // autoplay\n if (hasAutoplay) {\n var autoplayDirection = options.autoplayDirection === 'forward' ? 1 : -1,\n autoplayButton = options.autoplayButton,\n autoplayButtonHTML = options.autoplayButton ? options.autoplayButton.outerHTML : '',\n autoplayHtmlStrings = ['', ' animation'],\n autoplayTimer,\n animating,\n autoplayHoverPaused,\n autoplayUserPaused,\n autoplayVisibilityPaused;\n }\n\n if (hasTouch || hasMouseDrag) {\n var initPosition = {},\n lastPosition = {},\n translateInit,\n disX,\n disY,\n panStart = false,\n rafIndex,\n getDist = horizontal ?\n function(a, b) { return a.x - b.x; } :\n function(a, b) { return a.y - b.y; };\n }\n\n // disable slider when slidecount <= items\n if (!autoWidth) { resetVariblesWhenDisable(disable || freeze); }\n\n if (TRANSFORM) {\n transformAttr = TRANSFORM;\n transformPrefix = 'translate';\n\n if (HAS3DTRANSFORMS) {\n transformPrefix += horizontal ? '3d(' : '3d(0px, ';\n transformPostfix = horizontal ? ', 0px, 0px)' : ', 0px)';\n } else {\n transformPrefix += horizontal ? 'X(' : 'Y(';\n transformPostfix = ')';\n }\n\n }\n\n if (carousel) { container.className = container.className.replace('tns-vpfix', ''); }\n initStructure();\n initSheet();\n initSliderTransform();\n\n // === COMMON FUNCTIONS === //\n function resetVariblesWhenDisable (condition) {\n if (condition) {\n controls = nav = touch = mouseDrag = arrowKeys = autoplay = autoplayHoverPause = autoplayResetOnVisibility = false;\n }\n }\n\n function getCurrentSlide () {\n var tem = carousel ? index - cloneCount : index;\n while (tem < 0) { tem += slideCount; }\n return tem%slideCount + 1;\n }\n\n function getStartIndex (ind) {\n ind = ind ? Math.max(0, Math.min(loop ? slideCount - 1 : slideCount - items, ind)) : 0;\n return carousel ? ind + cloneCount : ind;\n }\n\n function getAbsIndex (i) {\n if (i == null) { i = index; }\n\n if (carousel) { i -= cloneCount; }\n while (i < 0) { i += slideCount; }\n\n return Math.floor(i%slideCount);\n }\n\n function getCurrentNavIndex () {\n var absIndex = getAbsIndex(),\n result;\n\n result = navAsThumbnails ? absIndex :\n fixedWidth || autoWidth ? Math.ceil((absIndex + 1) * pages / slideCount - 1) :\n Math.floor(absIndex / items);\n\n // set active nav to the last one when reaches the right edge\n if (!loop && carousel && index === indexMax) { result = pages - 1; }\n\n return result;\n }\n\n function getItemsMax () {\n // fixedWidth or autoWidth while viewportMax is not available\n if (autoWidth || (fixedWidth && !viewportMax)) {\n return slideCount - 1;\n // most cases\n } else {\n var str = fixedWidth ? 'fixedWidth' : 'items',\n arr = [];\n\n if (fixedWidth || options[str] < slideCount) { arr.push(options[str]); }\n\n if (responsive) {\n for (var bp in responsive) {\n var tem = responsive[bp][str];\n if (tem && (fixedWidth || tem < slideCount)) { arr.push(tem); }\n }\n }\n\n if (!arr.length) { arr.push(0); }\n\n return Math.ceil(fixedWidth ? viewportMax / Math.min.apply(null, arr) : Math.max.apply(null, arr));\n }\n }\n\n function getCloneCountForLoop () {\n var itemsMax = getItemsMax(),\n result = carousel ? Math.ceil((itemsMax * 5 - slideCount)/2) : (itemsMax * 4 - slideCount);\n result = Math.max(itemsMax, result);\n\n return hasOption('edgePadding') ? result + 1 : result;\n }\n\n function getWindowWidth () {\n return win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth;\n }\n\n function getInsertPosition (pos) {\n return pos === 'top' ? 'afterbegin' : 'beforeend';\n }\n\n function getClientWidth (el) {\n if (el == null) { return; }\n var div = doc.createElement('div'), rect, width;\n el.appendChild(div);\n rect = div.getBoundingClientRect();\n width = rect.right - rect.left;\n div.remove();\n return width || getClientWidth(el.parentNode);\n }\n\n function getViewportWidth () {\n var gap = edgePadding ? edgePadding * 2 - gutter : 0;\n return getClientWidth(containerParent) - gap;\n }\n\n function hasOption (item) {\n if (options[item]) {\n return true;\n } else {\n if (responsive) {\n for (var bp in responsive) {\n if (responsive[bp][item]) { return true; }\n }\n }\n return false;\n }\n }\n\n // get option:\n // fixed width: viewport, fixedWidth, gutter => items\n // others: window width => all variables\n // all: items => slideBy\n function getOption (item, ww) {\n if (ww == null) { ww = windowWidth; }\n\n if (item === 'items' && fixedWidth) {\n return Math.floor((viewport + gutter) / (fixedWidth + gutter)) || 1;\n\n } else {\n var result = options[item];\n\n if (responsive) {\n for (var bp in responsive) {\n // bp: convert string to number\n if (ww >= parseInt(bp)) {\n if (item in responsive[bp]) { result = responsive[bp][item]; }\n }\n }\n }\n\n if (item === 'slideBy' && result === 'page') { result = getOption('items'); }\n if (!carousel && (item === 'slideBy' || item === 'items')) { result = Math.floor(result); }\n\n return result;\n }\n }\n\n function getSlideMarginLeft (i) {\n return CALC ?\n CALC + '(' + i * 100 + '% / ' + slideCountNew + ')' :\n i * 100 / slideCountNew + '%';\n }\n\n function getInnerWrapperStyles (edgePaddingTem, gutterTem, fixedWidthTem, speedTem, autoHeightBP) {\n var str = '';\n\n if (edgePaddingTem !== undefined) {\n var gap = edgePaddingTem;\n if (gutterTem) { gap -= gutterTem; }\n str = horizontal ?\n 'margin: 0 ' + gap + 'px 0 ' + edgePaddingTem + 'px;' :\n 'margin: ' + edgePaddingTem + 'px 0 ' + gap + 'px 0;';\n } else if (gutterTem && !fixedWidthTem) {\n var gutterTemUnit = '-' + gutterTem + 'px',\n dir = horizontal ? gutterTemUnit + ' 0 0' : '0 ' + gutterTemUnit + ' 0';\n str = 'margin: 0 ' + dir + ';'\n }\n\n if (!carousel && autoHeightBP && TRANSITIONDURATION && speedTem) { str += getTransitionDurationStyle(speedTem); }\n return str;\n }\n\n function getContainerWidth (fixedWidthTem, gutterTem, itemsTem) {\n if (fixedWidthTem) {\n return (fixedWidthTem + gutterTem) * slideCountNew + 'px';\n } else {\n return CALC ?\n CALC + '(' + slideCountNew * 100 + '% / ' + itemsTem + ')' :\n slideCountNew * 100 / itemsTem + '%';\n }\n }\n\n function getSlideWidthStyle (fixedWidthTem, gutterTem, itemsTem) {\n var width;\n\n if (fixedWidthTem) {\n width = (fixedWidthTem + gutterTem) + 'px';\n } else {\n if (!carousel) { itemsTem = Math.floor(itemsTem); }\n var dividend = carousel ? slideCountNew : itemsTem;\n width = CALC ?\n CALC + '(100% / ' + dividend + ')' :\n 100 / dividend + '%';\n }\n\n width = 'width:' + width;\n\n // inner slider: overwrite outer slider styles\n return nested !== 'inner' ? width + ';' : width + ' !important;';\n }\n\n function getSlideGutterStyle (gutterTem) {\n var str = '';\n\n // gutter maybe interger || 0\n // so can't use 'if (gutter)'\n if (gutterTem !== false) {\n var prop = horizontal ? 'padding-' : 'margin-',\n dir = horizontal ? 'right' : 'bottom';\n str = prop + dir + ': ' + gutterTem + 'px;';\n }\n\n return str;\n }\n\n function getCSSPrefix (name, num) {\n var prefix = name.substring(0, name.length - num).toLowerCase();\n if (prefix) { prefix = '-' + prefix + '-'; }\n\n return prefix;\n }\n\n function getTransitionDurationStyle (speed) {\n return getCSSPrefix(TRANSITIONDURATION, 18) + 'transition-duration:' + speed / 1000 + 's;';\n }\n\n function getAnimationDurationStyle (speed) {\n return getCSSPrefix(ANIMATIONDURATION, 17) + 'animation-duration:' + speed / 1000 + 's;';\n }\n\n function initStructure () {\n var classOuter = 'tns-outer',\n classInner = 'tns-inner',\n hasGutter = hasOption('gutter');\n\n outerWrapper.className = classOuter;\n innerWrapper.className = classInner;\n outerWrapper.id = slideId + '-ow';\n innerWrapper.id = slideId + '-iw';\n\n // set container properties\n if (container.id === '') { container.id = slideId; }\n newContainerClasses += PERCENTAGELAYOUT || autoWidth ? ' tns-subpixel' : ' tns-no-subpixel';\n newContainerClasses += CALC ? ' tns-calc' : ' tns-no-calc';\n if (autoWidth) { newContainerClasses += ' tns-autowidth'; }\n newContainerClasses += ' tns-' + options.axis;\n container.className += newContainerClasses;\n\n // add constrain layer for carousel\n if (carousel) {\n middleWrapper = doc.createElement('div');\n middleWrapper.id = slideId + '-mw';\n middleWrapper.className = 'tns-ovh';\n\n outerWrapper.appendChild(middleWrapper);\n middleWrapper.appendChild(innerWrapper);\n } else {\n outerWrapper.appendChild(innerWrapper);\n }\n\n if (autoHeight) {\n var wp = middleWrapper ? middleWrapper : innerWrapper;\n wp.className += ' tns-ah';\n }\n\n containerParent.insertBefore(outerWrapper, container);\n innerWrapper.appendChild(container);\n\n // add id, class, aria attributes\n // before clone slides\n forEach(slideItems, function(item, i) {\n addClass(item, 'tns-item');\n if (!item.id) { item.id = slideId + '-item' + i; }\n if (!carousel && animateNormal) { addClass(item, animateNormal); }\n setAttrs(item, {\n 'aria-hidden': 'true',\n 'tabindex': '-1'\n });\n });\n\n // ## clone slides\n // carousel: n + slides + n\n // gallery: slides + n\n if (cloneCount) {\n var fragmentBefore = doc.createDocumentFragment(),\n fragmentAfter = doc.createDocumentFragment();\n\n for (var j = cloneCount; j--;) {\n var num = j%slideCount,\n cloneFirst = slideItems[num].cloneNode(true);\n addClass(cloneFirst, slideClonedClass);\n removeAttrs(cloneFirst, 'id');\n fragmentAfter.insertBefore(cloneFirst, fragmentAfter.firstChild);\n\n if (carousel) {\n var cloneLast = slideItems[slideCount - 1 - num].cloneNode(true);\n addClass(cloneLast, slideClonedClass);\n removeAttrs(cloneLast, 'id');\n fragmentBefore.appendChild(cloneLast);\n }\n }\n\n container.insertBefore(fragmentBefore, container.firstChild);\n container.appendChild(fragmentAfter);\n slideItems = container.children;\n }\n\n }\n\n function initSliderTransform () {\n // ## images loaded/failed\n if (hasOption('autoHeight') || autoWidth || !horizontal) {\n var imgs = container.querySelectorAll('img');\n\n // add img load event listener\n forEach(imgs, function(img) {\n var src = img.src;\n\n if (!lazyload) {\n // not data img\n if (src && src.indexOf('data:image') < 0) {\n img.src = '';\n addEvents(img, imgEvents);\n addClass(img, 'loading');\n\n img.src = src;\n // data img\n } else {\n imgLoaded(img);\n }\n }\n });\n\n // set imgsComplete\n raf(function(){ imgsLoadedCheck(arrayFromNodeList(imgs), function() { imgsComplete = true; }); });\n\n // reset imgs for auto height: check visible imgs only\n if (hasOption('autoHeight')) { imgs = getImageArray(index, Math.min(index + items - 1, slideCountNew - 1)); }\n\n lazyload ? initSliderTransformStyleCheck() : raf(function(){ imgsLoadedCheck(arrayFromNodeList(imgs), initSliderTransformStyleCheck); });\n\n } else {\n // set container transform property\n if (carousel) { doContainerTransformSilent(); }\n\n // update slider tools and events\n initTools();\n initEvents();\n }\n }\n\n function initSliderTransformStyleCheck () {\n if (autoWidth && slideCount > 1) {\n // check styles application\n var num = loop ? index : slideCount - 1;\n\n (function stylesApplicationCheck() {\n var left = slideItems[num].getBoundingClientRect().left;\n var right = slideItems[num - 1].getBoundingClientRect().right;\n\n (Math.abs(left - right) <= 1) ?\n initSliderTransformCore() :\n setTimeout(function(){ stylesApplicationCheck() }, 16);\n })();\n\n } else {\n initSliderTransformCore();\n }\n }\n\n\n function initSliderTransformCore () {\n // run Fn()s which are rely on image loading\n if (!horizontal || autoWidth) {\n setSlidePositions();\n\n if (autoWidth) {\n rightBoundary = getRightBoundary();\n if (freezable) { freeze = getFreeze(); }\n indexMax = getIndexMax(); // <= slidePositions, rightBoundary <=\n resetVariblesWhenDisable(disable || freeze);\n } else {\n updateContentWrapperHeight();\n }\n }\n\n // set container transform property\n if (carousel) { doContainerTransformSilent(); }\n\n // update slider tools and events\n initTools();\n initEvents();\n }\n\n function initSheet () {\n // gallery:\n // set animation classes and left value for gallery slider\n if (!carousel) {\n for (var i = index, l = index + Math.min(slideCount, items); i < l; i++) {\n var item = slideItems[i];\n item.style.left = (i - index) * 100 / items + '%';\n addClass(item, animateIn);\n removeClass(item, animateNormal);\n }\n }\n\n // #### LAYOUT\n\n // ## INLINE-BLOCK VS FLOAT\n\n // ## PercentageLayout:\n // slides: inline-block\n // remove blank space between slides by set font-size: 0\n\n // ## Non PercentageLayout:\n // slides: float\n // margin-right: -100%\n // margin-left: ~\n\n // Resource: https://docs.google.com/spreadsheets/d/147up245wwTXeQYve3BRSAD4oVcvQmuGsFteJOeA5xNQ/edit?usp=sharing\n if (horizontal) {\n if (PERCENTAGELAYOUT || autoWidth) {\n addCSSRule(sheet, '#' + slideId + ' > .tns-item', 'font-size:' + win.getComputedStyle(slideItems[0]).fontSize + ';', getCssRulesLength(sheet));\n addCSSRule(sheet, '#' + slideId, 'font-size:0;', getCssRulesLength(sheet));\n } else if (carousel) {\n forEach(slideItems, function (slide, i) {\n slide.style.marginLeft = getSlideMarginLeft(i);\n });\n }\n }\n\n\n // ## BASIC STYLES\n if (CSSMQ) {\n // middle wrapper style\n if (TRANSITIONDURATION) {\n var str = middleWrapper && options.autoHeight ? getTransitionDurationStyle(options.speed) : '';\n addCSSRule(sheet, '#' + slideId + '-mw', str, getCssRulesLength(sheet));\n }\n\n // inner wrapper styles\n str = getInnerWrapperStyles(options.edgePadding, options.gutter, options.fixedWidth, options.speed, options.autoHeight);\n addCSSRule(sheet, '#' + slideId + '-iw', str, getCssRulesLength(sheet));\n\n // container styles\n if (carousel) {\n str = horizontal && !autoWidth ? 'width:' + getContainerWidth(options.fixedWidth, options.gutter, options.items) + ';' : '';\n if (TRANSITIONDURATION) { str += getTransitionDurationStyle(speed); }\n addCSSRule(sheet, '#' + slideId, str, getCssRulesLength(sheet));\n }\n\n // slide styles\n str = horizontal && !autoWidth ? getSlideWidthStyle(options.fixedWidth, options.gutter, options.items) : '';\n if (options.gutter) { str += getSlideGutterStyle(options.gutter); }\n // set gallery items transition-duration\n if (!carousel) {\n if (TRANSITIONDURATION) { str += getTransitionDurationStyle(speed); }\n if (ANIMATIONDURATION) { str += getAnimationDurationStyle(speed); }\n }\n if (str) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); }\n\n // non CSS mediaqueries: IE8\n // ## update inner wrapper, container, slides if needed\n // set inline styles for inner wrapper & container\n // insert stylesheet (one line) for slides only (since slides are many)\n } else {\n // middle wrapper styles\n update_carousel_transition_duration();\n\n // inner wrapper styles\n innerWrapper.style.cssText = getInnerWrapperStyles(edgePadding, gutter, fixedWidth, autoHeight);\n\n // container styles\n if (carousel && horizontal && !autoWidth) {\n container.style.width = getContainerWidth(fixedWidth, gutter, items);\n }\n\n // slide styles\n var str = horizontal && !autoWidth ? getSlideWidthStyle(fixedWidth, gutter, items) : '';\n if (gutter) { str += getSlideGutterStyle(gutter); }\n\n // append to the last line\n if (str) { addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet)); }\n }\n\n // ## MEDIAQUERIES\n if (responsive && CSSMQ) {\n for (var bp in responsive) {\n // bp: convert string to number\n bp = parseInt(bp);\n\n var opts = responsive[bp],\n str = '',\n middleWrapperStr = '',\n innerWrapperStr = '',\n containerStr = '',\n slideStr = '',\n itemsBP = !autoWidth ? getOption('items', bp) : null,\n fixedWidthBP = getOption('fixedWidth', bp),\n speedBP = getOption('speed', bp),\n edgePaddingBP = getOption('edgePadding', bp),\n autoHeightBP = getOption('autoHeight', bp),\n gutterBP = getOption('gutter', bp);\n\n // middle wrapper string\n if (TRANSITIONDURATION && middleWrapper && getOption('autoHeight', bp) && 'speed' in opts) {\n middleWrapperStr = '#' + slideId + '-mw{' + getTransitionDurationStyle(speedBP) + '}';\n }\n\n // inner wrapper string\n if ('edgePadding' in opts || 'gutter' in opts) {\n innerWrapperStr = '#' + slideId + '-iw{' + getInnerWrapperStyles(edgePaddingBP, gutterBP, fixedWidthBP, speedBP, autoHeightBP) + '}';\n }\n\n // container string\n if (carousel && horizontal && !autoWidth && ('fixedWidth' in opts || 'items' in opts || (fixedWidth && 'gutter' in opts))) {\n containerStr = 'width:' + getContainerWidth(fixedWidthBP, gutterBP, itemsBP) + ';';\n }\n if (TRANSITIONDURATION && 'speed' in opts) {\n containerStr += getTransitionDurationStyle(speedBP);\n }\n if (containerStr) {\n containerStr = '#' + slideId + '{' + containerStr + '}';\n }\n\n // slide string\n if ('fixedWidth' in opts || (fixedWidth && 'gutter' in opts) || !carousel && 'items' in opts) {\n slideStr += getSlideWidthStyle(fixedWidthBP, gutterBP, itemsBP);\n }\n if ('gutter' in opts) {\n slideStr += getSlideGutterStyle(gutterBP);\n }\n // set gallery items transition-duration\n if (!carousel && 'speed' in opts) {\n if (TRANSITIONDURATION) { slideStr += getTransitionDurationStyle(speedBP); }\n if (ANIMATIONDURATION) { slideStr += getAnimationDurationStyle(speedBP); }\n }\n if (slideStr) { slideStr = '#' + slideId + ' > .tns-item{' + slideStr + '}'; }\n\n // add up\n str = middleWrapperStr + innerWrapperStr + containerStr + slideStr;\n\n if (str) {\n sheet.insertRule('@media (min-width: ' + bp / 16 + 'em) {' + str + '}', sheet.cssRules.length);\n }\n }\n }\n }\n\n function initTools () {\n // == slides ==\n updateSlideStatus();\n\n // == live region ==\n outerWrapper.insertAdjacentHTML('afterbegin', '
slide ' + getLiveRegionStr() + ' of ' + slideCount + '
');\n liveregionCurrent = outerWrapper.querySelector('.tns-liveregion .current');\n\n // == autoplayInit ==\n if (hasAutoplay) {\n var txt = autoplay ? 'stop' : 'start';\n if (autoplayButton) {\n setAttrs(autoplayButton, {'data-action': txt});\n } else if (options.autoplayButtonOutput) {\n outerWrapper.insertAdjacentHTML(getInsertPosition(options.autoplayPosition), '');\n autoplayButton = outerWrapper.querySelector('[data-action]');\n }\n\n // add event\n if (autoplayButton) {\n addEvents(autoplayButton, {'click': toggleAutoplay});\n }\n\n if (autoplay) {\n startAutoplay();\n if (autoplayHoverPause) { addEvents(container, hoverEvents); }\n if (autoplayResetOnVisibility) { addEvents(container, visibilityEvent); }\n }\n }\n\n // == navInit ==\n if (hasNav) {\n var initIndex = !carousel ? 0 : cloneCount;\n // customized nav\n // will not hide the navs in case they're thumbnails\n if (navContainer) {\n setAttrs(navContainer, {'aria-label': 'Carousel Pagination'});\n navItems = navContainer.children;\n forEach(navItems, function(item, i) {\n setAttrs(item, {\n 'data-nav': i,\n 'tabindex': '-1',\n 'aria-label': navStr + (i + 1),\n 'aria-controls': slideId,\n });\n });\n\n // generated nav\n } else {\n var navHtml = '',\n hiddenStr = navAsThumbnails ? '' : 'style=\"display:none\"';\n for (var i = 0; i < slideCount; i++) {\n // hide nav items by default\n navHtml += '';\n }\n navHtml = '
' + navHtml + '
';\n outerWrapper.insertAdjacentHTML(getInsertPosition(options.navPosition), navHtml);\n\n navContainer = outerWrapper.querySelector('.tns-nav');\n navItems = navContainer.children;\n }\n\n updateNavVisibility();\n\n // add transition\n if (TRANSITIONDURATION) {\n var prefix = TRANSITIONDURATION.substring(0, TRANSITIONDURATION.length - 18).toLowerCase(),\n str = 'transition: all ' + speed / 1000 + 's';\n\n if (prefix) {\n str = '-' + prefix + '-' + str;\n }\n\n addCSSRule(sheet, '[aria-controls^=' + slideId + '-item]', str, getCssRulesLength(sheet));\n }\n\n setAttrs(navItems[navCurrentIndex], {'aria-label': navStr + (navCurrentIndex + 1) + navStrCurrent});\n removeAttrs(navItems[navCurrentIndex], 'tabindex');\n addClass(navItems[navCurrentIndex], navActiveClass);\n\n // add events\n addEvents(navContainer, navEvents);\n }\n\n\n\n // == controlsInit ==\n if (hasControls) {\n if (!controlsContainer && (!prevButton || !nextButton)) {\n outerWrapper.insertAdjacentHTML(getInsertPosition(options.controlsPosition), '
');\n\n controlsContainer = outerWrapper.querySelector('.tns-controls');\n }\n\n if (!prevButton || !nextButton) {\n prevButton = controlsContainer.children[0];\n nextButton = controlsContainer.children[1];\n }\n\n if (options.controlsContainer) {\n setAttrs(controlsContainer, {\n 'aria-label': 'Carousel Navigation',\n 'tabindex': '0'\n });\n }\n\n if (options.controlsContainer || (options.prevButton && options.nextButton)) {\n setAttrs([prevButton, nextButton], {\n 'aria-controls': slideId,\n 'tabindex': '-1',\n });\n }\n\n if (options.controlsContainer || (options.prevButton && options.nextButton)) {\n setAttrs(prevButton, {'data-controls' : 'prev'});\n setAttrs(nextButton, {'data-controls' : 'next'});\n }\n\n prevIsButton = isButton(prevButton);\n nextIsButton = isButton(nextButton);\n\n updateControlsStatus();\n\n // add events\n if (controlsContainer) {\n addEvents(controlsContainer, controlsEvents);\n } else {\n addEvents(prevButton, controlsEvents);\n addEvents(nextButton, controlsEvents);\n }\n }\n\n // hide tools if needed\n disableUI();\n }\n\n function initEvents () {\n // add events\n if (carousel && TRANSITIONEND) {\n var eve = {};\n eve[TRANSITIONEND] = onTransitionEnd;\n addEvents(container, eve);\n }\n\n if (touch) { addEvents(container, touchEvents, options.preventScrollOnTouch); }\n if (mouseDrag) { addEvents(container, dragEvents); }\n if (arrowKeys) { addEvents(doc, docmentKeydownEvent); }\n\n if (nested === 'inner') {\n events.on('outerResized', function () {\n resizeTasks();\n events.emit('innerLoaded', info());\n });\n } else if (responsive || fixedWidth || autoWidth || autoHeight || !horizontal) {\n addEvents(win, {'resize': onResize});\n }\n\n if (autoHeight) {\n if (nested === 'outer') {\n events.on('innerLoaded', doAutoHeight);\n } else if (!disable) { doAutoHeight(); }\n }\n\n doLazyLoad();\n if (disable) { disableSlider(); } else if (freeze) { freezeSlider(); }\n\n events.on('indexChanged', additionalUpdates);\n if (nested === 'inner') { events.emit('innerLoaded', info()); }\n if (typeof onInit === 'function') { onInit(info()); }\n isOn = true;\n }\n\n function destroy () {\n // sheet\n sheet.disabled = true;\n if (sheet.ownerNode) { sheet.ownerNode.remove(); }\n\n // remove win event listeners\n removeEvents(win, {'resize': onResize});\n\n // arrowKeys, controls, nav\n if (arrowKeys) { removeEvents(doc, docmentKeydownEvent); }\n if (controlsContainer) { removeEvents(controlsContainer, controlsEvents); }\n if (navContainer) { removeEvents(navContainer, navEvents); }\n\n // autoplay\n removeEvents(container, hoverEvents);\n removeEvents(container, visibilityEvent);\n if (autoplayButton) { removeEvents(autoplayButton, {'click': toggleAutoplay}); }\n if (autoplay) { clearInterval(autoplayTimer); }\n\n // container\n if (carousel && TRANSITIONEND) {\n var eve = {};\n eve[TRANSITIONEND] = onTransitionEnd;\n removeEvents(container, eve);\n }\n if (touch) { removeEvents(container, touchEvents); }\n if (mouseDrag) { removeEvents(container, dragEvents); }\n\n // cache Object values in options && reset HTML\n var htmlList = [containerHTML, controlsContainerHTML, prevButtonHTML, nextButtonHTML, navContainerHTML, autoplayButtonHTML];\n\n tnsList.forEach(function(item, i) {\n var el = item === 'container' ? outerWrapper : options[item];\n\n if (typeof el === 'object' && el) {\n var prevEl = el.previousElementSibling ? el.previousElementSibling : false,\n parentEl = el.parentNode;\n el.outerHTML = htmlList[i];\n options[item] = prevEl ? prevEl.nextElementSibling : parentEl.firstElementChild;\n }\n });\n\n\n // reset variables\n tnsList = animateIn = animateOut = animateDelay = animateNormal = horizontal = outerWrapper = innerWrapper = container = containerParent = containerHTML = slideItems = slideCount = breakpointZone = windowWidth = autoWidth = fixedWidth = edgePadding = gutter = viewport = items = slideBy = viewportMax = arrowKeys = speed = rewind = loop = autoHeight = sheet = lazyload = slidePositions = slideItemsOut = cloneCount = slideCountNew = hasRightDeadZone = rightBoundary = updateIndexBeforeTransform = transformAttr = transformPrefix = transformPostfix = getIndexMax = index = indexCached = indexMin = indexMax = resizeTimer = swipeAngle = moveDirectionExpected = running = onInit = events = newContainerClasses = slideId = disable = disabled = freezable = freeze = frozen = controlsEvents = navEvents = hoverEvents = visibilityEvent = docmentKeydownEvent = touchEvents = dragEvents = hasControls = hasNav = navAsThumbnails = hasAutoplay = hasTouch = hasMouseDrag = slideActiveClass = imgCompleteClass = imgEvents = imgsComplete = controls = controlsText = controlsContainer = controlsContainerHTML = prevButton = nextButton = prevIsButton = nextIsButton = nav = navContainer = navContainerHTML = navItems = pages = pagesCached = navClicked = navCurrentIndex = navCurrentIndexCached = navActiveClass = navStr = navStrCurrent = autoplay = autoplayTimeout = autoplayDirection = autoplayText = autoplayHoverPause = autoplayButton = autoplayButtonHTML = autoplayResetOnVisibility = autoplayHtmlStrings = autoplayTimer = animating = autoplayHoverPaused = autoplayUserPaused = autoplayVisibilityPaused = initPosition = lastPosition = translateInit = disX = disY = panStart = rafIndex = getDist = touch = mouseDrag = null;\n // check variables\n // [animateIn, animateOut, animateDelay, animateNormal, horizontal, outerWrapper, innerWrapper, container, containerParent, containerHTML, slideItems, slideCount, breakpointZone, windowWidth, autoWidth, fixedWidth, edgePadding, gutter, viewport, items, slideBy, viewportMax, arrowKeys, speed, rewind, loop, autoHeight, sheet, lazyload, slidePositions, slideItemsOut, cloneCount, slideCountNew, hasRightDeadZone, rightBoundary, updateIndexBeforeTransform, transformAttr, transformPrefix, transformPostfix, getIndexMax, index, indexCached, indexMin, indexMax, resizeTimer, swipeAngle, moveDirectionExpected, running, onInit, events, newContainerClasses, slideId, disable, disabled, freezable, freeze, frozen, controlsEvents, navEvents, hoverEvents, visibilityEvent, docmentKeydownEvent, touchEvents, dragEvents, hasControls, hasNav, navAsThumbnails, hasAutoplay, hasTouch, hasMouseDrag, slideActiveClass, imgCompleteClass, imgEvents, imgsComplete, controls, controlsText, controlsContainer, controlsContainerHTML, prevButton, nextButton, prevIsButton, nextIsButton, nav, navContainer, navContainerHTML, navItems, pages, pagesCached, navClicked, navCurrentIndex, navCurrentIndexCached, navActiveClass, navStr, navStrCurrent, autoplay, autoplayTimeout, autoplayDirection, autoplayText, autoplayHoverPause, autoplayButton, autoplayButtonHTML, autoplayResetOnVisibility, autoplayHtmlStrings, autoplayTimer, animating, autoplayHoverPaused, autoplayUserPaused, autoplayVisibilityPaused, initPosition, lastPosition, translateInit, disX, disY, panStart, rafIndex, getDist, touch, mouseDrag ].forEach(function(item) { if (item !== null) { console.log(item); } });\n\n for (var a in this) {\n if (a !== 'rebuild') { this[a] = null; }\n }\n isOn = false;\n }\n\n// === ON RESIZE ===\n // responsive || fixedWidth || autoWidth || !horizontal\n function onResize (e) {\n raf(function(){ resizeTasks(getEvent(e)); });\n }\n\n function resizeTasks (e) {\n if (!isOn) { return; }\n if (nested === 'outer') { events.emit('outerResized', info(e)); }\n windowWidth = getWindowWidth();\n var bpChanged,\n breakpointZoneTem = breakpointZone,\n needContainerTransform = false;\n\n if (responsive) {\n setBreakpointZone();\n bpChanged = breakpointZoneTem !== breakpointZone;\n // if (hasRightDeadZone) { needContainerTransform = true; } // *?\n if (bpChanged) { events.emit('newBreakpointStart', info(e)); }\n }\n\n var indChanged,\n itemsChanged,\n itemsTem = items,\n disableTem = disable,\n freezeTem = freeze,\n arrowKeysTem = arrowKeys,\n controlsTem = controls,\n navTem = nav,\n touchTem = touch,\n mouseDragTem = mouseDrag,\n autoplayTem = autoplay,\n autoplayHoverPauseTem = autoplayHoverPause,\n autoplayResetOnVisibilityTem = autoplayResetOnVisibility,\n indexTem = index;\n\n if (bpChanged) {\n var fixedWidthTem = fixedWidth,\n autoHeightTem = autoHeight,\n controlsTextTem = controlsText,\n centerTem = center,\n autoplayTextTem = autoplayText;\n\n if (!CSSMQ) {\n var gutterTem = gutter,\n edgePaddingTem = edgePadding;\n }\n }\n\n // get option:\n // fixed width: viewport, fixedWidth, gutter => items\n // others: window width => all variables\n // all: items => slideBy\n arrowKeys = getOption('arrowKeys');\n controls = getOption('controls');\n nav = getOption('nav');\n touch = getOption('touch');\n center = getOption('center');\n mouseDrag = getOption('mouseDrag');\n autoplay = getOption('autoplay');\n autoplayHoverPause = getOption('autoplayHoverPause');\n autoplayResetOnVisibility = getOption('autoplayResetOnVisibility');\n\n if (bpChanged) {\n disable = getOption('disable');\n fixedWidth = getOption('fixedWidth');\n speed = getOption('speed');\n autoHeight = getOption('autoHeight');\n controlsText = getOption('controlsText');\n autoplayText = getOption('autoplayText');\n autoplayTimeout = getOption('autoplayTimeout');\n\n if (!CSSMQ) {\n edgePadding = getOption('edgePadding');\n gutter = getOption('gutter');\n }\n }\n // update options\n resetVariblesWhenDisable(disable);\n\n viewport = getViewportWidth(); // <= edgePadding, gutter\n if ((!horizontal || autoWidth) && !disable) {\n setSlidePositions();\n if (!horizontal) {\n updateContentWrapperHeight(); // <= setSlidePositions\n needContainerTransform = true;\n }\n }\n if (fixedWidth || autoWidth) {\n rightBoundary = getRightBoundary(); // autoWidth: <= viewport, slidePositions, gutter\n // fixedWidth: <= viewport, fixedWidth, gutter\n indexMax = getIndexMax(); // autoWidth: <= rightBoundary, slidePositions\n // fixedWidth: <= rightBoundary, fixedWidth, gutter\n }\n\n if (bpChanged || fixedWidth) {\n items = getOption('items');\n slideBy = getOption('slideBy');\n itemsChanged = items !== itemsTem;\n\n if (itemsChanged) {\n if (!fixedWidth && !autoWidth) { indexMax = getIndexMax(); } // <= items\n // check index before transform in case\n // slider reach the right edge then items become bigger\n updateIndex();\n }\n }\n\n if (bpChanged) {\n if (disable !== disableTem) {\n if (disable) {\n disableSlider();\n } else {\n enableSlider(); // <= slidePositions, rightBoundary, indexMax\n }\n }\n }\n\n if (freezable && (bpChanged || fixedWidth || autoWidth)) {\n freeze = getFreeze(); // <= autoWidth: slidePositions, gutter, viewport, rightBoundary\n // <= fixedWidth: fixedWidth, gutter, rightBoundary\n // <= others: items\n\n if (freeze !== freezeTem) {\n if (freeze) {\n doContainerTransform(getContainerTransformValue(getStartIndex(0)));\n freezeSlider();\n } else {\n unfreezeSlider();\n needContainerTransform = true;\n }\n }\n }\n\n resetVariblesWhenDisable(disable || freeze); // controls, nav, touch, mouseDrag, arrowKeys, autoplay, autoplayHoverPause, autoplayResetOnVisibility\n if (!autoplay) { autoplayHoverPause = autoplayResetOnVisibility = false; }\n\n if (arrowKeys !== arrowKeysTem) {\n arrowKeys ?\n addEvents(doc, docmentKeydownEvent) :\n removeEvents(doc, docmentKeydownEvent);\n }\n if (controls !== controlsTem) {\n if (controls) {\n if (controlsContainer) {\n showElement(controlsContainer);\n } else {\n if (prevButton) { showElement(prevButton); }\n if (nextButton) { showElement(nextButton); }\n }\n } else {\n if (controlsContainer) {\n hideElement(controlsContainer);\n } else {\n if (prevButton) { hideElement(prevButton); }\n if (nextButton) { hideElement(nextButton); }\n }\n }\n }\n if (nav !== navTem) {\n if (nav) {\n showElement(navContainer);\n updateNavVisibility();\n } else {\n hideElement(navContainer)\n }\n }\n if (touch !== touchTem) {\n touch ?\n addEvents(container, touchEvents, options.preventScrollOnTouch) :\n removeEvents(container, touchEvents);\n }\n if (mouseDrag !== mouseDragTem) {\n mouseDrag ?\n addEvents(container, dragEvents) :\n removeEvents(container, dragEvents);\n }\n if (autoplay !== autoplayTem) {\n if (autoplay) {\n if (autoplayButton) { showElement(autoplayButton); }\n if (!animating && !autoplayUserPaused) { startAutoplay(); }\n } else {\n if (autoplayButton) { hideElement(autoplayButton); }\n if (animating) { stopAutoplay(); }\n }\n }\n if (autoplayHoverPause !== autoplayHoverPauseTem) {\n autoplayHoverPause ?\n addEvents(container, hoverEvents) :\n removeEvents(container, hoverEvents);\n }\n if (autoplayResetOnVisibility !== autoplayResetOnVisibilityTem) {\n autoplayResetOnVisibility ?\n addEvents(doc, visibilityEvent) :\n removeEvents(doc, visibilityEvent);\n }\n\n if (bpChanged) {\n if (fixedWidth !== fixedWidthTem || center !== centerTem) { needContainerTransform = true; }\n\n if (autoHeight !== autoHeightTem) {\n if (!autoHeight) { innerWrapper.style.height = ''; }\n }\n\n if (controls && controlsText !== controlsTextTem) {\n prevButton.innerHTML = controlsText[0];\n nextButton.innerHTML = controlsText[1];\n }\n\n if (autoplayButton && autoplayText !== autoplayTextTem) {\n var i = autoplay ? 1 : 0,\n html = autoplayButton.innerHTML,\n len = html.length - autoplayTextTem[i].length;\n if (html.substring(len) === autoplayTextTem[i]) {\n autoplayButton.innerHTML = html.substring(0, len) + autoplayText[i];\n }\n }\n } else {\n if (center && (fixedWidth || autoWidth)) { needContainerTransform = true; }\n }\n\n if (itemsChanged || fixedWidth && !autoWidth) {\n pages = getPages();\n updateNavVisibility();\n }\n\n indChanged = index !== indexTem;\n if (indChanged) {\n events.emit('indexChanged', info());\n needContainerTransform = true;\n } else if (itemsChanged) {\n if (!indChanged) { additionalUpdates(); }\n } else if (fixedWidth || autoWidth) {\n doLazyLoad();\n updateSlideStatus();\n updateLiveRegion();\n }\n\n if (itemsChanged && !carousel) { updateGallerySlidePositions(); }\n\n if (!disable && !freeze) {\n // non-mediaqueries: IE8\n if (bpChanged && !CSSMQ) {\n // middle wrapper styles\n\n // inner wrapper styles\n if (edgePadding !== edgePaddingTem || gutter !== gutterTem) {\n innerWrapper.style.cssText = getInnerWrapperStyles(edgePadding, gutter, fixedWidth, speed, autoHeight);\n }\n\n if (horizontal) {\n // container styles\n if (carousel) {\n container.style.width = getContainerWidth(fixedWidth, gutter, items);\n }\n\n // slide styles\n var str = getSlideWidthStyle(fixedWidth, gutter, items) +\n getSlideGutterStyle(gutter);\n\n // remove the last line and\n // add new styles\n removeCSSRule(sheet, getCssRulesLength(sheet) - 1);\n addCSSRule(sheet, '#' + slideId + ' > .tns-item', str, getCssRulesLength(sheet));\n }\n }\n\n // auto height\n if (autoHeight) { doAutoHeight(); }\n\n if (needContainerTransform) {\n doContainerTransformSilent();\n indexCached = index;\n }\n }\n\n if (bpChanged) { events.emit('newBreakpointEnd', info(e)); }\n }\n\n\n\n\n\n // === INITIALIZATION FUNCTIONS === //\n function getFreeze () {\n if (!fixedWidth && !autoWidth) {\n var a = center ? items - (items - 1) / 2 : items;\n return slideCount <= a;\n }\n\n var width = fixedWidth ? (fixedWidth + gutter) * slideCount : slidePositions[slideCount],\n vp = edgePadding ? viewport + edgePadding * 2 : viewport + gutter;\n\n if (center) {\n vp -= fixedWidth ? (viewport - fixedWidth) / 2 : (viewport - (slidePositions[index + 1] - slidePositions[index] - gutter)) / 2;\n }\n\n return width <= vp;\n }\n\n function setBreakpointZone () {\n breakpointZone = 0;\n for (var bp in responsive) {\n bp = parseInt(bp); // convert string to number\n if (windowWidth >= bp) { breakpointZone = bp; }\n }\n }\n\n // (slideBy, indexMin, indexMax) => index\n var updateIndex = (function () {\n return loop ?\n carousel ?\n // loop + carousel\n function () {\n var leftEdge = indexMin,\n rightEdge = indexMax;\n\n leftEdge += slideBy;\n rightEdge -= slideBy;\n\n // adjust edges when has edge paddings\n // or fixed-width slider with extra space on the right side\n if (edgePadding) {\n leftEdge += 1;\n rightEdge -= 1;\n } else if (fixedWidth) {\n if ((viewport + gutter)%(fixedWidth + gutter)) { rightEdge -= 1; }\n }\n\n if (cloneCount) {\n if (index > rightEdge) {\n index -= slideCount;\n } else if (index < leftEdge) {\n index += slideCount;\n }\n }\n } :\n // loop + gallery\n function() {\n if (index > indexMax) {\n while (index >= indexMin + slideCount) { index -= slideCount; }\n } else if (index < indexMin) {\n while (index <= indexMax - slideCount) { index += slideCount; }\n }\n } :\n // non-loop\n function() {\n index = Math.max(indexMin, Math.min(indexMax, index));\n };\n })();\n\n function disableUI () {\n if (!autoplay && autoplayButton) { hideElement(autoplayButton); }\n if (!nav && navContainer) { hideElement(navContainer); }\n if (!controls) {\n if (controlsContainer) {\n hideElement(controlsContainer);\n } else {\n if (prevButton) { hideElement(prevButton); }\n if (nextButton) { hideElement(nextButton); }\n }\n }\n }\n\n function enableUI () {\n if (autoplay && autoplayButton) { showElement(autoplayButton); }\n if (nav && navContainer) { showElement(navContainer); }\n if (controls) {\n if (controlsContainer) {\n showElement(controlsContainer);\n } else {\n if (prevButton) { showElement(prevButton); }\n if (nextButton) { showElement(nextButton); }\n }\n }\n }\n\n function freezeSlider () {\n if (frozen) { return; }\n\n // remove edge padding from inner wrapper\n if (edgePadding) { innerWrapper.style.margin = '0px'; }\n\n // add class tns-transparent to cloned slides\n if (cloneCount) {\n var str = 'tns-transparent';\n for (var i = cloneCount; i--;) {\n if (carousel) { addClass(slideItems[i], str); }\n addClass(slideItems[slideCountNew - i - 1], str);\n }\n }\n\n // update tools\n disableUI();\n\n frozen = true;\n }\n\n function unfreezeSlider () {\n if (!frozen) { return; }\n\n // restore edge padding for inner wrapper\n // for mordern browsers\n if (edgePadding && CSSMQ) { innerWrapper.style.margin = ''; }\n\n // remove class tns-transparent to cloned slides\n if (cloneCount) {\n var str = 'tns-transparent';\n for (var i = cloneCount; i--;) {\n if (carousel) { removeClass(slideItems[i], str); }\n removeClass(slideItems[slideCountNew - i - 1], str);\n }\n }\n\n // update tools\n enableUI();\n\n frozen = false;\n }\n\n function disableSlider () {\n if (disabled) { return; }\n\n sheet.disabled = true;\n container.className = container.className.replace(newContainerClasses.substring(1), '');\n removeAttrs(container, ['style']);\n if (loop) {\n for (var j = cloneCount; j--;) {\n if (carousel) { hideElement(slideItems[j]); }\n hideElement(slideItems[slideCountNew - j - 1]);\n }\n }\n\n // vertical slider\n if (!horizontal || !carousel) { removeAttrs(innerWrapper, ['style']); }\n\n // gallery\n if (!carousel) {\n for (var i = index, l = index + slideCount; i < l; i++) {\n var item = slideItems[i];\n removeAttrs(item, ['style']);\n removeClass(item, animateIn);\n removeClass(item, animateNormal);\n }\n }\n\n // update tools\n disableUI();\n\n disabled = true;\n }\n\n function enableSlider () {\n if (!disabled) { return; }\n\n sheet.disabled = false;\n container.className += newContainerClasses;\n doContainerTransformSilent();\n\n if (loop) {\n for (var j = cloneCount; j--;) {\n if (carousel) { showElement(slideItems[j]); }\n showElement(slideItems[slideCountNew - j - 1]);\n }\n }\n\n // gallery\n if (!carousel) {\n for (var i = index, l = index + slideCount; i < l; i++) {\n var item = slideItems[i],\n classN = i < index + items ? animateIn : animateNormal;\n item.style.left = (i - index) * 100 / items + '%';\n addClass(item, classN);\n }\n }\n\n // update tools\n enableUI();\n\n disabled = false;\n }\n\n function updateLiveRegion () {\n var str = getLiveRegionStr();\n if (liveregionCurrent.innerHTML !== str) { liveregionCurrent.innerHTML = str; }\n }\n\n function getLiveRegionStr () {\n var arr = getVisibleSlideRange(),\n start = arr[0] + 1,\n end = arr[1] + 1;\n return start === end ? start + '' : start + ' to ' + end;\n }\n\n function getVisibleSlideRange (val) {\n if (val == null) { val = getContainerTransformValue(); }\n var start = index, end, rangestart, rangeend;\n\n // get range start, range end for autoWidth and fixedWidth\n if (center || edgePadding) {\n if (autoWidth || fixedWidth) {\n rangestart = - (parseFloat(val) + edgePadding);\n rangeend = rangestart + viewport + edgePadding * 2;\n }\n } else {\n if (autoWidth) {\n rangestart = slidePositions[index];\n rangeend = rangestart + viewport;\n }\n }\n\n // get start, end\n // - check auto width\n if (autoWidth) {\n slidePositions.forEach(function(point, i) {\n if (i < slideCountNew) {\n if ((center || edgePadding) && point <= rangestart + 0.5) { start = i; }\n if (rangeend - point >= 0.5) { end = i; }\n }\n });\n\n // - check percentage width, fixed width\n } else {\n\n if (fixedWidth) {\n var cell = fixedWidth + gutter;\n if (center || edgePadding) {\n start = Math.floor(rangestart/cell);\n end = Math.ceil(rangeend/cell - 1);\n } else {\n end = start + Math.ceil(viewport/cell) - 1;\n }\n\n } else {\n if (center || edgePadding) {\n var a = items - 1;\n if (center) {\n start -= a / 2;\n end = index + a / 2;\n } else {\n end = index + a;\n }\n\n if (edgePadding) {\n var b = edgePadding * items / viewport;\n start -= b;\n end += b;\n }\n\n start = Math.floor(start);\n end = Math.ceil(end);\n } else {\n end = start + items - 1;\n }\n }\n\n start = Math.max(start, 0);\n end = Math.min(end, slideCountNew - 1);\n }\n\n return [start, end];\n }\n\n function doLazyLoad () {\n if (lazyload && !disable) {\n var arg = getVisibleSlideRange();\n arg.push(lazyloadSelector);\n\n getImageArray.apply(null, arg).forEach(function (img) {\n if (!hasClass(img, imgCompleteClass)) {\n // stop propagation transitionend event to container\n var eve = {};\n eve[TRANSITIONEND] = function (e) { e.stopPropagation(); };\n addEvents(img, eve);\n\n addEvents(img, imgEvents);\n\n // update src\n img.src = getAttr(img, 'data-src');\n\n // update srcset\n var srcset = getAttr(img, 'data-srcset');\n if (srcset) { img.srcset = srcset; }\n\n addClass(img, 'loading');\n }\n });\n }\n }\n\n function onImgLoaded (e) {\n imgLoaded(getTarget(e));\n }\n\n function onImgFailed (e) {\n imgFailed(getTarget(e));\n }\n\n function imgLoaded (img) {\n addClass(img, 'loaded');\n imgCompleted(img);\n }\n\n function imgFailed (img) {\n addClass(img, 'failed');\n imgCompleted(img);\n }\n\n function imgCompleted (img) {\n addClass(img, imgCompleteClass);\n removeClass(img, 'loading');\n removeEvents(img, imgEvents);\n }\n\n function getImageArray (start, end, imgSelector) {\n var imgs = [];\n if (!imgSelector) { imgSelector = 'img'; }\n\n while (start <= end) {\n forEach(slideItems[start].querySelectorAll(imgSelector), function (img) { imgs.push(img); });\n start++;\n }\n\n return imgs;\n }\n\n // check if all visible images are loaded\n // and update container height if it's done\n function doAutoHeight () {\n var imgs = getImageArray.apply(null, getVisibleSlideRange());\n raf(function(){ imgsLoadedCheck(imgs, updateInnerWrapperHeight); });\n }\n\n function imgsLoadedCheck (imgs, cb) {\n // execute callback function if all images are complete\n if (imgsComplete) { return cb(); }\n\n // check image classes\n imgs.forEach(function (img, index) {\n if (!lazyload && img.complete) { imgCompleted(img); } // Check image.complete\n if (hasClass(img, imgCompleteClass)) { imgs.splice(index, 1); }\n });\n\n // execute callback function if selected images are all complete\n if (!imgs.length) { return cb(); }\n\n // otherwise execute this functiona again\n raf(function(){ imgsLoadedCheck(imgs, cb); });\n }\n\n function additionalUpdates () {\n doLazyLoad();\n updateSlideStatus();\n updateLiveRegion();\n updateControlsStatus();\n updateNavStatus();\n }\n\n\n function update_carousel_transition_duration () {\n if (carousel && autoHeight) {\n middleWrapper.style[TRANSITIONDURATION] = speed / 1000 + 's';\n }\n }\n\n function getMaxSlideHeight (slideStart, slideRange) {\n var heights = [];\n for (var i = slideStart, l = Math.min(slideStart + slideRange, slideCountNew); i < l; i++) {\n heights.push(slideItems[i].offsetHeight);\n }\n\n return Math.max.apply(null, heights);\n }\n\n // update inner wrapper height\n // 1. get the max-height of the visible slides\n // 2. set transitionDuration to speed\n // 3. update inner wrapper height to max-height\n // 4. set transitionDuration to 0s after transition done\n function updateInnerWrapperHeight () {\n var maxHeight = autoHeight ? getMaxSlideHeight(index, items) : getMaxSlideHeight(cloneCount, slideCount),\n wp = middleWrapper ? middleWrapper : innerWrapper;\n\n if (wp.style.height !== maxHeight) { wp.style.height = maxHeight + 'px'; }\n }\n\n // get the distance from the top edge of the first slide to each slide\n // (init) => slidePositions\n function setSlidePositions () {\n slidePositions = [0];\n var attr = horizontal ? 'left' : 'top',\n attr2 = horizontal ? 'right' : 'bottom',\n base = slideItems[0].getBoundingClientRect()[attr];\n\n forEach(slideItems, function(item, i) {\n // skip the first slide\n if (i) { slidePositions.push(item.getBoundingClientRect()[attr] - base); }\n // add the end edge\n if (i === slideCountNew - 1) { slidePositions.push(item.getBoundingClientRect()[attr2] - base); }\n });\n }\n\n // update slide\n function updateSlideStatus () {\n var range = getVisibleSlideRange(),\n start = range[0],\n end = range[1];\n\n forEach(slideItems, function(item, i) {\n // show slides\n if (i >= start && i <= end) {\n if (hasAttr(item, 'aria-hidden')) {\n removeAttrs(item, ['aria-hidden', 'tabindex']);\n addClass(item, slideActiveClass);\n }\n // hide slides\n } else {\n if (!hasAttr(item, 'aria-hidden')) {\n setAttrs(item, {\n 'aria-hidden': 'true',\n 'tabindex': '-1'\n });\n removeClass(item, slideActiveClass);\n }\n }\n });\n }\n\n // gallery: update slide position\n function updateGallerySlidePositions () {\n var l = index + Math.min(slideCount, items);\n for (var i = slideCountNew; i--;) {\n var item = slideItems[i];\n\n if (i >= index && i < l) {\n // add transitions to visible slides when adjusting their positions\n addClass(item, 'tns-moving');\n\n item.style.left = (i - index) * 100 / items + '%';\n addClass(item, animateIn);\n removeClass(item, animateNormal);\n } else if (item.style.left) {\n item.style.left = '';\n addClass(item, animateNormal);\n removeClass(item, animateIn);\n }\n\n // remove outlet animation\n removeClass(item, animateOut);\n }\n\n // removing '.tns-moving'\n setTimeout(function() {\n forEach(slideItems, function(el) {\n removeClass(el, 'tns-moving');\n });\n }, 300);\n }\n\n // set tabindex on Nav\n function updateNavStatus () {\n // get current nav\n if (nav) {\n navCurrentIndex = navClicked >= 0 ? navClicked : getCurrentNavIndex();\n navClicked = -1;\n\n if (navCurrentIndex !== navCurrentIndexCached) {\n var navPrev = navItems[navCurrentIndexCached],\n navCurrent = navItems[navCurrentIndex];\n\n setAttrs(navPrev, {\n 'tabindex': '-1',\n 'aria-label': navStr + (navCurrentIndexCached + 1)\n });\n removeClass(navPrev, navActiveClass);\n\n setAttrs(navCurrent, {'aria-label': navStr + (navCurrentIndex + 1) + navStrCurrent});\n removeAttrs(navCurrent, 'tabindex');\n addClass(navCurrent, navActiveClass);\n\n navCurrentIndexCached = navCurrentIndex;\n }\n }\n }\n\n function getLowerCaseNodeName (el) {\n return el.nodeName.toLowerCase();\n }\n\n function isButton (el) {\n return getLowerCaseNodeName(el) === 'button';\n }\n\n function isAriaDisabled (el) {\n return el.getAttribute('aria-disabled') === 'true';\n }\n\n function disEnableElement (isButton, el, val) {\n if (isButton) {\n el.disabled = val;\n } else {\n el.setAttribute('aria-disabled', val.toString());\n }\n }\n\n // set 'disabled' to true on controls when reach the edges\n function updateControlsStatus () {\n if (!controls || rewind || loop) { return; }\n\n var prevDisabled = (prevIsButton) ? prevButton.disabled : isAriaDisabled(prevButton),\n nextDisabled = (nextIsButton) ? nextButton.disabled : isAriaDisabled(nextButton),\n disablePrev = (index <= indexMin) ? true : false,\n disableNext = (!rewind && index >= indexMax) ? true : false;\n\n if (disablePrev && !prevDisabled) {\n disEnableElement(prevIsButton, prevButton, true);\n }\n if (!disablePrev && prevDisabled) {\n disEnableElement(prevIsButton, prevButton, false);\n }\n if (disableNext && !nextDisabled) {\n disEnableElement(nextIsButton, nextButton, true);\n }\n if (!disableNext && nextDisabled) {\n disEnableElement(nextIsButton, nextButton, false);\n }\n }\n\n // set duration\n function resetDuration (el, str) {\n if (TRANSITIONDURATION) { el.style[TRANSITIONDURATION] = str; }\n }\n\n function getSliderWidth () {\n return fixedWidth ? (fixedWidth + gutter) * slideCountNew : slidePositions[slideCountNew];\n }\n\n function getCenterGap (num) {\n if (num == null) { num = index; }\n\n var gap = edgePadding ? gutter : 0;\n return autoWidth ? ((viewport - gap) - (slidePositions[num + 1] - slidePositions[num] - gutter))/2 :\n fixedWidth ? (viewport - fixedWidth) / 2 :\n (items - 1) / 2;\n }\n\n function getRightBoundary () {\n var gap = edgePadding ? gutter : 0,\n result = (viewport + gap) - getSliderWidth();\n\n if (center && !loop) {\n result = fixedWidth ? - (fixedWidth + gutter) * (slideCountNew - 1) - getCenterGap() :\n getCenterGap(slideCountNew - 1) - slidePositions[slideCountNew - 1];\n }\n if (result > 0) { result = 0; }\n\n return result;\n }\n\n function getContainerTransformValue (num) {\n if (num == null) { num = index; }\n\n var val;\n if (horizontal && !autoWidth) {\n if (fixedWidth) {\n val = - (fixedWidth + gutter) * num;\n if (center) { val += getCenterGap(); }\n } else {\n var denominator = TRANSFORM ? slideCountNew : items;\n if (center) { num -= getCenterGap(); }\n val = - num * 100 / denominator;\n }\n } else {\n val = - slidePositions[num];\n if (center && autoWidth) {\n val += getCenterGap();\n }\n }\n\n if (hasRightDeadZone) { val = Math.max(val, rightBoundary); }\n\n val += (horizontal && !autoWidth && !fixedWidth) ? '%' : 'px';\n\n return val;\n }\n\n function doContainerTransformSilent (val) {\n resetDuration(container, '0s');\n doContainerTransform(val);\n }\n\n function doContainerTransform (val) {\n if (val == null) { val = getContainerTransformValue(); }\n container.style[transformAttr] = transformPrefix + val + transformPostfix;\n }\n\n function animateSlide (number, classOut, classIn, isOut) {\n var l = number + items;\n if (!loop) { l = Math.min(l, slideCountNew); }\n\n for (var i = number; i < l; i++) {\n var item = slideItems[i];\n\n // set item positions\n if (!isOut) { item.style.left = (i - index) * 100 / items + '%'; }\n\n if (animateDelay && TRANSITIONDELAY) {\n item.style[TRANSITIONDELAY] = item.style[ANIMATIONDELAY] = animateDelay * (i - number) / 1000 + 's';\n }\n removeClass(item, classOut);\n addClass(item, classIn);\n\n if (isOut) { slideItemsOut.push(item); }\n }\n }\n\n // make transfer after click/drag:\n // 1. change 'transform' property for mordern browsers\n // 2. change 'left' property for legacy browsers\n var transformCore = (function () {\n return carousel ?\n function () {\n resetDuration(container, '');\n if (TRANSITIONDURATION || !speed) {\n // for morden browsers with non-zero duration or\n // zero duration for all browsers\n doContainerTransform();\n // run fallback function manually\n // when duration is 0 / container is hidden\n if (!speed || !isVisible(container)) { onTransitionEnd(); }\n\n } else {\n // for old browser with non-zero duration\n jsTransform(container, transformAttr, transformPrefix, transformPostfix, getContainerTransformValue(), speed, onTransitionEnd);\n }\n\n if (!horizontal) { updateContentWrapperHeight(); }\n } :\n function () {\n slideItemsOut = [];\n\n var eve = {};\n eve[TRANSITIONEND] = eve[ANIMATIONEND] = onTransitionEnd;\n removeEvents(slideItems[indexCached], eve);\n addEvents(slideItems[index], eve);\n\n animateSlide(indexCached, animateIn, animateOut, true);\n animateSlide(index, animateNormal, animateIn);\n\n // run fallback function manually\n // when transition or animation not supported / duration is 0\n if (!TRANSITIONEND || !ANIMATIONEND || !speed || !isVisible(container)) { onTransitionEnd(); }\n };\n })();\n\n function render (e, sliderMoved) {\n if (updateIndexBeforeTransform) { updateIndex(); }\n\n // render when slider was moved (touch or drag) even though index may not change\n if (index !== indexCached || sliderMoved) {\n // events\n events.emit('indexChanged', info());\n events.emit('transitionStart', info());\n if (autoHeight) { doAutoHeight(); }\n\n // pause autoplay when click or keydown from user\n if (animating && e && ['click', 'keydown'].indexOf(e.type) >= 0) { stopAutoplay(); }\n\n running = true;\n transformCore();\n }\n }\n\n /*\n * Transfer prefixed properties to the same format\n * CSS: -Webkit-Transform => webkittransform\n * JS: WebkitTransform => webkittransform\n * @param {string} str - property\n *\n */\n function strTrans (str) {\n return str.toLowerCase().replace(/-/g, '');\n }\n\n // AFTER TRANSFORM\n // Things need to be done after a transfer:\n // 1. check index\n // 2. add classes to visible slide\n // 3. disable controls buttons when reach the first/last slide in non-loop slider\n // 4. update nav status\n // 5. lazyload images\n // 6. update container height\n function onTransitionEnd (event) {\n // check running on gallery mode\n // make sure trantionend/animationend events run only once\n if (carousel || running) {\n events.emit('transitionEnd', info(event));\n\n if (!carousel && slideItemsOut.length > 0) {\n for (var i = 0; i < slideItemsOut.length; i++) {\n var item = slideItemsOut[i];\n // set item positions\n item.style.left = '';\n\n if (ANIMATIONDELAY && TRANSITIONDELAY) {\n item.style[ANIMATIONDELAY] = '';\n item.style[TRANSITIONDELAY] = '';\n }\n removeClass(item, animateOut);\n addClass(item, animateNormal);\n }\n }\n\n /* update slides, nav, controls after checking ...\n * => legacy browsers who don't support 'event'\n * have to check event first, otherwise event.target will cause an error\n * => or 'gallery' mode:\n * + event target is slide item\n * => or 'carousel' mode:\n * + event target is container,\n * + event.property is the same with transform attribute\n */\n if (!event ||\n !carousel && event.target.parentNode === container ||\n event.target === container && strTrans(event.propertyName) === strTrans(transformAttr)) {\n\n if (!updateIndexBeforeTransform) {\n var indexTem = index;\n updateIndex();\n if (index !== indexTem) {\n events.emit('indexChanged', info());\n\n doContainerTransformSilent();\n }\n }\n\n if (nested === 'inner') { events.emit('innerLoaded', info()); }\n running = false;\n indexCached = index;\n }\n }\n\n }\n\n // # ACTIONS\n function goTo (targetIndex, e) {\n if (freeze) { return; }\n\n // prev slideBy\n if (targetIndex === 'prev') {\n onControlsClick(e, -1);\n\n // next slideBy\n } else if (targetIndex === 'next') {\n onControlsClick(e, 1);\n\n // go to exact slide\n } else {\n if (running) {\n if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }\n }\n\n var absIndex = getAbsIndex(),\n indexGap = 0;\n\n if (targetIndex === 'first') {\n indexGap = - absIndex;\n } else if (targetIndex === 'last') {\n indexGap = carousel ? slideCount - items - absIndex : slideCount - 1 - absIndex;\n } else {\n if (typeof targetIndex !== 'number') { targetIndex = parseInt(targetIndex); }\n\n if (!isNaN(targetIndex)) {\n // from directly called goTo function\n if (!e) { targetIndex = Math.max(0, Math.min(slideCount - 1, targetIndex)); }\n\n indexGap = targetIndex - absIndex;\n }\n }\n\n // gallery: make sure new page won't overlap with current page\n if (!carousel && indexGap && Math.abs(indexGap) < items) {\n var factor = indexGap > 0 ? 1 : -1;\n indexGap += (index + indexGap - slideCount) >= indexMin ? slideCount * factor : slideCount * 2 * factor * -1;\n }\n\n index += indexGap;\n\n // make sure index is in range\n if (carousel && loop) {\n if (index < indexMin) { index += slideCount; }\n if (index > indexMax) { index -= slideCount; }\n }\n\n // if index is changed, start rendering\n if (getAbsIndex(index) !== getAbsIndex(indexCached)) {\n render(e);\n }\n\n }\n }\n\n // on controls click\n function onControlsClick (e, dir) {\n if (running) {\n if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }\n }\n var passEventObject;\n\n if (!dir) {\n e = getEvent(e);\n var target = getTarget(e);\n\n while (target !== controlsContainer && [prevButton, nextButton].indexOf(target) < 0) { target = target.parentNode; }\n\n var targetIn = [prevButton, nextButton].indexOf(target);\n if (targetIn >= 0) {\n passEventObject = true;\n dir = targetIn === 0 ? -1 : 1;\n }\n }\n\n if (rewind) {\n if (index === indexMin && dir === -1) {\n goTo('last', e);\n return;\n } else if (index === indexMax && dir === 1) {\n goTo('first', e);\n return;\n }\n }\n\n if (dir) {\n index += slideBy * dir;\n if (autoWidth) { index = Math.floor(index); }\n // pass e when click control buttons or keydown\n render((passEventObject || (e && e.type === 'keydown')) ? e : null);\n }\n }\n\n // on nav click\n function onNavClick (e) {\n if (running) {\n if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }\n }\n\n e = getEvent(e);\n var target = getTarget(e), navIndex;\n\n // find the clicked nav item\n while (target !== navContainer && !hasAttr(target, 'data-nav')) { target = target.parentNode; }\n if (hasAttr(target, 'data-nav')) {\n var navIndex = navClicked = Number(getAttr(target, 'data-nav')),\n targetIndexBase = fixedWidth || autoWidth ? navIndex * slideCount / pages : navIndex * items,\n targetIndex = navAsThumbnails ? navIndex : Math.min(Math.ceil(targetIndexBase), slideCount - 1);\n goTo(targetIndex, e);\n\n if (navCurrentIndex === navIndex) {\n if (animating) { stopAutoplay(); }\n navClicked = -1; // reset navClicked\n }\n }\n }\n\n // autoplay functions\n function setAutoplayTimer () {\n autoplayTimer = setInterval(function () {\n onControlsClick(null, autoplayDirection);\n }, autoplayTimeout);\n\n animating = true;\n }\n\n function stopAutoplayTimer () {\n clearInterval(autoplayTimer);\n animating = false;\n }\n\n function updateAutoplayButton (action, txt) {\n setAttrs(autoplayButton, {'data-action': action});\n autoplayButton.innerHTML = autoplayHtmlStrings[0] + action + autoplayHtmlStrings[1] + txt;\n }\n\n function startAutoplay () {\n setAutoplayTimer();\n if (autoplayButton) { updateAutoplayButton('stop', autoplayText[1]); }\n }\n\n function stopAutoplay () {\n stopAutoplayTimer();\n if (autoplayButton) { updateAutoplayButton('start', autoplayText[0]); }\n }\n\n // programaitcally play/pause the slider\n function play () {\n if (autoplay && !animating) {\n startAutoplay();\n autoplayUserPaused = false;\n }\n }\n function pause () {\n if (animating) {\n stopAutoplay();\n autoplayUserPaused = true;\n }\n }\n\n function toggleAutoplay () {\n if (animating) {\n stopAutoplay();\n autoplayUserPaused = true;\n } else {\n startAutoplay();\n autoplayUserPaused = false;\n }\n }\n\n function onVisibilityChange () {\n if (doc.hidden) {\n if (animating) {\n stopAutoplayTimer();\n autoplayVisibilityPaused = true;\n }\n } else if (autoplayVisibilityPaused) {\n setAutoplayTimer();\n autoplayVisibilityPaused = false;\n }\n }\n\n function mouseoverPause () {\n if (animating) {\n stopAutoplayTimer();\n autoplayHoverPaused = true;\n }\n }\n\n function mouseoutRestart () {\n if (autoplayHoverPaused) {\n setAutoplayTimer();\n autoplayHoverPaused = false;\n }\n }\n\n // keydown events on document\n function onDocumentKeydown (e) {\n e = getEvent(e);\n var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode);\n\n if (keyIndex >= 0) {\n onControlsClick(e, keyIndex === 0 ? -1 : 1);\n }\n }\n\n // on key control\n function onControlsKeydown (e) {\n e = getEvent(e);\n var keyIndex = [KEYS.LEFT, KEYS.RIGHT].indexOf(e.keyCode);\n\n if (keyIndex >= 0) {\n if (keyIndex === 0) {\n if (!prevButton.disabled) { onControlsClick(e, -1); }\n } else if (!nextButton.disabled) {\n onControlsClick(e, 1);\n }\n }\n }\n\n // set focus\n function setFocus (el) {\n el.focus();\n }\n\n // on key nav\n function onNavKeydown (e) {\n e = getEvent(e);\n var curElement = doc.activeElement;\n if (!hasAttr(curElement, 'data-nav')) { return; }\n\n // var code = e.keyCode,\n var keyIndex = [KEYS.LEFT, KEYS.RIGHT, KEYS.ENTER, KEYS.SPACE].indexOf(e.keyCode),\n navIndex = Number(getAttr(curElement, 'data-nav'));\n\n if (keyIndex >= 0) {\n if (keyIndex === 0) {\n if (navIndex > 0) { setFocus(navItems[navIndex - 1]); }\n } else if (keyIndex === 1) {\n if (navIndex < pages - 1) { setFocus(navItems[navIndex + 1]); }\n } else {\n navClicked = navIndex;\n goTo(navIndex, e);\n }\n }\n }\n\n function getEvent (e) {\n e = e || win.event;\n return isTouchEvent(e) ? e.changedTouches[0] : e;\n }\n function getTarget (e) {\n return e.target || win.event.srcElement;\n }\n\n function isTouchEvent (e) {\n return e.type.indexOf('touch') >= 0;\n }\n\n function preventDefaultBehavior (e) {\n e.preventDefault ? e.preventDefault() : e.returnValue = false;\n }\n\n function getMoveDirectionExpected () {\n return getTouchDirection(toDegree(lastPosition.y - initPosition.y, lastPosition.x - initPosition.x), swipeAngle) === options.axis;\n }\n\n function onPanStart (e) {\n if (running) {\n if (preventActionWhenRunning) { return; } else { onTransitionEnd(); }\n }\n\n if (autoplay && animating) { stopAutoplayTimer(); }\n\n panStart = true;\n if (rafIndex) {\n caf(rafIndex);\n rafIndex = null;\n }\n\n var $ = getEvent(e);\n events.emit(isTouchEvent(e) ? 'touchStart' : 'dragStart', info(e));\n\n if (!isTouchEvent(e) && ['img', 'a'].indexOf(getLowerCaseNodeName(getTarget(e))) >= 0) {\n preventDefaultBehavior(e);\n }\n\n lastPosition.x = initPosition.x = $.clientX;\n lastPosition.y = initPosition.y = $.clientY;\n if (carousel) {\n translateInit = parseFloat(container.style[transformAttr].replace(transformPrefix, ''));\n resetDuration(container, '0s');\n }\n }\n\n function onPanMove (e) {\n if (panStart) {\n var $ = getEvent(e);\n lastPosition.x = $.clientX;\n lastPosition.y = $.clientY;\n\n if (carousel) {\n if (!rafIndex) { rafIndex = raf(function(){ panUpdate(e); }); }\n } else {\n if (moveDirectionExpected === '?') { moveDirectionExpected = getMoveDirectionExpected(); }\n if (moveDirectionExpected) { preventScroll = true; }\n }\n\n if ((typeof e.cancelable !== 'boolean' || e.cancelable) && preventScroll) {\n e.preventDefault();\n }\n }\n }\n\n function panUpdate (e) {\n if (!moveDirectionExpected) {\n panStart = false;\n return;\n }\n caf(rafIndex);\n if (panStart) { rafIndex = raf(function(){ panUpdate(e); }); }\n\n if (moveDirectionExpected === '?') { moveDirectionExpected = getMoveDirectionExpected(); }\n if (moveDirectionExpected) {\n if (!preventScroll && isTouchEvent(e)) { preventScroll = true; }\n\n try {\n if (e.type) { events.emit(isTouchEvent(e) ? 'touchMove' : 'dragMove', info(e)); }\n } catch(err) {}\n\n var x = translateInit,\n dist = getDist(lastPosition, initPosition);\n if (!horizontal || fixedWidth || autoWidth) {\n x += dist;\n x += 'px';\n } else {\n var percentageX = TRANSFORM ? dist * items * 100 / ((viewport + gutter) * slideCountNew): dist * 100 / (viewport + gutter);\n x += percentageX;\n x += '%';\n }\n\n container.style[transformAttr] = transformPrefix + x + transformPostfix;\n }\n }\n\n function onPanEnd (e) {\n if (panStart) {\n if (rafIndex) {\n caf(rafIndex);\n rafIndex = null;\n }\n if (carousel) { resetDuration(container, ''); }\n panStart = false;\n\n var $ = getEvent(e);\n lastPosition.x = $.clientX;\n lastPosition.y = $.clientY;\n var dist = getDist(lastPosition, initPosition);\n\n if (Math.abs(dist)) {\n // drag vs click\n if (!isTouchEvent(e)) {\n // prevent \"click\"\n var target = getTarget(e);\n addEvents(target, {'click': function preventClick (e) {\n preventDefaultBehavior(e);\n removeEvents(target, {'click': preventClick});\n }});\n }\n\n if (carousel) {\n rafIndex = raf(function() {\n if (horizontal && !autoWidth) {\n var indexMoved = - dist * items / (viewport + gutter);\n indexMoved = dist > 0 ? Math.floor(indexMoved) : Math.ceil(indexMoved);\n index += indexMoved;\n } else {\n var moved = - (translateInit + dist);\n if (moved <= 0) {\n index = indexMin;\n } else if (moved >= slidePositions[slideCountNew - 1]) {\n index = indexMax;\n } else {\n var i = 0;\n while (i < slideCountNew && moved >= slidePositions[i]) {\n index = i;\n if (moved > slidePositions[i] && dist < 0) { index += 1; }\n i++;\n }\n }\n }\n\n render(e, dist);\n events.emit(isTouchEvent(e) ? 'touchEnd' : 'dragEnd', info(e));\n });\n } else {\n if (moveDirectionExpected) {\n onControlsClick(e, dist > 0 ? -1 : 1);\n }\n }\n }\n }\n\n // reset\n if (options.preventScrollOnTouch === 'auto') { preventScroll = false; }\n if (swipeAngle) { moveDirectionExpected = '?'; }\n if (autoplay && !animating) { setAutoplayTimer(); }\n }\n\n // === RESIZE FUNCTIONS === //\n // (slidePositions, index, items) => vertical_conentWrapper.height\n function updateContentWrapperHeight () {\n var wp = middleWrapper ? middleWrapper : innerWrapper;\n wp.style.height = slidePositions[index + items] - slidePositions[index] + 'px';\n }\n\n function getPages () {\n var rough = fixedWidth ? (fixedWidth + gutter) * slideCount / viewport : slideCount / items;\n return Math.min(Math.ceil(rough), slideCount);\n }\n\n /*\n * 1. update visible nav items list\n * 2. add \"hidden\" attributes to previous visible nav items\n * 3. remove \"hidden\" attrubutes to new visible nav items\n */\n function updateNavVisibility () {\n if (!nav || navAsThumbnails) { return; }\n\n if (pages !== pagesCached) {\n var min = pagesCached,\n max = pages,\n fn = showElement;\n\n if (pagesCached > pages) {\n min = pages;\n max = pagesCached;\n fn = hideElement;\n }\n\n while (min < max) {\n fn(navItems[min]);\n min++;\n }\n\n // cache pages\n pagesCached = pages;\n }\n }\n\n function info (e) {\n return {\n container: container,\n slideItems: slideItems,\n navContainer: navContainer,\n navItems: navItems,\n controlsContainer: controlsContainer,\n hasControls: hasControls,\n prevButton: prevButton,\n nextButton: nextButton,\n items: items,\n slideBy: slideBy,\n cloneCount: cloneCount,\n slideCount: slideCount,\n slideCountNew: slideCountNew,\n index: index,\n indexCached: indexCached,\n displayIndex: getCurrentSlide(),\n navCurrentIndex: navCurrentIndex,\n navCurrentIndexCached: navCurrentIndexCached,\n pages: pages,\n pagesCached: pagesCached,\n sheet: sheet,\n isOn: isOn,\n event: e || {},\n };\n }\n\n return {\n version: '2.9.4',\n getInfo: info,\n events: events,\n goTo: goTo,\n play: play,\n pause: pause,\n isOn: isOn,\n updateSliderHeight: updateInnerWrapperHeight,\n refresh: initSliderTransform,\n destroy: destroy,\n rebuild: function() {\n return tns(extend(options, optionsElements));\n }\n };\n};\n","// get css-calc \n// @return - false | calc | -webkit-calc | -moz-calc\n// @usage - var calc = getCalc(); \nimport { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function calc() {\n var doc = document, \n body = getBody(),\n docOverflow = setFakeBody(body),\n div = doc.createElement('div'), \n result = false;\n\n body.appendChild(div);\n try {\n var str = '(10px * 10)',\n vals = ['calc' + str, '-moz-calc' + str, '-webkit-calc' + str],\n val;\n for (var i = 0; i < 3; i++) {\n val = vals[i];\n div.style.width = val;\n if (div.offsetWidth === 100) { \n result = val.replace(str, ''); \n break;\n }\n }\n } catch (e) {}\n \n body.fake ? resetFakeBody(body, docOverflow) : div.remove();\n\n return result;\n}","// get subpixel support value\n// @return - boolean\nimport { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function percentageLayout() {\n // check subpixel layout supporting\n var doc = document,\n body = getBody(),\n docOverflow = setFakeBody(body),\n wrapper = doc.createElement('div'),\n outer = doc.createElement('div'),\n str = '',\n count = 70,\n perPage = 3,\n supported = false;\n\n wrapper.className = \"tns-t-subp2\";\n outer.className = \"tns-t-ct\";\n\n for (var i = 0; i < count; i++) {\n str += '
';\n }\n\n outer.innerHTML = str;\n wrapper.appendChild(outer);\n body.appendChild(wrapper);\n\n supported = Math.abs(wrapper.getBoundingClientRect().left - outer.children[count - perPage].getBoundingClientRect().left) < 2;\n\n body.fake ? resetFakeBody(body, docOverflow) : wrapper.remove();\n\n return supported;\n}","import { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function mediaquerySupport () {\n if (window.matchMedia || window.msMatchMedia) {\n return true;\n }\n \n var doc = document,\n body = getBody(),\n docOverflow = setFakeBody(body),\n div = doc.createElement('div'),\n style = doc.createElement('style'),\n rule = '@media all and (min-width:1px){.tns-mq-test{position:absolute}}',\n position;\n\n style.type = 'text/css';\n div.className = 'tns-mq-test';\n\n body.appendChild(style);\n body.appendChild(div);\n\n if (style.styleSheet) {\n style.styleSheet.cssText = rule;\n } else {\n style.appendChild(doc.createTextNode(rule));\n }\n\n position = window.getComputedStyle ? window.getComputedStyle(div).position : div.currentStyle['position'];\n\n body.fake ? resetFakeBody(body, docOverflow) : div.remove();\n\n return position === \"absolute\";\n}\n","import { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function has3DTransforms(tf){\n if (!tf) { return false; }\n if (!window.getComputedStyle) { return false; }\n \n var doc = document,\n body = getBody(),\n docOverflow = setFakeBody(body),\n el = doc.createElement('p'),\n has3d,\n cssTF = tf.length > 9 ? '-' + tf.slice(0, -9).toLowerCase() + '-' : '';\n\n cssTF += 'transform';\n\n // Add it to the body to get the computed style\n body.insertBefore(el, null);\n\n el.style[tf] = 'translate3d(1px,1px,1px)';\n has3d = window.getComputedStyle(el).getPropertyValue(cssTF);\n\n body.fake ? resetFakeBody(body, docOverflow) : el.remove();\n\n return (has3d !== undefined && has3d.length > 0 && has3d !== \"none\");\n}\n","// create and append style sheet\nexport function createStyleSheet (media, nonce) {\n // Create the