// widgets custom selector script // // var app_url = "https://stocksheep.tnl.vc"; // if (window.name === "stocksheep-staging") { // app_url = "https://stocksheep-staging.vercel.app/"; // } else if (window.name === "stocksheep") { // app_url = "https://stocksheep.vercel.app/"; // } // if ((window.name === "stocksheep-staging" || window.name === "stocksheep-production" || window.name === "stocksheep-dev") && !document.getElementById("stocksheep_target_Element")) { // document.getElementsByTagName("body")[0].innerHTML = `

Move your mouse and hover over the page to see how each element in the page is highlighted in a green doted border. Choose an element relative to which you want to position the bundle youcreated by clicking on it. Select only element that are present in the original theme template DO NOT select any elements generated by other apps you are currently using such as trustbadges, widgets, icons, timers etc.

Custom Element Selector

.product-form__quantity
` + document.getElementsByTagName("body")[0].innerHTML; // const clrButton = document.getElementById("stocksheep_selector_clear"); // clrButton.addEventListener("click", (e) => { // e.preventDefault(); // document.getElementById("stocksheep_target_Element").innerText = null; // }); // const confirmButton = document.getElementById("stocksheep_selector_confirm"); // confirmButton.addEventListener("click", (e) => { // window.opener.postMessage( // { // selection: document.getElementById("stocksheep_target_Element").innerText, // type: "id", // }, // app_url // ); // window.close(); // }); // // get first parent div that has an ID or class // const getDIV = (el) => { // const div = el.closest("div"); // if (div === null) { // return { div, selector: null }; // } else if (div.className.length || div.id.length) { // const styles = getComputedStyle(div); // if (styles && styles.position !== "sticky" && styles.position !== "fixed") { // return { // div, // selector: div.id.length ? `#${div.id}` : `.${div.className.trim().split(/\s+/).join(" .")}`, // }; // } else { // return getDIV(div.parentElement); // } // } else { // return getDIV(div.parentElement); // } // }; // // disable all default click events and capture element ID or class // const clickHandler = (e) => { // // disable normal click behavior // if (e.target.id !== "stocksheep_selector_clear" && e.target.id !== "stocksheep_selector_confirm") { // e.preventDefault(); // e.stopPropagation(); // // get the first parent div that has an ID or class // const { div, selector } = getDIV(e.target); // if (div !== null) { // document.getElementById("stocksheep_target_Element").innerText = selector; // } else { // } // } // }; // // highlight the element on hover // const mouseOverHandler = (e) => { // // get the first parent div that has an ID or class // const { div, selector } = getDIV(e.target); // if (div !== null) { // div.style.border = "5px dashed green"; // } // }; // // remove highlight when mouse leaves // const mouseOutHandler = (e) => { // // get the first parent div that has an ID or class // const { div, selector } = getDIV(e.target); // if (div !== null) { // div.style.border = "none"; // } // }; // // handle clicks // document.addEventListener("click", clickHandler, true); // // handle hover // document.addEventListener("mouseover", mouseOverHandler); // document.addEventListener("mouseout", mouseOutHandler); // } // widget custom selector script v2 // const widget_goUp = () => { try { const current_selector = document?.querySelector("#widget_selector")?.value?.trim(); if (!current_selector || (current_selector && !document?.querySelector(current_selector)?.parentElement)) { return; } widget_select_element(document.querySelector(current_selector).parentElement); } catch (e) { console.log("error", e); } }; const widget_goDown = () => { try { const current_selector = document?.querySelector("#widget_selector")?.value?.trim(); if (!current_selector || (current_selector && !document?.querySelector(current_selector)?.children?.length)) { return; } let done = false; let i = 0; const children = document.querySelector(current_selector).children; while (!done && i < children.length) { if (((children[i] && children[i].id) || children[i].classList.length > 0) && children[i].tagName.toLowerCase() == "div" && !children[i].classList.find((e) => e == "widget_preview")) { widget_select_element(document.querySelector(children[i].id || children[i].classList?.join("."))); done = true; } i++; } } catch (e) { console.log("error", e); } }; const widget_select_element = (target) => { const { el, selector } = widget_getElement(target); // remove class widget_selected from any element that has it const selected = document.querySelectorAll(".widget_selected"); if (selected.length) { selected.forEach((el) => { el.classList.remove("widget_selected"); }); } if (el && selector) { widget_selector = selector; document.querySelector("#widget_selector").value = widget_selector; el.classList.add("widget_selected"); return true; } else { return false; } }; const widget_getElement = (el) => { const newEl = el.closest("div, section, article, aside, header, footer, nav, main, button, img, p, h1, h2, h3, h4, h5, h6, ul, ol, form, input, select, table"); if (newEl === null) { return { el: null, selector: null }; } else if (newEl.className.length || newEl.id.length) { const styles = getComputedStyle(newEl); if (styles && styles.position !== "sticky" && styles.position !== "fixed" && !newEl.classList.contains("widget")) { const tagName = newEl.tagName.toLowerCase(); const id = newEl.id.length ? `${tagName}#${newEl.id}` : null; const classListArray = Array.from(newEl.classList); const classList = classListArray?.filter((className) => className !== "widget_selected" && className !== "widget_hover"); const className = classList.length ? classList.reduce((acc, className) => { const selector = `${tagName}.${className}`; if (widget_uniqueSelector(selector)) { if (selector.length < acc.length) { return selector; } else { return acc; } } else { return acc; } }, `${tagName}.${classList[0]}`) : null; if (className && widget_uniqueSelector(className)) { return { el: newEl, selector: className, }; } else if (id && widget_uniqueSelector(id)) { return { el: newEl, selector: id, }; } } } return widget_getElement(newEl.parentElement); }; const widget_uniqueSelector = (selector) => { const elements = document.querySelectorAll(selector); if (elements.length === 1) { return true; } else { return false; } }; // send init message to opener then wait for a response // in case it's the position picker we are opening (function () { const identifier = window?.location?.href?.split("#")?.[1]; setTimeout(() => { window?.opener?.postMessage( { action: `init_${identifier}`, }, "*" ); }, 50); })(); // wait for a message from the opener // in case it's the position picker we are opening window.addEventListener("message", (event) => { if (event?.data?.action === "show position picker") { // we are picking a position for the bundle in the new editor let widget_selector; let last_selector; let last_position; // highlight the element on hover const handleMouseOver = (e) => { const { el, selector } = widget_getElement(e.target); if (el !== null && el?.style && selector && selector !== document?.querySelector("#widget_selector")?.value && !el.classList.contains("widget")) { el.classList.add("widget_hover"); } }; // remove highlight when mouse leaves const handleMouseOut = (e) => { const elements = document.querySelectorAll(".widget_hover"); elements.forEach((el) => { el.classList.remove("widget_hover"); }); }; // handle clicks const handleClick = (e) => { try { if (!e.target.classList.contains("widget")) { // disable normal click behavior e.preventDefault(); e.stopPropagation(); // set cursor for the entire document to be crosshair document.body.style.cursor = "crosshair"; // capture clicked element widget_select_element(e.target); } if (e.target.id === "widget_confirm") { const widget_selector_value = document.querySelector("#widget_selector")?.value.trim(); const widget_position_value = document.querySelector("#widget_position")?.value.trim(); if (widget_selector_value.length && widget_position_value.length) { const identifier = window?.location?.href?.split("#")?.[1]; window?.opener?.postMessage( { action: `position_${identifier}`, selector: widget_selector_value, position: widget_position_value, }, "*" ); } } } catch (e) { widget_selector = null; } }; const updatePreview = () => { // get the selector and the position const selector = document.querySelector("#widget_selector").value.trim(); const position = document.querySelector("#widget_position").value.trim(); // if the selector or position has changed, update the preview if (selector !== last_selector || position !== last_position) { // remove all elements with class widget_preview from the DOM const elements = document.querySelectorAll(".widget_preview"); elements.forEach((el) => { el.remove(); }); // insert the preview element if (selector !== "") { // get the element const element = document.querySelector(selector); if (element) { // create a new element to preview the selection const preview = document.createElement("div"); preview.innerText = "YOUR WIDGET WILL BE DISPLAYED HERE"; preview.classList.add("widget_preview"); // if the position is above insert the preview above the element if (position === "above") { element.insertAdjacentElement("beforebegin", preview); } // if the position is below insert the preview below the element else if (position === "below") { element.insertAdjacentElement("afterend", preview); } // if the position is inside-top insert the preview inside the element as the first child else if (position === "inside-top") { element.insertAdjacentElement("afterbegin", preview); } // if the position is inside-bottom insert the preview inside the element as the last child else if (position === "inside-bottom") { element.insertAdjacentElement("beforeend", preview); } } } } // update the last selector and position last_selector = selector; last_position = position; }; const updateConfirmButtonState = () => { const widgetSelector = document.querySelector("#widget_selector"); const widgetConfirm = document.querySelector("#widget_confirm"); if (widgetSelector && widgetConfirm) { if (widgetSelector.value.trim() === "") { widgetConfirm.setAttribute("disabled", true); } else { widgetConfirm.removeAttribute("disabled"); } } }; const showPicker = () => { document.getElementsByTagName("body")[0].innerHTML = `
Click in the page where you want to place the bundle. The element will be highlighted in green. Click "Confirm Selection" to save the selection.
Please avoid selecting elements that are added by other apps, and not part of the theme (e.g. reviews, trust badges, widgets, icons, timers, announcement bars etc.)
` + document.getElementsByTagName("body")[0].innerHTML; }; // add event listeners document.addEventListener("mouseover", handleMouseOver); document.addEventListener("mouseout", handleMouseOut); document.addEventListener("click", handleClick, true); // show position picker showPicker(); // set initial position if (event?.data?.position) { document.querySelector("#widget_position").value = event?.data?.position; } // set initial selector if (event?.data?.selector) { document.querySelector("#widget_selector").value = event?.data?.selector; } // state loop to handle preview and confirm button setInterval(() => { updateConfirmButtonState(); updatePreview(); }, 100); } }); // popups and widgets injection script // // append fonts link in the head // const appendFont = (url) => { if (url) { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = url; document.head.appendChild(link); // if google font pre-connect link tags not present in head, append them // if (!document.querySelector("link[rel='preconnect']")) { const preconnect = document.createElement("link"); preconnect.rel = "preconnect"; preconnect.href = "https://fonts.gstatic.com"; document.head.appendChild(preconnect); const preconnect2 = document.createElement("link"); preconnect2.rel = "preconnect"; preconnect2.href = "https://fonts.googleapis.com"; preconnect2.crossOrigin = true; document.head.appendChild(preconnect2); } } }; // popup layouts // const PopupLayoutOne = ({ popup, id, data }) => { const now = new Date().getTime(); // current time in milliseconds const then = data.updated_at ? data.updated_at * 1000 : data.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const hide_close_button = popup.display?.hide_close_button; let timeElapsesd; if (diff < 60) { timeElapsesd = `${Math.floor(diff)} seconds ago`; } else if (diff < 3600) { timeElapsesd = `${Math.floor(diff / 60)} minutes ago`; } else if (diff < 3600 * 24) { timeElapsesd = `${Math.floor(diff / 3600)} hours ago`; } else { timeElapsesd = `${Math.floor(diff / (3600 * 24))} days ago`; } return `
${popup?.design?.text?.html_string || ""} `; }; const PopupLayoutTwo = ({ popup, id, data }) => { const now = new Date().getTime(); // current time in milliseconds const then = data.updated_at ? data.updated_at * 1000 : data.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const hide_close_button = popup.display?.hide_close_button; let timeElapsesd; if (diff < 60) { timeElapsesd = `${Math.floor(diff)} seconds ago`; } else if (diff < 3600) { timeElapsesd = `${Math.floor(diff / 60)} minutes ago`; } else if (diff < 3600 * 24) { timeElapsesd = `${Math.floor(diff / 3600)} hours ago`; } else { timeElapsesd = `${Math.floor(diff / (3600 * 24))} days ago`; } return `
${popup?.design?.text?.html_string || ""} `; }; const PopupLayoutThree = ({ popup, id, data }) => { const now = new Date().getTime(); // current time in milliseconds const then = data.updated_at ? data.updated_at * 1000 : data.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const hide_close_button = popup.display?.hide_close_button; let timeElapsesd; if (diff < 60) { timeElapsesd = `${Math.floor(diff)} seconds ago`; } else if (diff < 3600) { timeElapsesd = `${Math.floor(diff / 60)} minutes ago`; } else if (diff < 3600 * 24) { timeElapsesd = `${Math.floor(diff / 3600)} hours ago`; } else { timeElapsesd = `${Math.floor(diff / (3600 * 24))} days ago`; } return `
`; }; // widget layouts const Text_Above = ({ widget }) => { return ` ${widget?.design?.text?.html_string || ""}

${widget.design.text.sold_text.value.replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0).replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0)}

${widget.design.text.stock_text.value.replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0).replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0)}

${ widget.design.progress_bar.display ? `
` : `` }
`; }; const Text_Above_Above = ({ widget }) => { return ` ${widget?.design?.text?.html_string || ""}

${widget.design.text.sold_text.value.replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0).replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0)}

${widget.design.text.stock_text.value.replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0).replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0)}

${ widget.design.progress_bar.display ? `
` : "" }
`; }; const Text_Above_Below = ({ widget }) => { return ` ${widget?.design?.text?.html_string || ""}

${widget.design.text.sold_text.value.replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0).replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0)}

${ widget.design.progress_bar.display ? `
` : "" }

${widget?.design.text.stock_text.value.includes("{{quantity_sold}}") && widget.design.text.sold_text.value && widget?.inventory.sold_inventory == 0 ? "" : widget.design.text.stock_text.value.replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0).replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0)}

`; }; const Text_Below = ({ widget }) => { return ` ${widget?.design?.text?.html_string || ""}
${ widget.design.progress_bar.display ? `
` : "" }

${widget.design.text.sold_text.value.replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0).replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0)}

${widget.design.text.stock_text.value.replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0).replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0)}

`; }; const Text_Below_Above = ({ widget }) => { return ` ${widget?.design?.text?.html_string || ""}

${widget.design.text.stock_text.value.replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0).replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0)}

${ widget.design.progress_bar.display ? `
` : "" }

${widget.design.text.sold_text.value.replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0).replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0)}

`; }; const Text_Below_Below = ({ widget }) => { return ` ${widget?.design?.text?.html_string || ""}
${ widget.design.progress_bar.display ? `
` : "" }

${widget.design.text.sold_text.value.replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0).replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0)}

${widget.design.text.stock_text.value.replace("{{quantity_left}}", parseInt(widget?.inventory.available_inventory) || 0).replace("{{quantity_sold}}", parseInt(widget?.inventory.sold_inventory) || 0)}

`; }; // function to display popup const displayPopup = async (popups) => { let orderData, cartData, ordersLastKey, cartLastKey, validateCartTimeLimit = true, validateOrdersTimeLimit = true; // turn to false if there are any cart items that are older than the time limit // function to fetch recent orders const fetchOrderData = async ({ key, session_limit }) => { const ordersResponse = await fetch(`/apps/stocksheep/shop-requests/getOrdersData`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ key, session_limit }), }); const data = await ordersResponse.json(); ordersLastKey = data.LastEvaluatedKey; if (data.Items?.length > 0) { return { items: [...data.Items] }; } return; }; // function to fetch recent add to cart activity const fetchCartData = async ({ key, session_limit }) => { const cartResponse = await fetch(`/apps/stocksheep/shop-requests/getCartData`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ key, session_limit }), }); const data = await cartResponse.json(); cartLastKey = data.LastEvaluatedKey; if (data.Items?.length > 0) { return { items: [...data.Items] }; } return; }; const addPopupHtml = ({ popup, data, type }) => { const stop_cycle = popup.display?.stop_cycle_items; const session_limit = popup.display?.items_per_session || 10; data.forEach((item, index) => { // append popup after set interval setTimeout(() => { const randomId = Math.random().toString(36).substring(2, 15); ["mobile", "desktop"].forEach((device) => { const querySelector = device === "mobile" ? `stocksheep_popup_mobile_${popup.position.mobile.position}` : `stocksheep_popup_desktop_${popup.position.desktop.position}_${popup.position.desktop.alignment}`; let popupElement = document.querySelector(`.${querySelector}`); // justify content center for mobile popups if (device === "mobile") { const popupElementStyle = document.createElement("style"); popupElementStyle.innerHTML = ` .stocksheep_popup_mobile_${popup.position.mobile.position} .${randomId} { display: flex; justify-content: center; } `; document.head.appendChild(popupElementStyle); } /// if popup element not found create new one if (!popupElement) { popupElement = document.createElement("div"); popupElement.classList.add(querySelector); document.body.appendChild(popupElement); const popupElementStyle = document.createElement("style"); popupElementStyle.innerHTML = device === "mobile" ? ` .stocksheep_popup_mobile_${popup.position.mobile.position} { position: fixed; z-index: 999999999999; } @media (max-width: 600px) { .stocksheep_popup_mobile_${popup.position.mobile.position} { max-width: 100%; transform-origin: ${popup.position.mobile.position} left; transform: translateX(-50%); top: ${popup.position.mobile.position === "top" ? "1%" : "auto"}; bottom: ${popup.position.mobile.position === "bottom" ? "1%" : "auto"}; left: 50%; } } @media (min-width: 600px) { .stocksheep_popup_mobile_${popup.position.mobile.position} { display: none; } } ` : ` .stocksheep_popup_desktop_${popup.position.desktop.position}_${popup.position.desktop.alignment} { position: fixed; z-index: 999999999999; } @media (max-width: 600px) { .stocksheep_popup_desktop_${popup.position.desktop.position}_${popup.position.desktop.alignment} { display: none; } } @media (min-width: 600px) { .stocksheep_popup_desktop_${popup.position.desktop.position}_${popup.position.desktop.alignment} { max-width: 100%; transform-origin: ${popup.position.desktop.position} ${popup.position.desktop.alignment === "right" ? "right" : "left"}; ${popup.position.desktop.alignment === "center" ? "transform: translateX(-50%);" : ""} top: ${popup.position.desktop.position === "top" ? "4%" : "auto"}; bottom: ${popup.position.desktop.position === "bottom" ? "0" : "auto"}; left: ${popup.position.desktop.alignment === "left" ? "0" : popup.position.desktop.alignment === "center" ? "50%" : "auto"}; right: ${popup.position.desktop.alignment === "right" ? "0" : "auto"}; } } `; document.head.appendChild(popupElementStyle); } // append font if font url is provided if (popup.design.text.font_url) { appendFont(popup.design.text.font_url); } // append popup html based on layout switch (popup.design.layout) { case "layout-1": popupElement.insertAdjacentHTML("beforeend", PopupLayoutOne({ popup, id: randomId, data: item })); break; case "layout-2": popupElement.insertAdjacentHTML("beforeend", PopupLayoutTwo({ popup, id: randomId, data: item })); break; case "layout-3": popupElement.insertAdjacentHTML("beforeend", PopupLayoutThree({ popup, id: randomId, data: item })); break; } // remove popup after display time setTimeout(() => { [...document.getElementsByClassName(randomId)].forEach((popup) => { popup.remove(); }); }, 1000 * parseInt(popup.display.display_time)); }); //if this is last item for which popup is displayed then fetch another cart data and display popup if (stop_cycle !== true) { if (index === data.length - 1) { // if type is cart then fetch cart data if (type === "cart") { setTimeout(() => { // if LastEvaluatedKey for cart data is not null then fetch previous cart data if (cartLastKey && validateCartTimeLimit) { fetchCartData({ session_limit, key: cartLastKey }).then((data) => { const items = data?.items; if (items && items.length > 0) { let filteredData = items.filter((cartItem) => { if (popup.display?.display_time_limit) { const now = new Date().getTime(); // current time in milliseconds const then = cartItem.updated_at ? cartItem.updated_at * 1000 : cartItem.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const limit = popup.display?.display_time_limit_unit == "all" ? now / 1000 - 1592200722 : popup.display?.display_time_limit_unit == "days" ? popup.display?.display_time_limit * 24 * 60 * 60 : popup.display?.display_time_limit * 60 * 60; if (diff < limit) { return true; } else { validateCartTimeLimit = false; } } return false; }); filteredData = popup.display.type == "global" ? [...filteredData] : filteredData .filter((cart) => { return popup.display?.display_products?.some((product) => { return product.variants.some((variant) => { return variant.id == cart.product.variant_id; }); }); }) .sort((a, b) => a.updated_at - b.updated_at); if (filteredData && filteredData.length > 0) { addPopupHtml({ popup, data: filteredData, type: "cart" }); } } }); } else { // if LastEvaluatedKey for cart data is null then fetch new cart data fetchCartData({ session_limit }).then((data) => { if (data.items && data.items.length > 0) { const newItems = data.items.filter((item) => !cartData.find((cart) => cart.id == item.id)); if (newItems && newItems.length > 0) { const filteredData = popup.display.type == "global" ? [...newItems] : newItems .filter((cart) => { return popup.display?.display_products?.some((product) => { return product.variants.some((variant) => { return variant.id == cart.product.variant_id; }); }); }) .sort((a, b) => b.updated_at - a.updated_at); if (filteredData && filteredData.length > 0) { addPopupHtml({ popup, data: filteredData, type: "cart" }); } } } }); } }, 1000 * parseInt(popup.display.session_delay)); } // if type is order then fetch order data else if (type === "orders") { setTimeout(() => { // if LastEvaluatedKey for orders data is not null then fetch previous orders data if (ordersLastKey && validateOrdersTimeLimit) { fetchOrderData({ key: ordersLastKey, session_limit }).then((data) => { const items = data?.items; if (items && items.length > 0) { let filteredData = items.filter((orderItem) => { if (popup.display?.display_time_limit) { const now = new Date().getTime(); // current time in milliseconds const then = orderItem.updated_at ? orderItem.updated_at * 1000 : orderItem.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const limit = popup.display?.display_time_limit_unit == "all" ? now / 1000 - 1592200722 : popup.display?.display_time_limit_unit == "days" ? popup.display?.display_time_limit * 24 * 60 * 60 : popup.display?.display_time_limit * 60 * 60; if (diff < limit) { return true; } else { validateOrdersTimeLimit = false; } } return false; }); filteredData = popup.display.type == "global" ? [...filteredData] : filteredData.filter((order) => { return popup.display?.display_products?.some((product) => { return product.variants.some((variant) => { return variant.id == order.product.variant_id; }); }); }); if (filteredData && filteredData.length > 0) { addPopupHtml({ popup, data: filteredData, type: "orders" }); } } }); } else { // if LastEvaluatedKey for orders data is null then fetch new orders data fetchOrderData({ session_limit }).then((data) => { if (data.items && data.items.length > 0) { const newItems = data.items.filter((item) => !orderData.find((e) => e.id == item.id)); if (newItems && newItems.length > 0) { const filteredData = popup.display.type == "global" ? [...newItems] : newItems.filter((order) => { return popup.display?.display_products?.some((product) => { return product.variants.some((variant) => { return variant.id == order.product.variant_id; }); }); }); if (filteredData && filteredData.length > 0) { addPopupHtml({ popup, data: filteredData, type: "orders" }); } } } }); } }, 1000 * parseInt(popup.display.session_delay)); } } } // remove popup after close button click [...document.getElementsByClassName(`stocksheep_popup_close_${randomId}`)].forEach((element) => { element.addEventListener("click", () => { [...document.getElementsByClassName(randomId)].forEach((popup) => { popup.remove(); }); }); }); }, index * (parseInt(popup.display.popup_delay) + parseInt(popup.display.display_time)) * 1000); }); }; // function to append popups html const appendSalesPopup = async (popup) => { const session_limit = popup.display?.items_per_session || 10; //add session delay for popup const orderItems = await fetchOrderData({ session_limit }); orderData = [...orderItems.items]; if (orderData && orderData.length > 0) { setTimeout(() => { // filter order data based on popup time settings let filteredData = orderData.filter((orderItem) => { if (popup.display?.display_time_limit_unit !== "all") { return true; } if (popup.display?.display_time_limit) { const now = new Date().getTime(); // current time in milliseconds const then = orderItem.updated_at ? orderItem.updated_at * 1000 : orderItem.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const limit = popup.display?.display_time_limit_unit == "all" ? now / 1000 - 1592200722 : popup.display?.display_time_limit_unit == "days" ? popup.display?.display_time_limit * 24 * 60 * 60 : popup.display?.display_time_limit * 60 * 60; if (diff < limit) { return true; } else { validateOrdersTimeLimit = false; } } return false; }); // filter order data based on popup display settings filteredData = popup.display.type == "global" ? [...filteredData] : filteredData.filter((order) => { return popup.display?.display_products?.some((product) => { return product.variants.some((variant) => { return variant.id == order.product.variant_id; }); }); }); if (filteredData && filteredData.length > 0) { addPopupHtml({ popup, data: filteredData, type: "orders" }); } }, parseInt(popup.display.session_delay) * 1000); } }; const appendCartPopup = async (popup) => { const session_limit = popup.display?.items_per_session || 10; const cartItems = await fetchCartData({ session_limit }); cartData = [...cartItems.items]; //add session delay for popup setTimeout(() => { // filter data based on popup display time settings let filteredData = cartData.filter((cartItem) => { if (popup.display?.display_time_limit_unit == "all") { return true; } if (popup.display?.display_time_limit) { const now = new Date().getTime(); // current time in milliseconds const then = cartItem.updated_at ? cartItem.updated_at * 1000 : cartItem.created_at * 1000; // Unix timestamp in milliseconds const diff = (now - then) / 1000; // difference in seconds const limit = popup.display?.display_time_limit_unit == "days" ? popup.display?.display_time_limit * 24 * 60 * 60 : popup.display?.display_time_limit * 60 * 60; if (diff < limit) { return true; } else { validateCartTimeLimit = false; } } return false; }); // filter cart data based on popup display products settings filteredData = popup.display.type == "global" ? [...filteredData] : filteredData.filter((cartItem) => { return popup.display?.display_products?.some((product) => { return product.variants.some((variant) => { return variant.id == cartItem.product.variant_id; }); }); }); if (filteredData && filteredData.length > 0) { addPopupHtml({ popup, data: filteredData, type: "cart" }); } }, parseInt(popup.display.session_delay) * 1000); }; popups.forEach(async (popup) => { let displayCondition = true; let geoLocationCondition = true; // check for the display conditions if (popup.additional_settings) { if (popup.additional_settings.exclude_from_pages == "home" && window.location.pathname == "/") { displayCondition = false; } if (popup.additional_settings.exclude_from_pages == "url" && popup.additional_settings.exclude_for_url && window.location.href.split(/[?#]/)[0] == popup.additional_settings.exclude_for_url) { displayCondition = false; } if (popup.additional_settings.exclude_from_pages == "keyword" && popup.additional_settings.exclude_for_keyword?.length > 0) { popup.additional_settings.exclude_for_keyword.forEach((keyword) => { if (window.location.href.includes(keyword)) { displayCondition = false; } }); } if ((!popup.position.desktop.display && window.innerWidth > 768) || (!popup.position.mobile.display && window.innerWidth < 768)) { displayCondition = false; } } // check if the user satisfies the geo location condition if (popup.additional_settings?.geo_targeting?.include?.length > 0 || popup.additional_settings?.geo_targeting?.exclude?.length > 0) { const locationResponse = await fetch("https://ipapi.co/json/"); const { country_code } = await locationResponse.json(); if (popup.additional_settings.geo_targeting.include.length > 0) { if (!popup.additional_settings.geo_targeting.include.find((country) => country.code == country_code)) { geoLocationCondition = false; } } if (popup.additional_settings.geo_targeting.exclude.length > 0) { if (popup.additional_settings.geo_targeting.exclude.find((country) => country.code == country_code)) { geoLocationCondition = false; } } } if (displayCondition && geoLocationCondition) { if (popup.type == "cart") { appendCartPopup(popup); } else if (popup.type == "sales") { appendSalesPopup(popup); } } }); }; // function to display widgets const displayWidgets = (widgets) => { if (widgets && widgets.length > 0) { widgets.forEach(async (widget) => { let displayCondition = true; let geoLocationCondition = true; if (widget?.inventory && parseInt(widget?.inventory.available_inventory) < 1) { widget.inventory.available_inventory = 0; } if (widget?.inventory && parseInt(widget?.inventory.sold_inventory) < 1) { widget.inventory.sold_inventory = 0; } if (widget.additional_settings.display_lower_limit && widget.inventory.available_inventory > widget.additional_settings.display_lower_limit) { // check if the widgets satisfies minimum availaible quantity limit displayCondition = false; } // check if the user satisfies the geo location condition if (widget.additional_settings?.geo_targeting?.include?.length > 0 || widget.additional_settings?.geo_targeting?.exclude?.length > 0) { const locationResponse = await fetch("https://ipapi.co/json/"); const { country_code } = await locationResponse.json(); if (widget.additional_settings.geo_targeting.include.length > 0) { if (!widget.additional_settings.geo_targeting.include.find((country) => country.code == country_code)) { geoLocationCondition = false; } } if (widget.additional_settings.geo_targeting.exclude.length > 0) { if (widget.additional_settings.geo_targeting.exclude.find((country) => country.code == country_code)) { geoLocationCondition = false; } } } if (displayCondition && geoLocationCondition) { if (widget.design.text.stock_text.font_url) { appendFont(widget.design.text.stock_text.font_url); } if (widget.design.text.sold_text.font_url && widget.design.text.sold_text.font_url !== widget.design.text.stock_text.font_url) { appendFont(widget.design.text.sold_text.font_url); } const widgetHTML = widget.design.progress_bar.layout === "text_above" ? Text_Above({ widget }) : widget.design.progress_bar.layout === "text_below" ? Text_Below({ widget }) : widget.design.progress_bar.layout === "text_below_above" ? Text_Below_Above({ widget }) : widget.design.progress_bar.layout === "text_above_below" ? Text_Above_Below({ widget }) : widget.design.progress_bar.layout === "text_above_above" ? Text_Above_Above({ widget }) : widget.design.progress_bar.layout === "text_below_below" ? Text_Below_Below({ widget }) : null; const selector = document.querySelector(widget.position.selector.replace(/\s+/g, "")); const WidgetWrapper = document.querySelector(".sold_mainbox") || document.createElement("div"); if (!WidgetWrapper.classList.contains("sold_mainbox")) { WidgetWrapper.classList.add(["sold_mainbox"]); } WidgetWrapper.innerHTML = widgetHTML; if (selector && widget.position.position === "above") { selector.insertAdjacentElement("beforebegin", WidgetWrapper); } if (selector && widget.position.position === "below") { selector.insertAdjacentElement("afterend", WidgetWrapper); } if (selector && widget.position.position === "inside-top") { selector.insertAdjacentElement("afterbegin", WidgetWrapper); } if (selector && widget.position.position === "inside-bottom") { selector.insertAdjacentElement("beforeend", WidgetWrapper); } } }); } }; // Fetch popups from the app // const fetchItems = async () => { // fetch popups const popups = []; const getPopups = async () => { try { const response = await fetch(`/apps/stocksheep/shop-requests/getPopups`, { method: "GET", }); const data = await response.json(); if (data && data.length > 0) { data.forEach((popup) => { popups.push(popup); }); } } catch (err) {} }; // fetch widgets const widgets = []; let pageId; let product_handle; if (window.location.href.includes("products") && window.location.href.split("/").findIndex((e) => e.includes("products")) <= window.location.href.split("/").length - 1) { if (window.location.href.split("variant=")[1]) { pageId = `VARIANT#${ window.location.href .split("?")[1] ?.split("&") ?.find((e) => e.includes("variant")) ?.split("variant=")[1] }`; product_handle = window.location.pathname.split("/")[window.location.pathname.split("/").length - 1]; } else { try { const response = await fetch(window.location.pathname + ".js", { method: "GET", headers: { "Content-type": "application/json", }, }); const data = await response.json(); product_handle = data.handle; if (data?.variants?.length >= 1) { pageId = `VARIANT#${data.variants.find((variant) => variant.available) ? data.variants.find((variant) => variant.available).id : data.variants[0].id}`; } } catch (error) {} } } const getWidgets = async (pageId, product_handle) => { if (pageId && product_handle) { try { const response = await fetch(`/apps/stocksheep/shop-requests/getWidgets`, { method: "POST", headers: { "Content-type": "application/json", }, body: JSON.stringify({ pageId, product_handle, }), }); const data = await response.json(); if (data) { data.forEach((widget) => { widgets.push(widget); }); } } catch (err) {} } }; let commands = [getPopups()]; if (pageId) { commands.push(getWidgets(pageId, product_handle)); } await Promise.all(commands); if (popups && popups.length > 0) { displayPopup(popups); } if (widgets && widgets.length > 0) { displayWidgets(widgets); } }; fetchItems(); // fetch widgets on variant change const refreshWidget = async () => { const widgets = []; if (window.location.href.includes("products") && window.location.href.split("/").findIndex((e) => e.includes("products")) <= window.location.href.split("/").length - 1) { let pageId; let product_handle; if (window.location.href.split("variant=")[1]) { pageId = `VARIANT#${ window.location.href .split("?")[1] ?.split("&") ?.find((e) => e.includes("variant")) ?.split("variant=")[1] }`; product_handle = window.location.pathname.split("/")[window.location.pathname.split("/").length - 1]; } else { try { const response = await fetch(window.location.pathname + ".js", { method: "GET", headers: { "Content-type": "application/json", }, }); const data = await response.json(); product_handle = data.handle; if (data?.variants?.length >= 1) { pageId = `VARIANT#${data.variants.find((variant) => variant.available) ? data.variants.find((variant) => variant.available).id : data.variants[0].id}`; } } catch (error) {} } try { const response = await fetch(`/apps/stocksheep/shop-requests/getWidgets`, { method: "POST", headers: { "Content-type": "application/json", }, body: JSON.stringify({ pageId, product_handle, }), }); const data = await response.json(); if (data) { data.forEach((widget) => { widgets.push(widget); }); } if (widgets && widgets.length > 0) { displayWidgets(widgets); } } catch (err) {} } }; const variantSwitchSelector = [".single-option-selector.product-form__input", "stocksheep_variant_switcher"]; let variantSwitcher; for (const selector of variantSwitchSelector) { if (document.querySelector(selector)) { variantSwitcher = document.querySelector(selector); break; } } if (variantSwitcher) { variantSwitcher.addEventListener("change", () => { refreshWidget(); }); } else { let previousUrl = ""; const observer = new MutationObserver(function (mutations) { if (window.location.href !== previousUrl) { previousUrl = window.location.href; refreshWidget(); } }); const config = { subtree: true, childList: true }; // start listening to changes observer.observe(document, config); }