1/** 2 * ForoBeta/Ads â beacon de impresiones VIEWABLE (+ rotación A/B client-side). 3 * 4 * Cuenta una impresión cuando un banner ha estado al menos 50% visible durante 1s continuo 5 * (estándar MRC). Junta lo visto y manda UN solo navigator.sendBeacon a /anunciate-beacon.php. 6 * 7 * A/B (v2): un banner con prueba A/B se renderiza con AMBAS creatividades dentro de 8 * .fbAds-banner.fbAds-ab (cada variante = .fbAds-ab-v con data-fbads-variant 0|1, la B oculta). 9 * Como ~80% de las páginas anónimas salen de LSCache, la elección NO puede hacerse en el 10 * servidor (se congelarÃa): aquà se elige 50/50 por impresión, se muestra una y se reporta cuál 11 * (token "id" para variante 0, "id:1" para la B). Sin JS â queda visible la A (degradación amable). 12 * 13 * Solo los navegadores reales ejecutan JS â filtra de paso a los bots que no corren JS. 14 */ 15(function () { 16 if (!('IntersectionObserver' in window) || !navigator.sendBeacon) { return; } 17 18 var seen = {}; // tokens ya contados en esta página 19 var pending = {}; // tokens vistos pendientes de enviar 20 var timer = null; 21 22 function flush() { 23 if (timer) { clearTimeout(timer); timer = null; } 24 var keys = Object.keys(pending); 25 if (!keys.length) { return; } 26 pending = {}; 27 try { 28 navigator.sendBeacon('/anunciate-beacon.php', 'ids=' + keys.join(',')); 29 } catch (e) {} 30 } 31 32 function schedule() { 33 if (timer) { return; } 34 timer = setTimeout(flush, 1500); // agrupa varios banners en un envÃo 35 } 36 37 // Token de conteo: "id" para variante 0 (compacto + compatible), "id:1" para la B. 38 function tokenFor(el) { 39 var id = el.getAttribute('data-fbads-id'); 40 if (!id) { return null; } 41 var v = el.getAttribute('data-fbads-variant'); 42 return (v && v !== '0') ? (id + ':' + v) : id; 43 } 44 45 var io = new IntersectionObserver(function (entries) { 46 entries.forEach(function (en) { 47 var el = en.target; 48 var tok = tokenFor(el); 49 if (!tok || seen[tok]) { return; } 50 51 if (en.isIntersecting && en.intersectionRatio >= 0.5) { 52 if (!el._fbTimer) { 53 el._fbTimer = setTimeout(function () { 54 el._fbTimer = null; 55 if (seen[tok]) { return; } 56 seen[tok] = 1; 57 pending[tok] = 1; 58 io.unobserve(el); 59 schedule(); 60 }, 1000); 61 } 62 } else { 63 if (el._fbTimer) { clearTimeout(el._fbTimer); el._fbTimer = null; } 64 } 65 }); 66 }, { threshold: [0, 0.5, 1] }); 67 68 // Elige 50/50 qué variante mostrar en un contenedor A/B (la otra queda oculta). 69 function pickAbVariant(container) { 70 if (container._fbAb) { return; } 71 container._fbAb = 1; 72 var vs = container.querySelectorAll('.fbAds-ab-v'); 73 if (vs.length < 2) { return; } // sin 2 variantes no hay nada que rotar 74 var showB = Math.random() < 0.5; 75 for (var i = 0; i < vs.length; i++) { 76 var isB = (vs[i].getAttribute('data-fbads-variant') === '1'); 77 vs[i].hidden = showB ? !isB : isB; 78 } 79 } 80 81 function scan() { 82 // 1) resolver A/B: decidir qué variante se muestra (antes de observar) 83 var abs = document.querySelectorAll('.fbAds-banner.fbAds-ab'); 84 for (var i = 0; i < abs.length; i++) { pickAbVariant(abs[i]); } 85 86 // 2) observar los elementos contables VISIBLES (no-A/B + la variante A/B mostrada) 87 var nodes = document.querySelectorAll('.fbAds-banner[data-fbads-id], .fbAds-ab-v[data-fbads-id]'); 88 for (var j = 0; j < nodes.length; j++) { 89 var el = nodes[j]; 90 if (el._fbObs || el.hidden) { continue; } 91 el._fbObs = 1; 92 io.observe(el); 93 } 94 } 95 96 if (document.readyState === 'loading') { 97 document.addEventListener('DOMContentLoaded', scan); 98 } else { 99 scan(); 100 } 101 102 document.addEventListener('visibilitychange', function () { 103 if (document.visibilityState === 'hidden') { flush(); } 104 }); 105 window.addEventListener('pagehide', flush); 106})();
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.