Skip to content

Instantly share code, notes, and snippets.

@danielbonifacio
Last active April 14, 2026 16:21
Show Gist options
  • Select an option

  • Save danielbonifacio/ab92ee1a62efe80820cd188e4c49db0a to your computer and use it in GitHub Desktop.

Select an option

Save danielbonifacio/ab92ee1a62efe80820cd188e4c49db0a to your computer and use it in GitHub Desktop.
anl.js
console.log('[PA] anl.js loaded');
console.log('[PA] analytics:', typeof analytics, '| init:', typeof init, '| api:', typeof api);
// Helper function to get cookie values
function getCookieValue(cookieName) {
const cookies = document.cookie.split(';').map(cookie => cookie.trim());
for (let cookie of cookies) {
if (cookie.startsWith(`${cookieName}=`)) {
return cookie.substring(cookieName.length + 1);
}
}
return null;
}
// Function to determine environment
function isStagingEnvironment() {
const previewTheme = getCookieValue('preview_theme');
const hostname = location.hostname;
// Check if cookie is set or hostname matches staging patterns
// IMPORTANT: UPDATE / ADD STAGING DOMAIN CONDITIONS
return previewTheme === '1' ||
hostname.includes('.dev') ||
hostname.includes('-dev') ||
hostname.includes('.staging') ||
hostname.includes('-staging')
}
function gtmScriptInstall() {
if (isStagingEnvironment()) {
// IMPORTANT: UPDATE GTM Container ID, gtm_auth, & gtm_preview VALUES
// Google Tag Manager Install Script (gtm.js) | STAGING
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl+ '&gtm_auth=XXXXXXXXXXXXXXXXXXXXXX&gtm_preview=env-XXX&gtm_cookies_win=x';f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');
window.dataLayer = window.dataLayer || [];
}
else {
// IMPORTANT: UPDATE GTM Container ID, gtm_auth, & gtm_preview VALUES
// Google Tag Manager Install Script (gtm.js) | PRODUCTION
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl+ '&gtm_auth=XXXXXXXXXXXXXXXXXXXXXX&gtm_preview=env-X&gtm_cookies_win=x';f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');
window.dataLayer = window.dataLayer || [];
}
}
function generateUniqueId(microseconds) {
// Get the current timestamp in milliseconds
const timestamp = Date.now();
// Get the high-resolution time in microseconds
const highResTime = (performance.now() || 0) * microseconds;
// Combine both parts to form a unique ID
const uniqueId = `${timestamp}-${Math.floor(highResTime)}`;
return uniqueId;
}
function pushToDataLayer(eventName, eventDetails, items, event_data) {
try {
if(eventName == 'page_loaded'){
window.dataLayer.push({ page_details: null });
window.dataLayer.push({
event: eventName,
page_details: eventDetails
});
}
else {
window.dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object.
window.dataLayer.push({
event: eventName,
ecommerce: {
event_details: eventDetails,
user_details: userDetails(),
items: items,
event_data: event_data
}
});
}
console.log(`--- DataLayer | Customer Event: ${eventName} ---`, window.dataLayer);
} catch (error) {
console.error(`DataLayer | Customer Event: Failed to push ${eventName} event:`, error);
}
}
function pushToMainWindow(eventName, eventDetails, items, event_data, origin) {
try {
if(eventName == 'page_loaded'){
parent.postMessage({'message': 'shopify_pixel_event',
'event_name': eventName,
'json': JSON.stringify(eventDetails)
}, origin);
console.log(`--- DataLayer | Customer Event: ${eventName} ---`, eventDetails);
}
else if (eventName == 'shopify_consent_updated'){
const consent_data = {
consent_data_booleans: eventDetails?.consent_data_booleans,
consent_data_values: eventDetails?.consent_data_values,
};
parent.postMessage({'message': 'shopify_pixel_event',
'event_name': eventName,
'json': JSON.stringify(consent_data)
}, origin);
console.log(`--- DataLayer | Customer Event: ${eventName} ---`, consent_data);
}
else{
const ecommerce_data = {
event_details: eventDetails,
user_details: userDetails(),
items: items,
event_data: event_data
};
parent.postMessage({'message': 'shopify_pixel_event',
'event_name': eventName,
'json': JSON.stringify(ecommerce_data)
}, origin);
console.log(`--- DataLayer | Customer Event: ${eventName} ---`, ecommerce_data);
}
} catch (error) {
console.error(`DataLayer | Customer Event: Failed to push ${eventName} event:`, error);
}
}
function userDetails() {
let user_data = null; // Default value
try {
const storedData = localStorage.getItem("gtm_user_data");
user_data = JSON.parse(storedData);
} catch (error) {
console.error("Failed to parse user data from localStorage:", error);
// user_data remains null or you can handle the error as needed
}
const user_details = {
log_state: user_data?.em != null && user_data?.em != '' ? "Logged In" : "Logged Out", // User Account Status | Logged In or Logged Out
user_id: user_data?.userId || null, // User Account ID
// GOOGLE ADS ENHANCED CONVERSION
enhanced_conversion: {
email: user_data?.em || null, // Return the users email address
phone_number: user_data?.ph || null, // Return the users phone number
first_name: user_data?.fn || null, // Return the users first name
last_name: user_data?.ln || null, // Return the users last name
street: user_data?.street || null, // Return the users street address
city: user_data?.ct || null, // Return the users City
state: user_data?.st || null, // Return the users state
zip_code: user_data?.zp || null, // Return the users postal zip code
country: user_data?.country || null // Return the users country
},
};
return user_details;
}
function checkoutUserData(checkout) {
const shippingAddress = checkout?.shippingAddress || {};
var fn = shippingAddress?.firstName || null;
var ln = shippingAddress?.lastName || null;
var ctry = shippingAddress?.country || null;
var street = shippingAddress?.address1 || null;
var ct = shippingAddress?.city || null;
var st = shippingAddress?.province || null;
var zp = shippingAddress?.zip || null;
var em = checkout?.email || null;
var ph = shippingAddress?.phone || null;
var userId = checkout?.order?.customer?.id || null;
// Safely parse localStorage data with a try/catch block
let existingUserData;
try {
existingUserData = JSON.parse(localStorage.getItem("gtm_user_data")) || {};
} catch (error) {
console.error("Error parsing gtm_user_data from localStorage", error);
existingUserData = {}; // Fall back to an empty object if there's an error
}
// only update cookie with missing data
//Set Key Naming Convention and value after checking
const userActualData = {
fn: fn,
ln: ln,
street: street,
ct: ct,
st: st,
zp: zp,
country: ctry,
em: em,
ph: ph,
userId: userId
};
// Merge only non-null and changed values into existingUserData
let updatedUserData = Object.assign({}, existingUserData);
Object.keys(userActualData).forEach(key => {
if (userActualData[key] !== null && userActualData[key] !== undefined && userActualData[key] !== existingUserData[key]) {
updatedUserData[key] = userActualData[key]; // Update only the changed and non-null fields
}
});
// If there are changes, update localStorage
if (JSON.stringify(updatedUserData) !== JSON.stringify(existingUserData)) {
localStorage.setItem("gtm_user_data", JSON.stringify(updatedUserData));
console.log("checkoutUserData: Updated Fields")
} else {
console.log("checkoutUserData: No valid data to update.");
}
}
function getContentGroup(event_data){
const event_location = event_data?.context?.document?.location || null;
const page_title = event_location?.title || null;
const page_path = event_location?.pathname || null;
const contentGroups = [
{ pattern: /^\/$/, content_group: 'Home' }, // Exact match for home page
{ pattern: /\/search/, content_group: 'Search Product Listing' },
{ pattern: /\/products/, content_group: 'Product Detail' },
{ pattern: /\/pages\/fragrances|\/pages\/gift-guide/, content_group: 'Product Category' },
{ pattern: /\/collections|\/bundles|\/sale|\/clean|\/shop/, content_group: 'Product Listing' },
{ pattern: /\/cart/, content_group: 'Cart' },
{ pattern: /(?=.*thank-you)(?=.*checkout)/, content_group: 'Confirmation' },
{ pattern: /orders/, content_group: 'Orders' },
{ pattern: /checkouts/, content_group: 'Checkout' },
{ pattern: /blog/, content_group: 'Blog' },
{ pattern: /\/account/, content_group: 'Account' },
{ pattern: /\/login/, content_group: 'Account' },
{ pattern: /\/about-us/, content_group: 'About Us' },
{ pattern: /\/leadership/, content_group: 'About Us' },
{ pattern: /\/pages/, content_group: 'Company Pages' },
{ titleCondition: title => title.includes('404 Not Found'), content_group: '404 Page Result' },
// Add more patterns as needed
];
// Find the first match based on page_path and optional title conditions
const match = contentGroups.find(({ pattern, titleCondition }) =>
(!pattern || pattern.test(page_path)) && (!titleCondition || titleCondition(page_title))
);
return match ? match.content_group : 'Other';
}
function getEventDetails(event_data) {
const event_name = event_data?.name;
const event_main = event_data;
const event_data_section = event_data?.data;
const checkout = event_data?.data?.checkout;
let content_group = getContentGroup(event_main);
const subtotal_amount = Math.round((Number(checkout?.subtotalPrice?.amount) || 0) * 100) / 100;
const order_discount = Math.round((Number(checkout?.discountsAmount?.amount) || 0) * 100) / 100;
const shipping_amount = Math.round((Number(checkout?.shippingLine?.price?.amount) || 0) * 100) / 100;
const tax_amount = Math.round((Number(checkout?.totalTax?.amount) || 0) * 100) / 100;
const order_total = Math.round((Number(checkout?.totalPrice?.amount) || 0) * 100) / 100;
const shipping_discount = checkout?.delivery?.selectedDeliveryOptions?.map(option => (option?.cost?.amount || 0) - (option?.costAfterDiscounts?.amount || 0)).reduce((sum, discount) => sum + discount, 0) || 0;
let event_id = event_main?.id || null;
let client_id = event_main?.clientId || null;
let timestamp = event_main?.timestamp || null;
let environment = isStagingEnvironment() === true ? 'Staging': 'Production';
let search_term = event_data_section?.searchResult?.query || null;
let token = checkout?.token || event_data_section?.cart?.id || null;
let coupon = checkout?.discountApplications?.map(application => application?.title).filter(title => title).join(",") || null;
const eventConfig = {
checkout_started: {
checkout_section: "Step 1: Checkout Started",
coupon: coupon,
discount: order_discount,
value: subtotal_amount,
currency: checkout?.currencyCode || "USD",
token: token,
cart_qty: checkout?.lineItems?.reduce((sum, lineItem) => sum + (lineItem?.quantity || 0), 0) || 0,
sku_count: checkout?.lineItems.length,
},
checkout_contact_info_submitted: {
checkout_section: "Step 2: Contact Info Submitted",
email_marketing: checkout?.buyerAcceptsEmailMarketing === undefined ? null : (checkout.buyerAcceptsEmailMarketing ? 'Approved' : 'Not Approved'),
coupon: coupon,
discount: order_discount,
value: subtotal_amount,
currency: checkout?.currencyCode || "USD",
token: token,
cart_qty: checkout?.lineItems?.reduce((sum, lineItem) => sum + (lineItem?.quantity || 0), 0) || 0,
sku_count: checkout?.lineItems.length,
},
checkout_shipping_info_submitted: {
checkout_section: "Step 3: Shipping Method Completed",
shipping_tier: checkout?.delivery?.selectedDeliveryOptions?.map(deliveryOption => deliveryOption?.title).filter(title => title).join(",") || null,
coupon: coupon,
discount: order_discount,
value: subtotal_amount,
currency: checkout?.currencyCode || "USD",
token: token,
cart_qty: checkout?.lineItems?.reduce((sum, lineItem) => sum + (lineItem?.quantity || 0), 0) || 0,
sku_count: checkout?.lineItems.length,
},
payment_info_submitted: {
checkout_section: "Step 4: Payment Info Completed",
payment_type: checkout?.transactions?.[0]?.paymentMethod?.name || null,
payment_method: checkout?.transactions?.[0]?.paymentMethod?.type || null,
coupon: coupon,
discount: order_discount,
value: subtotal_amount,
currency: checkout?.currencyCode || "USD",
token: token,
cart_qty: checkout?.lineItems?.reduce((sum, lineItem) => sum + (lineItem?.quantity || 0), 0) || 0,
sku_count: checkout?.lineItems.length,
},
checkout_completed: {
payment_type: checkout?.transactions?.[0]?.paymentMethod?.name || null,
payment_method: checkout?.transactions?.[0]?.paymentMethod?.type || null,
transaction_id: checkout?.order?.id || null,
value: order_total,
subtotal: subtotal_amount,
shipping: shipping_amount,
shipping_discount: shipping_discount || null,
tax: tax_amount,
discount: order_discount,
shipping_tier: checkout?.delivery?.selectedDeliveryOptions?.map(deliveryOption => deliveryOption?.title).filter(title => title).join(",") || null,
email_marketing: checkout?.buyerAcceptsEmailMarketing === undefined ? null : (checkout.buyerAcceptsEmailMarketing ? 'Approved' : 'Not Approved'),
coupon: coupon,
currency: checkout?.currencyCode || "USD",
token: token,
cart_qty: checkout?.lineItems?.reduce((sum, lineItem) => sum + (lineItem?.quantity || 0), 0) || 0,
sku_count: checkout?.lineItems.length,
},
search_submitted: {
collection_id: "Search Results",
collection_name: "Search Product Listing",
search_term: search_term,
},
collection_viewed: {
collection_id: event_data_section?.collection?.id,
collection_name: event_data_section?.collection?.title,
},
product_viewed: {
value: event_data_section?.productVariant?.price?.amount,
currency: event_data_section?.productVariant?.price?.currencyCode || 'USD',
},
product_added_to_cart: {
value: event_data_section?.cartLine?.cost?.totalAmount?.amount,
currency: event_data_section?.cartLine?.cost?.totalAmount?.currencyCode,
search_term: search_term,
},
product_removed_from_cart: {
value: event_data_section?.cartLine?.cost?.totalAmount?.amount,
currency: event_data_section?.cartLine?.cost?.totalAmount?.currencyCode,
},
cart_viewed: {
value: event_data_section?.cart?.cost?.totalAmount?.amount,
currency: event_data_section?.cart?.cost?.totalAmount?.currencyCode,
cart_qty: event_data_section?.cart?.totalQuantity,
sku_count: event_data_section?.cart?.lines.length,
},
};
let event_details = {
event_id: event_id,
timestamp: timestamp,
environment: environment,
client_id: client_id,
content_group: content_group,
};
// Merge the common fields with event-specific fields
if (eventConfig[event_name]) {
Object.assign(event_details, eventConfig[event_name]);
}
return event_details;
}
// Define categorization rules for each level
// IMPORTANT: UPDATE ITEM CATEGORY CONDITIONS BASED ON APP UI SETTINGS
const categoryRules = [
{ // Product Category
level: "item_category",
options: [
{
name: "Beauty",
keywords: [
"bath", "body", "body scrubs", "body scrub", "body serums", "body serum",
"body wash", "body oil", "bubble bath", "body lotions", "body lotion",
"body creams", "body cream", "hand lotion", "hand cream", "lotion",
"perfume", "parfum", "hair mist", "personal care", "sink set", "personal fragrance",
"lip balm", "deodorant", "shampoo powder", "shave cream", "hand wash refills", "hand wash refill",
"hand wash", "hand soap", "bar soap"
]
},
{
name: "Home Fragrance",
keywords: [
"candles", "candle", "diffuser", "diffuser oil refills", "diffuser oil refill",
"diffuser oil", "diffuser reed refills", "reed refill", "reed refill for diffusers",
"diffuser refill", "pura diffuser refill", "pura car pro diffuser refill",
"pura car diffuser refill", "refill for diffuser", "pura smart home diffuser kit",
"pura v4 smart home diffuser kit", "pura car pro diffuser kit",
"pura car diffuser kit", "car diffusers", "car diffuser", "car pro diffuser",
"pura car diffuser", "pura car pro diffuser", "diffusers", "room spray", "room sprays"
]
},
{
name: "Home Care",
keywords: [
"cleaner", "tissue paper", "linen mist", "linen spray", "laundry detergent + fabric softener", "laundry detergent",
"laundry fragrance oil", "dryer ball", "scent booster", "wrinkle release spray",
"laundry", "fabric softener", "mixed set",
"dish soap", "soap", "surface wipes", "multi-surface cleaner",
"cleaning concentrate", "signature home care"
]
},
{
name: "Gifts",
keywords: ["gift wrap", "gift tags", "wrapping paper", "gift sets", "gift set"]
}
]
},
{ // Product Type
level: "item_category2",
options: [
{ name: "Body Scrubs", keywords: ["body scrubs", "body scrub"] },
{ name: "Body Serums", keywords: ["body serums", "body serum"] },
{ name: "Body Wash", keywords: ["body wash"] },
{ name: "Body Oil", keywords: ["body oil"] },
{ name: "Bubble Bath", keywords: ["bubble bath"] },
{ name: "Candles", keywords: ["candles", "candle"] },
{ name: "Cleaner", keywords: ["cleaner"] },
{ name: "Diffuser", keywords: ["diffuser"] },
{ name: "Gift Wrap", keywords: ["tissue paper", "gift tags", "wrapping paper"] },
{ name: "Linen Mist", keywords: ["linen mist", "linen spray"] },
{ name: "Body Lotions & Creams", keywords: ["body lotions", "body lotion", "body creams", "body cream"] },
{ name: "Hand Lotions & Creams", keywords: ["hand lotion", "hand cream"] },
{ name: "Lotions", keywords: ["lotion"] },
{ name: "Lip Balm", keywords: ["lip balm"] },
{ name: "Perfume", keywords: ["perfume", "parfum"] },
{ name: "Deodorant", keywords: ["deodorant"] },
{ name: "Shaving Cream", keywords: ["shave cream"] },
{ name: "Dry Shampoo Powder", keywords: ["dry shampoo powder"] },
{ name: "Hair Mist", keywords: ["hair mist"] },
{ name: "Hand Wash Refills", keywords: ["hand wash refills", "hand wash refill"] },
{ name: "Hand Wash", keywords: ["hand wash", "hand soap"] },
{ name: "Diffuser Oil Refills", keywords: ["diffuser oil refills", "diffuser oil refill", "diffuser oil"] },
{ name: "Diffuser Reed Refills", keywords: ["diffuser reed refills", "reed refill", "reed refill for diffusers"] },
{ name: "Diffuser Refills", keywords: ["pura diffuser refill", "pura car pro diffuser refill", "pura car diffuser refill", "diffuser refill", "refill for diffuser"] },
{ name: "Diffuser Kits", keywords: ["pura smart home diffuser kit", "pura v4 smart home diffuser kit", "pura car pro diffuser kit", "pura car diffuser kit"] },
{ name: "Car Diffusers", keywords: ["car diffusers", "car diffuser", "car pro diffuser", "pura car diffuser", "pura car pro diffuser"] },
{ name: "Diffusers", keywords: ["diffusers", "diffuser"] },
{ name: "Laundry Detergent & Fabric Softener", keywords: ["laundry detergent + fabric softener"] },
{ name: "Laundry Detergent", keywords: ["laundry detergent"] },
{ name: "Laundry Fragrance Oil", keywords: ["laundry fragrance oil"] },
{ name: "Dryer Balls", keywords: ["dryer ball"] },
{ name: "Scent Booster", keywords: ["scent booster"] },
{ name: "Wrinkle Release Spray", keywords: ["wrinkle release spray"] },
{ name: "Laundry Care", keywords: ["laundry"] },
{ name: "Fabric Softener", keywords: ["fabric softener"] },
{ name: "Gift Sets", keywords: ["gift sets", "gift set"] },
{ name: "Gift Wrap", keywords: ["gift wrap", "wrapping paper", "giftwrap"] },
{ name: "Gift Tags", keywords: ["gift tags"] },
{ name: "Room Sprays", keywords: ["room spray"] },
{ name: "Sink Set", keywords: ["sink set"] },
{ name: "Mixed Set", keywords: ["mixed set"] },
{ name: "Dish Soap", keywords: ["dish soap"] },
{ name: "Bar Soap", keywords: ["bar soap"] },
{ name: "Soap", keywords: ["soap"] },
{ name: "Surface Wipes", keywords: ["surface wipes"] },
{ name: "Multi-Surface Cleaner", keywords: ["multi-surface cleaner"] },
{ name: "Cleaning Concentrate", keywords: ["cleaning concentrate"] },
{ name: "Personal Care", keywords: ["personal care"] },
{ name: "Personal Fragrance", keywords: ["personal fragrance"] },
{ name: "Signature Home Care", keywords: ["signature home care"] },
{ name: "Bath & Body", keywords: ["bath", "body"] },
]
},
{ // Fragrance
level: "item_category3",
options: [
{ name: "Aloha Orchid", keywords: ["aloha orchid"] },
{ name: "Alpine Juniper", keywords: ["alpine juniper"] },
{ name: "Apple Cider Social", keywords: ["apple cider social"] },
{ name: "Blue Jean", keywords: ["blue jean"] },
{ name: "Chestnut Embers", keywords: ["chestnut embers"] },
{ name: "Citrus & Violet Haze", keywords: ["citrus & violet haze"] },
{ name: "Coconut Santal", keywords: ["coconut santal"] },
{ name: "Crystal Pine", keywords: ["crystal pine"] },
{ name: "Exotic Blossom & Basil", keywords: ["exotic blossom & basil"] },
{ name: "Frosted Fireside", keywords: ["frosted fireside"] },
{ name: "Guava Blossom", keywords: ["guava blossom"] },
{ name: "Havana Vanilla", keywords: ["havana vanilla"] },
{ name: "Honeydew Crush", keywords: ["honeydew crush"] },
{ name: "Multiple", keywords: ["tinsel & spice & volcano", "pumpkin dulce & volcano", "glimmer holiday gift set"] },
{ name: "Paris Blue", keywords: ["paris blue"] },
{ name: "Paris", keywords: ["paris"] },
{ name: "Pink Grapefruit & Prosecco", keywords: ["pink grapefruit & prosecco"] },
{ name: "Pineapple Flower", keywords: ["pineapple flower"] },
{ name: "Pumpkin Dulce", keywords: ["pumpkin dulce"] },
{ name: "Smoked Clove & Tabac", keywords: ["smoked clove & tabac"] },
{ name: "Tinsel & Spice", keywords: ["tinsel & spice"] },
{ name: "Volcano", keywords: ["volcano"] },
{ name: "Watery Moon", keywords: ["watery moon"] },
{ name: "Wild Citron", keywords: ["wild citron"] }
]
},
{ // Collection
level: "item_category4",
options: [
{ name: "Neutrals - Gilded Muse", keywords: ["glided"] },
{ name: "Neutrals - Mercury Iridescent", keywords: ["mercury iridescent"] },
{ name: "Seasonal - Glam", keywords: ["glam"] },
{ name: "Seasonal - Glimmer", keywords: ["glimmer"] },
{ name: "Seasonal - Glitz", keywords: ["glitz"] },
{ name: "Seasonal - Holiday Mercury", keywords: ["holiday mercury", "mercury holiday"] },
{ name: "Seasonal - Holiday Pattern Play", keywords: ["holiday pattern play"] },
{ name: "Seasonal - Volcano Gifting", keywords: ["volcano fragranced", "volcano scented"] },
{ name: "Bold & Colorful - Modern Marble", keywords: ["modern marble"] },
{ name: "Bold & Colorful - Dual Tone", keywords: ["dual tone"] },
{ name: "Bold & Colorful - Pattern Play", keywords: ["pattern play"] },
{ name: "Bold & Colorful - Watercolor", keywords: ["watercolor"] },
{ name: "Signature - CB X Pura", keywords: ["cb+ pura", "cb + pura"] },
{ name: "Signature - Signature Beauty Care", keywords: ["parfum", "hand wash", "body cream", "hand cream", "body oil", "body wash", "lotion",
"lip balm", "body scrub", "bar soap", "deodorant", "body serum", "shampoo powder", "gift set", "hair mist", "shave cream", "sink set"] },
{ name: "Signature - Signature Home Care", keywords: ["cleaner", "tissue paper", "linen mist", "linen spray",
"hand wash", "hand soap", "laundry detergent + fabric softener", "laundry detergent", "laundry fragrance oil", "dryer ball", "scent booster", "wrinkle release spray",
"laundry", "fabric softener", "mixed set", "dish soap", "soap", "surface wipes", "multi-surface cleaner",
"cleaning concentrate", "signature home care"] },
{ name: "Signature - Home Fragrance", keywords: ["signature","volcano white", "volcano blue", "volcano diffuser", "blue jean", "havana vanilla",
"aloha orchid diffuser", "volcano black", "volcano printed", "volcano bubblegum", "aloha orchid blue", "aloha orchid printed", "paris blue", "pineapple flower",
"volcano digital", "guava blossom", "volcano boxed tumbler", "aloha orchid", "coconut santal", "paris printed", "honeydew crush" ] },
]
},
{ // Size of Product
level: "item_category5",
options: [
{ name: "0.5 fl oz", keywords: ["0.5 fl oz"] },
{ name: "1 oz", keywords: ["1 oz"] },
{ name: "1.6 fl oz", keywords: ["1.6 fl oz"] },
{ name: "10 fl oz", keywords: ["10 fl oz"] },
{ name: "12 fl oz", keywords: ["12 fl oz"] },
{ name: "15 fl oz", keywords: ["15 fl oz"] },
{ name: "16 fl oz", keywords: ["16 fl oz"] },
{ name: "19 oz", keywords: ["19 oz"] },
{ name: "3 oz", keywords: ["3 oz"] },
{ name: "3.4 oz", keywords: ["3.4 oz"] },
{ name: "30 oz", keywords: ["30 oz"] },
{ name: "4 oz", keywords: ["4 oz"] },
{ name: "48 oz", keywords: ["48 oz"] },
{ name: "5.7 fl oz", keywords: ["5.7 fl oz"] },
{ name: "6 fl oz", keywords: ["6 fl oz"] },
{ name: "7.75 fl oz", keywords: ["7.75 fl oz"] },
{ name: "8 fl oz", keywords: ["8 fl oz"] },
{ name: "8 oz", keywords: ["8 oz"] },
{ name: "8.5 oz", keywords: ["8.5 oz"] },
{ name: "10 oz", keywords: ["10 oz"] },
{ name: "11 oz", keywords: ["11 oz"] },
{ name: "28 oz", keywords: ["28 oz"] },
{ name: "2-Pack Bundle", keywords: ["2-pack bundle"] },
{ name: "Travel Size", keywords: ["travel size", "travel set"] },
{ name: "Sample", keywords: ["sample"] },
{ name: "Refill", keywords: ["refill", "refills"] },
{ name: "Bundle", keywords: ["bundle"] },
{ name: "Set", keywords: ["set", "gift"] },
{ name: "Kit", keywords: ["kit"] },
{ name: "Tin", keywords: ["tin"] },
{ name: "Jar", keywords: ["jar"] },
{ name: "Pouch", keywords: ["pouch"] }
]
}
];
// Function to categorize product based on item name
function categorizeProduct(itemName) {
const categorizedItem = {};
const lowerItemName = itemName.toLowerCase(); // Ensure case-insensitivity
categoryRules.forEach(rule => {
// Find all matches for the given level
const matches = rule.options
.filter(option => option.keywords.some(keyword => lowerItemName.includes(keyword)))
.map(option => option.name);
/* OPTION #1: Set one category */
categorizedItem[rule.level] = matches.length > 0 ? matches[0] : undefined;
/* OPTION #2: Option for multiple categories if desired
if (rule.level === "item_category") {
categorizedItem[rule.level] = matches.length > 0 ? matches[0] : "Other";
} else {
categorizedItem[rule.level] = matches.length > 0 ? matches.join(", ") : null;
}
*/
});
return categorizedItem;
}
function getItemsData(event_data) {
const event_name = event_data?.name;
const referrer = event_data?.context?.document?.referrer || '';
const page_path = event_data?.context?.document?.location?.pathname || '';
const search_result_products = event_data?.data?.searchResult?.productVariants || []; // array
const collection_products = event_data?.data?.collection?.productVariants || []; // Collection Viewed
const pdp_product = event_data?.data.productVariant || {};
const cart_line = event_data?.data?.cart?.lines || {}; // Cart Viewed,
const atc_merchandise = event_data?.data?.cartLine?.merchandise || {}; // ATC for products
const atc_cartline = event_data?.data?.cartLine || {}; // Top level for quantity
const checkout = event_data?.data?.checkout || {};
const lineItems = checkout?.lineItems || []; // Array of checkout items
const item_collection = event_data?.data?.collection?.title || null;
const item_collection_id = event_data?.data?.collection?.id || null;
let items_array = [];
const event_location = getContentGroup(event_data);
let item_list_name = null;
let item_list_id = null;
function getListName(item_variant) {
if (event_name === "search_submitted") {
item_list_name = 'Search Results';
item_list_id = 'Search Product Listing';
} else if (event_name === "collection_viewed") {
item_list_name = 'Site Browsing';
item_list_id = 'Product Listing';
} else if (event_name === "product_viewed") {
if (page_path.includes('/search') || referrer.includes('/search')) {
item_list_name = 'Search Results';
item_list_id = 'Search Product Listing';
} else if (referrer.includes('/collections/')) {
item_list_name = 'Site Browsing';
item_list_id = 'Product Listing';
} else {
item_list_name = 'Site Browsing';
item_list_id = 'Product Detail';
}
} else if (event_name === "product_added_to_cart") {
if (page_path == '/'){
item_list_name = 'Site Browsing';
item_list_id = 'Home';
} else if (page_path.includes('/search') || referrer.includes('/search')) {
item_list_name = 'Search Results';
item_list_id = 'Search Product Listing';
} else if ((page_path.includes('/collections/') && !page_path.includes('/products/')) || referrer.includes('/collections/')) {
item_list_name = 'Site Browsing';
item_list_id = 'Product Listing';
} else {
item_list_name = 'Site Browsing';
item_list_id = 'Product Detail';
}
// Store both item_list_name and item_list_id in sessionStorage with item_id as the key
const storedData = JSON.parse(sessionStorage.getItem('itemListNames') || '{}');
storedData[item_variant] = {
item_list_name: item_list_name,
item_list_id: item_list_id
};
sessionStorage.setItem('itemListNames', JSON.stringify(storedData));
} else {
const itemListNames = JSON.parse(sessionStorage.getItem('itemListNames') || '{}');
item_list_name = itemListNames[item_variant]?.item_list_name || 'Site Browsing';
item_list_id = itemListNames[item_variant]?.item_list_id || 'Product Detail';
}
return { item_list_name, item_list_id };
}
// Helper function to create product line items with common structure
function createProductLine(item, index = 0) {
const product = item?.product || item;
const variant = item?.variant || item;
// Categorize the product based on its title
const categories = categorizeProduct(product?.title || "");
const { item_list_name, item_list_id } = getListName(variant?.id || null);
const calculatedPrice = (item?.finalLinePrice?.amount ?? cart_line[index]?.cost?.totalAmount?.amount ?? null) >= 0 && (item?.quantity || cart_line[index]?.quantity) ? (item?.finalLinePrice?.amount ?? cart_line[index]?.cost?.totalAmount?.amount) / (item?.quantity || cart_line[index]?.quantity) : null;
const itemDiscountFromAllocations = item?.quantity ? (item?.discountAllocations?.reduce((sum, app) => sum + parseFloat(app?.amount?.amount || 0),0) || 0) / item?.quantity : 0;
const calculatedDiscount = itemDiscountFromAllocations > 0 ? itemDiscountFromAllocations : (calculatedPrice != null && variant?.price?.amount != null && calculatedPrice != variant?.price?.amount) ? (variant?.price?.amount - calculatedPrice) : 0;
// Define item structure with default values and apply categories
let product_line = {
item_id: variant?.product?.id || product?.id || null,
item_name: product?.title || null,
affiliation: "Shopify Store",
currency: variant?.price?.currencyCode || 'USD',
coupon: item?.discountAllocations?.map(allocation => allocation?.discountApplication?.title).filter(title => title).join(",") || null,
discount: calculatedDiscount,
index: index,
item_brand: product?.vendor || variant?.product?.vendor || null,
item_category: categories.item_category || null,
item_category2: categories.item_category2 || product?.type || null,
item_category3: categories.item_category3 || null,
item_category4: categories.item_category4 || null,
item_category5: categories.item_category5 || null,
item_variant: variant?.id || null,
item_list_id: item_list_id,
item_list_name: item_list_name,
location_id: event_location,
price: calculatedPrice ?? variant?.price?.amount ?? null,
quantity: item?.quantity || atc_cartline?.quantity || cart_line[index]?.quantity || 1,
item_sku: variant?.sku || null,
item_shopify_id: `${variant?.product?.id || product?.id || ''}_${variant?.id || ''}`,
item_image: variant?.image?.src ? variant?.image?.src.startsWith('http') ? variant?.image?.src : 'https:' + variant.image.src : null,
item_collection: item_collection,
item_collection_id: item_collection_id
};
return product_line;
}
// Process items based on event_name
if (event_name === 'product_added_to_cart' || event_name === 'product_removed_from_cart') {
if (atc_merchandise) {
// Single cart line item
items_array.push(createProductLine(atc_merchandise));
}
} else if (event_name === 'cart_viewed') {
if (Array.isArray(cart_line) && cart_line.length > 0) {
cart_line.forEach((product, index) => {
items_array.push(createProductLine(product.merchandise, index));
});
}
} else if (event_name === 'search_submitted') {
if (Array.isArray(search_result_products) && search_result_products.length > 0) {
search_result_products.forEach((product, index) => {
items_array.push(createProductLine(product, index));
});
}
} else if (event_name === 'collection_viewed') {
if (Array.isArray(collection_products) && collection_products.length > 0) {
collection_products.forEach((product, index) => {
items_array.push(createProductLine(product, index));
});
}
} else if (event_name === 'product_viewed') {
if (pdp_product) {
// Single product detail page item
items_array.push(createProductLine(pdp_product));
}
} else if (event_name === 'checkout_started' || event_name === 'checkout_completed' || event_name === 'checkout_contact_info_submitted' || event_name === 'checkout_shipping_info_submitted' || event_name === 'payment_info_submitted') {
if (Array.isArray(lineItems) && lineItems.length > 0) {
lineItems.forEach((item, index) => {
items_array.push(createProductLine(item, index));
});
}
}
return items_array;
}
// Initialize customer privacy status
let customerPrivacyStatus = init.customerPrivacy;
// Use the Customer Privacy Standard API to subscribe to consent collected events
api.customerPrivacy.subscribe('visitorConsentCollected', (event) => {
const origin = window?.location?.origin;
customerPrivacyStatus = event.customerPrivacy;
const consent_data = {
consent_data_booleans: {
functional: true,
analytics: customerPrivacyStatus.analyticsProcessingAllowed,
marketing:customerPrivacyStatus.marketingAllowed,
},
consent_data_values: {
functional: 'granted',
analytics: customerPrivacyStatus.analyticsProcessingAllowed === true ? 'granted' : 'denied',
marketing: customerPrivacyStatus.marketingAllowed === true ? 'granted' : 'denied',
}
};
pushToMainWindow("shopify_consent_updated", consent_data, null, null, origin)
});
//page_viewed
function pageLoaded(event_data, event_id) {
const page_details = {
client_id: event_data?.clientId,
timestamp: event_data?.timestamp,
environment: isStagingEnvironment() === true ? 'Staging': 'Production', // Staging or Production
page_path: event_data?.context?.document?.location?.pathname || null, // Page Path
page_url: event_data?.context?.document?.location?.href || null, // Full Page Path
content_group: getContentGroup(event_data), // Page Type
event_id: event_id || 0, // Unique Event ID
user_details: userDetails() // User Data Object | Enhanced Conversion
};
return page_details;
}
// Shopify: page_viewed | GA4: page_loaded
analytics.subscribe("page_viewed", (event) => {
console.log('[PA] >>> page_viewed', event?.context?.document?.location?.pathname);
const event_data = event;
const event_id = event?.id;
const page_path = event?.context?.document?.location?.pathname;
const origin = event?.context?.document?.location?.origin;
if(!page_path.includes('/checkouts/')){
const page_details = pageLoaded(event_data, event_id);
pushToMainWindow('page_loaded', page_details, null, null, origin);
}
});
// Shopify: checkout_completed | GA4: purchase
analytics.subscribe('checkout_completed', (event) => {
console.log('[PA] >>> checkout_completed');
const event_data = event;
const event_id = generateUniqueId(200000000);
const checkout = event?.data?.checkout;
// Check if GTM (gtm.js) has been initialized by looking for the 'gtm.js' event in dataLayer
if (Array.isArray(window.dataLayer) && window.dataLayer.some(e => e.event === 'gtm.js')) {
// gtm.js has been initialized, so push the event only
//console.log("GTM has been initialized already.");
}
else {
//console.log("GTM has not been initialized yet.");
gtmScriptInstall();
}
const page_details = pageLoaded(event_data, event_id);
pushToDataLayer('page_loaded', page_details, null, null);
checkoutUserData(checkout);
let items = getItemsData(event_data);
let event_details = getEventDetails(event_data);
// Console Log | Event Data for Review
//console.log(`--- DataLayer | Customer Event: checkout_completed ---`, event_data);
pushToDataLayer("purchase", event_details, items, event_data);
});
// Shopify: checkout_started | GA4: begin_checkout
// Checkout Step 1
analytics.subscribe('checkout_started', async (event) => {
console.log('[PA] >>> checkout_started');
const event_data = event;
const event_id = generateUniqueId(100000000);
const checkout = event?.data?.checkout;
// Check if GTM (gtm.js) has been initialized by looking for the 'gtm.js' event in dataLayer
if (window.dataLayer && window.dataLayer.some(e => e.event === 'gtm.js')) {
// gtm.js has been initialized, so push the event only
//console.log("GTM has been initialized already.");
}
else {
//console.log("GTM has not been initialized yet.");
gtmScriptInstall();
}
const page_details = pageLoaded(event_data, event_id);
pushToDataLayer('page_loaded', page_details, null, null);
checkoutUserData(checkout);
let items = getItemsData(event_data);
let event_details = getEventDetails(event_data);
// Console Log | Event Data for Review
//console.log(`--- DataLayer | Customer Event: checkout_started ---`, event_data);
pushToDataLayer("begin_checkout", event_details, items, event_data);
});
// Shopify: checkout_contact_info_submitted | GA4: add_contact_info
// Checkout Step 2
analytics.subscribe('checkout_contact_info_submitted', (event) => {
console.log('[PA] >>> checkout_contact_info_submitted');
const event_data = event;
const checkout = event?.data?.checkout;
if (window.dataLayer && window.dataLayer.some(e => e.event === 'gtm.js')) {}
else { gtmScriptInstall(); }
checkoutUserData(checkout);
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToDataLayer("add_contact_info", event_details, items, event_data);
});
// Shopify: checkout_shipping_info_submitted | GA4: add_shipping_info
// Checkout Step 3
analytics.subscribe('checkout_shipping_info_submitted', (event) => {
console.log('[PA] >>> checkout_shipping_info_submitted');
const event_data = event;
const checkout = event?.data?.checkout;
// Check if GTM (gtm.js) has been initialized by looking for the 'gtm.js' event in dataLayer
if (window.dataLayer && window.dataLayer.some(e => e.event === 'gtm.js')) {
// gtm.js has been initialized, so push the event
//console.log("GTM has been initialized already.");
}
else {
//console.log("GTM has not been initialized yet.");
gtmScriptInstall();
}
checkoutUserData(checkout);
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToDataLayer("add_shipping_info", event_details, items, event_data);
});
// Shopify: payment_info_submitted | GA4: add_payment_info
// Checkout Step 4
analytics.subscribe('payment_info_submitted', (event) => {
console.log('[PA] >>> payment_info_submitted');
const event_data = event;
const checkout = event?.data?.checkout;
// Check if GTM (gtm.js) has been initialized by looking for the 'gtm.js' event in dataLayer
if (window.dataLayer && window.dataLayer.some(e => e.event === 'gtm.js')) {
// gtm.js has been initialized, so push the event only
//console.log("GTM has been initialized already.");
}
else {
//console.log("GTM has not been initialized yet.");
gtmScriptInstall();
}
checkoutUserData(checkout);
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToDataLayer("add_payment_info", event_details, items, event_data);
});
/************ Early Stage eCommerce Funnel Events | Push to Main Window **************/
// EXAMPLE: pushToMainWindow(eventName, eventDetails, items, event_data, origin)
// Shopify: product_added_to_cart | GA4: add_to_cart
analytics.subscribe("product_added_to_cart", (event) => {
console.log('[PA] >>> product_added_to_cart');
const event_data = event;
const origin = event?.context?.document?.location?.origin;
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToMainWindow("add_to_cart", event_details, items, event_data, origin);
});
// Shopify: product_removed_from_cart | GA4: remove_from_cart
analytics.subscribe("product_removed_from_cart", (event) => {
console.log('[PA] >>> product_removed_from_cart');
const event_data = event;
const origin = event?.context?.document?.location?.origin;
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToMainWindow("remove_from_cart", event_details, items, event_data, origin);
});
// Shopify: cart_viewed | GA4: view_cart
analytics.subscribe("cart_viewed", (event) => {
console.log('[PA] >>> cart_viewed');
const event_data = event;
const origin = event?.context?.document?.location?.origin;
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToMainWindow("view_cart", event_details, items, event_data, origin);
});
// Shopify: product_viewed | GA4: view_item
analytics.subscribe("product_viewed", (event) => {
console.log('[PA] >>> product_viewed');
const event_data = event;
const origin = event?.context?.document?.location?.origin;
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
if(event?.context?.document?.referrer.includes('/search') || event?.context?.document?.referrer.includes('/collections/')){
pushToMainWindow("select_item", event_details, items, event_data, origin);
pushToMainWindow("view_item", event_details, items, event_data, origin);
}
else {
pushToMainWindow("view_item", event_details, items, event_data, origin);
}
});
// Shopify: search_submitted | GA4: view_item_list
analytics.subscribe("search_submitted", (event) => {
console.log('[PA] >>> search_submitted');
const event_data = event;
const origin = event?.context?.document?.location?.origin;
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToMainWindow("view_item_list", event_details, items, event_data, origin);
});
// Shopify: collection_viewed | GA4: view_item_list
analytics.subscribe("collection_viewed", (event) => {
console.log('[PA] >>> collection_viewed');
const event_data = event;
const origin = event?.context?.document?.location?.origin;
let event_details = getEventDetails(event_data);
let items = getItemsData(event_data);
pushToMainWindow("view_item_list", event_details, items, event_data, origin);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment