(function() { var _a; "use strict"; var __vite_style__ = document.createElement("style"); __vite_style__.textContent = "/*@TODO: Update Variables for customization*/\n:root {\n --loop-primary-color: #030712;\n --loop-text-color: white;\n --loop-badge-background: #F9FAFB;\n --loop-badge-text: #374151;\n --loop-button-background: #D1D5DB;\n --loop-button-text: #1F2937;\n\n --loop-onstore-button-padding: 14px 16px;\n --loop-onstore-checkout-background: #030712;\n --loop-onstore-checkout-text-color: white;\n --loop-onstore-button-radius: 0;\n --loop-onstore-button-gap: 0.25rem;\n --loop-onstore-add-more-background: white;\n --loop-onstore-add-more-text-color: black;\n --loop-onstore-add-more-border: black;\n --loop-onstore-casing: uppercase;\n}\n\n.loop-returns-activated .loop-onstore {\n transform: translateY(0);\n display: flex;\n}\n\n.loop-onstore {\n box-sizing: border-box;\n width: 100%;\n position: fixed;\n display: none;\n align-items: center;\n justify-content: space-between;\n top: auto !important;\n bottom: 0 !important;\n left: 0;\n z-index: 2147483647;\n background-color: var(--onstore-bg-color);\n color: var(--onstore-text-color);\n transform: translateY(100%);\n transition: transform 0.2s;\n font-family: 'SF Pro Text', untitled sans, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' !important;\n font-size: 1rem;\n padding: 1.25rem 2rem;\n}\n\n.loop-onstore *,\n.loop-onstore *::before,\n.loop-onstore *::after {\n box-sizing: inherit;\n}\n\n.loop-onstore--full {\n width: 100% !important;\n left: 0 !important;\n right: 0 !important;\n}\n\n.loop-onstore--half-right {\n width: 50% !important;\n right: 0 !important;\n left: unset !important;\n}\n\n.loop-onstore--half-left {\n width: 50% !important;\n left: 0 !important;\n right: unset !important;\n}\n\n.loop-onstore__credit {\n display: flex;\n align-items: baseline;\n}\n\n.loop-onstore__amount {\n margin: 0;\n margin-right: 0.5rem;\n font-size: var(--onstore-value-size);\n font-weight: 800;\n color: var(--onstore-text-color);\n}\n\n.loop-onstore__percent-discount {\n display: none;\n align-items: baseline;\n}\n\n.loop-onstore__percent-discount-amount {\n font-size: var(--onstore-value-size) !important;\n color: var(--onstore-bg-color) !important;\n background-color: var(--onstore-text-color) !important;\n padding: 0.25rem 0.75rem !important;\n border-radius: 0.5rem;\n margin-right: 0.5rem;\n}\n\n.loop-onstore__copy {\n margin: 0;\n color: var(--onstore-text-color);\n font-size: var(--onstore-label-size);\n}\n\n.loop-onstore__back {\n color: var(--onstore-bg-color);\n background: var(--onstore-text-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1.25rem;\n margin-left: 0;\n border: none;\n appearance: none;\n transition: all 0.2s;\n font-size: var(--onstore-button-size);\n border-radius: 5px;\n cursor: pointer;\n}\n\n.loop-onstore__back-icon {\n color: var(--onstore-bg-color);\n fill: var(--onstore-bg-color);\n font-size: var(--button-size);\n}\n\n.loop-onstore__back span {\n font-size: var(--onstore-button-size);\n}\n\n.loop-onstore__back:hover {\n opacity: 0.7;\n}\n\n.loop-returns-modal {\n font-family: 'SF Pro Text', untitled sans, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,\n Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji !important;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-style: normal;\n display: flex;\n justify-content: center;\n align-items: center;\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 999;\n}\n\n.loop-onstore__percent-copy {\n display: none;\n}\n\n.loop-returns-modal__backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(65, 69, 74, 0.7);\n}\n\n.loop-returns-modal__content {\n overflow: auto;\n max-height: 600px;\n background-color: #fff;\n box-shadow: 0px 1px 4px rgb(0 0 0 / 12%), 0px 6px 12px rgb(0 0 0 / 8%);\n border-radius: 8px;\n width: 500px;\n max-width: 700px;\n position: relative;\n z-index: 1000;\n}\n\n.loop-returns-modal__header {\n color: #3256e5;\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 15px 20px;\n border-bottom: 1px solid #eaeaea;\n font-size: 1.25rem;\n font-weight: 500;\n}\n\n.loop-returns-modal__close--icon {\n height: 20px;\n width: 20px;\n cursor: pointer;\n}\n\n.loop-returns-modal__body {\n padding: 20px;\n}\n\n.loop-onstore__credit-copy {\n display: none;\n}\n\n.loop-onstore__mobile-credit-copy {\n display: block;\n}\n\nloop-onstore {\n flex: 1;\n}\nloop-onstore::part(loop-onstore-checkout-btn) {\n padding: var(--loop-onstore-button-padding);\n background: var(--loop-onstore-checkout-background);\n color: var(--loop-onstore-checkout-text-color);\n border-radius: var(--loop-onstore-button-radius);\n cursor: pointer;\n font-weight: 500;\n text-transform: var(--loop-onstore-casing);\n}\nloop-onstore::part(loop-onstore-add-more-btn) {\n padding: var(--loop-onstore-button-padding);\n background: transparent;\n border: 1px solid var(--loop-onstore-add-more-border);\n border-radius: var(--loop-onstore-button-radius);\n color: var(--loop-onstore-add-more-text-color);\n cursor: pointer;\n font-weight: 500;\n text-transform: var(--loop-onstore-casing);\n}\n\n@media screen and (max-width: 680px) {\n .loop-onstore {\n z-index: 200;\n bottom: 3.2rem;\n padding: 1rem 1.5rem;\n }\n\n .loop-onstore__copy {\n font-size: 0.875rem !important;\n }\n\n .loop-onstore__amount,\n .loop-onstore__percent-discount-amount {\n font-size: 1rem !important;\n }\n\n .loop-onstore__back {\n padding: 0.25rem 0.5rem;\n }\n\n .loop-onstore__percent-discount-amount {\n padding: 0.25rem 0.5rem;\n }\n\n .loop-onstore--half-left, .loop-onstore--half-right {\n width: 100% !important;\n }\n}\n\n@media screen and (min-width: 680px) {\n .loop-onstore__credit-copy {\n display: block;\n }\n\n .loop-onstore__mobile-credit-copy {\n display: none;\n }\n}\n"; document.head.appendChild(__vite_style__); const onstore = ""; const onstoreBlock = document.querySelector("#loop-onstore-data"); const onstoreRootEl = document.querySelector(":root"); if (onstoreBlock) { const { textcolor, bgcolor, valuesize, labelsize, buttonsize } = onstoreBlock.dataset; if (onstoreRootEl) { onstoreRootEl.style.setProperty("--onstore-bg-color", bgcolor || "#000000"); onstoreRootEl.style.setProperty("--onstore-text-color", textcolor || "#ffffff"); onstoreRootEl.style.setProperty("--onstore-value-size", !!valuesize ? `${valuesize}px` : "15px"); onstoreRootEl.style.setProperty("--onstore-label-size", !!labelsize ? `${labelsize}px` : "10px"); onstoreRootEl.style.setProperty( "--onstore-button-size", `${buttonsize}px` || "10px" ); } } const { showOnstoreBar, apikey, checkoutselector, cartdetails, preservecart, label, mobileLabel, alignment, replacecheckoutbutton } = onstoreBlock.dataset; const storeName = "loop-onstore-data"; const storage = { /** * Get an item from localstorage * @param {string} name * @returns {*} */ get(name = storeName) { return JSON.parse(localStorage.getItem(name) || null); }, /** * Save an item to localstorage * @param {*} storeData * @param {string} name - the key to save to * @returns {void} */ set(storeData, name = storeName) { localStorage.setItem(name, JSON.stringify(storeData)); }, /** * Remove an item from localstorage * @param {string} name * @returns {void} */ remove(name = storeName) { localStorage.removeItem(name); } }; let loopOnStoreState = { debug: false, testMode: false, redirect: true, active: false, params: null, key: null, api: null, token: null, id: null, fullPayload: false, preserveCart: false, draftOrderId: null, inExperiment: false, singleItem: false, attach: null, isEmbeddedPortal: false }; const getState = (key) => { if (key) { return loopOnStoreState[key]; } return loopOnStoreState; }; const setState = (key, data) => { loopOnStoreState[key] = data; storage.set(loopOnStoreState); }; const initState = () => { const saved = storage.get(); if (saved) { loopOnStoreState = { ...loopOnStoreState, ...saved }; } }; const loopOnstoreEncrypt = (value) => { const isObject = typeof value === "object" && value !== null; const valueAsString = isObject ? JSON.stringify(value) : String(value); return window.btoa(unescape(encodeURIComponent(valueAsString))); }; const getApi = () => { return getState("api") ?? "https://api.loopreturns.com/api/v1/"; }; const mapCart = (shopifyCart) => { const { token, total_discount, original_total_price, total_price, items, currency, items_subtotal_price, cart_level_discount_applications } = shopifyCart; return loopOnstoreEncrypt({ token, total_discount, original_total_price, total_price, items: items.map((item) => { const { id, quantity, variant_id, price, original_price, discounted_price, original_line_price, line_price, total_discount: total_discount2, final_price, final_line_price, taxable, product_id, discount_allocations, line_level_discount_allocations, properties, } = item; return { id, quantity, variant_id, price, original_price, discounted_price, original_line_price, line_price, total_discount: total_discount2, final_price, final_line_price, taxable, product_id, discount_allocations, line_level_discount_allocations, properties, }; }), currency, items_subtotal_price, cart_level_discount_applications }); }; const request = async (url, type = "GET", payload, key = getState("key") || "") => { try { const data = payload !== void 0 ? JSON.stringify(payload) : null; const response = await fetch(url, { method: type, body: data, headers: { "X-Authorization": key, "Content-Type": "application/json" } }); return await response.json(); } catch (err) { console.error(err); return { error: err }; } }; const getShopifyCart = () => { const url = "/cart.js"; return request(url); }; const clearShopifyCart = () => { const url = "/cart/clear.js"; return request(url, "POST"); }; const updateShopifyCart = (cart) => { if (!(cart == null ? void 0 : cart.length)) { return clearShopifyCart(); } const url = "/cart/update.js"; const updates = cart == null ? void 0 : cart.reduce((acc, cur) => { if (acc[cur]) { return { ...acc, [cur]: acc[cur] + 1 }; } return { ...acc, [cur]: 1 }; }, {}); return request(url, "POST", { updates }); }; const getLoopCart = (token) => { const api = getApi(); const url = `${api}onstore/${token}`; return request(url); }; const createLoopCart = (variants, cart) => { const api = getApi(); const url = `${api}onstore/`; let fullPayload = {}; if (getState("fullPayload")) { fullPayload = { shopify: mapCart(cart) }; } return request(url, "POST", { cart: variants, draftOrderId: getState("draftOrderId"), ...fullPayload }); }; const updateLoopCart = (token, variants, cart) => { const api = getApi(); const url = `${api}onstore/${token}`; let fullPayload = {}; if (getState("fullPayload")) { fullPayload = { shopify: mapCart(cart) }; } return request(url, "POST", { cart: variants, draftOrderId: getState("draftOrderId"), ...fullPayload }); }; const checkOnstoreEligibility = async (apikey2) => { const api = getApi(); const url = `${api}onstore/check`; try { const response = await request(url, "GET", void 0, apikey2); if (response.enabled) { return { ...response, successful: true }; } if (response.error) { return { ...response, successful: false }; } return response; } catch (err) { console.error(err); return { enabled: null, successful: false, error: err }; } }; const loopLogger = new Proxy( {}, { get(obj, prop) { if (!obj[prop] && getState("debug")) { return (arg) => { if (typeof arg === "string") { return console[prop](`%c${arg}`, "color: #3256E6"); } return console[prop](arg); }; } return obj[prop] || function() { }; } } ); class LoopOnstoreButton extends HTMLElement { constructor() { super(); } connectedCallback() { if (!LoopOnstore.isActive() || !getState("inExperiment")) return; const attach = getState("attach"); const checkoutButtonList = document.querySelectorAll(attach) ?? []; checkoutButtonList.forEach((btn) => btn.remove()); const container = this.buildBtnContainer(); const shadowRoot = this.attachShadow({ mode: "open" }); shadowRoot.appendChild(container); } buildBtnContainer() { const container = document.createElement("div"); container.className = "loop-onstore-btn-container"; container.style = "display:flex; flex-direction: column; gap: 1rem;"; const checkoutBtn = this.buildCheckoutBtn(); const addMoreBtn = this.buildAddMoreBtn(); container.appendChild(checkoutBtn); if (!getState("singleItem")) { container.appendChild(addMoreBtn); } return container; } buildAddMoreBtn() { const addMoreBtn = document.createElement("button"); addMoreBtn.textContent = "Select more items to return"; addMoreBtn.part = "loop-onstore-add-more-btn"; addMoreBtn.addEventListener("click", this.handleAddMore); return addMoreBtn; } buildCheckoutBtn() { const checkoutBtn = document.createElement("button"); checkoutBtn.textContent = "Checkout"; checkoutBtn.part = "loop-onstore-checkout-btn"; checkoutBtn.addEventListener("click", this.handleCheckout); return checkoutBtn; } handleCheckout() { LoopOnstore.submit(); } handleAddMore() { LoopOnstore.submit(getState("key"), null, "lineItems"); } } const attachListeners = (selectors, listener) => { const attached = [...document.querySelectorAll(selectors)]; if (attached.length > 1) { loopLogger.warn(`Attaching Loop.submit() to more than one element. Make sure you're using the right selector!`); } attached.forEach((element) => { console.log(`Attaching Onstore listener for element with selector ${selectors}: `, element); element.addEventListener("click", listener); }); if (!customElements.get("loop-onstore")) { customElements.define("loop-onstore", LoopOnstoreButton); } return attached; }; const getNodes = (list, type) => { return list.filter((item) => item[type].length).reduce((acc, item) => { return [ ...acc, ...item[type] ]; }, []); }; const loopOnstoreCurrencyMap = { JPY: { constant: 1, decimals: 0, decimalSymbol: "", thousandSymbol: ",", symbol: "¥" }, GBP: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "£" }, EUR: { constant: 100, decimals: 2, decimalSymbol: ",", thousandSymbol: ".", symbol: "€" }, USD: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "$" }, CAD: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "$" }, AUD: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "$" }, HKD: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "$" }, NZD: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "$" }, SGD: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: ",", symbol: "$" }, CHF: { constant: 100, decimals: 2, decimalSymbol: ".", thousandSymbol: "'", symbol: "CHF" } }; const splitCurrency = (value, rules) => { const [dollars, cents] = Number.parseFloat(value / rules.constant).toFixed(rules.decimals).split("."); return { dollars: dollars ? dollars.replace(/\B(?=(\d{3})+(?!\d))/g, rules.thousandSymbol) : "0", cents: cents ? cents : "" }; }; const format = (value, currency = "USD") => { if (!value) { return ""; } const rules = loopOnstoreCurrencyMap[currency]; if (rules) { const { dollars, cents } = splitCurrency(value, rules); return `${rules.symbol}${dollars}${rules.decimalSymbol}${cents}`; } return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(value / rules.constant); }; const syncCart = (variants, cart) => { loopLogger.info("Creating cart request..."); loopLogger.info("Syncing these variants to the Loop cart:"); loopLogger.info(variants); const token = getState("token"); if (token) { return updateLoopCart(token, variants, cart).then((res) => { return { ...res, token }; }); } return createLoopCart(variants, cart); }; const convertNumbers = (str) => { const converted = Number(str); return Number.isNaN(converted) ? str : converted; }; const params = { /** * Get an object of key/values pairs from the query params * @param {string} str * @returns {object} */ get(str = window.location.search) { return str.substr(1).split("&").reduce((acc, cur) => { const [key, value] = decodeURIComponent(cur).split("="); return { ...acc, [key]: convertNumbers(value) }; }, {}); }, /** * Looks for loop data in the query params * @param {string} str * @returns {boolean} */ hasLoopData(str = window.location.search) { return str.includes("loop_total"); }, /** * * @param {object} obj - an object of key/value pairs from the current query params * @param {string} prefix - the query param prefix to look for * @returns {array} */ getKeys(obj, prefix = "loop") { return Object.keys(obj).filter((key) => key.startsWith(prefix)); }, /** * Removes a list of params from query params * @param {array} keys - an array of keys to remove * @returns {string} */ clear(keys = []) { const params2 = Object.entries(this.get()).reduce((acc, [key, value]) => { if (keys.includes(key)) { return acc; } const prefix = !acc.length ? "?" : "&"; return `${acc}${prefix}${encodeURIComponent(key)}=${encodeURIComponent(value)}`; }, ""); window.history.replaceState({}, "", `${window.location.origin}${window.location.pathname}${params2}`); } }; const stringToBool = (boolString) => boolString === "true"; const onstoreBannerTemplate = (label2 = "in available credit", mobileLabel2 = "credit") => `
applied at checkout
${label2} ${mobileLabel2}
`; const loopOnstoreSelectors = { credit: { container: ".loop-onstore__credit", amount: ".loop-onstore__credit-amount" }, percentDiscount: { container: ".loop-onstore__percent-discount", amount: ".loop-onstore__percent-discount-amount" } }; const createBar = (data = {}, barOpts = {}) => { const element = document.createElement("div"); element.classList.add("loop-onstore"); element.classList.add(`loop-onstore--${barOpts.alignment}`); element.id = "loop-onstore"; element.innerHTML = onstoreBannerTemplate(barOpts.label, barOpts.mobileLabel); Object.keys(data).forEach((sectionKey) => { const sectionData = data[sectionKey]; const sectionSelectors = loopOnstoreSelectors[sectionKey]; if (sectionSelectors && sectionData) { const sectionElement = element.querySelector(sectionSelectors.container); if (sectionElement && sectionElement.matches(sectionSelectors.container)) { sectionElement.style.display = "flex"; const amountElement = sectionElement.querySelector(sectionSelectors.amount); if (amountElement && sectionData.amount) { amountElement.textContent = sectionData.amount; renderProperty(sectionElement, sectionData.amount, sectionSelectors.amount); } } } }); const backButton = element.querySelector(".loop-onstore__back"); if (backButton) { backButton.addEventListener("click", async () => { const cart = await getShopifyCart(); const variants = cart.items.reduce((acc, item) => { return [...acc, ...Array(item.quantity).fill(item.variant_id)]; }, []); const loopCart = await syncCart(variants, cart); LoopOnstore$1.backToLoop(loopCart.token, "lineItems"); }); } return element; }; const liftCart = () => { const onstoreBlock2 = document.querySelector("#loop-onstore-data"); if (onstoreBlock2) { const { bumpsidecart, sidecartselector } = onstoreBlock2.dataset; if (stringToBool(bumpsidecart)) { const sidecartEl = document.querySelector(sidecartselector); const loopOnstoreBar = document.querySelector(".loop-onstore"); if (sidecartEl && loopOnstoreBar) { try { sidecartEl.style.height = `calc(100% - ${loopOnstoreBar.clientHeight}px)`; } catch (e) { } } } } }; const renderProperty = (ref, prop, selector) => { const element = ref.querySelector(selector); element.textContent = prop; return ref; }; const loopRender = (existing, data = {}, barOpts = {}) => { if (existing) { return existing; } const element = createBar(data, barOpts); return document.body.appendChild(element); }; const generateModalTemplate = (modalOpts = { modalHeader: "Loop On-Store Installed", modalBody: "Test Mode Successful" }) => { return `
${modalOpts.modalHeader}
${modalOpts.modalBody}
`; }; const loopOnstoreOpenModal = (modalOpts) => { document.body.style.overflow = "hidden"; const modal = document.createElement("div"); modal.classList.add("loop-returns-modal"); modal.innerHTML = generateModalTemplate(modalOpts); document.body.appendChild(modal); const closeModal = document.querySelector(".loop-returns-modal__close"); const closeOutsideModal = document.querySelector( ".loop-returns-modal__backdrop" ); const handler = (e) => { if (e.key === "Escape") { modal.remove(); document.removeEventListener("keydown", handler); } }; document.addEventListener("keydown", handler); closeModal == null ? void 0 : closeModal.addEventListener("click", () => { modal.remove(); document.removeEventListener("keydown", handler); document.body.style.overflow = "auto"; }); closeOutsideModal == null ? void 0 : closeOutsideModal.addEventListener("click", () => { modal.remove(); document.removeEventListener("keydown", handler); document.body.style.overflow = "auto"; }); }; const loopCheckoutBtnSelector = "loop-checkout-button"; const handleCheckoutButton = async (oldCheckoutBtnSelector, listener, attachListeners2, replaceCheckoutButton, apikey2) => { try { if (!oldCheckoutBtnSelector) throw new Error("No checkout button selector provided."); if (!replaceCheckoutButton) return attachListeners2(oldCheckoutBtnSelector, listener); const oldSelectors = oldCheckoutBtnSelector.split(","); return oldSelectors.map((selector) => { const shopifyBtn = document.querySelector(selector); if (!shopifyBtn) { throw new Error(`Button with ${selector} selector not found in DOM.`, apikey2); } const newBtn = document.createElement("button"); newBtn.classList = shopifyBtn.classList; newBtn.setAttribute("id", loopCheckoutBtnSelector); newBtn.innerText = shopifyBtn.innerText; newBtn.innerHTML = shopifyBtn.innerHTML; const shopifyBtnParent = shopifyBtn.parentElement; shopifyBtnParent.appendChild(newBtn); shopifyBtn.remove(); return attachListeners2(`#${loopCheckoutBtnSelector}`, listener); }); } catch (e) { console.error(e); return []; } }; const isUnique = (selector) => document.querySelectorAll(selector).length === 1; const getUniqueClassSelector = (element) => { if (element.classList.length > 0) { const classNames = Array.from(element.classList); let selector = ""; for (const className of classNames) { selector += `.${className}`; if (isUnique(selector)) { return selector; } } } return ""; }; const getUniqueAttributesSelector = (element, attributes) => { for (let attr of attributes) { if (element.hasAttribute(attr)) { let selector = `[${attr}="${element.getAttribute(attr)}"]`; if (isUnique(selector)) { return selector; } } } return ""; }; const buildUniqueSelector = (element) => { let selector = element.nodeName.toLowerCase(); const classSelector = getUniqueClassSelector(element); if (classSelector) { return classSelector; } if (element.id && isUnique(`#${element.id}`)) { return `#${element.id}`; } const attributeSelector = getUniqueAttributesSelector(element, ["name", "type", "alt", "title", "value"]); if (attributeSelector) { selector += attributeSelector; } if (!attributeSelector) { let parent = element.parentNode; if (!parent) return selector; let index = Array.prototype.indexOf.call(parent.children, element) + 1; return buildUniqueSelector(parent) + " > " + selector + `:nth-child(${index})`; } return selector; }; function inspect() { const fn = (e) => { e.preventDefault(); console.log(buildUniqueSelector(e.target)); console.log("Disabling LoopOnstore inspector."); window.removeEventListener("click", fn); }; console.log("Enabling LoopOnstore inspector. Click your checkout button to generate a unique selector."); window.addEventListener("click", fn); } const loopElements = { bar: null, attached: [] }; const loopInit = async (opts) => { initState(); if (opts == null ? void 0 : opts.key) { loopSetKey(opts.key); } else if (!getState("key")) { throw new Error("Loop Returns On-Store API requires an API key."); } if (opts.isInBuilder) { builderMode(opts.barOpts); } if (opts == null ? void 0 : opts.api) { loopLogger.info(`Setting api to ${opts.api}.`); setState("api", opts.api); } if (opts == null ? void 0 : opts.preservecart) { loopLogger.info(`Setting preserve cart to ${opts.preservecart}`); setState("preserveCart", opts.preservecart); } const loopCurrentParams = params.get(); if (params.hasLoopData()) { if (getState("id") !== loopCurrentParams.loop_return_id) { setState("token", null); } setState("params", loopCurrentParams); setState("id", loopCurrentParams.loop_return_id); setState("inExperiment", Boolean((loopCurrentParams == null ? void 0 : loopCurrentParams.loop_experiment_b_eligible) === "true") ?? false); setState("singleItem", Boolean((loopCurrentParams == null ? void 0 : loopCurrentParams.loop_single_item_eligible) === "true") ?? false); setState("isEmbeddedPortal", Boolean((loopCurrentParams == null ? void 0 : loopCurrentParams.loop_embedded_portal) === "true") ?? false); if (getState("inExperiment")) { setState("preserveCart", true); } params.clear(params.getKeys(loopCurrentParams)); if (!(opts == null ? void 0 : opts.preservecart)) { await clearShopifyCart(); } const token = getState("token"); if (token) { try { const cart = await getLoopCart(token); await updateShopifyCart(cart); loopLogger.info("Updating Shopify cart to match loop cart."); document.dispatchEvent(new Event("Loop:Cart Update")); } catch (error) { loopLogger.warn("Unable to reboot cart"); loopLogger.error(error); } } } const data = getState("params"); if (data) { setState("active", true); document.body.classList.add("loop-returns-activated"); const renderData = {}; if (data.loop_total ?? 0 > 0) { renderData.credit = { amount: format(data.loop_total, data.loop_currency) }; } if (data.loop_discount_percentage ?? 0 > 0) { renderData.percentDiscount = { amount: `${data.loop_discount_percentage}% off` }; } const bar = loopRender(null, renderData, opts.barOpts); loopElements.bar = bar; loopLogger.info("Loop returns activated"); document.dispatchEvent(new Event("Loop:Activated")); } if (opts == null ? void 0 : opts.attach) { setState("attach", opts == null ? void 0 : opts.attach); loopAttach(opts.attach, opts.replacecheckoutbutton, opts.isInBuilder, opts.key); } if (opts == null ? void 0 : opts.cartdetails) { setState("fullPayload", opts == null ? void 0 : opts.cartdetails); } window.LoopOnstore = { testMode: loopTestMode, testModeNewShopperFlow: loopTestModeNewShopperFlow, testModeOff: loopTestModeOff, isActive: loopIsActive, backToLoop, submit: loopSubmit, info, inspect }; }; const loopTestMode = (redirect = true) => { setState("debug", true); setState("testMode", true); loopLogger.info("Test mode activated"); setState("redirect", redirect); window.location = `${window.location.origin}${window.location.pathname}?loop_domain=example.loopreturns.com&loop_return_id=this-is-a-test&loop_currency=USD&loop_total=12099&loop_discount_percentage=25&loop_base=799&loop_credit=500&loop_subdomain=example&loop_redirect_url=example.loopreturns.com%2F%23%2Fcredit&loop_customer_name=Jane%20Doe`; }; const loopTestModeNewShopperFlow = () => { setState("debug", true); setState("testMode", true); setState("redirect", false); loopLogger.info("Test mode Flow B activated"); window.location = `${window.location.origin}${window.location.pathname}?loop_domain=example.loopreturns.com&loop_return_id=this-is-a-test&loop_currency=USD&loop_total=12099&loop_discount_percentage=25&loop_base=799&loop_credit=500&loop_subdomain=example&loop_redirect_url=example.loopreturns.com%2F%23%2Fcredit&loop_customer_name=Jane%20Doe&loop_experiment_b_eligible=true&loop_single_item_eligible=false`; }; const loopTestModeOff = () => { setState("debug", false); setState("testMode", false); loopLogger.info("Test mode deactivated"); setState("redirect", true); localStorage.removeItem("loop-onstore-data"); window.location.reload(); }; const builderMode = (barOpts) => { setState("debug", true); document.body.classList.add("loop-returns-activated"); const bar = loopRender(null, { credit: { amount: format(1e4, "USD") }, percentDiscount: { amount: "25% off" } }, barOpts); loopElements.bar = bar; }; const loopDebug = () => { const loopToggled = !getState("debug"); setState("debug", loopToggled); return `Debug ${loopToggled ? "on" : "off"}`; }; const loopSetKey = (key) => { if (!key || typeof key !== "string") { return console.error("Your api key is either undefined or not a string."); } setState("key", key); loopLogger.info(`Loop Returns On-Store API key set.`); }; const loopSubmit = async (apikey2, variants, redirectTo = null) => { var _a2; let cart = null; if (!variants) { loopLogger.info("Requesting updated cart from Shopify"); cart = await getShopifyCart(); variants = cart.items.reduce((acc, item) => { return [...acc, ...Array(item.quantity).fill(item.variant_id)]; }, []); } try { if (window.stackableService && typeof ((_a2 = window.stackableService) == null ? void 0 : _a2.send) === "function") { window.stackableService.send({ type: "checkout", data: { preventRedirect: true } }); let loopTries = 0; await (async () => { return await new Promise((resolve) => { const interval = setInterval(() => { if (window.stackableService.state.context.draftOrderId || loopTries === 6) { resolve(); clearInterval(interval); } loopTries++; }, 500); }); })(); setState( "draftOrderId", window.stackableService.state.context.draftOrderId ); } const loopCart = await syncCart(variants, cart); setState("token", loopCart.token); const preserveCart = getState("preserveCart"); if (!preserveCart) { await clearShopifyCart(); } backToLoop(loopCart.token, redirectTo); return Promise.resolve(loopCart.token); } catch (error) { loopLogger.info("Unable to create Loop Returns cart:"); loopLogger.error(error); return Promise.reject(error); } }; const loopAttach = (selectors, replaceCheckoutButton, isInBuilderMode, apikey2) => { if (getState("active") || isInBuilderMode) { liftCart(); const listener = async (e) => { if (getState("active") || isInBuilderMode) { e.stopImmediatePropagation(); e.preventDefault(); e.target.disabled = true; e.target.classList.add("loop-activated"); try { await loopSubmit(apikey2); e.target.disabled = false; e.target.classList.remove("loop-activated"); } catch (error) { e.target.disabled = false; e.target.classList.remove("loop-activated"); } } }; loopElements.attached = handleCheckoutButton(selectors, listener, attachListeners, replaceCheckoutButton, apikey2); const observer = new MutationObserver((list) => { const added = getNodes(list, "addedNodes"); if (added.length) { const changed = added.some((item) => { return item && item.nodeType === 1 && (item.matches(selectors) || item.querySelector(selectors)); }); if (changed) { let alreadyAttached = false; const splitSelectors = selectors.split(","); if ((loopElements == null ? void 0 : loopElements.attached) && loopElements.attached.length > 0) { loopElements.attached.forEach((element) => { splitSelectors.forEach((selector) => { const selectorElement = document.querySelector(selector); if (selectorElement === element) { alreadyAttached = true; } }); }); } if (!alreadyAttached) { loopElements.attached = handleCheckoutButton(selectors, listener, attachListeners, replaceCheckoutButton, apikey2); loopLogger.info(`Loop reattached to: "${selectors}"`); } } } }); observer.observe(document.body, { childList: true, subtree: true }); } }; const backToLoop = (token, redirectTo = null) => { const isTest = getState("testMode"); setState("active", false); const params2 = getState("params"); const isEmbeddedPortal = getState("isEmbeddedPortal"); let url = `http://${params2.loop_redirect_url}`; if (token && params2.loop_return_key) { url = `http://${params2.loop_domain}/#/cart/v2/${token}/${params2.loop_return_key}`; } else if (token) { url = `http://${params2.loop_domain}/#/cart/v2/${token}${redirectTo ? "?to=" + redirectTo : ""}`; } if (isEmbeddedPortal) { url = `${window.location.origin}/apps/returns?token=${token}${redirectTo ? "&to=" + redirectTo : ""}`; } setState("params", null); loopLogger.info("Redirecting to:"); loopLogger.info(url); document.dispatchEvent(new Event("Loop:Back To Loop")); if (isTest) { loopOnstoreOpenModal(); setState("testMode", false); return; } if (getState("redirect")) { window.location.href = url; } }; const info = () => { var _a2; console.log("Using Embedded On-Store"); console.log("API Key Last 4:", ((_a2 = getState("key")) == null ? void 0 : _a2.slice(-4)) ?? "No key set"); console.log("Attached To:", getState("attach") ?? "No selector set"); console.log("Portal Embedded:", getState("isEmbeddedPortal") ?? false); }; const loopIsActive = () => getState("active"); const LoopOnstore$1 = { init: loopInit, testMode: loopTestMode, testModeNewShopperFlow: loopTestModeNewShopperFlow, testModeOff: loopTestModeOff, submit: loopSubmit, attach: loopAttach, debug: loopDebug, setKey: loopSetKey, backToLoop, isActive: loopIsActive, info, inspect }; const isInBuilder = !!(Shopify == null ? void 0 : Shopify.designMode); const paramsInStorage = (_a = JSON.parse( localStorage.getItem("loop-onstore-data") )) == null ? void 0 : _a.params; const inBuilderBarEnabled = isInBuilder && stringToBool(showOnstoreBar); const hasLoopParams = params.hasLoopData() || !!paramsInStorage; if (!window.LoopOnstore || window.LoopOnstore === "undefined") { window.LoopOnstore = LoopOnstore$1; if (isInBuilder && apikey !== "test-key") { checkOnstoreEligibility(apikey).then(({ enabled, error }) => { if (enabled && (inBuilderBarEnabled || hasLoopParams)) { LoopOnstore$1.init({ key: apikey, attach: checkoutselector, cartdetails: stringToBool(cartdetails), preservecart: stringToBool(preservecart), isInBuilder, replacecheckoutbutton: stringToBool(replacecheckoutbutton), barOpts: { label, alignment, mobileLabel } }); } if (!enabled && isInBuilder) { const modalHeader = "On-Store Feature Not Available"; const modalBody = 'You may need to configure/add your API key or upgrade your plan to gain access to this feature. If you have already added your API key to the left, reach out to support@loopreturns.com to get help.'; loopOnstoreOpenModal({ modalHeader, modalBody }); return; } if (error) { console.error( "Something went wrong while enabling On-Store. Please try again later.", apikey, ...error ); } }); } else if (hasLoopParams) { LoopOnstore$1.init({ key: apikey, attach: checkoutselector, cartdetails: stringToBool(cartdetails), preservecart: stringToBool(preservecart), isInBuilder, replacecheckoutbutton: stringToBool(replacecheckoutbutton), barOpts: { label, alignment, mobileLabel } }); } } else if (isInBuilder) { const oldOnstoreRunningHeader = "Loop Manual On-Store is Already Running"; const oldOnstoreRunningBody = 'Your store is currently running the old Loop Manual On-Store. To migrate to the new Embedded On-Store, follow the migration instructions in the On-Store Troubleshooting article or reach out to your Merchant Success Manager.'; loopOnstoreOpenModal({ modalHeader: oldOnstoreRunningHeader, modalBody: oldOnstoreRunningBody }); } })();