1(function (w, d) { 2 const C = w.__AN_CFG || {}; 3 4 // flags com defaults inteligentes 5 // se tem GTM (Google Tag Manager) os demais são false por default, visto que ele fará o trabalho de disparar eventos. 6 const metaAutoPV = ('metaAutoPageView' in C) ? !!C.metaAutoPageView : false; // default = false | GTM != gtag (Google Analytics (GA4)) 7 const gaAutoPV = ('gaAutoPageView' in C) ? !!C.gaAutoPageView : false; 8 //const ttAutoPage = ('tiktokAutoPage' in C) ? !!C.tiktokAutoPage : false; 9 10 // util 11 function loadScript(src, { async = true, defer = true, id } = {}) { 12 if (id && d.getElementById(id)) return Promise.resolve(); 13 return new Promise((resolve, reject) => { 14 const s = d.createElement('script'); 15 if (id) s.id = id; 16 s.src = src; s.async = async; s.defer = defer; 17 s.onload = resolve; s.onerror = reject; 18 d.head.appendChild(s); 19 }); 20 } 21 22 // Inicializa gtag quando necessário para envio GA4 direto. 23 // Observação: o gtag usa uma fila própria de comandos (array de arguments). 24 function inicializaGtag() { 25 w.dataLayer = w.dataLayer || []; 26 w.gtag = w.gtag || function(){ w.dataLayer.push(arguments); }; 27 } 28 29 // ----------------------------- 30 // Google Tag Manager (preferir GTM se existir) 31 // ----------------------------- 32 if (C.gtm) { 33 (function(w,d,s,l,i){ 34 w[l]=w[l]||[]; w[l].push({'gtm.start': new Date().getTime(), event:'gtm.js'}); 35 const f=d.getElementsByTagName(s)[0], j=d.createElement(s), dl=l!='dataLayer' ? '&l='+l : ''; 36 j.async=true;
36 j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl; f.parentNode.insertBefore(j,f); 37 })(w,d,'script','dataLayer', C.gtm); 38 39 // Com GTM ativo, não inicializa GA4 direto via gtag para evitar conflito/duplicidade. 40 } else if (C.ga4 || C.ga4_retailon) { 41 // se NÃO usar GTM, carrega gtag.js direto para GA4 42 const primaryId = C.ga4 || C.ga4_retailon; 43 loadScript(`https://www.googletagmanager.com/gtag/js?id=${primaryId}`); 44 inicializaGtag(); 45 gtag('js', new Date()); 46 if (C.ga4) gtag('config', C.ga4, { send_page_view: gaAutoPV }); // default: false 47 if (C.ga4_retailon) gtag('config', C.ga4_retailon, { send_page_view: gaAutoPV }); // default: false 48 } 49 50 // ----------------------------- 51 // Meta Pixel 52 // ----------------------------- 53 if (C.fb_pixel) { 54 if (!w.fbq) { 55 !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? 56 n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; 57 n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; 58 t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)} 59 (w, d, 'script', 'https://connect.facebook.net/en_US/fbevents.js'); 60 } 61 w.__fb_inited_ids = w.__fb_inited_ids || new Set(); 62 if (!w.__fb_inited_ids.has(C.fb_pixel)) { 63 trackFbq('init', C.fb_pixel); 64 w.__fb_inited_ids.add(C.fb_pixel); 65 } 66 if (metaAutoPV) trackFbq('track', 'PageView'); // só se flag ligada 67 } 68 69 // ----------------------------- 70 // TikTok Pixel 71 // ----------------------------- 72 // if (C.tiktok) { 73 // !(function (w, d, t) { 74 // w.TiktokAnalyticsObject = t; 75 // var ttq = w[t] = w[t] || []; 76 // ttq.methods = ['page','track','identify','instances','debug','on','off','once','ready','alias','group','enableCookie','disableCookie']; 77 // ttq.setAndDefer = function(obj, method){ obj[method] = function(){ obj.push([method].concat([].slice.call(arguments,0))) } }; 78 // for (var i=0;i<ttq.methods.length;i++) ttq.setAndDefer(ttq, ttq.methods[i]); 79 // ttq.load = function(id, opts){ var u='https://analytics.tiktok.com/i18n/pixel/events.js'; 80 // ttq._i = ttq._i || {}; ttq._i[id] = []; ttq._i[id]._u = u; ttq._t = ttq._t || {}; ttq._t[id] = +new Date; ttq._o = ttq._o || {}; ttq._o[id] = opts || {}; 81 // var s = d.createElement('script'); s.type='text/javascript'; s.async=true; s.src = u+'?sdkid='+id+'&lib='+t; 82 // var n = d.getElementsByTagName('script')[0]; n.parentNode.insertBefore(s,n); 83 // }; 84 // ttq.load(C.tiktok); 85 // if (ttAutoPage) ttq.page(); // só se flag ligada 86 // })(window, document, 'ttq'); 87 // } 88 89 // ----------------------------- 90 // Hotjar 91 // ----------------------------- 92 if (C.hotjar && C.hotjar.hjid) { 93 (function(h,o,t,j,a,r){ 94 h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; 95 h._hjSettings={hjid:C.hotjar.hjid,hjsv:C.hotjar.hjsv||6}; 96 a=o.getElementsByTagName('head')[0]; 97 r=o.createElement('script'); r.async=1; 98 r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; 99 a.appendChild(r); 100 })(w,d,'https://static.hotjar.com/c/hotjar-','.js?sv='); 101 } 102 103 // ----------------------------- 104 // Microsoft Bing UET 105 // ----------------------------- 106 if (C.bing_uet) { 107 (function(w,d,t,r,u){var f,n,i;w[u]=w[u]||[],f=function(){ 108 var o={ti:C.bing_uet, enableAutoSpaTracking: true}; o.q=w[u], w[u]=new UET(o), w[u].push("pageLoad"); 109 }; 110 n=d.createElement(t), n.src=r, n.async=1, 111 n.onload=n.onreadystatechange=function(){var s=this.readyState; s&&s!=="loaded"&&s!=="complete"||(f(),n.onload=n.onreadystatechange=null)}; 112 i=d.getElementsByTagName(t)[0], i.parentNode.insertBefore(n,i)})(w,d,"script","//bat.bing.com/bat.js","uetq"); 113 114 w.uet_report_conversion = function(vlr){ 115 w.uetq = w.uetq || []; 116 w.uetq.push('event','purchase', { revenue_value: Number(vlr)||0, currency: 'BRL' }); 117 }; 118 } 119 120 // ----------------------------- 121 // Clarity 122 // ----------------------------- 123// if (C.clarity) { 124// (function(c,l,a,r,i,t,y){ c[a]=c[a]||function(){ (c[a].q=c[a].q||[]).push(arguments) }; 125// t=l.createElement(r); t.async=1; t.src="https://www.clarity.ms/tag/"+C.clarity; 126// y=l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t,y); 127// })(w, d, "clarity", "script"); 128// } 129 130 // ----------------------------- 131 // Navegg 132 // ----------------------------- 133 if (C.navegg_acc) { 134 (function(n,v,g){o="Navegg";if(!n[o]){a=v.createElement('script');a.src=g;b=document.getElementsByTagName('script')[0]; 135 b.parentNode.insertBefore(a,b);n[o]=n[o]||function(parms){n[o].q=n[o].q||[];n[o].q.push([this, parms])};}}) 136 (w, d, 'https://tag.navdmp.com/universal.min.js'); 137 w.naveggReady = w.naveggReady || []; 138 w['nvg'+C.navegg_acc] = new w.Navegg({ acc: Number(C.navegg_acc) }); 139 } 140 141 // ----------------------------- 142 // API simples para SPA: chame em cada troca de rota 143 // ----------------------------- 144 // Normaliza page_location: apenas pathname + ordenacao e pagina (se houver), em ordem fixa 145 function normalizePageLocation(href) { 146 try { 147 var u = new URL(href, w.location.origin); 148 var params = u.searchParams; 149 var ordenacao = params.get('ordenacao'); 150 var pagina = params.get('pagina'); 151 var parts = []; 152 if (ordenacao != null && ordenacao !== '') parts.push('ordenacao=' + encodeURIComponent(ordenacao)); 153 if (pagina != null && pagina !== '') parts.push('pagina=' + encodeURIComponent(pagina)); 154 var query = parts.length ? '?' + parts.join('&') : ''; 155 return u.origin + u.pathname + query; 156 } catch (e) { 157 return href; 158 } 159 } 160 161 w.Analytics = w.Analytics || {}; 162 163 w.Analytics.page = function page(url) { 164 var loc = url || w.location.href;
165 var shouldSendPageView = true; 166 try { 167 var u = new URL(loc, w.location.origin); 168 var params = u.searchParams; 169 var ordenacao = params.get('ordenacao'); 170 var pagina = params.get('pagina'); 171 var hasOrdenacao = ordenacao != null && ordenacao !== ''; 172 var hasPagina = pagina != null && pagina !== ''; 173 // Regra: "par ou nenhum". Se só um existir, não dispara page_view. 174 shouldSendPageView = (hasOrdenacao && hasPagina) || (!hasOrdenacao && !hasPagina); 175 } catch (e) { 176 shouldSendPageView = true; 177 } 178 if (!shouldSendPageView) return; 179 var pageLocation = normalizePageLocation(loc); 180 181 // Cliente (GTM): recebe via dataLayer. 182 pushToClientDataLayer('page_view', { page_location: pageLocation }); 183 // GA4 (Retailon + fallback cliente sem GTM): recebe via gtag + send_to. 184 sendToGA4('page_view', { page_location: pageLocation }); 185 if (w.fbq && C.fb_pixel && !metaAutoPV) trackFbq('track', 'PageView'); 186 //if (w.ttq && C.tiktok && !C.tiktokAutoPage) ttq.page(); 187 188 // Sempre local (ok com ou sem GTM) 189 if (w.uetq) w.uetq.push('event', 'page_view'); 190 if (w.hj) hj('stateChange', loc); 191 if (w.clarity) clarity('set', 'page', loc); 192}; 193 194})(window, document); 195 196const EVENT_MAP = { 197 // descoberta / listas 198 ViewItemList: { ga4: 'view_item_list', meta: 'ViewItemList_retailon' }, 199 SelectItem: { ga4: 'select_item', meta: 'SelectItem_retailon' }, 200 201 // PDP / carrinho / checkout 202 ViewContent: { ga4: 'view_item', meta: 'ViewContent', tiktok: 'ViewContent' }, 203 AddToCart: { ga4: 'add_to_cart', meta: 'AddToCart_retailon', tiktok: 'AddToCart' }, // sensÃvel 204 RemoveFromCart: { ga4: 'remove_from_cart', meta: 'RemoveFromCart_retailon', tiktok: 'RemoveFromCart' }, 205 ViewCart: { ga4: 'view_cart', meta: 'ViewCart_retailon' }, 206 CloseCart: { ga4: 'close_cart', meta: 'CloseCart_retailon' }, 207 InitiateCheckout: { ga4: 'begin_checkout', meta: 'InitiateCheckout_retailon', tiktok: 'InitiateCheckout' }, // sensÃvel 208 AddShippingInfo: { ga4: 'add_shipping_info', meta: 'AddShippingInfo_retailon', tiktok: 'AddShippingInfo' }, 209 AddPaymentInfo: { ga4: 'add_payment_info', meta: 'AddPaymentInfo_retailon', tiktok: 'AddPaymentInfo' }, // sensÃvel 210 Purchase: { ga4: 'purchase', meta: 'Purchase_retailon', tiktok: 'CompletePayment' }, // sensÃvel 211 212 // aquisição / utilitários 213 GenerateLead: { ga4: 'generate_lead', meta: 'Lead' }, // Meta padrão 214 CompleteRegistration:{ ga4: 'sign_up', meta: 'CompleteRegistration', tiktok: 'CompleteRegistration' }, 215 Login: { ga4: 'login', meta: 'Login_retailon', tiktok: 'Login' }, 216 Search: { ga4: 'search', meta: 'Search', tiktok: 'Search' }, 217 MenuAndCallToAction:{ ga4: 'select_content', meta: 'SelectContent_retailon' }, 218 RequestContact: { ga4: 'request_contact', meta: 'FindLocation' } // Meta: evento padrão de localização 219}; 220 221function trackFbq() { 222 try { 223 if (typeof window.fbq === 'function') window.fbq.apply(window, arguments); 224 } catch (_err) { /* no-op */ } 225} 226 227// ========= helpers ========= 228const num = v => { 229 const s = String(v ?? '').trim(); 230 return s.includes(',') ? +s.replace(/\./g,'').replace(',', '.') : +s; 231}; 232const qty = v => Math.max(1, parseInt(v || 1, 10)); 233const MENU_EVENT_DEDUPE_MS = 500; 234 235// Evita "fbq is not defined" quando não há chave Meta 236// ou quando o arquivo do Meta é bloqueado por extensão. 237function ensureFbqNoop() { 238 if (typeof window.fbq === 'function') return; 239 const noop = function () {}; 240 noop.loaded = false; 241 noop.version = '2.0-noop'; 242 window.fbq = noop; 243} 244ensureFbqNoop(); 245 246function shouldDedupeMenuEvent(termo) { 247 const key = [ 248 termo?.event || '', 249 termo?.content_id != null ? String(termo.content_id) : '', 250 termo?.section || '', 251 termo?.position != null ? String(termo.position) : '' 252 ].join('|'); 253 if (!key.replace(/\|/g, '')) return false; 254 255 const now = Date.now(); 256 window.__AN_MENU_EVT_CACHE = window.__AN_MENU_EVT_CACHE || {};
257 const lastTs = window.__AN_MENU_EVT_CACHE[key]; 258 window.__AN_MENU_EVT_CACHE[key] = now; 259 260 // limpeza simples para evitar crescimento sem limite da cache 261 const keys = Object.keys(window.__AN_MENU_EVT_CACHE); 262 if (keys.length > 200) { 263 const cutoff = now - (MENU_EVENT_DEDUPE_MS * 4); 264 keys.forEach((k) => { 265 if (window.__AN_MENU_EVT_CACHE[k] < cutoff) delete window.__AN_MENU_EVT_CACHE[k]; 266 }); 267 } 268 269 return typeof lastTs === 'number' && (now - lastTs) < MENU_EVENT_DEDUPE_MS; 270} 271 272function normalizeLeadPayload(termo) { 273 if (typeof termo === 'string') return { lead_type: termo }; 274 if (!termo || typeof termo !== 'object' || Array.isArray(termo)) return {}; 275 276 const leadPayload = {}; 277 Object.keys(termo).forEach((k) => { 278 if (termo[k] !== undefined) leadPayload[k] = termo[k]; 279 }); 280 return leadPayload; 281} 282 283const mapGaItems = (arr=[]) => arr.map(i => { 284 const out = { 285 item_id: i.id, 286 item_name: i.item_name, 287 price: i.price, 288 quantity: qty(i.quantidade), 289 index: i.index || 1, 290 discount: i.discount, 291 brand: i.brand, 292 category: i.category, 293 }; 294 if (i.item_list_name != null) out.item_list_name = i.item_list_name; 295 if (i.item_list_id != null) out.item_list_id = i.item_list_id; 296 return out; 297}); 298 299const mapFbContents = (arr = []) => arr.map(i => ({ 300 id: String(i.id), 301 quantity: Math.max(1, parseInt(i.quantidade || 1, 10)), 302 item_price: num(i.price) 303})); 304 305const mapTtContents = (arr = []) => arr.map(i => ({ 306 content_id: String(i.id), 307 quantity: Math.max(1, parseInt(i.quantidade || 1, 10)), 308 price: num(i.price) 309})); 310 311// ========= destino GA4/GTM ========= 312function hasGTM() { 313 return !!window.__AN_CFG?.gtm; 314} 315 316function hasRetailonGA4() { 317 return !!window.__AN_CFG?.ga4_retailon; 318} 319 320function hasClientGA4() { 321 return !!window.__AN_CFG?.ga4; 322} 323 324function getGA4Destinations() { 325 const destinations = []; 326 if (!hasGTM() && hasRetailonGA4()) destinations.push(window.__AN_CFG.ga4_retailon); 327 if (!hasGTM() && hasClientGA4()) destinations.push(window.__AN_CFG.ga4); 328 return destinations; 329} 330 331const ECOMMERCE_EVENTS = new Set([ 332 'view_item_list', 333 'select_item', 334 'view_item', 335 'add_to_cart', 336 'remove_from_cart', 337 'view_cart', 338 'begin_checkout', 339 'add_shipping_info', 340 'add_payment_info', 341 'purchase' 342]); 343 344function buildEcommerceObject(params = {}) { 345 const ecommerce = {}; 346 const keys = [ 347 'currency', 348 'value', 349 'items', 350 'transaction_id', 351 'shipping', 352 'tax', 353 'coupon', 354 'affiliation', 355 'payment_type', 356 'shipping_tier' 357 ]; 358 keys.forEach((k) => { 359 if (params[k] !== undefined) ecommerce[k] = params[k]; 360 }); 361 return ecommerce; 362} 363 364function pushToClientDataLayer(eventName, params = {}) { 365 if (!hasGTM() || !Array.isArray(window.dataLayer)) return; 366 const isEcommerceEvent = ECOMMERCE_EVENTS.has(eventName); 367 368 // Evita reaproveitamento de ecommerce/items de eventos anteriores no data model do GTM. 369 if (isEcommerceEvent) { 370 window.dataLayer.push({ ecommerce: null }); 371 } 372 373 const payload = { event: eventName, ...params }; 374 if (isEcommerceEvent) { 375 payload.ecommerce = buildEcommerceObject(params); 376 delete payload.currency; 377 delete payload.value; 378 delete payload.items; 379 delete payload.transaction_id; 380 delete payload.shipping; 381 delete payload.tax; 382 delete payload.coupon; 383 delete payload.affiliation; 384 delete payload.payment_type; 385 delete payload.shipping_tier; 386 } 387 388 window.dataLayer.push(payload); 389} 390 391function sendToGA4(eventName, params = {}) { 392 if (typeof window.gtag !== 'function') return; 393 394 // Com GTM ativo, eventos de GA4 devem ser roteados somente pelo container. 395 if (hasGTM()) { 396 return; 397 } 398 399 // fallback sem GTM 400 const destinations = getGA4Destinations(); 401 if (!destinations.length) return; 402 403 window.gtag('event', eventName, { ...params, send_to: destinations }); 404} 405 406function trackAnalyticsEvent(eventName, payload) { 407 if (!eventName) return; 408 pushToClientDataLayer(eventName, payload); 409 sendToGA4(eventName, payload); 410} 411 412// ========= despachante genérico ========= 413function filtroParaDadosAnaliticos(referencia, termo) { 414 const cfg = EVENT_MAP[referencia]; 415 if (!cfg) return; 416 if (referencia === 'MenuAndCallToAction' && shouldDedupeMenuEvent(termo)) return; 417 const hasMetaConfigured = !!window.__AN_CFG?.fb_pixel; 418 419 const currency = (termo && termo.currency) || 'BRL'; 420 421 // Total do contexto do evento (número) 422 // Ex.: no AddToCart, é o total desse clique de comprar; no Purchase, é o total pago (itens + frete â desconto). 423 const value = termo && termo.value != null ? num(termo.value) : undefined; 424 425 const items = Array.isArray(termo?.items) ? termo.items : []; 426 const gaItems = items.length ? mapGaItems(items) : undefined; 427 const fbCont = items.length ? mapFbContents(items) : undefined; 428 const ttCont = items.length ? mapTtContents(items) : undefined; 429 const ttTopId = ttCont && ttCont[0]?.content_id; 430 431 // ---- GA4 ---- 432 if (cfg.ga4) { 433 const base = { currency }; 434 if (value != null) base.value = value; 435 436 // campos extras que o GA4 entende, se chegarem 437 if (gaItems) base.items = gaItems; 438 if (termo?.transaction_id) base.transaction_id = termo.transaction_id; 439 if (termo?.shipping != null) base.shipping = num(termo.shipping); 440 if (termo?.affiliation) base.affiliation = termo.affiliation; 441 if (termo?.coupon) base.coupon = termo.coupon; 442 if (window.__AN_CFG?.tenant_name) base.tenant_name = window.__AN_CFG.tenant_name; 443 if (window.__AN_CFG?.tenant_id) base.tenant_id = window.__AN_CFG.tenant_id; 444 if (window.__AN_CFG?.tenant_segment) base.tenant_segment = window.__AN_CFG.tenant_segment; 445 if (window.__AN_CFG?.price_zone) base.price_zone = window.__AN_CFG.price_zone; 446 447 // mapeamentos especiais de GA4 448 if (referencia === 'Search') { 449 const searchPayload = (typeof termo === 'string') 450 ? { search_term: termo } 451 : (termo || {}); 452 const normalizedSearchType = searchPayload.search_type || 'product'; 453 const searchEvent = { 454 search_term: searchPayload.search_term || '', 455 search_type: normalizedSearchType, 456 results_count: searchPayload.results_count != null ? Number(searchPayload.results_count) : undefined, 457 state: searchPayload.state, 458 is_24h: searchPayload.is_24h, 459 trigger: searchPayload.trigger 460 }; 461 Object.keys(searchEvent).forEach((k) => searchEvent[k] === undefined && delete searchEvent[k]); 462 trackAnalyticsEvent('search', searchEvent); 463 } else if (referencia === 'GenerateLead') { 464 const leadPayload = normalizeLeadPayload(termo); 465 const leadEvent = { ...base, ...leadPayload }; 466 Object.keys(leadEvent).forEach((k) => leadEvent[k] === undefined && delete leadEvent[k]); 467 trackAnalyticsEvent(cfg.ga4, leadEvent); 468 } else if (referencia === 'MenuAndCallToAction') { 469 const extra = { 470 event: termo?.event, // ex: 'click_banner' ou 'click_pill' 471 content_type: termo?.content_type, // ex: 'banner_carrossel' ou 'filtro' 472 content_id: termo?.content_id // ex: id, href, texto da pÃlula 473 }; 474 475 if (termo?.position != null) extra.position = termo.position; // posição no carrossel 476 if (termo?.section) extra.section = termo.section; // ex: 'home' 477 478 trackAnalyticsEvent(cfg.ga4, { // cfg.ga4 aqui é 'select_content' 479 ...base, // currency, tenant_name, tenant_id, etc. 480 ...extra 481 }); 482 } else if (referencia === 'SelectItem') { 483 const extra = { 484 event: termo?.event, 485 }; 486 if (termo?.item_list_name) extra.item_list_name = termo.item_list_name; 487 if (termo?.item_list_id) extra.item_list_id = termo.item_list_id; 488 489 trackAnalyticsEvent('select_item', { 490 ...base, // inclui currency, value, items, tenant_name, etc. 491 ...extra 492 }); 493 } else if (referencia === 'ViewItemList') { 494 if (termo?.item_list_name) base.item_list_name = termo.item_list_name; 495 if (termo?.item_list_id) base.item_list_id = termo.item_list_id; 496 trackAnalyticsEvent(cfg.ga4, base); 497 } else if (['CompleteRegistration', 'Login'].includes(referencia)) { 498 const method = (typeof termo === 'string' ? termo : termo?.method) || 'site_credentials'; 499 trackAnalyticsEvent(cfg.ga4, { method }); 500 } else { 501 trackAnalyticsEvent(cfg.ga4, base); 502 } 503 } 504 505 // ---- Meta (FB) ---- 506 if (hasMetaConfigured && typeof window.fbq === 'function' && cfg.meta) { 507 // quais nomes usar como 'track' (padrão) vs 'trackCustom' 508 const metaStandard = new Set(['ViewContent','Lead','CompleteRegistration','Search','FindLocation']); 509 const useStandard = metaStandard.has(cfg.meta); 510 511 const base = {}; 512 if (value != null) base.value = value; 513 if (currency) base.currency = currency; 514 if (fbCont && fbCont.length) { base.content_type = 'product'; base.contents = fbCont; } 515 516 if (referencia === 'Search') { 517 const searchPayload = (typeof termo === 'string') 518 ? { search_term: termo } 519 : (termo || {}); 520 const normalizedSearchType = searchPayload.search_type || 'product'; 521 base.search_string = searchPayload.search_term || ''; 522 base.search_type = normalizedSearchType; 523 if (searchPayload.results_count != null) base.results_count = Number(searchPayload.results_count); 524 if (searchPayload.state) base.state = searchPayload.state; 525 if (searchPayload.is_24h != null) base.is_24h = !!searchPayload.is_24h; 526 if (searchPayload.trigger) base.trigger = searchPayload.trigger; 527 } 528 if (referencia === 'GenerateLead') { 529 const leadPayload = normalizeLeadPayload(termo); 530 Object.assign(base, leadPayload); 531 } 532 if (referencia === 'MenuAndCallToAction') { 533 if (termo?.content_type) base.content_type = termo.content_type; 534 if (termo?.content_id) base.content_id = String(termo.content_id); 535 if (termo?.section) base.section = termo.section; 536 if (termo?.position != null) base.position = termo.position; 537 if (termo?.event) base.menu_event = termo.event; 538 } 539 540 if (useStandard) trackFbq('track', cfg.meta, base); 541 else trackFbq('trackCustom', cfg.meta, base); 542 } 543 544 // ---- DataLayer (GTM) para Meta/Facebook ---- 545 try { 546 if (hasGTM() && Array.isArray(window.dataLayer) && cfg?.meta) { 547 const dlEvent = { 548 event: `fb_${cfg.meta || referencia}`.toLowerCase(), // ex: fb_viewcontent 549 event_source: 'site', 550 event_name: cfg.meta || referencia, 551 reference: referencia, 552 currency, 553 value: value != null ? value : undefined, 554 contents: fbCont, 555 content_ids: fbCont?.map(c => c.id), 556 content_type: fbCont?.length ? 'product' : undefined, 557 }; 558 if (referencia === 'Search') { 559 const searchPayload = (typeof termo === 'string') 560 ? { search_term: termo } 561 : (termo || {}); 562 const normalizedSearchType = searchPayload.search_type || 'product';
563 dlEvent.search_term = searchPayload.search_term || ''; 564 dlEvent.search_type = normalizedSearchType; 565 dlEvent.results_count = searchPayload.results_count != null ? Number(searchPayload.results_count) : undefined; 566 dlEvent.state = searchPayload.state; 567 dlEvent.is_24h = searchPayload.is_24h; 568 dlEvent.trigger = searchPayload.trigger; 569 } 570 if (referencia === 'GenerateLead') { 571 const leadPayload = normalizeLeadPayload(termo); 572 Object.assign(dlEvent, leadPayload); 573 } 574 if (referencia === 'MenuAndCallToAction') { 575 dlEvent.content_type = termo?.content_type ?? dlEvent.content_type; 576 dlEvent.content_id = termo?.content_id != null ? String(termo.content_id) : undefined; 577 dlEvent.section = termo?.section; 578 dlEvent.position = termo?.position; 579 dlEvent.menu_event = termo?.event; 580 } 581 582 Object.keys(dlEvent).forEach((k) => dlEvent[k] === undefined && delete dlEvent[k]); 583 window.dataLayer.push(dlEvent); 584 } 585 } catch (_err) { /* no-op */ } 586 587 // ---- TikTok ---- 588 // if (window.ttq && typeof ttq.track === 'function' && cfg.tiktok) { 589 // const base = { content_type: 'product' }; 590 // if (ttTopId) base.content_id = ttTopId; 591 // if (currency) base.currency = currency; 592 // if (value != null) base.value = value; 593 // if (ttCont && ttCont.length) base.contents = ttCont; 594 // if (referencia === 'Search') base.query = (typeof termo === 'string' ? termo : termo?.search_term || ''); 595 // ttq.track(cfg.tiktok, base); 596 // } 597 598 // ---- SYNDI ---- 599 if (referencia === 'AddToCart' && window.SYNDI) { 600 const first = gaItems?.[0]; 601 window.SYNDI.push({ 602 type: 'track', 603 event: 'addToCart', 604 qty: first?.quantity, 605 sku: first?.id, 606 price: value, 607 items: gaItems 608 }); 609 } 610 611 if (referencia === 'Purchase' && typeof uet_report_conversion === 'function') { 612 const totalFb = num(termo.value) + num(termo.shipping); 613 uet_report_conversion(totalFb); 614 } 615} 616 617// --- Auto Pageview para SPA + dedupe por URL --- 618(function (w) { 619 function firePage(url) { 620 try { 621 const loc = url || w.location.href; 622 if (w.__AN_LAST_PV === loc) return; // dedupe 623 w.__AN_LAST_PV = loc; 624 if (w.Analytics && typeof w.Analytics.page === 'function') { 625 w.Analytics.page(loc); 626 } 627 } catch (e) { /* no-op */ } 628 } 629 630 // 1) Primeiro pageview (carregamento inicial) 631 if (document.readyState === 'complete' || document.readyState === 'interactive') { 632 setTimeout(() => firePage(), 0); 633 } else { 634 w.addEventListener('DOMContentLoaded', () => firePage()); 635 } 636 637 // 2) Navegação SPA via history API 638 ['pushState', 'replaceState'].forEach(fn => { 639 const orig = history[fn]; 640 history[fn] = function () { 641 const ret = orig.apply(this, arguments); 642 w.dispatchEvent(new Event('spa:navigation')); 643 return ret; 644 }; 645 }); 646 w.addEventListener('spa:navigation', () => firePage()); 647 w.addEventListener('popstate', () => firePage()); 648})(window);
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.