1/** 2 * Klaviyo Viewed Product + Kebchi Tracker ViewContent auto pour /dons/* 3 * 4 * - Mappe l'URL courante vers un produit (urlProductMap statique). 5 * - Override les prix depuis BD (window._kebchi_dbPrices setté inline plus haut). 6 * - Lit le bouton "add-tree-btn" / "add-pack-btn" pour récupérer nom/prix dynamiques. 7 * - Fire Klaviyo "Viewed Product" + KebchiTracker.track('ViewContent') si prix valide. 8 * 9 * Dépendances (déjà chargées avant) : 10 * - window._kebchi_dbPrices (inline dans header.php â bloc tracking pixels) 11 * - window.KebchiTracker (chargé async via /tracking/js/kebchi-tracker.js) 12 */ 13(function () { 14 // â ï¸ Ordre important : URLs plus spécifiques (mouton-afrique) AVANT les génériques (mouton). 15 // 'price' = fallback si BD indispo (override auto par window._kebchi_dbPrices si code matche). 16 var urlProductMap = { 17 '/dons/mouton-afrique' : { id: 'mouton-afrique', name: 'Mouton Afrique', categories: ['Donation', 'Sacrifice', 'Afrique'], price: 139 }, 18 '/dons/mouton-france' : { id: 'mouton-france', name: 'Mouton France', categories: ['Donation', 'Sacrifice', 'France'], price: 339 }, 19 '/dons/arbre-fruitier' : { id: 'arbre-fruitier', name: 'Arbre Fruitier', categories: ['Donation', 'Arbre'] }, 20 '/dons/puits' : { id: 'puits', name: 'Puits', categories: ['Donation', 'Eau'], price: 899 }, 21 '/dons/colis-alimentaire' : { id: 'colis-alimentaire', name: 'Colis Alimentaire', categories: ['Donation', 'Alimentaire'], price: 19 }, 22 '/dons/orphelins' : { id: 'orphelins', name: 'Orphelins', categories: ['Donation', 'Orphelins'], price: 39 }, 23 '/dons/parrainage' : { id: 'parrainage', name: 'Parrainage', categories: ['Donation', 'Abonnement'], price: 39 }, 24 '/dons/france' : { id: 'don-france', name: 'Don France', categories: ['Donation', 'France'], price: 309 }, 25 '/dons/aqiqa' : { id: 'aqiqa', name: 'Aqiqa', categories: ['Donation', 'Aqiqa'], price: 189 }, 26 '/dons/mouton' : { id: 'mouton', name: 'Mouton (Sacrifice)', categories: ['Donation', 'Sacrifice'], price: 179 }, 27 '/dons/afrique' : { id: 'don-afrique', name: 'Afrique', categories: ['Donation', 'Afrique'], price: 159 }, 28 '/dons/abonnement' : { id: 'abonnement', name: 'Abonnement', categories: ['Donation', 'Abonnement'] }, 29 '/dons/iftar' : { id: 'iftar', name: 'Iftar', categories: ['Donation', 'Ramadan'] }, 30 '/dons/ruche' : { id: 'ruche', name: 'Ruche', categories: ['Donation', 'Ruche'] }, 31 '/dons/libre' : { id: 'don-libre', name: 'Don Libre', categories: ['Donation'] }, 32 '/ramadan' : { id: 'ramadan', name: 'Ramadan', categories: ['Donation', 'Ramadan'] }, 33 }; 34 35 // Override prices depuis BD (table produits) â essaie 3 stratégies : 36 // 1. Match exact sur code (ex: mouton_afrique â 139â¬) 37 // 2. Snake_case du kebab (ex: don-libre â don_libre) 38 // 3. Premier segment = categorie BD (ex: colis-alimentaire â colis â 19â¬, arbre-fruitier â arbre â 16â¬) 39 var dbPrices = window._kebchi_dbPrices || {}; 40 Object.keys(urlProductMap).forEach(function (k) { 41 var item = urlProductMap[k]; 42 var dbPrice = dbPrices[item.id] 43 || dbPrices[item.id.replace(/-/g, '_')] 44 || dbPrices[item.id.split('-')[0]]; 45 if (dbPrice && dbPrice > 0) item.price = dbPrice; 46 }); 47 48 function getBaseProduct() { 49 var path = window.location.pathname; 50 for (var fragment in urlProductMap) { 51 if (path.indexOf(fragment) === 0) return urlProductMap[fragment]; 52 } 53 return null; 54 } 55 56 function getPageProduct() { 57 var btn = document.querySelector('.add-tree-btn, .add-pack-btn'); 58 if (!btn || !btn.dataset.item) return null; 59 try { 60 var item = JSON.parse(btn.dataset.item); 61 if (item.id && item.nom && item.prix) return item; 62 } catch (e) {} 63 return null; 64 } 65 66 function fireViewedProduct() { 67 var base = getBaseProduct(); 68 if (!base) return; 69 var page = getPageProduct(); 70 var price = (page && page.prix) || base.price || 0; 71 72 // Skip ViewContent si pas de prix valide : evite de polluer TikTok/Meta 73 // avec des events "value: undefined" (warning TikTok 38% events affected). 74 // Ces pages restent trackees via PageView (qui n'attend pas de value). 75 if (!price || price <= 0) return; 76 77 // Klaviyo â Viewed Product 78 var _learnq = window._learnq || []; 79 _learnq.push(['track', 'Viewed Product', { 80 'ProductName': page ? page.nom : base.name, 81 'ProductID': base.id, 82 'Categories': base.categories, 83 'URL': window.location.href, 84 'ImageURL': page ? (page.image_url || '') : '', 85 'Price': price, 86 }]); 87 88 // Kebchi Tracker â ViewContent (fan-out Meta/TikTok/Snap) 89 if (window.__kebchi_vc_fired) return; 90 (function waitTracker(retry) { 91 if (!window.KebchiTracker) { 92 if ((retry || 0) < 30) return setTimeout(function () { waitTracker((retry || 0) + 1); }, 200); 93 return; 94 } 95 if (window.__kebchi_vc_fired) return; 96 window.__kebchi_vc_fired = true; 97 var payload = { 98 content_ids: [base.id], 99 content_name: page ? page.nom : base.name, 100 content_type: 'donation', 101 content_category: (base.categories && base.categories[1]) ? base.categories[1].toLowerCase() : 'donation', 102 currency: 'EUR' 103 }; 104 if (price && price > 0) payload.value = price; 105 window.KebchiTracker.track('ViewContent', payload); 106 })(0); 107 } 108 109 function tryFire(attempt) { 110 var page = getPageProduct(); 111 if (page || attempt >= 5) { 112 fireViewedProduct(); 113 } else { 114 setTimeout(function () { tryFire(attempt + 1); }, 400); 115 } 116 } 117 118 if (document.readyState === 'loading') { 119 document.addEventListener('DOMContentLoaded', function () { tryFire(0); }); 120 } else { 121 tryFire(0); 122 } 123})(); 124 125// Marqueur pour sanity check (voir bas de header.php) 126window.__kebchi_loaded = window.__kebchi_loaded || {}; 127window.__kebchi_loaded.productViewed = true;
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.