1(function () { 2 // 1. Ensure this only runs on the client (browser) 3 if (typeof window === 'undefined') return; 4 5 var consentScript = document.currentScript || document.querySelector('script[src*="cookie-consent.js"]'); 6 var scriptSrc = consentScript ? (consentScript.getAttribute('src') || '') : ''; 7 var versionMatch = scriptSrc.match(/[?&]v=([^&]+)/); 8 var versionParam = versionMatch ? '?v=' + encodeURIComponent(versionMatch[1]) : ''; 9 var privacyPolicyUrl = consentScript 10 ? consentScript.getAttribute('data-privacy-policy-url') 11 : null; 12 var privacyPolicyLabel = consentScript 13 ? consentScript.getAttribute('data-privacy-policy-label') 14 : null; 15 16 // 2. CHECK FOR CAPACITOR: Abort if running in a native Android/iOS app 17 if (window.Capacitor && window.Capacitor.isNativePlatform()) { 18 console.log("Running in Capacitor Native App - Skipping Cookie Consent"); 19 return; // Stops the rest of the script from executing 20 } 21 22 window.dataLayer = window.dataLayer || []; 23 function gtag() { window.dataLayer.push(arguments); } 24 25 26 // 2. Store your raw HTML as a string 27 function getCookieHtml() { 28 var t = window.COOKIE_CONFIG?.text || {}; 29 30 return ` 31 <div id="cookie-wrapper" class="cookie-wrapper" style="display:none;"> 32 <div class="cookie-modal"> 33 <div class="tab-additional"> 34 <div class="cookie-close"> 35 <button class="close-btn" onclick="closeTab()" aria-label="Close"> 36 Ã 37 </button> 38 </div> 39 </div> 40 41 <div class="cookie-modal-inside"> 42 <div id="tab-consent" class="tab-content"> 43 <div class="tab-content-left-side"> 44 <div class="cookie-header"> 45 <div class="cookie-tabs"> 46 <button class="cookie-tab active" onclick="showTab('consent')"> 47 ${t.consentTab || 'consentTab'} 48 </button> 49 <button class="cookie-tab" onclick="showTab('info')"> 50 ${t.infoTab || 'infoTab'} 51 </button> 52 </div> 53 <div class="cookie-close" style="display:none"> 54 <button class="close-btn" onclick="closeTab()" aria-label="Close"> 55 Ã 56 </button> 57 </div> 58 </div> 59 <div class="cookie-content"> 60 <p> 61 ${t.description || 'description'} 62 </p> 63 <div class="cookie-disclosure-links"> 64 <a 65 class="cookie-disclosure-link" 66 href="${privacyPolicyUrl || '/pages/privacy_policy'}" 67 >${privacyPolicyLabel || 'Privacy Policy'}</a> 68 <a 69 class="cookie-disclosure-link" 70 href="https://business.safety.google/privacy/" 71 target="_blank" 72 rel="noopener noreferrer" 73 >${t.googleDataUseLink || 'Learn how Google uses personal data'}</a> 74 </div> 75 </div> 76 77 <div class="wrap-sections-headers"> 78 <div class="cookie-section"> 79 <div class="cookie-section-header"> 80 <label class="checkbox-container disabled"> 81 <input type="checkbox" checked disabled> 82 <span class="checkmark"></span> 83 </label> 84 <span>
84${t.necessaryCookies || 'necessaryCookies'}</span> 85 </div> 86 </div> 87 88 <div class="cookie-section"> 89 <div class="cookie-section-header" onclick="toggleSection(event,this)"> 90 <label class="checkbox-container"> 91 <input type="checkbox" id="statistics"> 92 <span class="checkmark"></span> 93 </label> 94 <span>${t.statisticsCookies || 'statisticsCookies'}</span> 95 </div> 96 </div> 97 98 <div class="cookie-section"> 99 <div class="cookie-section-header" onclick="toggleSection(event,this)"> 100 <label class="checkbox-container"> 101 <input type="checkbox" id="marketing"> 102 <span class="checkmark"></span> 103 </label> 104 <span>${t.marketingCookies || 'marketingCookies'}</span> 105 </div> 106 </div> 107 <div class="cookie-section"> 108 <div class="cookie-section-header" onclick="toggleSection(event,this)"> 109 <label class="checkbox-container"> 110 <input type="checkbox" id="functional"> 111 <span class="checkmark"></span> 112 </label> 113 <span>${t.functionalCookies || 'functionalCookies'}</span> 114 </div> 115 </div> 116 </div> 117 </div> 118 <div class="tab-content-right-side"> 119 120 <div class="cookie-buttons"> 121 <button id="primary-cookie-btn" onclick="handlePrimaryClick()" class="cookie-btn cookie-btn-primary"> 122 ${t.acceptAll || 'Accept All'} 123 </button> 124 <button onclick="declineAll()" class="cookie-btn cookie-btn-secondary"> 125 ${t.declineAll || 'declineAll'} 126 </button> 127 </div> 128 </div> 129 </div> 130 131 <div id="tab-info" class="tab-content hidden"> 132 <div class="cookie-tabs"> 133 <button class="cookie-tab" onclick="showTab('consent')"> 134 ${t.consentTab || 'consentTab'} 135 </button> 136 <button class="cookie-tab active" onclick="showTab('info')"> 137 ${t.infoTab || 'infoTab'} 138 </button> 139 </div> 140 <div class="cookie-content" style="display:flex;flex-direction:column;gap:16px;"> 141 <div class="cookie-group"> 142 <p> 143 ${t.infoText1 || 'infoText1'} </br> 144 ${t.infoText2 || 'infoText2'}</br> 145 ${t.infoText3 || 'infoText3'}</br> 146 ${t.infoText4 || 'infoText4'}</br> 147 </p> 148 </div> 149 <div class="cookie-group"> 150 <h4>
150${t.necessaryCookies || 'Necessary'}</h4> 151 <p>${t.necessaryDesc || 'Required for site to work.'}</p> 152 </div> 153 154 <div class="cookie-group"> 155 <h4>${t.statisticsCookies || 'Performance'}</h4> 156 <p>${t.performanceDesc || 'Used for analytics.'}</p> 157 </div> 158 159 <div class="cookie-group"> 160 <h4>${t.marketingCookies || 'Marketing'}</h4> 161 <p>${t.marketingDesc || 'Used for ads.'}</p> 162 </div> 163 164 <div class="cookie-group"> 165 <h4>${t.functionalCookies || 'Functional'}</h4> 166 <p>${t.functionalDesc || 'Remember preferences.'}</p> 167 </div> 168 </div> 169 </div> 170 </div> 171 </div> 172 </div> 173 `; 174 } 175 176 function getDeviceType() { 177 const ua = navigator.userAgent.toLowerCase() 178 179 const isTablet = /ipad|tablet|android(?!.*mobile)/.test(ua) 180 const isMobile = /iphone|ipod|android.*mobile|windows phone/.test(ua) 181 182 if (isTablet) return 'tablet' 183 if (isMobile) return 'mobile' 184 return 'desktop' 185 } 186 187 // 3. Function to inject HTML into the body 188 function injectHTML() { 189 if (document.getElementById('cookie-wrapper')) return 190 191 var container = document.createElement('div') 192 container.innerHTML = getCookieHtml() 193 194 var wrapper = container.firstElementChild 195 if (!wrapper) return 196 197 const device = getDeviceType() 198 199 if (device === 'mobile') { 200 wrapper.classList.add('cookie-wrapper-mobile') 201 } else { 202 wrapper.classList.add('cookie-wrapper-desktop') 203 } 204 205 document.body.appendChild(container) 206 } 207 208 var IDS = { 209 wrapper: "cookie-wrapper", 210 note: "notification", 211 marketing: "marketing", 212 statistics: "statistics", 213 functional: "functional" 214 }; 215 216 function $(id) { return document.getElementById(id); } 217 function getChecked(id) { var el = $(id); return !!(el && el.checked); } 218 function setChecked(id, v) { var el = $(id); if (el) el.checked = !!v; } 219 function hasEl(id) { return !!$(id); } 220 function uuidv4() { 221 return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 222 var r = Math.random() * 16 | 0, v = c === "x" ? r : (r & 0x3 | 0x8); 223 return v.toString(16); 224 }); 225 } 226 227 // NEW: State for the requested behavior 228 var consentCustomized = false; 229 var backgroundDismissArmed = false; 230 var backgroundDismissTimer = null; 231 232 // NEW: Update primary button text based on state 233 function updatePrimaryButton() { 234 var btn = document.getElementById('primary-cookie-btn'); 235 if (!btn) return; 236 var t = window.COOKIE_CONFIG?.text || {}; 237 var newText = consentCustomized 238 ? (t.saveAndClose || 'Save and Close') 239 : (t.acceptAll || 'Accept All'); 240 btn.textContent = newText; 241 } 242 243 // NEW: Handle click on the primary blue button 244 function handlePrimaryClick() { 245 if (!consentCustomized) { 246 // "Accept All" was clicked â grant everything 247 setChecked(IDS.statistics, true); 248 setChecked(IDS.marketing, true); 249 setChecked(IDS.functional, true); 250 } 251 // In both cases (Accept All OR Save and Close) we just save current state 252 saveSelected(); 253 } 254 255 // NEW: Listen for any checkbox change (including row clicks via toggleSection) 256 function setupCheckboxListeners() { 257 var checkboxes = [ 258 document.getElementById(IDS.statistics), 259 document.getElementById(IDS.marketing), 260 document.getElementById(IDS.functional) 261 ]; 262 checkboxes.forEach(function (cb) { 263 if (cb) { 264 cb.addEventListener('change', function () { 265 consentCustomized = true; 266 updatePrimaryButton(); 267 }); 268 } 269 }); 270 } 271
272 function resetBackgroundDismissHint() { 273 backgroundDismissArmed = false; 274 // if (backgroundDismissTimer) { 275 // window.clearTimeout(backgroundDismissTimer); 276 // backgroundDismissTimer = null; 277 // } 278 279 var wrapper = $(IDS.wrapper); 280 if (wrapper) wrapper.classList.remove("cookie-background-hint"); 281 } 282 283 function handleBackgroundClick(event) { 284 var wrapper = $(IDS.wrapper); 285 if (!wrapper) return; 286 287 // Only the dark overlay counts. Clicks inside the banner never close it. 288 if (event.target !== wrapper) { 289 resetBackgroundDismissHint(); 290 return; 291 } 292 293 if (backgroundDismissArmed) { 294 resetBackgroundDismissHint(); 295 closeTab(); 296 return; 297 } 298 299 backgroundDismissArmed = true; 300 wrapper.classList.add("cookie-background-hint"); 301 // backgroundDismissTimer = window.setTimeout(function () { 302 // resetBackgroundDismissHint(); 303 // }, 3900); 304 } 305 306 function setupBackgroundDismiss() { 307 var wrapper = $(IDS.wrapper); 308 if (!wrapper || wrapper.dataset.backgroundDismissReady === "true") return; 309 310 wrapper.dataset.backgroundDismissReady = "true"; 311 wrapper.addEventListener("click", handleBackgroundClick); 312 } 313 314 // 1. Updated showBanner: Now it ONLY handles UI visibility 315 function showBanner() { 316 resetBackgroundDismissHint(); 317 var w = $(IDS.wrapper); 318 if (w) w.style.display = "flex"; 319 320 var r = $(IDS.reopen); 321 if (r) r.classList.add("hidden"); 322 323 window.dataLayer.push({ 324 event: "consent_banner_shown" 325 }); 326 } 327 function hideBanner() { 328 resetBackgroundDismissHint(); 329 var w = $(IDS.wrapper); 330 if (w) 331 w.style.display = "none"; 332 var r = $(IDS.reopen); 333 if (r) 334 r.classList.remove("hidden"); 335 } 336 337 var LS_KEY = "cookieConsent"; 338 339 function getCookie(name) { 340 try { 341 var value = "; " + document.cookie; 342 var parts = value.split("; " + name + "="); 343 if (parts.length === 2) return decodeURIComponent(parts.pop().split(";").shift()); 344 } catch (e) {} 345 return null; 346 } 347 348 function setCookie(name, value, days) { 349 try { 350 var expires = ""; 351 if (days) { 352 var date = new Date(); 353 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 354 expires = "; expires=" + date.toUTCString(); 355 } 356 document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/; SameSite=Lax"; 357 } catch (e) {} 358 } 359 360 function readConsent() { 361 var raw = null; 362 try { 363 raw = window.localStorage.getItem(LS_KEY); 364 } catch (e) {} 365 366 if (!raw) { 367 raw = getCookie(LS_KEY); 368 } 369 370 if (!raw) { 371 try { 372 raw = window.sessionStorage.getItem(LS_KEY); 373 } catch (e) {} 374 } 375 376 if (!raw) return null; 377 378 try { 379 return JSON.parse(raw); 380 } catch (e) { 381 console.error("Consent parse error", e); 382 return null; 383 } 384 } 385 386 function writeConsent(c) { 387 var str = JSON.stringify(c); 388 try { 389 window.localStorage.setItem(LS_KEY, str); 390 } catch (e) { 391 console.error("Consent write error", e); 392 } 393 setCookie(LS_KEY, str, 180); 394 try { 395 window.sessionStorage.setItem(LS_KEY, str); 396 } catch (e) {} 397 } 398 399 var stored = readConsent(); 400 401 function isConsentExpired(consent) { 402 if (!consent || !consent.updatedAt) return false; 403 404 var lastUpdate = new Date(consent.updatedAt).getTime(); 405 var sixMonthsInMs = 180 * 24 * 60 * 60 * 1000; 406 407 return !isNaN(lastUpdate) && new Date().getTime() - lastUpdate > sixMonthsInMs; 408 } 409 410 if (stored && !isConsentExpired(stored)) { 411 // Tell Google RIGHT NOW so tags don't error out 412 gtag("consent", "update", { 413 ad_storage: stored.marketing ? "granted" : "denied", 414 ad_user_data: stored.marketing ? "granted" : "denied", 415 ad_personalization: stored.marketing ? "granted" : "denied", 416 analytics_storage: stored.statistics ? "granted" : "denied", 417 functionality_storage: stored.functional ? "granted" : "denied" 418 }); 419 window.dataLayer.push({ event: "consent_ready", hasStoredConsent: true }); 420 421 if (typeof fbq !== 'undefined') { 422 if (typeof window.fbq === 'function') { 423 if (stored.marketing) { 424 window.fbq('consent', 'grant'); 425 } else { 426 window.fbq('consent', 'revoke'); 427 } 428 } 429 } 430 } 431 432 function applyConsent(consent, source, previousConsent) { 433 var marketing = !!consent.marketing; 434 var statistics = !!consent.statistics; 435 var functional = !!consent.functional; 436 var statisticsWasGranted = !!(previousConsent && previousConsent.statistics); 437 438 gtag("consent", "update", { 439 ad_storage: marketing ? "granted" : "denied", 440 ad_user_data: marketing ? "granted" : "denied", 441 ad_personalization: marketing ? "granted" : "denied", 442 analytics_storage: statistics ? "granted" : "denied", 443 functionality_storage: functional ? "granted" : "denied", 444 }); 445 446 // â Facebook 447 if (typeof fbq !== 'undefined') { 448 if (typeof window.fbq === 'function') { 449 if (marketing) { 450 window.fbq('consent', 'grant'); 451 } else { 452 window.fbq('consent', 'revoke'); 453 } 454 } 455 } 456 457 // Initial page views are created while consent is denied. Send one 458 // when statistics consent is granted for the first time. 459 if (statistics && !statisticsWasGranted) { 460 var pageViewParameters = { 461 page_title: document.title, 462 page_location: window.location.href, 463 page_path: window.location.pathname + window.location.search 464 }; 465 466 if (window.__googleAnalyticsProvider === 'gtag' && typeof window.gtag === 'function') { 467 if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { 468 pageViewParameters.debug_mode = true; 469 } 470 window.gtag('event', 'page_view', pageViewParameters); 471 } else { 472 window.dataLayer.push({ ecommerce: null }); 473 window.dataLayer.push(Object.assign({ event: 'page_view' }, pageViewParameters)); 474 } 475 } 476 477 window.dataLayer.push({ 478 event: "consent_updated", 479 source: source || "unknown", 480 marketing: marketing, 481 statistics: statistics, 482 functional: functional, 483 necessary: true, 484 consentId: consent.consentId || null, 485 updatedAt: consent.updatedAt || null, 486 version: consent.version || 1 487 }); 488 489 window.dispatchEvent(new CustomEvent('consent_updated', { detail: consent })); 490 } 491 492 function saveSelected() { 493 var existing = readConsent() || {}; 494 var consent = { 495 marketing: hasEl(IDS.marketing) ? getChecked(IDS.marketing) : false, 496 statistics: hasEl(IDS.statistics) ? getChecked(IDS.statistics) : false, 497 functional: hasEl(IDS.functional) ? getChecked(IDS.functional) : false, 498 necessary: true, 499 consentId: existing.consentId || uuidv4(), 500 updatedAt: new Date().toISOString(), 501 version: (existing.version || 1) 502 }; 503 writeConsent(consent); 504 hideBanner(); 505 applyConsent(consent, "user_action", existing); 506 } 507 508 function declineAll(){ 509 setChecked(IDS.marketing, false); 510 setChecked(IDS.statistics, false); 511 setChecked(IDS.functional, false); 512 saveSelected(); 513 } 514 function reopenConsent() { 515 var stored = readConsent(); 516 517 // If we have stored data, use it. If not (new user), default to true. 518 var statsValue = (stored && stored.hasOwnProperty('statistics')) ? !!stored.statistics : true; 519 var marketingValue = (stored && stored.hasOwnProperty('marketing')) ? !!stored.marketing : true; 520 var functionalValue = (stored && stored.hasOwnProperty('functional')) ? !!stored.functional : true; 521 522 if (hasEl(IDS.statistics)) setChecked(IDS.statistics, statsValue); 523 if (hasEl(IDS.marketing)) setChecked(IDS.marketing, marketingValue); 524 if (hasEl(IDS.functional)) setChecked(IDS.functional, functionalValue); 525
526 // NEW: Re-open is always "customized" mode (user is editing previous consent) 527 consentCustomized = true; 528 updatePrimaryButton(); 529 530 showBanner(); 531 532 // hasStoredConsent is true here because we are RE-opening based on existing data 533 window.dataLayer.push({ event: "consent_ready", hasStoredConsent: true }); 534 } 535 536 function showTab(id) { 537 try { 538 var tabs = document.querySelectorAll(".tab-content"); 539 for (var i = 0; i < tabs.length; i++) { 540 tabs[i].classList.add("hidden"); 541 } 542 var btns = document.querySelectorAll(".cookie-tab"); 543 // for (var j = 0; j < btns.length; j++) { 544 // btns[j].classList.remove("active"); 545 // } 546 var tgt = document.getElementById("tab-" + id); 547 if (tgt) 548 tgt.classList.remove("hidden"); 549 // if (window.event && window.event.target) 550 // window.event.target.classList.add("active"); 551 } catch (e) { } 552 } 553 554 function closeTab(){ 555 declineAll() 556 } 557 558 function toggleSection(event, header) { 559 if (!header) return; 560 561 // 1. Check if the user clicked directly on the checkbox or its custom design. 562 // We use .closest() to see if the click happened inside our checkbox container. 563 var clickedElement = event.target; 564 if ( 565 clickedElement.tagName === 'INPUT' || 566 clickedElement.tagName === 'LABEL' || 567 clickedElement.classList.contains('checkmark') || 568 clickedElement.closest('.checkbox-container') 569 ) { 570 // Let the browser handle the native click/toggle 571 return; 572 } 573 574 // 2. Find the checkbox inside this specific header 575 var checkbox = header.querySelector('input[type="checkbox"]'); 576 577 // 3. Toggle its checked state manually since the user clicked the row background 578 if (checkbox) { 579 checkbox.checked = !checkbox.checked; 580 581 // 4. Important: Manually trigger the 'change' event 582 // because changing .checked via JS doesn't fire it automatically. 583 checkbox.dispatchEvent(new Event('change', { bubbles: true })); 584 } 585 } 586 587 window.saveSelected = saveSelected; 588 window.declineAll = declineAll; 589 window.reopenConsent = reopenConsent; 590 window.showTab = showTab; 591 window.closeTab = closeTab; 592 window.toggleSection = toggleSection; 593 window.handlePrimaryClick = handlePrimaryClick; // NEW 594 595 // 3. Updated initCookie: Logic remains clean 596 function initCookie() { 597 injectHTML(); 598 599 // NEW: Setup change listeners once (right after HTML is injected) 600 setupCheckboxListeners(); 601 setupBackgroundDismiss(); 602 603 var stored = readConsent(); 604 var isExpired = isConsentExpired(stored); 605 606 // Logic: If we have valid (non-expired) consent, hide the banner 607 // Logic: If we have valid (non-expired) consent, hide the banner 608 if (stored && !isExpired) { 609 if (hasEl(IDS.marketing)) setChecked(IDS.marketing, !!stored.marketing); 610 if (hasEl(IDS.statistics)) setChecked(IDS.statistics, !!stored.statistics); 611 if (hasEl(IDS.functional)) setChecked(IDS.functional, !!stored.functional); 612 hideBanner(); 613 } 614 // Logic: First time / expired â UNMARKED checkboxes + "Accept All" button 615 else { 616 if (hasEl(IDS.statistics)) setChecked(IDS.statistics, false); 617 if (hasEl(IDS.marketing)) setChecked(IDS.marketing, false); 618 if (hasEl(IDS.functional)) setChecked(IDS.functional, false); 619 620 consentCustomized = false; 621 updatePrimaryButton(); 622 623 showBanner(); 624 625 window.dataLayer.push({ 626 event: "consent_ready", 627 hasStoredConsent: false, 628 }); 629 } 630 } 631 632 function loadScript(src, callback, errorCallback) { 633 var s = document.createElement('script'); 634 s.src = src; 635 s.defer = true; 636 s.onload = callback; 637 s.onerror = errorCallback; 638 document.head.appendChild(s); 639 } 640 641 642 var domain = window.location.hostname; 643 // example: optimus, site.ee 644 645 var lang = document.documentElement.lang || navigator.language || 'en'; 646 lang = lang.toLowerCase().substring(0, 2); 647 648 domain = domain.replace('www.', ''); 649 650 var configFile = '/cookie/translations/cookie-' + lang + '.js' + versionParam; 651 var defaultFile = '/cookie/translations/cookie-en.js' + versionParam; 652 653 function loadConfigAndInit() { 654 loadScript(configFile, function () { 655 initCookie(); 656 }, function () { 657 if (configFile === defaultFile) { 658 initCookie(); 659 return; 660 } 661 662 loadScript(defaultFile, function () { 663 initCookie(); 664 }, function () { 665 // The banner still works with the inline fallback labels. 666 initCookie(); 667 }); 668 }); 669 } 670 671 // Run when DOM is ready 672 if (document.readyState === "loading") { 673 document.addEventListener("DOMContentLoaded", loadConfigAndInit); 674 } else { 675 loadConfigAndInit() 676 } 677})();
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.