try { window.dataLayer = window.dataLayer || []; window.analyzify = window.analyzify || {}; window.analyzify = { ...window.analyzify, ...window.analyzify_settings, logs: [], stopAtLog: false, analyzify_version: "4.2.37", attributes: { variant_options: { class: ["variant-radios", "variant-picker", ".product-form__input", ".variant-selects", ".product-form__variants"], } }, addtocart_btn_attributes: { type: ["submit"], name: ["add-to-cart", "add"], "data-add-to-cart-text": ["Add to Cart"], class: [ "addtocart-button", "pdp-form--atc-button", "button-add", "add-to-cart", "add_to_cart", "buttonAddtoCart", "product-form__add-to-cart", "gtmatc", "product-form__cart-submit", "AddToCartText", "AddToCart", "AddToCart-product-template", "product__add-to-cart", "single_add_to_cart_button", "js_frm_cart", "product-buy-buttons--cta", "jsfrmcart", "product-buy-buttons--cta", ], id: ["AddToCart"], }, wishlist_btn_attributes: { class: ["test-wishlist", "wishlist_button"], name: ["wishlist"], "aria-label": ["In Wishlist"], }, product_quantity: { name: ["quantity", "updates[]"], class: ["quantity-selector__input"] }, removefromcart_btn_attributes: { "data-remove-item": ["cart-template"], "data-cart-remove": ["Remove"], "aria-label": ["Remove"], class: [ "cart__remove-btn", "cart__remove", "cart__removee", "cart-item__remove", "item-remove", "remove", "rebuy-cart__flyout-item-remove", "cart_ac_remove", "cartacremove", "previewCartItem-remove", "cart-remove", "btn-remove", "remove-product", "ajaxcart__qty-remove", "quick-cart__item-remove", "cart-remove-line" ], id: ["CartDrawer-Remove"], href: ["/cart/change?id=", "/cart/change?line="], }, checkout_btn_attributes: { name: ["checkout"], class: [ "upcart-checkout-button", "cart__submit", "checkout-trigger", "rebuy-cart__checkout-button", "button-checkout", "checkout-btn", "cart__checkout-button" ], href: ["/checkout"], id: ["CartDrawer-Checkout", "checkout"], value: ["Checkout"], }, collection_prod_click_attributes: { href: ["/products/"], class: ["boost-pfs-addtocart-select-options"], }, collection_atc_attributes: { name: ["add"], class: [ "add-to-cart-btn", "hit-buy-button", "product-form__cart-submit", "spf-product__form-btn-addtocart", "add-to-cart", "boost-pfs-addtocart-btn", "js_addtc", "pratc", ], type: ["submit"], "aria-label": ["Add to cart"], id: ["product-add-to-cart"], }, search_prod_click_attributes: { href: ["/products/"], }, header_nav_btn_attributes: { class: ["header-shortlink", "header__menu-item", "nav-bar__link"], id: [], }, disclosure_attributes: { class: ["disclosure__link"], }, accordion_summary_attributes: { class: ["accordion__title", "accordion"], }, hero_banner_area_attributes: { class: [ "banner__box", "banner__column-inner banner__column-inner--hero banner__column-inner--hero-large", ], }, hero_banner_title_attributes: { class: [ "banner__heading", "content__title content__title--hero content__title--hero-large", ], }, hero_banner_subtitle_attributes: { class: [ "content__subtitle content__subtitle--hero content__subtitle--hero-large", ], }, hero_banner_cta_attributes: { class: [ "content__buttons content__buttons--hero content__buttons--hero-large", ], }, general_atc_btn_attributes: { class: ["Sd_addProduct"], }, contactForm_btn_attributes: { type: ["submit"], class: ["contact__button", "challenge__button"], value: ["contact"] }, submitNewsletterForm_btn_attributes: { type: ["submit"], class: ["newsletter-form__button"], value: ["contact"], id: ["Subscribe"] }, global_atc_functions: ["pplrAddToCartCompleted"], foundElements: [], foundAtcElementForms: [], foundBoostElements: [], }; /* Methods defined in this version: - analyzify - log - findQuantity - getQueryParam - getCookieValue - GetClickedProductPosition - hashUserData - getClientId - getSessionId - analyzify_updateCartAttributes - analyzify_checksendcartdata - findElemInPath - processProductIDFormat */ if(window.analyzify.hasOwnProperty("log") === false || window.analyzify.log === undefined) { window.analyzify.log = function(message, ...args) { console.log(message, ...args); }; } let custom_classes = window.analyzify_custom_classes; let each_element; if (custom_classes !== undefined && custom_classes != "" && custom_classes != "null") { if (custom_classes.includes(",")) { each_element = custom_classes.split(","); } else { custom_classes = custom_classes + ","; each_element = custom_classes.split(","); } for (var i = 0; i < each_element.length; i++) { if (each_element[i].includes(":")) { var aClass = each_element[i].split(":"); if (aClass[0] == "delete") { if (analyzify.hasOwnProperty(aClass[1])) { if (analyzify[aClass[1]].hasOwnProperty(aClass[2])) { if (analyzify[aClass[1]][aClass[2]].includes(aClass[3])) { var ind = analyzify[aClass[1]][aClass[2]].indexOf(aClass[3]); analyzify[aClass[1]][aClass[2]].splice(ind, 1); } } } } else if (analyzify.hasOwnProperty(aClass[0])) { if (analyzify[aClass[0]].hasOwnProperty(aClass[1])) { if (!analyzify[aClass[0]][aClass[1]].includes(aClass[2])) { analyzify[aClass[0]][aClass[1]].push(aClass[2]); } } else { analyzify[aClass[0]][aClass[1]] = []; analyzify[aClass[0]][aClass[1]].push(aClass[2]); } } } } }; window.analyzify.findQuantity = function () { try { const getQuantityValues = (attributes) => { const quantities = []; try { Object.entries(attributes).forEach(([key, values]) => { values.forEach((value) => { const selector = `[${key}="${value}"]`; const element = document.querySelector(selector); if (element && element.value) { quantities.push(element.value); } }); }); } catch (error) { console.error("Error finding quantity elements:", error); } return quantities; }; try { const quantities = getQuantityValues(analyzify.product_quantity); return Number(quantities.length > 0 ? quantities[0] : 1); } catch (error) { console.error("Error in findQuantity function:", error); return 1; } } catch (error) { console.error("Error in findQuantity function:", error); return 1; } }; window.analyzify.getQueryParam = function (name) { try { const urlParams = new URLSearchParams(window.location.search); return urlParams.get(name) || null; // Return null if the parameter is not found } catch (error) { console.error(`Error retrieving query parameter "${name}":`, error); return null; // Return null as a fallback in case of an error } }; window.analyzify.isPathSegmentPresent = function (segment) { try { if (typeof segment !== "string" || segment.trim() === "") { throw new Error("Invalid segment: must be a non-empty string."); } const currentPath = window.location.pathname; return currentPath.includes(segment); } catch (error) { console.error(`Error in isPathSegmentPresent function:`, error); return false; // Return false as a fallback in case of an error } }; // Local Storage Helpers window.analyzify.checkLocalStorageSize = function() { try { let totalSize = 0; for (let key in localStorage) { if (localStorage.hasOwnProperty(key)) { totalSize += localStorage[key].length + key.length; } } const estimatedLimit = 5 * 1024 * 1024; // 5MB in bytes const usagePercentage = (totalSize / estimatedLimit) * 100; window.analyzify.log(`LocalStorage usage: ${totalSize} bytes (${usagePercentage.toFixed(2)}%)`, "an_analyzify", "checkLocalStorageSize"); return { totalSize: totalSize, usagePercentage: usagePercentage, isOverLimit: usagePercentage >= 90 }; } catch (error) { window.analyzify.log(error, "an_analyzify", "checkLocalStorageSize"); return { totalSize: 0, usagePercentage: 0, isOverLimit: false }; } }; window.analyzify.resetLocalStorage = function(key) { try { if (key) { // Reset specific key only window.analyzify.log(`Resetting ${key} due to localStorage size limit`, "an_analyzify", "resetLocalStorage"); localStorage.removeItem(key); window.analyzify.log(`${key} reset completed`, "an_analyzify", "resetLocalStorage"); } else { localStorage.clear(); window.analyzify.log("All localStorage reset completed", "an_analyzify", "resetLocalStorage"); } } catch (error) { window.analyzify.log(error, "an_analyzify", "resetLocalStorage"); } }; window.analyzify.saveToLocalStorage = function (key, value) { try { if (value) { const valueToStore = typeof value === "object" ? JSON.stringify(value) : value; localStorage.setItem(key, valueToStore); } } catch (error) { window.analyzify.log(error, "an_analyzify", "saveToLocalStorage"); // Check localStorage size and reset if over 90% const sizeInfo = window.analyzify.checkLocalStorageSize(); if (sizeInfo.isOverLimit) { window.analyzify.resetLocalStorage('azfy_utm_history'); try { if (value) { const valueToStore = typeof value === "object" ? JSON.stringify(value) : value; localStorage.setItem(key, valueToStore); window.analyzify.log(`Successfully saved ${key} after localStorage reset`, "an_analyzify", "saveToLocalStorage"); } } catch (retryError) { window.analyzify.log(`Failed to save ${key} even after localStorage reset`, "an_analyzify", "saveToLocalStorage"); window.analyzify.log(retryError, "an_analyzify", "saveToLocalStorage"); } } } }; window.analyzify.getFromLocalStorage = function (key) { try { const value = localStorage.getItem(key); return value; } catch (error) { analyzify.log(error, 'an_analyzify', 'getFromLocalStorage'); return null; } }; window.analyzify.deleteFromLocalStorage = function (key) { try { localStorage.removeItem(key); } catch (error) { analyzify.log(`Error removing ${key} from localStorage:`, 'an_analyzify', 'deleteFromLocalStorage'); analyzify.log(error, 'an_analyzify', 'deleteFromLocalStorage'); } }; window.analyzify.cookieStorage = { get: function (cookieName) { try { const name = cookieName + "="; const decodedCookie = decodeURIComponent(document.cookie); const cookies = decodedCookie.split(";"); for (let cookie of cookies) { cookie = cookie.trim(); if (cookie.startsWith(name)) { return cookie.substring(name.length); } } return null; } catch (error) { window.analyzify.log(error, 'an_analyzify', 'cookieStorage.get'); return null; } }, save: function (key, value, maxAge = 2592000) { try { if (value) { const cookieValue = typeof value === "object" ? JSON.stringify(value) : value; document.cookie = `${key}=${cookieValue}; path=/; max-age=${maxAge}`; // Default: 30 days (30 * 24 * 60 * 60) } } catch (error) { window.analyzify.log(error, 'an_analyzify', 'cookieStorage.save'); } }, delete: function (key) { try { document.cookie = `${key}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`; } catch (error) { window.analyzify.log(error, 'an_analyzify', 'cookieStorage.delete'); } } }; // Backward compatibility function for v3; see analyzify-functions-v3.js window.analyzify.getCookieValue = function (cookieName) { return window.analyzify.cookieStorage.get(cookieName); }; window.analyzify.parseQueryString = function(queryString) { const query = {}; try { if (!queryString || typeof queryString !== 'string') { analyzify.log('queryString is empty', 'an_analyzify', 'parseQueryString'); return query; } const pairs = (queryString[0] === '?' ? queryString.slice(1) : queryString).split('&'); for (const pair of pairs) { if (!pair) continue; const [key, ...values] = pair.split('='); const decodedKey = decodeURIComponent(key); if (decodedKey) { query[decodedKey] = decodeURIComponent(values.join('=') || ''); } } } catch (error) { analyzify.log(error, 'an_analyzify', 'parseQueryString'); } analyzify.log(query, 'an_analyzify', 'parseQueryString'); return query; }; window.analyzify.urlReplace = (function(param) { let initialParams = null; return function(param) { try { if (!param) { analyzify.log('No param found', 'an_analyzify', 'urlReplace'); return window.location.href; } if (!initialParams) { initialParams = window.analyzify.parseQueryString(param); } const currentParams = window.analyzify.parseQueryString(window.location.search); const params = new URLSearchParams(window.location.search); let updated = false; for (const key in initialParams) { if (initialParams.hasOwnProperty(key) && !currentParams[key]) { params.set(key, initialParams[key]); updated = true; } } if (!updated) return window.location.href; const url = new URL(window.location.href); url.search = params.toString(); return url.toString(); } catch (error) { analyzify.log(error, 'an_analyzify', 'urlReplace'); return window.location.href; } }; })(); window.analyzify.storageService = function(key, value) { if (typeof key !== 'string') return null; if (typeof value === 'undefined') { return localStorage.getItem(key); } else { localStorage.setItem(key, value); return true; } }; window.analyzify.SSAP_vals = function() { try { const get_SSAP = window.analyzify.cookieStorage.get("_shopify_sa_p"); let SSAP_vals = {}; if (get_SSAP) { try { const decodedValue = decodeURIComponent(get_SSAP); if (decodedValue) { SSAP_vals = { cookie_decoded: decodedValue || null, herited_url: window.analyzify.urlReplace(decodedValue) || null, }; } } catch (error) { console.error('Error decoding _shopify_sa_p cookie:', error); } } else { // If no cookie, use URL parameters const urlParams = window.location.search; if (urlParams) { SSAP_vals = { cookie_decoded: urlParams.slice(1) || null, herited_url: window.location.href || null, }; } } return SSAP_vals; } catch (error) { analyzify.log(error, 'an_analyzify', 'SSAP_vals'); return {}; } }; window.getClientId = async function (measurementId) { try { // const cachedClientId = getFromLocalStorage("clientId"); // if (cachedClientId) return cachedClientId; const gaCookie = window.analyzify.cookieStorage.get("_ga"); if (gaCookie) { const match = gaCookie.match(/GA\d+\.\d+\.(\d+\.\d+)/); if (match) { const clientId = match[1]; window.analyzify.saveToLocalStorage("clientId", clientId); analyzify.log(`Client ID from cookie: ${clientId}`, 'an_analyzify', 'getClientId'); return clientId; } } if (window.gtag) { try { const clientId = await new Promise((resolve) => window.gtag("get", measurementId, "client_id", resolve) ).then((clientId) => clientId); window.analyzify.saveToLocalStorage("clientId", clientId); analyzify.log(`Client ID from gtag: ${clientId}`, 'an_analyzify', 'getClientId'); return clientId; } catch (error) { analyzify.log(error, 'an_analyzify', 'getClientId') } } return null; } catch (error) { analyzify.log(error, 'an_analyzify', 'getClientId'); return null; } }; window.getSessionId = async function(measurementId) { try { if (!measurementId?.startsWith('G-')) return null; analyzify.log(`measurementId: ${measurementId}`, measurementId, 'an_analyzify', 'getSessionId'); const cookieName = `_ga_${measurementId.substring(2)}`; const gaCookie = analyzify.cookieStorage.get(cookieName); analyzify.log('gaCookie', gaCookie, 'an_analyzify', 'getSessionId'); if (gaCookie) { let match; if (gaCookie.startsWith('GS1')) { match = gaCookie.match(/GS1\.\d+\.(\d+)/); } else if (gaCookie.startsWith('GS2')) { match = gaCookie.match(/GS2\.\d+\.s(\d+)/); } const sessionId = match?.[1]; if (sessionId && analyzify.storageService("sessionId") !== sessionId) { analyzify.storageService("sessionId", sessionId); } analyzify.log(`sessionId (gaCookie): ${sessionId}`, sessionId ? sessionId : null, 'an_analyzify', 'getSessionId'); if (sessionId) return sessionId; } if (window.gtag) { const sessionId = await new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject(new Error('gtag session_id retrieval timed out')); }, 1000); window.gtag("get", measurementId, "session_id", (sessionId) => { clearTimeout(timeoutId); resolve(sessionId); }); }).catch(error => { console.warn('Error fetching session ID from gtag:', error); return null; }); if (sessionId && analyzify.storageService("sessionId") !== sessionId) { analyzify.storageService("sessionId", sessionId); } analyzify.log(`sessionId (gtag): ${sessionId}`, sessionId ? sessionId : null, 'an_analyzify', 'getSessionId'); if (sessionId) return sessionId; } return analyzify.storageService("sessionId") || null; } catch (error) { analyzify.log(error, 'an_analyzify', 'getSessionId'); return null; } }; // Session Storage Utility window.analyzify.sessionStorage = { save: function (key, value) { try { if (value) { // If value is an object or array, stringify it const valueToStore = typeof value === "object" ? JSON.stringify(value) : value; sessionStorage.setItem(key, valueToStore); } } catch (error) { analyzify.log(error, "an_analyzify", "sessionStorage.save"); } }, get: function (key) { try { const value = sessionStorage.getItem(key); return value; } catch (error) { analyzify.log(error, 'an_analyzify', 'sessionStorage.get'); return null; } }, remove: function (key) { try { sessionStorage.removeItem(key); } catch (error) { analyzify.log(`Error removing ${key} from sessionStorage:`, 'an_analyzify', 'sessionStorage.remove'); analyzify.log(error, 'an_analyzify', 'sessionStorage.remove'); } } }; // Collect Cart Data window.collectCartData = async function (measurementId) { try { const paramConfig = { azfy_clids: { type: "clids", params: { gclid: { type: "query", method: "gclid", default: null }, fbclid: { type: "query", method: "fbclid", default: null }, ttclid: { type: "query", method: "ttclid", default: null }, }, }, azfy_cookies: { type: "cookies", params: { fbp: { type: "cookie", method: "_fbp", default: null }, fbc: { type: "cookie", method: "_fbc", default: null }, ttp: { type: "cookie", method: "_ttp", default: null }, ...(measurementId ? { ga: { type: "async", method: "getClientId", args: [measurementId], default: null }, [`ga_${measurementId.substring(2)}`]: { type: "async", method: "getSessionId", args: [measurementId], default: null } } : {}) } }, azfy_utm_history: { type: "utm_history", params: { utm_source: { type: "query", method: "utm_source", default: null, }, utm_medium: { type: "query", method: "utm_medium", default: null, }, utm_campaign: { type: "query", method: "utm_campaign", default: null, }, utm_content: { type: "query", method: "utm_content", default: null, }, utm_term: { type: "query", method: "utm_term", default: null }, utm_id: { type: "query", method: "utm_id", default: null }, azfy_referrer: { type: "direct", method: () => { const ref = document.referrer; if (ref === "") return "direct"; const currentDomain = window.location.hostname; const referrerDomain = new URL(ref).hostname; return currentDomain === referrerDomain ? null : ref; }, default: null, }, }, }, azfy_consent: { type: "direct", method: () => !window.analyzify.consent_active || (window.analyzify.current_consent?.ad_storage === "granted" && window.analyzify.current_consent?.analytics_storage === "granted"), default: null, }, }; window.analyzify.cart_attributes = {}; async function formatClidsAndCookies(config) { const values = {}; for (const [key, paramConfig] of Object.entries(config.params)) { if (paramConfig.type === "query") { const value = window.analyzify.getQueryParam(paramConfig.method) || paramConfig.default; if (value) { switch (key) { case "gclid": values[key] = `g:${value}`; break; case "fbclid": values[key] = `fb:${value}`; break; case "ttclid": values[key] = `tt:${value}`; break; default: values[key] = value; } } else { values[key] = null; } } else if (paramConfig.type === "cookie" || paramConfig.type === "async") { let value; if (paramConfig.type === "cookie") { value = window.analyzify.cookieStorage.get(paramConfig.method) || paramConfig.default; } else { value = await window[paramConfig.method](...paramConfig.args) || paramConfig.default; } if (value) { switch (key) { case "fbp": values[key] = `fbp:${value}`; break; case "fbc": values[key] = `fbc:${value}`; break; case "ttp": values[key] = `tt:${value}`; break; case "ga": values[key] = `ga:${value}`; break; default: if (key.startsWith('ga_')) { values[key] = `${key}:${value}`; } else { values[key] = value; } break; } } else { values[key] = null; } } } const valuesArray = Object.values(values); if (valuesArray.every((value) => value === null)) { return undefined; } return valuesArray; } function formatUtmHistory(config) { const currentCartId = window.analyzify.cart_id; const lastKnownCartId = window.analyzify.getFromLocalStorage("azfy_cart_id"); const cartIdChanged = currentCartId !== lastKnownCartId; const restoreSessionFromCookies = () => { const sessionHistoryStr = window.analyzify.sessionStorage.get("azfy_utm_history"); const cookieHistoryStr = window.analyzify.cookieStorage.get("azfy_utm_history"); const currentCartId = window.analyzify.cart_id; if ((!sessionHistoryStr || sessionHistoryStr === "{}") && cookieHistoryStr && currentCartId) { try { const cookieHistory = JSON.parse(cookieHistoryStr); const matchingEntries = {}; Object.keys(cookieHistory).forEach(timestamp => { const entry = cookieHistory[timestamp]; const entryCartId = entry.cartId || (Array.isArray(entry) ? currentCartId : null); if (entryCartId === currentCartId) { matchingEntries[timestamp] = entry; } }); if (Object.keys(matchingEntries).length > 0) { window.analyzify.sessionStorage.save("azfy_utm_history", JSON.stringify(matchingEntries)); } } catch (e) { window.analyzify.log("error restoring session from cookies:", e); } } }; restoreSessionFromCookies(); const currentValues = {}; const utmValues = { utm_source: config.params.utm_source.default, utm_medium: config.params.utm_medium.default, utm_campaign: config.params.utm_campaign.default, utm_content: config.params.utm_content.default, utm_term: config.params.utm_term.default, utm_id: config.params.utm_id.default, azfy_referrer: config.params.azfy_referrer.default, }; for (const [utmKey, utmConfig] of Object.entries(config.params)) { if (utmConfig.type === "direct") { currentValues[utmKey] = utmConfig.method(); } else if (utmConfig.type === "query") { const queryValue = window.analyzify.getQueryParam(utmConfig.method); currentValues[utmKey] = queryValue || utmConfig.default; } if (currentValues[utmKey] !== null) { utmValues[utmKey] = currentValues[utmKey]; } } const formattedValues = [ `${utmValues.utm_source}/${utmValues.utm_medium}`, `${utmValues.utm_campaign}$${utmValues.utm_content}$${utmValues.utm_term}$${utmValues.utm_id}`, utmValues.azfy_referrer ? `${utmValues.azfy_referrer}` : utmValues.azfy_referrer, ]; const existingSessionHistoryStr = window.analyzify.sessionStorage.get("azfy_utm_history"); const existingCookieHistoryStr = window.analyzify.cookieStorage.get("azfy_utm_history"); const existingLocalHistoryStr = window.analyzify.getFromLocalStorage("azfy_utm_history"); let existingSessionHistory = {}; let existingCookieHistory = {}; let existingLocalHistory = {}; // Parse session storage history if (existingSessionHistoryStr && existingSessionHistoryStr !== "{}") { try { existingSessionHistory = JSON.parse(existingSessionHistoryStr); } catch (e) { window.analyzify.log( "Error parsing utm_history", e, "an_analyzify", "handleUtmHistory" ); existingSessionHistory = {}; } } // Parse cookie history if (existingCookieHistoryStr && existingCookieHistoryStr !== "{}") { try { existingCookieHistory = JSON.parse(existingCookieHistoryStr); } catch (e) { console.log("Error parsing cookie utm_history", e); existingCookieHistory = {}; } } // Parse local storage history if (existingLocalHistoryStr && existingLocalHistoryStr !== "{}") { try { existingLocalHistory = JSON.parse(existingLocalHistoryStr); } catch (e) { console.log("Error parsing local utm_history", e); existingLocalHistory = {}; } } if (cartIdChanged) { if (currentCartId) { window.analyzify.saveToLocalStorage("azfy_cart_id", currentCartId); } } // Add cartId to all existing entries that don't have it const addCartIdToHistory = (history) => { const updated = {}; Object.keys(history).forEach(timestamp => { const entry = history[timestamp]; if (Array.isArray(entry)) { // Old format - convert to new format with cartId updated[timestamp] = { values: entry, cartId: window.analyzify.cart_id }; } else if (entry && typeof entry === 'object' && entry.values) { // Already has cartId format, but check if cartId is null/undefined updated[timestamp] = { values: entry.values, cartId: entry.cartId || window.analyzify.cart_id }; } }); return updated; }; // Merge all histories prioritizing session > cookie > local let existingHistory = { ...existingLocalHistory, ...existingCookieHistory, ...existingSessionHistory }; // Get the most recent entry (highest timestamp) const timestamps = Object.keys(existingHistory).sort((a, b) => parseInt(b) - parseInt(a)); const lastTimestamp = timestamps[0]; // Check if last entry needs cartId update (check against original format) let needsCartIdUpdate = false; if (lastTimestamp) { const lastEntry = existingHistory[lastTimestamp]; needsCartIdUpdate = Array.isArray(lastEntry) || !lastEntry.cartId || lastEntry.cartId === null; } let isDuplicate = false; if (lastTimestamp) { const lastEntry = existingHistory[lastTimestamp]; // Handle both old format (array) and new format (object with values) const entryValues = Array.isArray(lastEntry) ? lastEntry : lastEntry.values; isDuplicate = entryValues && entryValues.every( (value, index) => value === formattedValues[index] ); } const isSameDomain = document.referrer ? new URL(document.referrer).hostname === window.location.hostname : false; if (!isDuplicate && !isSameDomain) { const timestamp = Math.floor(Date.now() / 1000).toString(); const cartId = window.analyzify.cart_id; // Ensure all histories have cartId structure before adding new entry existingSessionHistory = addCartIdToHistory(existingSessionHistory); existingCookieHistory = addCartIdToHistory(existingCookieHistory); existingLocalHistory = addCartIdToHistory(existingLocalHistory); // Create UTM entry with both values and cartId for all storage types const utmEntryWithCartId = { values: formattedValues, cartId: cartId }; existingHistory[timestamp] = utmEntryWithCartId; existingSessionHistory[timestamp] = utmEntryWithCartId; existingCookieHistory[timestamp] = utmEntryWithCartId; existingLocalHistory[timestamp] = utmEntryWithCartId; // Save to all storage types with cartId structure - save each individual history window.analyzify.sessionStorage.save("azfy_utm_history", JSON.stringify(existingSessionHistory)); window.analyzify.cookieStorage.save("azfy_utm_history", JSON.stringify(existingCookieHistory)); window.analyzify.saveToLocalStorage( "azfy_utm_history", JSON.stringify(existingLocalHistory) ); } else if (needsCartIdUpdate) { existingSessionHistory = addCartIdToHistory(existingSessionHistory); existingCookieHistory = addCartIdToHistory(existingCookieHistory); existingLocalHistory = addCartIdToHistory(existingLocalHistory); window.analyzify.sessionStorage.save("azfy_utm_history", JSON.stringify(existingSessionHistory)); window.analyzify.cookieStorage.save("azfy_utm_history", JSON.stringify(existingCookieHistory)); window.analyzify.saveToLocalStorage("azfy_utm_history", JSON.stringify(existingLocalHistory)); } // For cart_attributes, return only the values without cartId structure const sessionHistoryForCartAttributes = {}; Object.keys(existingSessionHistory).forEach(timestamp => { const entry = existingSessionHistory[timestamp]; sessionHistoryForCartAttributes[timestamp] = entry.values || entry; }); const formattedUTMHistory = Object.keys(sessionHistoryForCartAttributes).length === 0 ? null : sessionHistoryForCartAttributes; return formattedUTMHistory; } async function getValue(key, config) { try { let value; switch (config.type) { case "utm_history": value = formatUtmHistory(config) || config.default; break; case "clids": case "cookies": value = await formatClidsAndCookies(config); break; case "cookie": value = window.analyzify.cookieStorage.get(config.method) || config.default; break; case "query": value = window.analyzify.getQueryParam(config.method) || config.default; break; case "async": value = (await window[config.method](...config.args)) || config.default; break; case "direct": value = config.method(); break; default: value = config.default; } return value; } catch (error) { window.analyzify.log( `Error getting ${key}: ${error}`, "an_analyzify", "collectCartData" ); return null; } } async function processParam(key, config) { let value = await getValue(key, config); if (value === null || value === undefined) { await new Promise((resolve) => setTimeout(resolve, 1000)); value = await getValue(key, config); } if (value !== null && value !== undefined) { window.analyzify.cart_attributes[key] = value; // Skip normal storage for azfy_utm_history as it's handled internally with cartId structure if (key !== "azfy_utm_history") { window.analyzify.sessionStorage.save(key, value); window.analyzify.cookieStorage.save(key, value); window.analyzify.saveToLocalStorage(key, value); } } else { // Try to get from sessionStorage first, then cookies, then localStorage let storedValue = window.analyzify.sessionStorage.get(key); if (!storedValue) { storedValue = window.analyzify.cookieStorage.get(key); } if (!storedValue) { storedValue = window.analyzify.getFromLocalStorage(key); } if (storedValue) { // Parse JSON strings back to objects for specific keys if ((key === "azfy_clids" && typeof storedValue === 'string' && storedValue.startsWith('[')) || (key === "azfy_utm_history" && typeof storedValue === 'string' && storedValue.startsWith('{'))) { try { const parsed = JSON.parse(storedValue); // For azfy_utm_history, extract only values (remove cartId structure) if (key === "azfy_utm_history") { const valuesOnly = {}; Object.keys(parsed).forEach(timestamp => { const entry = parsed[timestamp]; valuesOnly[timestamp] = entry.values || entry; }); window.analyzify.cart_attributes[key] = valuesOnly; } else { window.analyzify.cart_attributes[key] = parsed; } } catch (e) { window.analyzify.cart_attributes[key] = storedValue; } } else { window.analyzify.cart_attributes[key] = storedValue; } } } } const promises = Object.keys(paramConfig).map(async (key) => { await processParam(key, paramConfig[key]); }); await Promise.all(promises); for (const key in paramConfig) { if ( paramConfig.hasOwnProperty(key) && !window.analyzify.cart_attributes.hasOwnProperty(key) ) { window.analyzify.cart_attributes[key] = null; } } window.analyzify.log( "Final cart_attributes", "an_analyzify", "collectCartData" ); window.analyzify.log( window.analyzify.cart_attributes, "an_analyzify", "collectCartData" ); return window.analyzify.cart_attributes; } catch (error) { window.analyzify.log( "Error in collectCartData:", "an_analyzify", "collectCartData" ); window.analyzify.log(error, "an_analyzify", "collectCartData"); const fallbackObject = {}; for (const key in paramConfig) { if (paramConfig.hasOwnProperty(key)) { fallbackObject[key] = null; } } return fallbackObject; } }; window.analyzify_updateCartAttributes = async function (attributes) { try { window.analyzify.log(JSON.stringify(attributes, null, 2), 'an_analyzify', 'analyzify_updateCartAttributes'); const response = await fetch("/cart/update.js", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ attributes }), }); window.analyzify.log(`Shopify API Response: ${response.status}`, 'an_analyzify', 'analyzify_updateCartAttributes'); if (response.ok) { window.analyzify.log("Cart attributes updated successfully", 'an_analyzify', 'analyzify_updateCartAttributes'); } else { window.analyzify.log(`Failed to update cart attributes: ${response.status}`, 'an_analyzify', 'analyzify_updateCartAttributes'); } } catch (error) { window.analyzify.log(error, 'an_analyzify', 'analyzify_updateCartAttributes'); } }; window.analyzify_checksendcartdata = async function () { try{ if(window.analyzify.checksendcartdata_status == false){ const measurementId = window.analyzify?.SERVERSIDE?.measurement_id || window?.analyzify_measurement_id_v3 || window?.analyzify_measurement_id || null; // @check if (!measurementId) { window.analyzify.log("Measurement ID not found", 'an_analyzify', 'analyzify_checksendcartdata'); return; } const cartData = await window.collectCartData(measurementId); // if (cartData) await analyzify_updateCartAttributes(cartData); const updateCartWithTimeout = async (measurementId) => { const cartData = await window.collectCartData(measurementId); if (cartData && Object.keys(cartData).length) { setTimeout(async () => { try { await window.analyzify_updateCartAttributes(cartData); } catch (error) { console.error('Failed to update cart attributes:', error); } }, 1000); } }; updateCartWithTimeout(measurementId); return cartData; } } catch(error) { window.analyzify.log(error, 'an_analyzify', 'analyzify_checksendcartdata'); } }; window.analyzify.GetClickedProductPosition = function (elementHref, sku) { if (sku != "") { for (const collectionProductsSku in window.collection_sku_list) { if (sku == collectionProductsSku) { return window.collection_sku_list.indexOf(collectionProductsSku); } } return 0; } else { var elementIndex = -1; collectionProductsElements = document.querySelectorAll( 'main a[href*="/products/"]', ); let hrefValues = []; let uniqueCollectionProductsElements = []; collectionProductsElements.forEach((element) => { let href = element.getAttribute("href"); if (!hrefValues.includes(href)) { uniqueCollectionProductsElements.push(element); hrefValues.push(href); } }); uniqueCollectionProductsElements.forEach(function (element, index) { if (element.href.includes(elementHref)) { elementIndex = index + 1; } }); return elementIndex; } }; window.analyzify.getShopifyId = function (feed, itemId, variantId, sku) { try { // Validate inputs if (!feed || !itemId || !variantId) { if (sku) { return sku; // Return SKU if any parameter is missing } else { return null; } } // Construct and return the Shopify ID return `shopify_${feed}_${itemId}_${variantId}`; } catch (error) { console.error("Error in getShopifyId function:", error); return null; // Return null as a fallback in case of an error } }; window.analyzify.findElemInPath = function (pathArray, attributeObj) { let buttonFound = null; if (pathArray) { // Loop through the path array for (let i = 0; i < pathArray.length; i++) { // Loop through the attribute object for (const attribute in attributeObj) { if (attributeObj.hasOwnProperty(attribute)) { const attributeName = attribute; const attributeValues = attributeObj[attribute]; if ( pathArray[i].hasAttribute !== undefined && pathArray[i].hasAttribute(attributeName) === true ) { // Loop through the attribute values attributeValues.forEach(function (selectedValue) { // Check if the current path element's attribute contains the selected value if ( pathArray[i] .getAttribute(attributeName) .indexOf(selectedValue) > -1 ) { analyzify.log(`'${selectedValue}' found in '${attributeName}' attribute list.`, 'an_analyzify', 'findElemInPath'); analyzify.log(pathArray[i], 'an_analyzify', 'findElemInPath'); buttonFound = pathArray[i]; analyzify.foundElements.push(pathArray[i]); analyzify.foundAtcElementForms.push( pathArray[i].closest("form[action='/cart/add']"), ); analyzify.foundBoostElements.push( pathArray[i].closest(".boost-pfs-filter-product-item"), ); } }); } } } } } return buttonFound; }; window.analyzify.getVariantDetails = (variants, variantId) => { try { if(!variants || !variantId) return null; if (!Array.isArray(variants)) { console.warn('variants is not an array', 'an_analyzify', 'getVariantDetails'); return null; } // Convert variantId to string for safe comparison const variantIdStr = variantId?.toString(); // Find the variant by id or default to the first variant const variant = variantIdStr ? variants.find((v) => v.id?.toString() === variantId?.toString()) : variants[0]; // Return the variant details with safe access and default values return { id: variant?.id?.toString() || null, sku: variant?.sku?.toString() || null, title: variant?.title?.trim() || null, price: (variant?.price || 0) / 100, compare_at_price: (variant?.compare_at_price || 0) / 100, barcode: variant?.barcode?.toString() || null, }; } catch (error) { console.error("Error in getVariantDetails function:", error); return { id: null, sku: null, title: null, price: 0, compare_at_price: 0, barcode: null, }; // Return a default object in case of error } }; // Add hashValue as a separate function window.analyzify.hashValue = async function (value) { try { if (!value) return null; // Check if crypto and subtle are available if (typeof window === 'undefined' || !window.crypto || !window.crypto.subtle) { console.error('Crypto API is not available in this browser. Unable to hash value.'); return null; } const encoder = new TextEncoder(); const data = encoder.encode(value); const hashBuffer = await window.crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } catch (error) { console.error('Error while hashing value:', error); return null; } }; // Modify hashUserData to use the separated hashValue function window.analyzify.hashUserData = async function (userData) { try { const hashedData = {}; for (const [key, value] of Object.entries(userData)) { hashedData[key] = await window.analyzify.hashValue(value); } return hashedData; } catch (error) { console.error('Error in hashUserData:', error); return {}; } }; window.analyzify.getCurrentVariant = () => { try { return window.analyzify.getVariantDetails( window.analyzify?.getProductObj?.product?.variants, window.analyzify?.getQueryParam('variant') ) || null; } catch (error) { console.error('Error in getCurrentVariant:', error); return null; } }; window.analyzify.getItemIds = function ({ productObj, product_id_format, variantDetails, eventName, feedRegion }) { try { // Return default structure if missing required params if (!productObj || !product_id_format) { analyzify.log('Missing required parameters in getItemIds', 'an_analyzify', 'getItemIds'); return { selected: null, ids: { product_id: null, variant_id: null, sku: null, shopify_id: null, first_variant: null } }; } const isCartEvent = ['view_cart', 'begin_checkout'].includes(eventName); const baseIds = { productId: isCartEvent ? productObj?.product_id : productObj?.id, variantId: productObj?.item_variant_id || variantDetails?.id || productObj?.variant_id || productObj?.variants?.[0]?.id, sku: productObj?.item_sku || variantDetails?.sku || productObj?.sku || productObj?.variants?.[0]?.sku }; const ids = { product_id: baseIds.productId ?? null, variant_id: baseIds.variantId ?? null, sku: baseIds.sku ?? null, shopify_id: typeof window.analyzify?.getShopifyId === 'function' ? window.analyzify.getShopifyId( feedRegion || window.analyzify?.feed_region || null, baseIds.productId ?? null, baseIds.variantId ?? null, baseIds.sku ?? null ) : null, first_variant: variantDetails ?? null }; const idMap = { 'product_sku': ids.sku, 'variant_id': ids.variant_id, 'shopify_item_id': ids.shopify_id, 'default': ids.product_id }; const selected = (idMap[product_id_format] || idMap['default'] || ids.sku || ids.variant_id)?.toString() ?? null; analyzify.log('selected', 'an_analyzify', 'getItemIds'); analyzify.log(selected, 'an_analyzify', 'getItemIds'); return { selected, ids }; } catch (error) { analyzify.log(error, 'an_analyzify', 'getItemIds_error'); // Return default structure on error return { selected: null, ids: { product_id: null, variant_id: null, sku: null, shopify_id: null, first_variant: null } }; } }; try{ // Ensure shopify_customer is initialized if not already present if (!window.analyzify.shopify_customer) { window.analyzify.shopify_customer = {}; } } catch(error) { console.error('Error in shopify_customer:', error); } try { if (window.analyzify.shopify_customer?.type === "member") { window.analyzify?.hashUserData({ user_id: window.analyzify.shopify_customer?.user_id || null, id: window.analyzify.shopify_customer?.user_id || null, first_name: window.analyzify.shopify_customer?.first_name || null, last_name: window.analyzify.shopify_customer?.last_name || null, email_address: window.analyzify.shopify_customer?.email_address || null, phone_number: window.analyzify.shopify_customer?.phone_number || null, }).then(hashedData => { // Store user_id, id, email_address hashed value in localStorage const keysToStore = ['user_id', 'id', 'email_address']; Object.entries(hashedData).forEach(([key, value]) => { if (value && keysToStore.includes(key)) { window.analyzify.saveToLocalStorage(`azfy_sha256_${key}`, value); } }); // Append hashed data to the existing shopify_customer object window.analyzify.shopify_customer = { ...window.analyzify.shopify_customer, sha256_id: hashedData?.id || window.analyzify.getFromLocalStorage('azfy_sha256_id'), sha256_user_id: hashedData?.user_id || window.analyzify.getFromLocalStorage('azfy_sha256_user_id'), sha256_first_name: hashedData?.first_name || null, sha256_last_name: hashedData?.last_name || null, sha256_email_address: hashedData?.email_address || window.analyzify.getFromLocalStorage('azfy_sha256_email_address'), sha256_phone_number: hashedData?.phone_number || null, }; analyzify.log(window.analyzify.shopify_customer, 'an_analyzify', 'hashUserData'); // Dispatch event indicating user data is ready document.dispatchEvent(new CustomEvent('userDataReady', { detail: window.analyzify.shopify_customer })); }).catch(error => { // If hashing fails, try to load from localStorage window.analyzify.shopify_customer = { ...window.analyzify.shopify_customer, sha256_id: window.analyzify.getFromLocalStorage('azfy_sha256_id'), sha256_user_id: window.analyzify.getFromLocalStorage('azfy_sha256_user_id'), sha256_email_address: window.analyzify.getFromLocalStorage('azfy_sha256_email_address'), }; console.error('Error hashing data:', error); }); } else { // Clear hashed data from localStorage for non-members ['user_id', 'id', 'email_address'].forEach(key => { window.analyzify.deleteFromLocalStorage(`azfy_sha256_${key}`); }); window.analyzify.shopify_customer = { type: "visitor", ...(window.analyzify.shopify_customer?.email_address ? { email_address: window.analyzify.shopify_customer.email_address } : {}), ...(window.analyzify.shopify_customer?.phone_number ? { phone_number: window.analyzify.shopify_customer.phone_number } : {}), }; analyzify.log(window.analyzify.shopify_customer, 'an_analyzify', 'hashUserData'); // Dispatch event for visitor as well document.dispatchEvent(new CustomEvent('userDataReady', { detail: window.analyzify.shopify_customer })); }; } catch(error) { console.error('Error in hashUserData:', error); } window.analyzify.generateEventId = function(prefix = 'sh', separator = '-', salt = '') { try { const generateHexSegment = (length) => { const randomValues = Array.from({ length }, () => Math.floor(Math.random() * 16).toString(16)); const saltedValues = randomValues.map((val, index) => { const saltChar = salt.charCodeAt(index % salt.length) || 0; return ((parseInt(val, 16) + saltChar) % 16).toString(16); }); return saltedValues.join(''); }; const segments = [8, 4, 4, 4, 12].map(generateHexSegment); const uuid = segments.join(separator); return `${prefix}${separator}${uuid}`.toLowerCase(); } catch (error) { console.error('Error generating event ID:', error); return null; } }; window.analyzify.eventId = (function(salt) { const eventIds = {}; const defaultEventType = 'default'; return { get: function(eventType = defaultEventType) { if (!eventIds[eventType]) { eventIds[eventType] = window.analyzify.generateEventId('sh', '-', salt); } return eventIds[eventType]; }, reset: function(eventType = defaultEventType) { eventIds[eventType] = window.analyzify.generateEventId('sh', '-', salt); return eventIds[eventType]; } }; })('z9y8x7w6v5u4t3s2'); window.analyzify.formatPrice = (price, cents = false) => { try { // Check for null or undefined values and warn if necessary if (price == null) { // Handles both null and undefined // console.warn( // "Invalid price provided (null or undefined):", // price, // "\nStack trace:\n", // new Error().stack // ); return 0; // Return 0 for null or undefined prices } // Use regex to extract numeric values, including decimal points const match = price.toString().match(/-?\d+(\.\d+)?/); // Convert matched value to a number, or return 0 if no match const numericPrice = match ? Number(match[0]) : NaN; if (isNaN(numericPrice)) { console.warn( "Invalid price provided (not numeric):", price, "\nStack trace:\n", new Error().stack ); return 0; // Return 0 for invalid numeric prices } return Number(parseFloat((numericPrice / (cents ? 100 : 1)).toFixed(2))); // Convert cents to dollars } catch (error) { console.error("Error formatting price:", error); return 0; // Return 0 in case of an error } }; window.analyzify.getFirstVariant = (prod) => { try { if(!prod) return null; return { id: prod?.variant_id || prod.variants?.[0]?.id || null, price: prod?.price || prod.variants?.[0]?.price || null, compare_at_price: prod?.compare_at_price || prod.variants?.[0]?.compare_at_price || null, barcode: null || prod.variants?.[0]?.barcode || null, title: prod?.variant_title || prod.variants?.[0]?.title || null, sku: prod?.sku || prod.variants?.[0]?.sku || null }; // Return the first variant } catch (error) { console.error("Error in getFirstVariant function:", error); return null; // Return null in case of an error } }; window.analyzify.cart_id = window.analyzify?.cookieStorage.get('cart')?.split('?')[0] || null; window.analyzify.findClickedProduct = (element, colProds) => { try { analyzify.log('Element found', 'an_analyzify', 'findClickedProduct'); analyzify.log(element, 'an_analyzify', 'findClickedProduct'); analyzify.log('Collection products', 'an_analyzify', 'findClickedProduct'); analyzify.log(colProds, 'an_analyzify', 'findClickedProduct'); if (element.hasAttribute('href')) { const href = element.getAttribute('href'); if (href.includes('/products/')) { const handle = href.split('/products/')[1]; if(!colProds) return analyzify.log('Collection products not found'); let clickedProduct = colProds?.find(product => product.handle === handle); if (!clickedProduct) { analyzify.log('Product not found by handle, checking element attributes'); // Extract potential product IDs from various attributes const extractIdFromAttribute = (attr) => { const matches = attr?.match(/[^-]*-(\d+)/); return matches?.[1]; }; const extractIdFromForm = (element) => { const form = element.closest('form'); if (form) { // find the inputs name is or any attribute is id or data-id const inputs = form.querySelectorAll('input'); for (const input of inputs) { if (input.name === 'id' || input.getAttribute('id') === 'id' || input.getAttribute('data-id') === 'id') { return input.value; } } } return null; }; const potentialIds = [ extractIdFromAttribute(element.id), extractIdFromAttribute(element.className), extractIdFromForm(element), ...Array.from(element.attributes).map(attr => extractIdFromAttribute(attr.value)) ].filter(Boolean); if (potentialIds.length) { clickedProduct = colProds.find(product => potentialIds.includes(product.id.toString()) || product.variants.some(variant => potentialIds.includes(variant.id.toString())) ); } } if (!clickedProduct) { return analyzify.log('Product not found in collection product list'); } analyzify.log('Clicked product', 'an_analyzify', 'findClickedProduct'); analyzify.log(clickedProduct, 'an_analyzify', 'findClickedProduct'); return clickedProduct; } else { analyzify.log('Found element\'s href does not include a product handle.'); } } else if (element.hasAttribute('data-id')) { const prodId = element.getAttribute('data-id'); const clickedProduct = colProds.find(product => product.id.toString() === prodId.toString()); if (!clickedProduct) { return analyzify.log('Clicked product does not found in collection product list'); } return clickedProduct; } else { analyzify.log('Found element does not have an href or data-id attribute.'); } } catch (error) { console.error("Error processing findClickedProduct:", error); } }; window.analyzify.findAddedProduct = (element, colProds) => { try { if (!element) { return analyzify.log('Parent form element not found for quick view atc'); } if(!colProds) return analyzify.log('Collection products not found'); const productId = element.querySelector('.pid')?.value; const possibleIDs = element.getAttributeNames() .flatMap(name => element.getAttribute(name).match(/([0-9]+)/g)) .filter(Boolean); let addedProduct = colProds.find(product => { if (productId && product.id.toString() === productId.toString()) { return true; } else if (product.variants) { for (let i = 0; i < product.variants.length; i++) { if (possibleIDs.includes(product.variants[i].id.toString())) return true; } } return possibleIDs.includes(product.id.toString()); }); const extractIdFromForm = (element) => { const form = element.closest('form'); if (form) { const inputs = form.querySelectorAll('input'); for (const input of inputs) { if (input.name === 'id' || input.getAttribute('id') === 'id' || input.getAttribute('data-id') === 'id') { return input.value; } } } return null; }; const potentialIds = [ extractIdFromForm(element), ].filter(Boolean); if (potentialIds.length) { addedProduct = colProds.find(product => potentialIds.includes(product.id.toString()) || product.variants.some(variant => potentialIds.includes(variant.id.toString())) ); } if (!addedProduct) return analyzify.log('Parent form element found but product id did not match'); window.analyzify.addedProduct = addedProduct; return addedProduct; } catch (error) { console.error("Error processing findAddedProduct:", error); } }; window.analyzify.getVariantInput = (formElement) => { try { let variantInput = window.analyzify.getCurrentVariant()?.id; if (formElement) { const formVariantInput = Array.from(formElement.elements).find(item => item.name === 'id'); variantInput = formVariantInput ? formVariantInput.value : variantInput; } return variantInput; } catch (error) { return analyzify.log('Error getting variant input', error); } }; window.analyzify.getTypeFromTag = function (tagName) { const typeMap = { A: "link", BUTTON: "button", NAV: "layer", }; return typeMap[tagName] || "text"; }; window.processProductIDFormat = function ( userInput, product_id, variant_id, product_sku, ) { try { let output = userInput; output = output.replace("[product_id]", product_id); output = output.replace("[variant_id]", variant_id); output = output.replace("[product_sku]", product_sku); return output; } catch (error) { console.error("Error in processProductIDFormat:", error); return null; } }; console.log(`Analyzify ${window?.analyzify?.analyzify_version} is ready.`); window.analyzify.initial_load.an_analyzify = true; } catch(error) { console.error("Error in an_analyzify:", error); }