1// Meta Pixel via GTM (Elevar 2.0): dl_user_data, dl_view_item, dl_add_to_cart, dl_begin_checkout. 2 3// Ensure both data layer arrays exist (dataLayer for GTM, ElevarDataLayer for Elevar 2.0 pipe). 4function initDataLayer() { 5 window.dataLayer = window.dataLayer || []; 6 window.ElevarDataLayer = window.ElevarDataLayer || []; 7} 8 9// Push only to ElevarDataLayer; Elevar's handler enriches and forwards the copy to dataLayer. 10function pushToDataLayers(payload) { 11 window.ElevarDataLayer = window.ElevarDataLayer || []; 12 window.ElevarDataLayer.push(payload); 13} 14 15function getDataLayerItemId(productId) { 16 const match = String(productId).match(/^gid:\/\/shopify\/Product\/(\d+)$/); 17 return match ? match[1] : productId; 18} 19 20function getDataLayerVariantId(variantId) { 21 const match = String(variantId || '').match(/^gid:\/\/shopify\/ProductVariant\/(\d+)$/); 22 return match ? match[1] : (variantId || ''); 23} 24 25function getFirstValue(...values) { 26 return values.find((value) => value !== undefined && value !== null && value !== '') || ''; 27} 28 29function getPriceValue(product) { 30 const rawPrice = getFirstValue( 31 product.productPrice, 32 product.price, 33 product.priceRange?.minVariantPrice?.amount, 34 product.variant?.price?.amount, 35 ); 36 const price = parseFloat(rawPrice || 0); 37 return Number.isNaN(price) ? '0.00' : price.toFixed(2); 38} 39 40function getProductImage(product) { 41 return getFirstValue( 42 product.image, 43 product.imageUrl, 44 product.featuredImage?.url, 45 product.variant?.image?.url, 46 product.images?.edges?.[0]?.node?.url, 47 ); 48} 49 50function buildElevarProduct(product = {}) { 51 const productId = getFirstValue(product.product_id, product.productId, product.id); 52 const variantId = getFirstValue( 53 product.variant_id, 54 product.variantId, 55 product.variantID, 56 product.variant?.id, 57 ); 58 const normalizedProductId = getDataLayerItemId(productId); 59 const normalizedVariantId = getDataLayerVariantId(variantId); 60 const name = getFirstValue(product.productName, product.name, product.title); 61 const sku = getFirstValue( 62 product.sku, 63 product.variant?.sku, 64 normalizedVariantId, 65 normalizedProductId, 66 ); 67 const quantity = String(product.quantity || 1); 68 const category = getFirstValue( 69 product.productCategory, 70 product.category, 71 product.productType, 72 product.type, 73 ); 74 const variant = getFirstValue( 75 product.variantTitle, 76 product.variantName, 77 product.variant?.title, 78 product.variant, 79 ); 80 const brand = getFirstValue(product.brand, product.vendor); 81 82 return { 83 id: sku, 84 name, 85 brand, 86 category, 87 variant, 88 price: getPriceValue(product), 89 quantity, 90 list: product.list || '', 91 product_id: normalizedProductId, 92 variant_id: normalizedVariantId, 93 image: getProductImage(product), 94 url: product.url || '', 95 selling_plan_name: product.selling_plan_name || product.sellingPlanName || 'one-time', 96 item_id: sku, 97 item_name: name, 98 Item_category: category, 99 Item_variant: variant, 100 Item_brand: brand, 101 }; 102} 103 104// Push dl_user_data once per page load; Elevar 2.0 requires it before other dl_* events same tick. 105export function initializeUserData() { 106 initDataLayer(); 107 108 if (window.elevarUserDataPushed) return; 109 110 const userDataEvent = { 111 event: 'dl_user_data', 112 user_id: null, 113 }; 114 115 const isDebugMode = window.location?.hostname?.includes('localhost') 116 || window.location?.hostname?.includes('hlx.') 117 || window.location?.hostname?.includes('serta-dev') 118 || window.location?.hostname?.includes('staging'); 119 120 if (isDebugMode) { 121 // eslint-disable-next-line no-console 122 console.log('[Meta Pixel - Debug] Pushing user_data event:', userDataEvent); 123 } 124 125 pushToDataLayers(userDataEvent); 126 window.elevarUserDataPushed = true; 127} 128 129// Track PDP view: push dl_view_item in Elevar 2.0 format with Meta standard params. 130export function trackViewContent(product) { 131 initDataLayer(); 132 initializeUserData(); 133 134 const productDetail = [buildElevarProduct(product)]; 135 const [item] = productDetail; 136 const { 137 id: itemId, 138 name: productName, 139 price: itemPrice, 140 category, 141 } = item; 142 const price = parseFloat(itemPrice); 143 const productId = getFirstValue(product.productId, product.sku, itemId); 144 const productGroup = product.productGroup || 'SSB Products'; 145 146 // Skip back-to-back duplicate pushes for the same product/variant (e.g. re-render races). 147 const signature = `${item.product_id}:${item.variant_id}:${price}`; 148 if (window.elevarLastViewItemSignature === signature) return; 149 window.elevarLastViewItemSignature = signature; 150 151 // Clear previous ecommerce data to prevent cross-event contamination. 152 pushToDataLayers({ ecommerce: null }); 153 pushToDataLayers({ 154 event: 'dl_view_item', 155 ecommerce: { 156 currency: 'USD', 157 currencyCode: 'USD', 158 value: price, 159 items: productDetail, 160 detail: { 161 products: productDetail, 162 }, 163 }, 164 content_ids: [productId], 165 content_type: 'product', 166 content_name: productName, 167 content_category: category, 168 value: price, 169 currency: 'USD', 170 'dlv - Product View - Name': productName, 171 'dlv - Product View - Category Name': category, 172 'dlv - Product View - SKU': productId, 173 'dlv - Product View - Price': price, 174 'dlv - Product View - Product Group': productGroup, 175 'dlv - Product View - Details Array': productDetail, 176 'dlv - Global - Currency Code': 'USD', 177 }); 178 179 if (window.location?.hostname === 'localhost' || window.location?.hostname?.includes('hlx.')) { 180 // eslint-disable-next-line no-console 181 console.log('[Meta Pixel - Elevar] ViewContent (dl_view_item):', { 182 productName, 183 price, 184 products: productDetail, 185 }); 186 } 187} 188
189// Track add-to-cart: push dl_add_to_cart in Elevar 2.0 format with Meta standard params. 190export function trackAddToCart(product) { 191 initDataLayer(); 192 initializeUserData(); 193 194 const cartItem = [buildElevarProduct(product)]; 195 const [item] = cartItem; 196 const { 197 id: itemId, 198 name: productName, 199 price: itemPrice, 200 quantity: itemQuantity, 201 category, 202 } = item; 203 const price = parseFloat(itemPrice); 204 const quantity = parseFloat(itemQuantity); 205 const totalValue = price * quantity; 206 const productId = getFirstValue(product.productId, product.sku, itemId); 207 const productGroup = product.productGroup || 'SSB Products'; 208 209 // Clear previous ecommerce data to prevent cross-event contamination. 210 pushToDataLayers({ ecommerce: null }); 211 pushToDataLayers({ 212 event: 'dl_add_to_cart', 213 ecommerce: { 214 currency: 'USD', 215 currencyCode: 'USD', 216 value: totalValue, 217 items: cartItem, 218 add: { 219 products: cartItem, 220 }, 221 }, 222 content_ids: [productId], 223 content_type: 'product', 224 content_name: productName, 225 content_category: category, 226 value: totalValue, 227 currency: 'USD', 228 num_items: quantity, 229 'dlv - Add to Cart - Name': productName, 230 'dlv - Add to Cart - Category Name': category, 231 'dlv - Add to Cart - SKU': productId, 232 'dlv - Add to Cart - Price': price, 233 'dlv - Add to Cart - Quantity': quantity, 234 'dlv - Add to Cart - Product Group': productGroup, 235 'dlv - Add to Cart - Details Array': cartItem, 236 'dlv - Global - Currency Code': 'USD', 237 }); 238 239 if (window.location?.hostname === 'localhost' || window.location?.hostname?.includes('hlx.')) { 240 // eslint-disable-next-line no-console 241 console.log('[Meta Pixel - Elevar] AddToCart (dl_add_to_cart):', { 242 productName, 243 price, 244 quantity, 245 totalValue, 246 products: cartItem, 247 }); 248 } 249} 250 251export function trackViewItemList(products = [], list = '') { 252 initDataLayer(); 253 initializeUserData(); 254 255 const productList = products.map((product) => buildElevarProduct({ ...product, list })); 256 if (productList.length === 0) return; 257 258 const signature = JSON.stringify({ 259 list, 260 products: productList.map((product) => `${product.product_id}:${product.variant_id}`), 261 }); 262 if (window.elevarLastViewItemList === list) return; 263 window.elevarLastViewItemList = list; 264 265 pushToDataLayers({ ecommerce: null }); 266 pushToDataLayers({ 267 event: 'dl_view_item_list', 268 ecommerce: { 269 currencyCode: 'USD', 270 impressions: productList, 271 items: productList, 272 }, 273 }); 274} 275 276export function trackSelectItem(product, list = '') { 277 initDataLayer(); 278 initializeUserData(); 279 280 const productList = [buildElevarProduct({ ...product, list })]; 281 pushToDataLayers({ ecommerce: null }); 282 pushToDataLayers({ 283 event: 'dl_select_item', 284 ecommerce: { 285 currencyCode: 'USD', 286 click: { 287 actionField: { list }, 288 products: productList, 289 }, 290 items: productList, 291 }, 292 }); 293 294 if (window.location?.hostname === 'localhost' || window.location?.hostname?.includes('hlx.')) { 295 // eslint-disable-next-line no-console 296 console.log('[Meta Pixel - Elevar] SelectItem (dl_select_item):', productList[0]); 297 } 298} 299 300// Track checkout start: push dl_begin_checkout with Shopify cart data. 301export function trackBeginCheckout(cartData) { 302 initDataLayer(); 303 initializeUserData(); 304 305 const cartLines = cartData?.lines?.edges || []; 306 const items = cartLines.map((edge) => { 307 const line = edge?.node || {}; 308 const merchandise = line.merchandise || {}; 309 const product = merchandise.product || {}; 310 311 return buildElevarProduct({ 312 sku: product.sku || merchandise.sku || '', 313 product_id: product.id, 314 variant_id: merchandise.id, 315 name: product.title, 316 price: merchandise.price?.amount, 317 quantity: line.quantity || 1, 318 category: product.category?.name, 319 variantTitle: merchandise.title, 320 brand: product.vendor, 321 image: merchandise.image?.url, 322 url: product.handle ? `${window.location.origin}/products/${product.handle}` : '', 323 }); 324 }); 325 326 const totalValue = items.reduce((sum, item) => sum + (item.price * item.quantity), 0); 327 328 pushToDataLayers({ 329 event: 'dl_begin_checkout', 330 ecommerce: { 331 currency: 'USD', 332 currencyCode: 'USD', 333 value: totalValue, 334 items, 335 checkout: { 336 products: items, 337 }, 338 }, 339 content_ids: items.map((item) => item.id), 340 content_type: 'product', 341 value: totalValue, 342 currency: 'USD', 343 num_items: items.reduce((count, item) => count + Number(item.quantity), 0), 344 'dlv - Begin Checkout - Details Array': items, 345 'dlv - Global - Currency Code': 'USD', 346 }); 347 348 if (window.location?.hostname === 'localhost' || window.location?.hostname?.includes('hlx.')) { 349 // eslint-disable-next-line no-console 350 console.log('[Meta Pixel - Elevar] BeginCheckout (dl_begin_checkout):', { 351 totalValue, 352 items, 353 }); 354 } 355} 356 357// Legacy shim: map old Meta event names to the Elevar tracker functions. 358export function trackMetaPixelEvent(eventName, eventData = {}) { 359 if (eventName === 'ViewContent') { 360 trackViewContent(eventData); 361 } else if (eventName === 'AddToCart') { 362 trackAddToCart(eventData); 363 } 364} 365 366export default { 367 trackViewContent, 368 trackAddToCart, 369 trackViewItemList, 370 trackSelectItem, 371 trackBeginCheckout, 372 trackMetaPixelEvent, 373 initializeUserData, 374};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.