1/* Zgody na cookies â jeden mechanizm dla serwisu (React) i landingów (statyczny HTML). 2 * 3 * Zasada nadrzÄdna: przed zgodÄ NIE Åadujemy żadnego skryptu ÅledzÄ cego. 4 * Baner, który tylko wyÅwietla komunikat, a tagi i tak lecÄ , jest gorszy niż brak 5 * banera â daje zÅudzenie zgodnoÅci. Dlatego Google Analytics, Clarity i Meta 6 * Pixel sÄ wstrzykiwane dopiero po decyzji użytkownika, a Google dostaje 7 * dodatkowo Consent Mode v2 z domyÅlnÄ odmowÄ . 8 * 9 * Plik jest serwowany z /zgody.js (public/), wiÄc korzystajÄ z niego zarówno 10 * strony Reacta, jak i landingi /lp/*. Jedna implementacja = jedna prawda. 11 * 12 * API dla reszty strony: 13 * window.UTWZgody.ma('analityczne') -> boolean 14 * window.UTWZgody.otworz() -> otwiera okno ustawieÅ 15 */ 16(function () { 17 'use strict' 18 19 var COOKIE = 'utw_zgody' 20 var WERSJA = 1 21 var DNI = 365 22 23 var TAGI = window.__UTW_TAGI || {} 24 var GA_ID = TAGI.ga || '' 25 var CLARITY_ID = TAGI.clarity || '' 26 var PIXEL_ID = TAGI.pixel || '' 27 28 // ââ Consent Mode v2: domyÅlnie odmowa, zanim cokolwiek siÄ zaÅaduje ââââââ 29 window.dataLayer = window.dataLayer || [] 30 function gtag() { window.dataLayer.push(arguments) } 31 window.gtag = window.gtag || gtag 32 33 gtag('consent', 'default', { 34 ad_storage: 'denied', 35 ad_user_data: 'denied', 36 ad_personalization: 'denied', 37 analytics_storage: 'denied', 38 functionality_storage: 'granted', 39 security_storage: 'granted', 40 wait_for_update: 500 41 }) 42 43 // ââ Zapis decyzji ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 44 function czytaj() { 45 try { 46 var m = document.cookie.match(new RegExp('(?:^|; )' + COOKIE + '=([^;]*)')) 47 if (!m) return null 48 var dane = JSON.parse(decodeURIComponent(m[1])) 49 if (!dane || dane.v !== WERSJA) return null 50 return dane 51 } catch (e) { return null } 52 } 53 54 function zapisz(stan) { 55 var dane = { v: WERSJA, analityczne: !!stan.analityczne, marketingowe: !!stan.marketingowe, data: new Date().toISOString().slice(0, 10) } 56 try { 57 var wygasa = new Date(Date.now() + DNI * 864e5).toUTCString() 58 document.cookie = COOKIE + '=' + encodeURIComponent(JSON.stringify(dane)) + 59 ';expires=' + wygasa + ';path=/;SameSite=Lax' + (location.protocol === 'https:' ? ';Secure' : '') 60 } catch (e) { /* przeglÄ darka blokuje cookies â decyzja zadziaÅa na tÄ sesjÄ */ } 61 return dane 62 } 63 64 var stan = czytaj() 65 66 // ââ Åadowanie narzÄdzi (dopiero po zgodzie) âââââââââââââââââââââââââââââ 67 var zaladowane = { ga: false, clarity: false, pixel: false } 68 69 function dodajSkrypt(src) { 70 var s = document.createElement('script') 71 s.async = true 72 s.src = src 73 s.setAttribute('data-cfasync', 'false') 74 document.head.appendChild(s) 75 return s 76 } 77 78 function wlaczAnalityke() { 79 if (GA_ID && !zaladowane.ga) { 80 zaladowane.ga = true 81 dodajSkrypt('https://www.googletagmanager.com/gtag/js?id=' + encodeURIComponent(GA_ID)) 82 gtag('js', new Date()) 83 gtag('config', GA_ID) 84 } 85 if (CLARITY_ID && !zaladowane.clarity) { 86 zaladowane.clarity = true 87 ;(function (c, l, a, r, i, t, y) { 88 c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments) } 89 t = l.createElement(r); t.async = 1; t.src = 'https://www.clarity.ms/tag/' + i 90 t.setAttribute('data-cfasync', 'false') 91 y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y) 92 })(window, document, 'clarity', 'script', CLARITY_ID) 93 } 94 } 95 96 function wlaczMarketing() { 97 if (!PIXEL_ID || zaladowane.pixel) return 98 zaladowane.pixel = true 99 ;(function (f) { 100 if (f.fbq) return 101 var n = f.fbq = function () { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments) } 102 if (!f._fbq) f._fbq = n 103 n.push = n; n.loaded = !0; n.version = '2.0'; n.queue = [] 104 })(window) 105 dodajSkrypt('https://connect.facebook.net/en_US/fbevents.js') 106 window.fbq('init', PIXEL_ID) 107 window.fbq('track', 'PageView') 108 } 109 110 function zastosuj(s, pierwszeWejscie) { 111 // Bez zgody marketingowej Google i tak wysyÅa bezciasteczkowe pingi (taki jest 112 // Consent Mode). ads_data_redaction usuwa z nich identyfikatory reklamowe â 113 // to oficjalny mechanizm Google i maksimum, co da siÄ zrobiÄ, nie rezygnujÄ c 114 // z analityki (GA4 i Google Ads dzielÄ jeden skrypt gtag.js). 115 if (!s.marketingowe) gtag('set', 'ads_data_redaction', true) 116 117 gtag('consent', 'update', { 118 ad_storage: s.marketingowe ? 'granted' : 'denied', 119 ad_user_data: s.marketingowe ? 'granted' : 'denied', 120 ad_personalization: s.marketingowe ? 'granted' : 'denied', 121 analytics_storage: s.analityczne ? 'granted' : 'denied' 122 }) 123 if (s.analityczne) wlaczAnalityke() 124 if (s.marketingowe) wlaczMarketing() 125 126 // CofniÄcie zgody po tym, jak skrypty już wstaÅy: samo `consent update` nie 127 // usuwa dziaÅajÄ cych nasÅuchów, wiÄc odÅwieżamy stronÄ â to jedyny uczciwy 128 // sposób, żeby naprawdÄ przestaÄ zbieraÄ dane. 129 if (!pierwszeWejscie && ((zaladowane.ga && !s.analityczne) || (zaladowane.pixel && !s.marketingowe))) { 130 location.reload() 131 } 132 } 133 134 if (stan) zastosuj(stan, true) 135 136 // ââ Interfejs ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 137 // Karta w rogu, nie okno modalne: nie ma przyciemnienia tÅa, nie blokuje 138 // przewijania ani klikania. Strona jest w peÅni używalna z widocznym banerem, 139 // a decyzja i tak musi byÄ Åwiadomym klikniÄciem â brak decyzji nigdy nie 140 // oznacza zgody (art. 4 pkt 11 RODO, wyrok TSUE C-673/17 Planet49). 141 var STYLE = [ 142 '.utwz-karta{position:fixed;z-index:2147483000;left:0;right:0;bottom:0;background:#fff;color:#0B2447;', 143 'font-family:Outfit,system-ui,-apple-system,sans-serif;line-height:1.55;box-shadow:0 -8px 40px -8px rgba(11,36,71,.28);', 144 'border-top:1px solid rgba(11,36,71,.1);padding:20px 20px calc(20px + env(safe-area-inset-bottom,0px));max-height:70vh;overflow-y:auto}',
145 '@media(min-width:900px){.utwz-karta{left:24px;right:auto;bottom:24px;width:400px;border-radius:20px;border:1px solid rgba(11,36,71,.1);padding:24px}}', 146 '.utwz-karta h2{font-family:Georgia,serif;font-size:20px;line-height:1.3;margin:0 0 8px;font-weight:700;padding-right:28px}', 147 '.utwz-karta p{font-size:15.5px;margin:0 0 12px;color:rgba(11,36,71,.75)}', 148 '.utwz-karta a{color:#B8932A;font-weight:700}', 149 '.utwz-zamknij{position:absolute;top:12px;right:12px;width:34px;height:34px;border:0;background:transparent;', 150 'font-size:24px;line-height:1;color:rgba(11,36,71,.45);cursor:pointer;border-radius:10px}', 151 '.utwz-zamknij:hover{background:rgba(11,36,71,.06);color:#0B2447}', 152 '.utwz-btny{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}', 153 '.utwz-btn{flex:1 1 100%;font-family:inherit;font-size:15.5px;font-weight:700;padding:13px 18px;border-radius:12px;', 154 'border:2px solid transparent;cursor:pointer;text-align:center;transition:transform .15s,background .15s}', 155 '.utwz-btn:active{transform:scale(.98)}', 156 '.utwz-btn:focus-visible,.utwz-zamknij:focus-visible{outline:3px solid #D4AF37;outline-offset:2px}', 157 '.utwz-akceptuj{background:#D4AF37;color:#0B2447}', 158 '.utwz-akceptuj:hover{background:#C6A230}', 159 '.utwz-odrzuc{background:#fff;color:#0B2447;border-color:rgba(11,36,71,.22)}', 160 '.utwz-odrzuc:hover{border-color:#0B2447}', 161 '.utwz-ustawienia{flex:1 1 100%;background:transparent;color:rgba(11,36,71,.7);text-decoration:underline;padding:8px;font-size:14.5px}', 162 '.utwz-kat{border:1.5px solid rgba(11,36,71,.12);border-radius:12px;padding:13px 15px;margin-bottom:8px}', 163 '.utwz-kat-gora{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}', 164 '.utwz-kat b{font-size:15.5px;display:block;margin-bottom:2px}', 165 '.utwz-kat p{font-size:14px;margin:0;color:rgba(11,36,71,.65)}', 166 '.utwz-kat small{display:block;font-size:12.5px;color:rgba(11,36,71,.5);margin-top:5px}', 167 '.utwz-przel{position:relative;flex:none;width:52px;height:30px}', 168 '.utwz-przel input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}', 169 '.utwz-przel span{position:absolute;inset:0;background:rgba(11,36,71,.2);border-radius:999px;transition:background .2s;pointer-events:none}', 170 '.utwz-przel span::after{content:"";position:absolute;top:4px;left:4px;width:22px;height:22px;background:#fff;border-radius:50%;transition:transform .2s}', 171 '.utwz-przel input:checked+span{background:#1F7A4D}', 172 '.utwz-przel input:checked+span::after{transform:translateX(22px)}', 173 '.utwz-przel input:disabled+span{background:#1F7A4D;opacity:.45}', 174 '.utwz-przel input:focus-visible+span{outline:3px solid #D4AF37;outline-offset:3px}', 175 // Przyklejony pasek CTA i baner zgody obydwa siedzÄ na dole â dopóki trwa 176 // decyzja, pasek chowamy, żeby nie zasÅaniaÅy siÄ nawzajem. 177 'html[data-zgody="oczekuje"] .utw-pasek-cta{display:none}' 178 ].join('') 179 180 var okno = null 181 182 function stylKiedyTrzeba() { 183 if (document.getElementById('utwz-style')) return 184 var st = document.createElement('style') 185 st.id = 'utwz-style' 186 st.textContent = STYLE 187 document.head.appendChild(st) 188 } 189 190 function zamknij() { 191 if (!okno) return 192 okno.remove() 193 okno = null 194 document.documentElement.removeAttribute('data-zgody') 195 } 196 197 function decyzja(analityczne, marketingowe) { 198 var wybor = zapisz({ analityczne: analityczne, marketingowe: marketingowe }) 199 zamknij() 200 zastosuj(wybor, false) 201 } 202 203 /** 204 * ZamkniÄcie krzyżykiem NIE jest ani zgodÄ , ani odmowÄ â chowa kartÄ na czas 205 * tej sesji przeglÄ darki, żeby nie wracaÅa na każdej podstronie. Nic siÄ wtedy 206 * nie Åaduje, a przy kolejnej wizycie pytamy ponownie. Milczenie nie może 207 * zastÄ piÄ zgody, wiÄc nie zapisujemy go jako decyzji. 208 */ 209 function odloz() { 210 try { sessionStorage.setItem('utw_zgody_odlozone', '1') } catch (e) {} 211 zamknij() 212 } 213 214 function odlozone() { 215 try { return sessionStorage.getItem('utw_zgody_odlozone') === '1' } catch (e) { return false } 216 } 217 218 function kategoria(id, tytul, opis, narzedzia, zaznaczone, zablokowane) { 219 return '<div class="utwz-kat"><div class="utwz-kat-gora"><div>' + 220 '<b>' + tytul + '</b>' + 221 '<p>' + opis + '</p>' + 222 '<small>' + narzedzia + '</small>' + 223 '</div>' + 224 '<label class="utwz-przel">' + 225 '<input type="checkbox" id="utwz-' + id + '"' + (zaznaczone ? ' checked' : '') + (zablokowane ? ' disabled' : '') + 226 ' aria-label="Zgoda: ' + tytul + '">' + 227 '<span></span>' + 228 '</label></div></div>' 229 } 230 231 function pokaz(tylkoUstawienia) { 232 stylKiedyTrzeba() 233 zamknij() 234 235 var biezacy = czytaj() || { analityczne: false, marketingowe: false } 236 var pierwszePytanie = !czytaj() 237 238 okno = document.createElement('div') 239 okno.className = 'utwz-karta' 240 okno.setAttribute('role', 'region')
241 okno.setAttribute('aria-label', 'Zgoda na pliki cookies') 242 okno.innerHTML = 243 '<button type="button" class="utwz-zamknij" data-akcja="odloz" aria-label="Zamknij â zapytamy przy nastÄpnej wizycie">Ã</button>' + 244 '<h2>Czy możemy używaÄ plików cookies?</h2>' + 245 '<p>Pliki niezbÄdne sÄ zawsze wÅÄ czone. PozostaÅe pomagajÄ nam sprawdziÄ, co na stronie dziaÅa dobrze, i pokazywaÄ nasze zajÄcia osobom, które mogÄ byÄ nimi zainteresowane. Strona dziaÅa tak samo niezależnie od Twojego wyboru.</p>' + 246 '<div id="utwz-kategorie" hidden>' + 247 kategoria('niezbedne', 'NiezbÄdne', 'Logowanie, formularze i zapamiÄtanie Twojej decyzji o cookies.', 'Zawsze wÅÄ czone', true, true) + 248 kategoria('analityczne', 'Analityczne', 'PokazujÄ , które strony sÄ odwiedzane i gdzie ludzie siÄ gubiÄ .', 'Google Analytics, Microsoft Clarity', biezacy.analityczne, false) + 249 kategoria('marketingowe', 'Marketingowe', 'PozwalajÄ pokazywaÄ nasze zajÄcia w reklamach i mierzyÄ ich skutecznoÅÄ.', 'Meta Pixel (Facebook), Google Ads', biezacy.marketingowe, false) + 250 '</div>' + 251 '<div class="utwz-btny">' + 252 '<button type="button" class="utwz-btn utwz-akceptuj" data-akcja="wszystkie">AkceptujÄ wszystkie</button>' + 253 '<button type="button" class="utwz-btn utwz-odrzuc" data-akcja="zadne">Tylko niezbÄdne</button>' + 254 '<button type="button" class="utwz-btn utwz-ustawienia" data-akcja="ustawienia">Wybierz kategorie</button>' + 255 '</div>' + 256 '<p style="font-size:13px;margin:12px 0 0;color:rgba(11,36,71,.55)">' + 257 'Zmienisz to w każdej chwili â âUstawienia cookiesâ w stopce. ' + 258 '<a href="/polityka-prywatnosci">Polityka prywatnoÅci</a>' + 259 '</p>' 260 261 document.body.appendChild(okno) 262 if (pierwszePytanie) document.documentElement.setAttribute('data-zgody', 'oczekuje') 263 264 var kategorie = okno.querySelector('#utwz-kategorie') 265 var przyciskUstawien = okno.querySelector('[data-akcja="ustawienia"]') 266 267 function pokazKategorie() { 268 kategorie.hidden = false 269 przyciskUstawien.textContent = 'Zapisz wybór' 270 przyciskUstawien.dataset.akcja = 'zapisz' 271 przyciskUstawien.className = 'utwz-btn utwz-odrzuc' 272 przyciskUstawien.style.flex = '1 1 100%' 273 } 274 275 if (tylkoUstawienia) pokazKategorie() 276 277 okno.addEventListener('click', function (e) { 278 var btn = e.target.closest ? e.target.closest('[data-akcja]') : null 279 if (!btn) return 280 var akcja = btn.dataset.akcja 281 if (akcja === 'wszystkie') return decyzja(true, true) 282 if (akcja === 'zadne') return decyzja(false, false) 283 if (akcja === 'odloz') return odloz() 284 if (akcja === 'ustawienia') return pokazKategorie() 285 if (akcja === 'zapisz') { 286 return decyzja( 287 okno.querySelector('#utwz-analityczne').checked, 288 okno.querySelector('#utwz-marketingowe').checked 289 ) 290 } 291 }) 292 293 // Escape chowa kartÄ (jak krzyżyk) â nie zapisuje żadnej zgody. 294 okno.addEventListener('keydown', function (e) { 295 if (e.key === 'Escape') odloz() 296 }) 297 } 298 // ââ Link âUstawienia cookies" ââââââââââââââââââââââââââââââââââââââââââââ 299 // Serwis ma go w stopce Reacta. Landingi to statyczny HTML, wiÄc żeby nikt nie 300 // musiaÅ o nim pamiÄtaÄ przy kolejnym landingu, dokÅadamy go automatycznie â 301 // wycofanie zgody musi byÄ tak samo dostÄpne jak jej udzielenie. 302 function podepnijLinki() { 303 var recznie = document.querySelectorAll('[data-utw-zgody]') 304 for (var i = 0; i < recznie.length; i++) { 305 recznie[i].addEventListener('click', function (e) { e.preventDefault(); pokaz(true) }) 306 } 307 if (recznie.length) return 308 309 var stopki = document.getElementsByTagName('footer') 310 if (!stopki.length) return 311 var cel = stopki[stopki.length - 1].querySelector('.legal') || stopki[stopki.length - 1] 312 var a = document.createElement('a') 313 a.href = '#' 314 a.textContent = 'Ustawienia cookies' 315 a.style.cssText = 'display:inline-block;margin-top:8px;text-decoration:underline;cursor:pointer' 316 a.addEventListener('click', function (e) { e.preventDefault(); pokaz(true) }) 317 var sep = document.createElement('span') 318 sep.textContent = ' · ' 319 cel.appendChild(sep) 320 cel.appendChild(a) 321 } 322 323 // ââ Start ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 324 function start() { 325 podepnijLinki()
326 if (!czytaj() && !odlozone()) pokaz(false) 327 } 328 329 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start) 330 else start() 331 332 window.UTWZgody = { 333 ma: function (kategoria) { 334 var s = czytaj() 335 return !!(s && s[kategoria]) 336 }, 337 otworz: function () { pokaz(true) } 338 } 339})()
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.