(function(shopify) {shopify.extend('WebPixel::Render', function(api) { var analytics=api.analytics,browser=api.browser,init=api.init; // Added with Analyzify v4 - "23-01-2025 10:10" // VARIABLES const px_config = { GTM: { id: "GTM-TBN6C7N" }, GADS: { feed_region: "DE", business_vertical: "retail" //constant }, flags: { page_viewed: true, //constant product_added_to_cart: false, //constant product_removed_from_cart: false, //constant checkout_started: true, //constant accelerated_checkout_started: true, //constant payment_option: true, //constant checkout_contact_info_submitted: true, //constant checkout_address_info_submitted: true, //constant checkout_shipping_info_submitted: true, //constant payment_info_submitted: true, //constant checkout_completed: true, //constant }, user_data: null, //constant }; const add_to_cart_only_collections = false; //constant const add_to_cart_only_home = false; //constant const add_to_cart_only_pdp = true; //constant const add_to_cart_only_pages = false; //constant const consentModeEnabled = "true" == "true" ? true : false; /* consent_mode Options: - true: Consent Mode enabled - false: Consent Mode disabled */ const itemName_option = null; //constant /* itemName_option Options: - item_name_alt: untranslatedTitle - null: */ const totalValue_option = null; //constant /* totalValue_option Options: - net: without shipping and taxes - no_shipping: without shipping - no_tax: without taxes - null: total price with shipping and taxes */ const debug = { status: false, dlv: true, detail: false, callback: false, color: "background:#368bca;color:#fff;", msg: "Analyzify GTM Pixel ->" } const version = "1.3.1"; const gtmENV = ""; const gtmServer = "https://www.googletagmanager.com"; window.dataLayer = window.dataLayer || []; let gtmInitialized = false; const eventQueue = []; // Functions async function GTM_init(GTM_ID, consentModeEnabled) { if (debug.status) console.log(`%c%s Initializing GTM: ${GTM_ID}`, debug.color, debug.msg); if (!gtmInitialized) { // Check if consent mode is enabled if (consentModeEnabled) { await GTM_consent_mode(); } // Load GTM script loadGtmScript(GTM_ID); gtmInitialized = true; if (debug.status) console.log('%c%s GTM_init completed successfully.', debug.color, debug.msg); } } async function loadGtmScript(GTM_ID) { // GTM script (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" }); const f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l !== "dataLayer" ? "&l=" + l : ""; j.async = true; j.src = gtmServer.trim() + "/gtm.js?id=" + i + dl + gtmENV.trim(); f.parentNode.insertBefore(j, f); })(window, document, "script", "dataLayer", GTM_ID); if (debug.status) console.log('%c%s loadGtmScript is initiated!', debug.color, debug.msg); } const initData = init?.data || {}; const initCart = initData?.cart || {}; const initStore = initData?.shop?.myshopifyDomain || {}; const initCurrency = initCart?.cost?.totalAmount?.currencyCode; async function GTM_consent_mode() { // GTAG function gtag() { window.dataLayer.push(arguments); } // Consent Mode Initialization try { const consent_version = "1.1"; let initConsent = init?.customerPrivacy || {}; let getCMPCookieValue = null; let getTrackingConsentValue = null; // Check cookies only if initConsent is not available if (!initConsent?.marketingAllowed && !initConsent?.analyticsProcessingAllowed) { try { [getCMPCookieValue, getTrackingConsentValue] = await Promise.all([ browser.cookie.get("_cmp_a"), browser.cookie.get("_tracking_consent"), ]); if (debug?.status) { console.log("%c%s Loading cookie consent values", debug.color, debug.msg, { _cmp_a: getCMPCookieValue, _tracking_consent: getTrackingConsentValue, }); } } catch (error) { console.warn("%c%s Error fetching cookies:", debug?.color, debug?.msg, error); } } if (debug?.status) { console.log("%c%s Initial Consent", debug.color, debug.msg, initConsent || { _cmp_a: getCMPCookieValue, _tracking_consent: getTrackingConsentValue }); } let currentConsent = {}; let currentConsentPurpose = {}; try { const consentCookie = decodeURIComponent(getCMPCookieValue || getTrackingConsentValue); currentConsent = JSON.parse(consentCookie) || {}; currentConsentPurpose = currentConsent?.purposes || {}; } catch (parseError) { console.warn("%c%s Error parsing consent cookie, using defaults:", debug?.color, debug?.msg, parseError); } const cmpConsent = getTrackingConsentValue?.con?.CMP || {}; const consentSettings = { ad_storage: initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ? "granted" : "denied", analytics_storage: initConsent?.analyticsProcessingAllowed || currentConsentPurpose?.a === true || (cmpConsent?.a !== undefined && cmpConsent?.a !== '0') ? "granted" : "denied", ad_user_data: initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ? "granted" : "denied", ad_personalization: initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ? "granted" : "denied", analyzify_consent: ( initConsent?.analyticsProcessingAllowed || (currentConsentPurpose?.a === true || (cmpConsent?.a !== undefined && cmpConsent?.a !== '0')) ) && ( initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ) ? "granted" : "denied", }; if (debug?.status) { console.log("%c%s Final Consent Settings", debug.color, debug.msg, consentSettings); } // Custom Event window.dataLayer.push({ event: "consentUpdate", status: 'default', ...consentSettings }); const consentObj = { ...consentSettings, wait_for_update: 500 }; // GTAG - Consent gtag("consent", "default", consentObj); if (debug.status) console.log(`%c%s Consent settings applied (v${consent_version})`, debug.color, debug.msg, consentObj); } catch (error) { if (debug.status) console.log("%c%s Error fetching or parsing consent cookie:", debug.color, debug.msg, error); } if (debug.status) console.log('%c%s GTM_consent_mode is initiated!', debug.color, debug.msg); } // Event Handling and Queue async function pushDataLayer(event, data) { if (!gtmInitialized) { if (debug.status) console.log('%c%s Queuing event:', debug.color, debug.msg, event); eventQueue.push({ event, data }); return; } try { const resolvedData = await data; // Retrieve the _shopify_sa_p cookie const get_SSAP = await browser.cookie.get('_shopify_sa_p'); let SSAP_vals = {}; if (get_SSAP) { try { const decodedValue = decodeURIComponent(get_SSAP); if (decodedValue) { const [param, ...values] = decodedValue.split('='); SSAP_vals = { cookie_param: param || null, cookie_val: values.join('=') || null, // Handles cases where '=' exists in the value }; } } catch (error) { console.error('Error decoding _shopify_sa_p cookie:', error); } } window.dataLayer.push({ event, analyzify_source: "advanced", ...resolvedData, ...SSAP_vals, eventCallback: () => { if (debug.status) console.log(`%c%s Event "${event}" pushed successfully.`, debug.color, debug.msg); }, eventTimeout: 2000, }); if (debug.status) console.log(`%c%s ${event}`, debug.color, debug.msg, data); if (debug.dlv) console.log("%c%s dataLayer", debug.color, debug.msg, window.dataLayer); } catch (error) { console.error('%c%s Error in pushDataLayer:', debug.color, debug.msg, error); } } async function processEventQueue() { while (eventQueue.length > 0) { const { event, data } = eventQueue.shift(); // Get the first event await pushDataLayer(event, data); } } async function hashValue(value) { 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; } try { 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; } } const getTotalValue = (value = 0, shipping = 0, taxes = 0) => { switch (totalValue_option) { case "net": return value - shipping - taxes; case "no_shipping": return value - shipping; case "no_tax": return value - taxes; default: return value; } }; const pageData = async (event) => { const document = event?.context?.document; if (!document) { console.warn("Document context is not available in the event:", event); return {}; // Return an empty object or default values } // Destructure properties from document.location for better readability const { href: page_location, pathname: page_path, search: query, hostname, } = document?.location || {}; const page_referrer = document?.referrer; const page_title = document?.title; // Define path type mapping const pathTypeMapping = [ { type: 'category', includes: '/collections' }, { type: 'product', includes: '/products' }, { type: 'basket', includes: '/cart' }, { type: 'homepage', equals: '/' }, { type: 'checkout', includes: ['/checkouts', '/review', 'stock_problems', 'forward', 'processing'] }, { type: 'purchase', includes: ['/thank_you', '/thank-you'] }, ]; // Determine page type const page_type = pathTypeMapping.reduce((acc, { type, includes, equals }) => { if (acc !== 'other') return acc; // If already found, skip further checks if (equals && page_path === equals) { return type; // Match found for equals } if (includes && (Array.isArray(includes) ? includes.some(i => page_path.includes(i)) : page_path.includes(includes))) { return type; // Match found for includes } return acc; // Return current accumulator value }, 'other'); // Default value // Return the structured page data return { page_title, query, hostname, page_path, page_referrer, page_location: encodeURI(page_location), page_type, store_name: initStore.replace('https://','').replace('.myshopify.com',''), store_url: initStore.startsWith('https://') ? initStore : `https://${initStore}`, }; }; const getItemObj = async (itemObj) => { if(debug.status && debug.detail) console.log('itemObj', itemObj); try { const item = itemObj.variant || itemObj.merchandise; const discountAll = itemObj?.discountAllocations?.reduce((total, discount) => { return total + Number(parseFloat(discount?.amount?.amount) || 0); }, 0) || 0; const computedDiscount = itemObj?.finalLinePrice?.amount ? parseFloat(item?.price?.amount - itemObj.finalLinePrice?.amount) : 0; const getDiscount = Number((discountAll || computedDiscount).toFixed(2)); return { item_id: item?.product?.id || itemObj.id || null, item_name: itemName_option ? item.product.untranslatedTitle : item?.product?.title || itemObj?.title || null, item_category: item?.product?.type || null, item_brand: item?.product?.vendor || null, item_sku: item?.sku || null, discount: getDiscount || 0, item_variant: item?.title || null, item_variant_id: item?.id || null, price: item?.price?.amount || itemObj?.finalLinePrice?.amount || 0, quantity: item?.quantity || itemObj?.quantity || 1, currency: item?.price?.currencyCode || itemObj?.finalLinePrice?.currencyCode || initCurrency, id: `shopify_${px_config.GADS.feed_region}_${item?.product?.id}_${item?.id}`, business_vertical: px_config.GADS.business_vertical || "retail", }; } catch (err) { console.error('%c%s Error in getItemObj:', debug.color, debug.msg, err); } }; const getCartObj = async (cartData) => { if(debug.status && debug.detail) console.log('cartData', cartData); try { const productData = await getItemObj(cartData); const userData = await getUserData({}, initData); return { ecommerce: { items: [productData], value: cartData?.cost?.totalAmount?.amount || 0, total_quantity: cartData?.quantity || 1, total_items: [productData].length || 1, currency: cartData?.cost?.totalAmount?.currencyCode || initCurrency, }, user: userData, }; } catch (err) { console.error('%c%s Error in getCartObj:', debug.color, debug.msg, err); } }; const getCheckoutObj = async (checkoutData, step = null) => { if (debug.status && debug.detail) console.log('checkoutData', checkoutData); try { // Resolve promises for all line items const productData = await Promise.all( checkoutData.lineItems.map(async (item) => { const itemData = await getItemObj(item); return { ...itemData, quantity: item?.quantity || 1, }; }) ); const userData = await getUserData(checkoutData, initData); const allDiscountCodes = checkoutData?.discountApplications .filter(discount => discount.type === 'DISCOUNT_CODE' || discount.type === 'CODE') .map(discount => discount.title) || null; const shippingTiers = checkoutData?.delivery?.selectedDeliveryOptions .filter(option => option.type === 'shipping') .map(option => option.title) || null; const lineItemPrice = Array.isArray(productData) ? productData.reduce((total, item) => { const price = parseFloat(item?.price || 0); const quantity = parseInt(item?.quantity || 1, 10); return total + (price * quantity); }, 0) : 0; const subTotal = checkoutData?.subtotalPrice?.amount || 0; const discount = checkoutData?.discountsAmount?.amount || checkoutData?.discountApplications[0]?.amount || 0; const coupon = allDiscountCodes.length > 0 ? allDiscountCodes.join(', ') : checkoutData?.discountApplications[0]?.title || null; const token = checkoutData?.token || null; return { checkout_step: step, total_value_format: totalValue_option || null, ecommerce: { items: productData, coupon: coupon, discount: (coupon == null) ? 0 : (discount > 0) ? discount : lineItemPrice, value: checkoutData?.totalPrice?.amount || 0, currency: checkoutData?.currencyCode || initCurrency, subtotal: subTotal > 0 ? subTotal : lineItemPrice || 0, token: token, shipping: checkoutData?.shippingLine?.price?.amount || 0, tax: checkoutData?.totalTax?.amount || 0, transaction_id: checkoutData?.order?.id || token || null, shipping_tier: Array.isArray(shippingTiers) ? shippingTiers.join(', ') : shippingTiers, payment_type: checkoutData?.transactions.length > 0 ? checkoutData?.transactions[0]?.gateway : null, total_quantity: getTotalQuantity(productData), total_items: productData.length || 1, }, user: userData, }; } catch (err) { console.error('%c%s Error in getCheckoutObj:', debug.color, debug.msg, err); } }; const getCommonData = async (event) => { if (!event || !event.context) { console.warn("Invalid event object. Missing context or event is undefined.", event); return {}; // Return a default empty object or handle as needed } const pageObj = await pageData(event); return { timestamp: event.timestamp, event_id: event.id, analyzify_source: "advanced", implementation_type: "custom pixel", cd: event?.clientId, ...pageObj }; }; function doesPagePath(event, paths, scope = 'contain') { const document = event.context.document; const pagePath = document.location.pathname; return paths.some((path) => { switch (scope) { case 'start': return pagePath.startsWith(path); case 'equal': return pagePath === path; case 'contain': default: return pagePath.includes(path); } }); } const getUserData = async (checkoutData, initData) => { // Extract data from init const initCustomer = initData?.customer || {}; const initEmail = initCustomer.email || initData?.email || null; const initPhone = initCustomer.phone || initData?.phone || null; // Extract data from checkoutData const checkoutCustomer = checkoutData?.order?.customer || {}; const checkoutEmail = checkoutData?.email || checkoutCustomer?.email || null; const checkoutPhone = checkoutData?.phone || checkoutCustomer?.phone || null; const billingAddress = checkoutData?.billingAddress || {}; const shippingAddress = checkoutData?.shippingAddress || {}; // Determine the primary data source for user info const email = checkoutEmail || initEmail || null; const phone = checkoutPhone || initPhone || billingAddress?.phone || shippingAddress?.phone || null; const customerId = checkoutCustomer?.id || initCustomer?.id || null; const firstName = billingAddress?.firstName || shippingAddress?.firstName || initCustomer?.firstName || null; const lastName = billingAddress?.lastName || shippingAddress?.lastName || initCustomer?.lastName || null; const street = `${billingAddress?.address1 || shippingAddress?.address1 || ''} ${billingAddress?.address2 || shippingAddress?.address2 || ''}`.trim(); // Hash email and phone const email_hashed = await hashValue(email); const phone_hashed = await hashValue(phone); const firstName_hashed = await hashValue(firstName); const lastName_hashed = await hashValue(lastName); const street_hashed = await hashValue(street); // Create the final user data object const userData = px_config.user_data === 'email' ? { email: email, sha256_email_address: email_hashed, } : { id: customerId, email_address: email, // new version email: email, // old version sha256_email_address: email_hashed, phone_number: phone, // new version phone: phone, // old version sha256_phone_number: phone_hashed, first_name: firstName, sha256_first_name: firstName_hashed, last_name: lastName, sha256_last_name: lastName_hashed, address1: billingAddress?.address1 || shippingAddress?.address1 || null, address2: billingAddress?.address2 || shippingAddress?.address2 || null, sha256_street: street_hashed, city: billingAddress?.city || shippingAddress?.city || null, country: billingAddress?.country || shippingAddress?.country || null, countryCode: billingAddress?.countryCode || shippingAddress?.countryCode || null, province: billingAddress?.province || shippingAddress?.province || null, provinceCode: billingAddress?.provinceCode || shippingAddress?.provinceCode || null, zip: billingAddress?.zip || shippingAddress?.zip || null, orders_count: initCustomer?.ordersCount || 0, }; if (debug.status && debug.detail) console.log('%c%s userData', debug.color, debug.msg, userData); return userData; }; const getTotalQuantity = (price) => price.reduce((a, b) => a + b.quantity, 0); if (debug.status) console.log(`%c%s Checkout Only Customer Events Pixel for ${px_config.GTM.id} initiated. Version: ${version}`, debug.color, debug.msg); // BUILD-IN EVENTS // GENERAL EVENTS const logEventDebug = (eventName) => { if (debug.status) { console.log(`%c%s all_events -> ${eventName}`, debug.color, debug.msg); } } analytics.subscribe("all_events", async (event) => { try { const eventHandlers = { page_viewed: async () => { try { if(px_config.flags.page_viewed){ // Check if the page is a checkout page if (doesPagePath(event, ['/checkouts', '/orders'], 'contain')) { await GTM_init(px_config.GTM.id, consentModeEnabled); const commonData = await getCommonData(event); await pushDataLayer("ee_page_view", commonData); } } } catch (error) { console.error('Error in page_viewed:', error); } }, // cartLine product_added_to_cart: async () => { try { if(px_config.flags.product_added_to_cart){ await GTM_init(px_config.GTM.id, consentModeEnabled); const pushData = async () => { const commonData = await getCommonData(event); const cartData = await getCartObj(event.data.cartLine); await pushDataLayer("ee_add_to_cart_px", { ...commonData, ...cartData }); }; // only product detail pages if( add_to_cart_only_pdp && doesPagePath(event, ['/products'], 'contain') ){ await pushData(); } // only collection pages if( add_to_cart_only_collections && doesPagePath(event, ['/collections'], 'contain') ){ await pushData(); } // only for homepage if ( add_to_cart_only_home && doesPagePath(event, ["/"], 'equal') ) { await pushData(); } // only for homepage if ( add_to_cart_only_pages && doesPagePath(event, ["/pages"], 'contain') ) { await pushData(); } } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, product_removed_from_cart: async () => { try { if(px_config.flags.product_removed_from_cart){ await GTM_init(px_config.GTM.id, consentModeEnabled); const commonData = await getCommonData(event); const cartData = await getCartObj(event.data.cartLine); await pushDataLayer("ee_remove_from_cart_px", { ...commonData, ...cartData }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, // merchandise checkout_started: async () => { try { if(px_config.flags.checkout_started){ const checkoutData = await getCheckoutObj(event.data.checkout, "started"); const commonData = await getCommonData(event); await pushDataLayer("ee_begin_checkout_px", { ...commonData, ...checkoutData }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, accelerated_checkout_started: async () => { try { if(px_config.flags.accelerated_checkout_started){ const checkoutData = await getCheckoutObj(event.data.checkout, "accelerated"); const commonData = await getCommonData(event); await pushDataLayer("ee_accelerated_checkout", { ...commonData, data: { payment_method: event.data?.acceleratedCheckout?.name || null, ...checkoutData } }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, checkout_contact_info_submitted: async () => { try { if(px_config.flags.checkout_contact_info_submitted){ const checkoutData = await getCheckoutObj(event.data.checkout, "contact"); const commonData = await getCommonData(event); await pushDataLayer("ee_add_contact_info", { ...commonData, ...checkoutData }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, checkout_address_info_submitted: async () => { try { if(px_config.flags.checkout_address_info_submitted){ const checkoutData = await getCheckoutObj(event.data.checkout, "address"); const commonData = await getCommonData(event); await pushDataLayer("ee_add_address_info", { ...commonData, ...checkoutData }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, checkout_shipping_info_submitted: async () => { try { if(px_config.flags.checkout_shipping_info_submitted){ const checkoutData = await getCheckoutObj(event.data.checkout, "shipping"); const commonData = await getCommonData(event); await pushDataLayer("ee_add_shipping_info", { ...commonData, ...checkoutData }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, payment_info_submitted: async () => { try { if(px_config.flags.payment_info_submitted){ let paymentInfoSubmittedProcessed = false; if (!paymentInfoSubmittedProcessed) { const checkoutData = await getCheckoutObj(event.data.checkout, "payment"); const commonData = await getCommonData(event); await pushDataLayer("ee_add_payment_info", { ...commonData, ...checkoutData }); paymentInfoSubmittedProcessed = true; } } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, checkout_completed: async () => { try { if(px_config.flags.checkout_completed){ await GTM_init(px_config.GTM.id, consentModeEnabled); const checkoutData = await getCheckoutObj(event.data.checkout, "thank_you"); const commonData = await getCommonData(event); await pushDataLayer("ee_purchase", { ...commonData, ...checkoutData }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, clicked: async () => { try { if( px_config.flags.payment_option && event.data.element.id === "payment_basic" ) { const commonData = await getCommonData(event); await pushDataLayer("ee_payment_option", { ...commonData, option_id: event.data?.element?.id || null, option_value: event.data?.element?.value || null }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } }, input_changed: async () => { try { if( px_config.flags.input_changed && event.data.element.id === "summary_reductions" && event.data.element.value !== '' ) { const commonData = await getCommonData(event); await pushDataLayer("ee_coupon_defined", { ...commonData, option_id: event.data?.element?.id || null, option_value: event.data?.element?.value || null }); } } catch (error) { console.error(`Error in ${event.name}:`, error); } } } if (eventHandlers[event.name]) { eventHandlers[event.name](); logEventDebug(event.name); } } catch (error) { console.error('all_standard_events error:', error); } }); });})(self.webPixelsManager.createShopifyExtend('151290124', 'custom'));