1/** 2 * Bridge CVP (CVCheckout) analytics -> dataLayer, gated on consent. 3 * 4 * Everything is decided client-side at runtime, so it is page-cache safe: the 5 * markup is static, but consent is read live from the browser (Complianz / 6 * Google Consent Mode) on every visit. No PHP determines consent. 7 * 8 * The embedded checkout iframe never touches the dataLayer. It postMessages 9 * generic { id, name, ecommerce } events to the host, where the CVCheckout 10 * embed shim re-emits them on `window.CVCheckout`. We map them into GTM/GA4, 11 * but only after everything else is in place so our pushes always come last: 12 * 1. GTM has loaded -> { event: 'gtm.load' } 13 * 2. Consent Mode was updated -> ['consent', 'update', {...}] (Complianz) 14 * 3. Statistics consent given -> cmplz_has_consent('statistics') 15 */ 16(function () { 17 window.dataLayer = window.dataLayer || []; 18 var queue = []; // analytics events waiting to be pushed 19 var seenIds = {}; // exact-replay dedupe (same SDK id) 20 var firedOnce = {}; // singleton events already pushed this page 21 var ONCE = { view_item: 1, begin_checkout: 1, purchase: 1 }; 22 var attached = false; // analytics listener registered? 23 var timer = null; // pending flush re-check, if any 24 var tries = 0; // re-checks left in the current budget 25 var MAX_TRIES = 50; // ~10s at 200ms, then give up until consent changes 26 27 // Debug logging, opt-in via the CVP_ANALYTICS_DEBUG env var (set in PHP). 28 function log() { 29 if (window.CVP_ANALYTICS_DEBUG && window.console) { 30 console.log.apply(console, arguments); 31 } 32 } 33 34 // Statistics consent granted right now (handles late accept/revoke). 35 function granted() { 36 try { return !!window.cmplz_has_consent('statistics'); } catch (e) { return false; } 37 } 38 39 // Does the dataLayer already contain a row matching test()? 40 function inDataLayer(test) { 41 var dl = window.dataLayer; 42 for (var i = 0; i < dl.length; i++) { 43 if (dl[i] && test(dl[i])) return true; 44 } 45 return false; 46 } 47 48 // We only push once everything else is in place, so our events come 49 // last regardless of page load vs. late consent: 50 // 1. GTM has loaded -> { event: 'gtm.load' } 51 // 2. Consent Mode was updated -> ['consent', 'update', {...}] (Complianz) 52 // 3. Statistics consent given. 53 function ready() { 54 return granted() 55 && inDataLayer(function (r) { return r.event === 'gtm.load'; }) 56 && inDataLayer(function (r) { return r[0] === 'consent' && r[1] === 'update'; }); 57 } 58 59 // Drain the queue when ready, otherwise re-check until the budget runs 60 // out (e.g. consent never accepted). A consent change re-arms it. 61 function flush() { 62 timer = null; 63 if (!queue.length) return; 64 if (!ready()) { 65 if (tries-- > 0) timer = setTimeout(flush, 200); 66 return; 67 } 68 while (queue.length) { 69 var e = queue.shift(); 70 log('[CVP analytics]', e.name, e.ecommerce); 71 window.dataLayer.push({ ecommerce: null }); 72 window.dataLayer.push({ event: e.name, ecommerce: e.ecommerce }); 73 } 74 } 75 76 // Start (or restart) checking with a fresh budget. 77 function schedule() { 78 tries = MAX_TRIES; 79 if (!timer) flush(); 80 } 81 82 function onReady() { 83 var api = window.CVCheckout; 84 if (attached || !api || typeof api.on !== 'function') return; 85 attached = true; 86 87 // view_item, begin_checkout, add_payment_info, purchase, etc. 88 // Buffer and let flush() gate the push. 89 api.on('analytics', function (e) { 90 // The SDK buffers + replays events, and a consent flip (deny -> allow) 91 // can re-init the checkout, re-emitting events with *new* ids. 92 // - Singletons (view_item/begin_checkout/purchase) fire once per page, 93 // so dedupe by name to absorb replays and re-inits. 94 // - Everything else (e.g. add_payment_info) dedupes by id only, so a 95 // genuine payment-method switch back to A still pushes again. 96 if (ONCE[e.name]) { 97 if (firedOnce[e.name]) return; 98 firedOnce[e.name] = true; 99 } else { 100 if (seenIds[e.id]) return; 101 seenIds[e.id] = true; 102 } 103 queue.push(e); 104 schedule(); 105 }); 106 } 107 108 window.addEventListener('cv:checkout:ready', onReady); 109 window.addEventListener('cv:thankyou:ready', onReady); 110 111 // Re-arm checking when consent changes (e.g. late accept after giving up). 112 document.addEventListener('cmplz_status_change', schedule); 113 document.addEventListener('cmplz_enable_category', schedule); 114})();
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.