1/** 2 * beehome-cookie-banner.js â LGPD-compliant cookie banner. 3 * 4 * Replaces HubSpot's cookie banner. Zero deps. Shows once on first visit; 5 * stores choice in localStorage under `beehome_consent_v1`. Exposes 6 * window.BeehomeConsent with { status, canLoad(category), on(event, cb) }. 7 * 8 * Categories supported: 9 * - "necessary" â always true 10 * - "analytics" â Google Analytics, GTM, LinkedIn Insight 11 * - "marketing" â ads pixels, retargeting 12 * 13 * Consent options: accept-all, reject-non-essential, or per-category via settings. 14 */ 15(function () { 16 "use strict"; 17 18 var STORAGE_KEY = "beehome_consent_v1"; 19 var LISTENERS = { change: [] }; 20 var state = loadState(); 21 22 function loadState() { 23 try { 24 var raw = localStorage.getItem(STORAGE_KEY); 25 if (!raw) return null; 26 var parsed = JSON.parse(raw); 27 if (!parsed || typeof parsed !== "object") return null; 28 return parsed; 29 } catch (e) { return null; } 30 } 31 32 function saveState(s) { 33 try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch (e) {} 34 state = s; 35 applyConsent(s); 36 emit("change", s); 37 } 38 39 // Avisa o Google Consent Mode da escolha do usuário. 40 // Sem isto, o gtag fica DENIED por padrão pra sempre, o cookie _gcl_aw 41 // (GCLID) nunca é gravado e as conversões NÃO contam no Google Ads. 42 function applyConsent(s) { 43 if (typeof window.gtag !== "function") return; 44 var marketing = s && s.marketing ? "granted" : "denied"; 45 var analytics = s && s.analytics ? "granted" : "denied"; 46 window.gtag("consent", "update", { 47 ad_storage: marketing, 48 ad_user_data: marketing, 49 ad_personalization: marketing, 50 analytics_storage: analytics, 51 }); 52 } 53 54 function emit(event, payload) { 55 (LISTENERS[event] || []).forEach(function (cb) { 56 try { cb(payload); } catch (e) { console.error("[BeehomeConsent]", e); } 57 }); 58 } 59 60 function el(tag, attrs, children) { 61 var e = document.createElement(tag); 62 if (attrs) for (var k in attrs) { 63 if (k === "class") e.className = attrs[k]; 64 else if (k === "html") e.innerHTML = attrs[k]; 65 else if (k === "text") e.textContent = attrs[k]; 66 else e.setAttribute(k, attrs[k]); 67 } 68 (children || []).forEach(function (c) { 69 if (c == null) return; 70 e.appendChild(typeof c === "string" ? document.createTextNode(c) : c); 71 }); 72 return e; 73 } 74 75 function renderBanner() { 76 if (document.getElementById("beehome-cookie-banner")) return; 77 var wrap = el("div", { id: "beehome-cookie-banner", role: "dialog", "aria-live": "polite", "aria-label": "Aviso de cookies" }); 78 79 var inner = el("div", { class: "bh-cb-inner" }); 80 81 inner.appendChild( 82 el("div", { class: "bh-cb-text" }, [ 83 el("strong", {}, ["Usamos cookies "]), 84 "para melhorar sua experiência, analisar tráfego e personalizar conteúdo. Você pode aceitar todos ou apenas os essenciais. Veja nossa ", 85 el("a", { href: "/politica-de-privacidade", target: "_blank", rel: "noopener" }, ["PolÃtica de Privacidade"]), 86 ".", 87 ]) 88 ); 89 90 var btns = el("div", { class: "bh-cb-actions" }); 91 btns.appendChild(el("button", { class: "bh-cb-btn bh-cb-btn-secondary", type: "button", id: "bh-cb-reject" }, ["Apenas essenciais"])); 92 btns.appendChild(el("button", { class: "bh-cb-btn bh-cb-btn-primary", type: "button", id: "bh-cb-accept" }, ["Aceitar todos"])); 93 inner.appendChild(btns); 94 95 wrap.appendChild(inner); 96 document.body.appendChild(wrap); 97 98 document.getElementById("bh-cb-accept").addEventListener("click", function () { 99 saveState({ necessary: true, analytics: true, marketing: true, at: Date.now(), v: 1 });
100 dismiss(); 101 }); 102 document.getElementById("bh-cb-reject").addEventListener("click", function () { 103 saveState({ necessary: true, analytics: false, marketing: false, at: Date.now(), v: 1 }); 104 dismiss(); 105 }); 106 107 // Trigger slide-in after paint 108 requestAnimationFrame(function () { wrap.classList.add("bh-cb-visible"); }); 109 } 110 111 function dismiss() { 112 var wrap = document.getElementById("beehome-cookie-banner"); 113 if (wrap) { 114 wrap.classList.remove("bh-cb-visible"); 115 setTimeout(function () { if (wrap.parentNode) wrap.parentNode.removeChild(wrap); }, 300); 116 } 117 } 118 119 function boot() { 120 if (!state) { 121 renderBanner(); 122 } else { 123 // visitante que já decidiu: reaplica o consent (default da página é denied) 124 applyConsent(state); 125 } 126 } 127 128 // Public API 129 window.BeehomeConsent = { 130 get status() { return state; }, 131 canLoad: function (category) { 132 if (category === "necessary") return true; 133 if (!state) return false; // no decision yet â deny non-essentials 134 return Boolean(state[category]); 135 }, 136 on: function (event, cb) { 137 if (!LISTENERS[event]) LISTENERS[event] = []; 138 LISTENERS[event].push(cb); 139 return this; 140 }, 141 openSettings: function () { 142 try { localStorage.removeItem(STORAGE_KEY); } catch (e) {} 143 state = null; 144 renderBanner(); 145 }, 146 }; 147 148 if (document.readyState === "loading") { 149 document.addEventListener("DOMContentLoaded", boot); 150 } else { 151 boot(); 152 } 153})();
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.