1/** 2 * Cookie consent banner - jeden a ten isty subor pre vsetkych 5 domen 3 * (kazda domena ma vlastnu temu, ale kod sa nemusi upravovat - konfiguracia 4 * podla domeny je v SITE_CONFIG nizsie). 5 * 6 * Funguje nezavisle od Drupal 6 / Drupal 7 (ciste JS, ziadne Drupal API). 7 * 8 * VERZIA S GOOGLE CONSENT MODE V2 9 * -------------------------------- 10 * Rozdiel oproti povodnej verzii: 11 * - gtag.js sa nacitava HNED pri otvoreni stranky (nie az po suhlase), 12 * pricom vsetky consent kategorie su defaultne "denied". Google teda 13 * nezbiera ziadne osobne udaje, kym pouzivatel nesuhlasi, ale zaroven 14 * vie zaznamenat (anonymizovanu/modelovanu) navstevu. 15 * - Po kliknuti na "Suhlasim" sa iba zavola gtag('consent','update', ...) 16 * a hned nato sa manualne odosle page_view pre aktualnu stranku. 17 * Vdaka tomu sa zaznamenaju aj jednorazove navstevy, ktore by pri 18 * povodnom "nacitat GA az po suhlase" pristupe zostali nezachytene 19 * (pouzivatel klikne suhlas a hned odide, GA sa nestihne nacitat). 20 * - Ziadny reload stranky nie je potrebny. 21 * 22 * POUZITIE (rovnake pre vsetkych 5 tem): 23 * 1. Skopirujte tento subor bezo zmeny do 24 * /sites/all/themes/VASA_TEMA/js/cookie-consent.js 25 * v kazdej z piatich tem. 26 * 2. Pred znacku </body> v page.tpl.php danej temy pridajte: 27 * <script src="/sites/all/themes/VASA_TEMA/js/cookie-consent.js"></script> 28 * 3. Nic dalsie upravovat netreba - skript sam podla location.hostname 29 * zisti, ktora domena bezi, a pouzije jej GA kod a odkaz na zasady. 30 * 31 * Ak pribudne novy web alebo sa zmeni GA kod, staci upravit iba SITE_CONFIG 32 * tu na jednom mieste a skopirovat aktualizovany subor do vsetkych tem. 33 */ 34(function () { 35 "use strict"; 36 37 var COOKIE_NAME = "cookie_consent"; 38 var COOKIE_DAYS = 365; 39 40 // --- KONFIGURACIA PODLA DOMENY ----------------------------------------- 41 // gaId: GA4 Measurement ID (format G-XXXXXXXXXX) 42 // privacyUrl: odkaz na stranku so zasadami cookies/GDPR pre danu domenu 43 var SITE_CONFIG = { 44 "nulaodpadu.sk": { gaId: "G-KEPZG2X5RW", privacyUrl: "https://www.priateliazeme.sk/spz/ochrana-osobnych-udajov" }, 45 "stopspalovniam.sk": { gaId: "G-NT7K41KE27", privacyUrl: "https://www.priateliazeme.sk/spz/ochrana-osobnych-udajov" }, 46 "zerowastecities.sk": { gaId: "G-GT5ZZWLWG3", privacyUrl: "https://www.priateliazeme.sk/spz/ochrana-osobnych-udajov" }, 47 // pathPrefix: ak je nastavene, banner + tracking sa spusti LEN ak 48 // window.location.pathname zacina tymto retazcom (napr. zvysok domeny 49 // pouziva iny system/tracking - napr. CEPA - a tento skript sa ho 50 // netyka). 51 "priateliazeme.sk": { gaId: "G-39E2F5KQSB", pathPrefix: "/spz", privacyUrl: "https://www.priateliazeme.sk/spz/ochrana-osobnych-udajov" }, 52 "kompost.sk": { gaId: "G-ZG5WBDYRTS", privacyUrl: "https://www.priateliazeme.sk/spz/ochrana-osobnych-udajov" } 53 }; 54 // Ak nastavenie pre domenu chyba (napr. neocakavany subdomena tvar), 55 // pouzije sa toto: 56 var DEFAULT_CONFIG = { gaId: null, privacyUrl: "https://www.priateliazeme.sk/spz/ochrana-osobnych-udajov" }; 57 58 function getSiteConfig() { 59 var host = window.location.hostname.replace(/^www\./, "").toLowerCase(); 60 return SITE_CONFIG[host] || DEFAULT_CONFIG; 61 } 62 63 function getCookie(name) { 64 var match = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)")); 65 return match ? decodeURIComponent(match[1]) : null; 66 } 67 68 function setCookie(name, value, days) { 69 var expires = new Date(); 70 expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000); 71 document.cookie = 72 name + "=" + encodeURIComponent(value) + 73 "; expires=" + expires.toUTCString() + 74 "; path=/; SameSite=Lax"; 75 } 76 77 function removeBanner(banner) { 78 if (banner && banner.parentNode) { 79 banner.parentNode.removeChild(banner); 80 } 81 } 82 83 // --- JEDNODUCHY POCITADLO NAVSTEVNOSTI (bez cookies, bez GA) ----------- 84 // Zavola sa VZDY, bez ohladu na to, ci pouzivatel suhlasi s analytickymi 85 // cookies alebo nie - kedze nezapisuje ziadne cookie a nezaznamenava 86 // ziadny osobny udaj, nepodlieha cookie suhlasu. Sluzi len na to, aby 87 // ste mali aspon holy pocet navstev za den, aj od tych, ktori GA4 88 // odmietnu. Vyzaduje na serveri subor pageview-counter.php v tom istom 89 // priecinku ako tento skript (viz pageview-counter.php). 90 function getOwnScriptSrc() { 91 // document.currentScript funguje spolahlivo LEN pocas synchronneho 92 // vykonavania skriptu pri prvom nacitani - ak sa tato funkcia zavola 93 // neskor (napr. z DOMContentLoaded listenera), currentScript uz je 94 // null. Preto namiesto "posledneho skriptu na stranke" (nespolahlive, 95 // kedze medzitym mozu pribudnut dalsie <script> tagy) najdeme presne 96 // ten <script>, ktoreho src konci na "cookie-consent.js" - to funguje 97 // vzdy, bez ohladu na timing. 98 if (document.currentScript && document.currentScript.src) { 99 return document.currentScript.src; 100 } 101 var scripts = document.getElementsByTagName("script"); 102 for (var i = 0; i < scripts.length; i++) { 103 if (scripts[i].src && /cookie-consent[^\/]*\.js(\?.*)?(#.*)?$/.test(scripts[i].src)) { 104 return scripts[i].src; 105 } 106 } 107 return ""; 108 } 109 110 function pingPageviewCounter() { 111 try { 112 var src = getOwnScriptSrc(); 113 if (!src) { 114 return; 115 } 116 var counterUrl = src.replace(/[^\/]*$/, "pageview-counter.php"); 117 var img = new Image(); 118 img.src = counterUrl + (counterUrl.indexOf("?") === -1 ? "?" : "&") + "t=" + Date.now(); 119 } catch (e) { 120 // ticho ignorovat - pocitadlo nesmie nijak ovplyvnit fungovanie stranky 121 } 122 } 123 124 // --- GOOGLE CONSENT MODE V2 --------------------------------------------- 125 // gtag.js sa nacitava VZDY, hned pri starte stranky. Pred akymkolvek 126 // suhlasom vsak beziu vsetky ulozne kategorie s hodnotou "denied", takze 127 // sa neuklada ziadne osobne cookie a Google nezbiera identifikovatelne 128 // udaje - iba anonymizovany/modelovany signal. 129 window.dataLayer = window.dataLayer || []; 130 window.gtag = window.gtag || function () { 131 window.dataLayer.push(arguments); 132 }; 133 134 function setDefaultConsent() { 135 // Musi byt zavolane PRED nacitanim gtag.js / config, inak Google 136 // defaultne pouziva "granted" a tento krok stráca zmysel. 137 window.gtag("consent", "default", { 138 ad_storage: "denied", 139 ad_user_data: "denied", 140 ad_personalization: "denied", 141 analytics_storage: "denied", 142 wait_for_update: 500 143 }); 144 } 145 146 function bootAnalytics(gaId) { 147 if (!gaId || window.__gaBooted) { 148 return; 149 } 150 window.__gaBooted = true; 151 152 setDefaultConsent(); 153 154 window.gtag("js", new Date()); 155 // send_page_view: false - prvy page_view riadime rucne (viz nizsie), 156 // aby sme mali istotu, ze sa odosle az po nacitani skriptu / po 157 // prip. udeleni suhlasu, a aby sme predisli duplicitnym hitom. 158 window.gtag("config", gaId, { send_page_view: false }); 159 160 var s = document.createElement("script"); 161 s.async = true;
162 s.src = "https://www.googletagmanager.com/gtag/js?id=" + encodeURIComponent(gaId); 163 s.onload = function () { 164 window.__gaScriptReady = true; 165 // Ak uz medzitym pouzivatel udelil suhlas skor, nez sa skript stihol 166 // nacitat, odosleme page_view az teraz. 167 if (window.__consentGrantedBeforeReady) { 168 sendPageView(); 169 } else { 170 // Bez suhlasu Google aj tak posle anonymizovany/cookieless signal 171 // (potrebny pre "consent mode modeling"), takze zavolame page_view 172 // aj tu - realny pouzivatel s cookies sa zaznamena az po suhlase. 173 sendPageView(); 174 } 175 }; 176 document.head.appendChild(s); 177 } 178 179 function sendPageView() { 180 window.gtag("event", "page_view", { 181 page_title: document.title, 182 page_location: window.location.href, 183 page_path: window.location.pathname 184 }); 185 } 186 187 function grantConsent() { 188 window.gtag("consent", "update", { 189 analytics_storage: "granted" 190 // ad_storage / ad_user_data / ad_personalization ostavaju "denied", 191 // kedze web nepouziva reklamny remarketing. Ak by sa to niekedy 192 // zmenilo, pridaj sem aj ich "granted". 193 }); 194 195 if (window.__gaScriptReady) { 196 sendPageView(); 197 } else { 198 // gtag.js sa este stale stahuje - hned ako dobehne onload vyssie, 199 // page_view sa odosle automaticky. 200 window.__consentGrantedBeforeReady = true; 201 } 202 } 203 204 function injectStyles() { 205 var css = [ 206 "#cookie-consent-banner{position:fixed;left:0;right:0;bottom:0;z-index:99999;", 207 "background:#222;color:#fff;padding:14px 18px;font-family:Arial,Helvetica,sans-serif;", 208 "font-size:14px;line-height:1.5;box-shadow:0 -2px 8px rgba(0,0,0,.3);", 209 "padding-bottom:calc(14px + env(safe-area-inset-bottom));}", 210 "#cookie-consent-inner{max-width:1100px;margin:0 auto;display:flex;flex-wrap:wrap;", 211 "align-items:center;gap:14px;justify-content:space-between;}", 212 "#cookie-consent-inner p{margin:0;flex:1 1 320px;}", 213 "#cookie-consent-inner a{color:#9fd88d;text-decoration:underline;}", 214 "#cookie-consent-buttons{display:flex;gap:8px;flex:0 0 auto;}", 215 "#cookie-consent-buttons button{cursor:pointer;border-radius:4px;padding:9px 16px;", 216 "font-size:13px;font-weight:bold;}", 217 "#cookie-consent-accept{background:#4a9c3e;color:#fff;border:1px solid #4a9c3e;}", 218 "#cookie-consent-decline{background:transparent;color:#fff;border:1px solid #fff;}", 219 "@media (max-width:640px){#cookie-consent-inner{flex-direction:column;align-items:stretch;}", 220 "#cookie-consent-buttons{justify-content:stretch;}#cookie-consent-buttons button{flex:1;}}" 221 ].join(""); 222 var style = document.createElement("style"); 223 style.appendChild(document.createTextNode(css)); 224 document.head.appendChild(style); 225 } 226 227 // --- PREKLADY ----------------------------------------------------------- 228 // Jazyk sa zisÅ¥uje z atribútu <html lang="..."> danej stránky (bežné aj 229 // v mnohojazyÄných Drupal weboch). Ak nie je "en", použije sa slovenÄina. 230 var TRANSLATIONS = { 231 sk: { 232 ariaLabel: "Súhlas s použÃvanÃm cookies", 233 accept: "SúhlasÃm s použÃvanÃm cookies", 234 decline: "NesúhlasÃm s použÃvanÃm cookies", 235 text: 236 "Na správne fungovanie webovej stránky a analýzu návÅ¡tevnosti použÃvame súbory cookie. " + 237 "Konkrétne informácie o tom ako cookies použÃvame a taktiež informácie o ochrane osobných " + 238 'údajov (GDPR), nájdete na <a href="{privacyUrl}" target="_blank" rel="noopener">tejto adrese</a>.' 239 }, 240 en: { 241 ariaLabel: "Cookie consent", 242 accept: "I agree to the use of cookies", 243 decline: "I do not agree to the use of cookies", 244 text: 245 "To ensure the website functions correctly and to analyze traffic, we use cookies. " + 246 "For details on how we use cookies, as well as information about personal data protection " + 247 '(GDPR), please visit <a href="{privacyUrl}" target="_blank" rel="noopener">this page</a>.' 248 } 249 }; 250 251 function getLang() { 252 var path = window.location.pathname.toLowerCase(); 253 if (/(^|\/)en(\/|$)/.test(path)) { 254 return "en"; 255 } 256 // Fallback pre Drupal weby bez "clean URLs", kde je skutoÄná cesta 257 // v query stringu (napr. /?q=spz/en/titulka namiesto /spz/en/titulka) 258 var search = window.location.search.toLowerCase(); 259 if (/(^|\/|=)en(\/|$|&)/.test(search)) { 260 return "en"; 261 } 262 return "sk"; 263 } 264 265 function createBanner(privacyUrl, onAccept, onDecline) { 266 var t = TRANSLATIONS[getLang()]; 267 var textHtml = t.text.replace("{privacyUrl}", privacyUrl); 268 269 var banner = document.createElement("div"); 270 banner.id = "cookie-consent-banner"; 271 banner.setAttribute("role", "region"); 272 banner.setAttribute("aria-label", t.ariaLabel); 273 274 var inner = document.createElement("div"); 275 inner.id = "cookie-consent-inner"; 276 277 var text = document.createElement("p"); 278 text.innerHTML = textHtml; 279 280 var buttons = document.createElement("div"); 281 buttons.id = "cookie-consent-buttons"; 282 283 var acceptBtn = document.createElement("button"); 284 acceptBtn.type = "button"; 285 acceptBtn.id = "cookie-consent-accept"; 286 acceptBtn.textContent = t.accept; 287 acceptBtn.addEventListener("click", function () { 288 onAccept(); 289 removeBanner(banner); 290 }); 291 292 var declineBtn = document.createElement("button"); 293 declineBtn.type = "button"; 294 declineBtn.id = "cookie-consent-decline"; 295 declineBtn.textContent = t.decline; 296 declineBtn.addEventListener("click", function () { 297 onDecline(); 298 removeBanner(banner); 299 }); 300 301 buttons.appendChild(acceptBtn); 302 buttons.appendChild(declineBtn); 303 inner.appendChild(text); 304 inner.appendChild(buttons); 305 banner.appendChild(inner); 306 document.body.appendChild(banner); 307 } 308
309 function init() { 310 var config = getSiteConfig(); 311 312 // Ak ma domena nastaveny pathPrefix, skript (banner aj tracking) sa 313 // ma spustit LEN na strankach, ktorych cesta danym prefixom zacina. 314 // Zvysok domeny (napr. sekcie mimo /spz) tento skript vobec nezasahuje 315 // - typicky preto, ze tam bezi iny system/tracking (napr. CEPA). 316 if (config.pathPrefix && window.location.pathname.indexOf(config.pathPrefix) !== 0) { 317 return; 318 } 319 320 // Holy pocet navstev - vzdy, bez ohladu na cookie suhlas (viz vyssie). 321 pingPageviewCounter(); 322 323 // gtag.js sa nacitava VZDY hned na zaciatku, bez ohladu na to, ci uz 324 // suhlas existuje - vdaka Consent Mode v2 defaultom (denied) je to 325 // v poriadku aj z hladiska GDPR. 326 bootAnalytics(config.gaId); 327 328 var consent = getCookie(COOKIE_NAME); 329 330 if (consent === "1") { 331 grantConsent(); 332 return; // súhlas už bol daný skôr, banner sa nezobrazuje znova 333 } 334 if (consent === "0") { 335 return; // nesúhlas už bol daný skôr, banner sa nezobrazuje znova 336 } 337 338 injectStyles(); 339 createBanner( 340 config.privacyUrl, 341 function onAccept() { 342 setCookie(COOKIE_NAME, "1", COOKIE_DAYS); 343 grantConsent(); 344 }, 345 function onDecline() { 346 setCookie(COOKIE_NAME, "0", COOKIE_DAYS); 347 } 348 ); 349 } 350 351 if (document.readyState === "loading") { 352 document.addEventListener("DOMContentLoaded", init); 353 } else { 354 init(); 355 } 356})();
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.