1(function (window, document) { 2 'use strict'; 3 4 if (!window || !document) { 5 return; 6 } 7 8 const STORAGE_KEY = 'aura_cookie_consent'; 9 // Set when the visitor closes the banner without choosing: it stays closed for the rest of the browser session. 10 const CLOSED_KEY = 'aura_cookie_banner_closed'; 11 12 function safeParse(value) { 13 try { 14 return value ? JSON.parse(value) : null; 15 } catch (error) { 16 return null; 17 } 18 } 19 20 const CookieConsent = { 21 initialized: false, 22 options: null, 23 24 init: function (options) { 25 if (this.initialized) { 26 this.options = options || this.options || {}; 27 return; 28 } 29 30 this.options = options || {}; 31 this.initialized = true; 32 this.setupEvents(); 33 34 const serverConsent = this.getServerConsent(); 35 const localConsent = this.getConsent(); 36 const current = serverConsent || (localConsent && !this.isExpired(localConsent) ? localConsent : null); 37 // The visitor's choice, or until they make one, the default for their country (options.*Default). 38 const prefs = current ? current.preferences || {} : this.defaultPreferences(); 39 40 this.startGoogleTags(prefs); 41 this.applyConsent(prefs); 42 43 if (serverConsent) { 44 this.persistConsent(serverConsent); 45 this.showCollapsedTrigger(); 46 47 if (serverConsent.needsSessionSync) { 48 this.sendToServer(serverConsent.preferences || {}); 49 } 50 return; 51 } 52 53 if (current) { 54 this.showCollapsedTrigger(); 55 this.sendToServer(localConsent.preferences || {}); 56 return; 57 } 58 59 this.clearConsent(); 60 if ((this.options.initialMode || 'popup') === 'collapsed' || this.bannerClosed()) { 61 this.showCollapsedTrigger(); 62 } else { 63 this.showBanner(); 64 } 65 }, 66 67 getServerConsent: function () { 68 const consent = this.options.serverConsent || null; 69 if (!consent || !consent.preferences) { 70 return null; 71 } 72 73 return consent; 74 }, 75 76 getConsent: function () { 77 try { 78 return safeParse(window.localStorage.getItem(STORAGE_KEY)); 79 } catch (error) { 80 return null; 81 } 82 }, 83 84 isExpired: function (consent) { 85 if (!consent || !consent.timestamp) { 86 return true; 87 } 88 89 const expiry = new Date(consent.timestamp); 90 expiry.setDate(expiry.getDate() + (this.options.expiryDays || 182)); 91 return new Date() > expiry; 92 }, 93 94 isBannerActive: function () { 95 const banner = document.getElementById('cookie-consent-banner'); 96 const modal = document.getElementById('cookie-settings-modal'); 97 98 if (!banner || banner.classList.contains('d-none')) { 99 return false; 100 } 101 102 return !modal || modal.classList.contains('d-none'); 103 }, 104 105 acceptAll: function () { 106 this.saveConsent({ 107 necessary: true, 108 preferences: true, 109 analytics: true, 110 marketing: true, 111 }); 112 }, 113 114 showBanner: function () { 115 const banner = document.getElementById('cookie-consent-banner'); 116 if (banner) { 117 banner.classList.remove('d-none'); 118 banner.classList.add('show'); 119 } 120 121 this.hideCollapsedTrigger(); 122 }, 123 124 // Closing is not a choice: the defaults stay in force and the banner stays closed for this browser session. 125 closeBanner: function () { 126 try { 127 window.sessionStorage.setItem(CLOSED_KEY, '1'); 128 } catch (error) { 129 // storage blocked: the banner comes back on the next page 130 } 131 this.showCollapsedTrigger(); 132 }, 133 134 bannerClosed: function () { 135 try { 136 return window.sessionStorage.getItem(CLOSED_KEY) === '1'; 137 } catch (error) { 138 return false;
139 } 140 }, 141 142 hideBanner: function () { 143 const banner = document.getElementById('cookie-consent-banner'); 144 if (banner) { 145 banner.classList.remove('show'); 146 banner.classList.add('d-none'); 147 } 148 }, 149 150 showCollapsedTrigger: function () { 151 this.hideBanner(); 152 153 const collapsed = document.getElementById('cookie-consent-collapsed'); 154 if (collapsed) { 155 collapsed.classList.remove('d-none'); 156 } 157 }, 158 159 hideCollapsedTrigger: function () { 160 const collapsed = document.getElementById('cookie-consent-collapsed'); 161 if (collapsed) { 162 collapsed.classList.add('d-none'); 163 } 164 }, 165 166 showModal: function () { 167 const modal = document.getElementById('cookie-settings-modal'); 168 if (!modal) { 169 return; 170 } 171 172 modal.classList.remove('d-none'); 173 modal.classList.add('show', 'd-flex');
174 const consent = this.getConsent() || this.getServerConsent(); 175 if (consent && consent.preferences) { 176 this.loadPreferencesToModal(consent.preferences); 177 } else { 178 this.loadPreferencesToModal(this.defaultPreferences()); 179 } 180 }, 181 182 hideModal: function () { 183 const modal = document.getElementById('cookie-settings-modal'); 184 if (modal) { 185 modal.classList.remove('show'); 186 modal.classList.remove('d-flex'); 187 modal.classList.add('d-none'); 188 } 189 }, 190 191 defaultPreferences: function () { 192 return { 193 necessary: true, 194 preferences: false, 195 analytics: !!this.options.analyticsDefault, 196 marketing: !!this.options.marketingDefault, 197 }; 198 }, 199 200 loadPreferencesToModal: function (prefs) { 201 const normalized = this.normalizePreferences(prefs || {}); 202 const preferenceToggle = document.getElementById('cookie-preferences'); 203 const analyticsToggle = document.getElementById('cookie-analytics'); 204 const marketingToggle = document.getElementById('cookie-marketing'); 205 206 if (preferenceToggle) { 207 preferenceToggle.checked = !!normalized.preferences; 208 } 209 if (analyticsToggle) { 210 analyticsToggle.checked = !!normalized.analytics; 211 } 212 if (marketingToggle) { 213 marketingToggle.checked = !!normalized.marketing; 214 } 215 }, 216 217 normalizePreferences: function (prefs) { 218 return { 219 necessary: true, 220 preferences: !!prefs.preferences, 221 analytics: !!prefs.analytics, 222 marketing: !!prefs.marketing, 223 }; 224 }, 225 226 persistConsent: function (consent) { 227 try { 228 window.localStorage.setItem(STORAGE_KEY, JSON.stringify({ 229 preferences: this.normalizePreferences(consent.preferences || {}), 230 timestamp: consent.timestamp || new Date().toISOString(), 231 version: consent.version || '2.0' 232 })); 233 } catch (error) { 234 console.error('Cookie consent save failed:', error); 235 } 236 }, 237 238 clearConsent: function () { 239 try { 240 window.localStorage.removeItem(STORAGE_KEY); 241 } catch (error) { 242 console.error('Cookie consent clear failed:', error); 243 } 244 }, 245 246 saveConsent: function (prefs) { 247 const normalized = this.normalizePreferences(prefs || {}); 248 const consent = { 249 preferences: normalized, 250 timestamp: new Date().toISOString(), 251 version: '2.0', 252 }; 253 254 this.persistConsent(consent); 255 256 this.sendToServer(normalized); 257 if (window.ENABLE_GOOGLE_TAG_MANAGER && window.GTM_ID) { 258 this.setGoogleConsent('update', normalized); 259 window.dataLayer.push({ event: 'cookie_consent_update' }); 260 } 261 this.applyConsent(normalized); 262 this.showCollapsedTrigger(); 263 this.hideModal(); 264 }, 265 266 sendToServer: function (prefs) { 267 if (!this.options.saveUrl) { 268 return; 269 } 270 271 window.fetch(this.options.saveUrl, { 272 method: 'POST', 273 credentials: 'same-origin', 274 headers: { 275 'Accept': 'application/json', 276 'Content-Type': 'application/json', 277 'X-Requested-With': 'XMLHttpRequest', 278 'X-CSRF-Token': this.options.csrfToken || '', 279 }, 280 body: JSON.stringify({ consent: prefs }), 281 }).catch(function (error) { 282 console.error('Server save failed:', error); 283 }); 284 }, 285 286 // The scripts the site loads itself (GTM loads on every page, see startGoogleTags()). 287 applyConsent: function (prefs) { 288 const normalized = this.normalizePreferences(prefs || {}); 289 290 if (normalized.analytics) { 291 this.enableAnalytics(); 292 } 293 if (normalized.marketing) { 294 this.enableMarketing(); 295 } 296 }, 297 298 // Google Consent Mode v2: GTM loads on every page, after the consent default, so each tag stores and sends 299 // only what the visitor allows (Google tags check it themselves; other tags need consent settings in GTM). 300 startGoogleTags: function (prefs) { 301 if (!window.ENABLE_GOOGLE_TAG_MANAGER || !window.GTM_ID) { 302 return; 303 } 304 305 this.setGoogleConsent('default', prefs); 306 this.enableGoogleTagManager(); 307 }, 308 309 // command: 'default' (before GTM loads) or 'update' (the visitor chose). 310 setGoogleConsent: function (command, prefs) { 311 const normalized = this.normalizePreferences(prefs || {}); 312 const analytics = normalized.analytics ? 'granted' : 'denied'; 313 const marketing = normalized.marketing ? 'granted' : 'denied'; 314 315 window.dataLayer = window.dataLayer || []; 316 window.gtag = window.gtag || function () { window.dataLayer.push(arguments); }; 317 window.gtag('consent', command, { 318 analytics_storage: analytics, 319 ad_storage: marketing, 320 ad_user_data: marketing, 321 ad_personalization: marketing, 322 }); 323 }, 324 325 enableGoogleTagManager: function () { 326 if (!window.ENABLE_GOOGLE_TAG_MANAGER || !window.GTM_ID || document.querySelector('script[data-cookie-consent-gtm]')) { 327 return; 328 } 329 330 window.dataLayer = window.dataLayer || []; 331 window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); 332 333 const gtmScript = document.createElement('script'); 334 gtmScript.async = true; 335 gtmScript.src = 'https://www.googletagmanager.com/gtm.js?id=' + encodeURIComponent(window.GTM_ID); 336 gtmScript.setAttribute('data-cookie-consent-gtm', '1'); 337 document.head.appendChild(gtmScript); 338 }, 339 340 enableAnalytics: function () { 341 if (window.ENABLE_GOOGLE_ANALYTICS && window.GA_TRACKING_ID && !document.querySelector('script[data-cookie-consent-ga]')) { 342 const gaScript = document.createElement('script'); 343 gaScript.async = true; 344 gaScript.src = 'https://www.google-analytics.com/analytics.js'; 345 gaScript.setAttribute('data-cookie-consent-ga', '1'); 346 document.head.appendChild(gaScript); 347 } 348 349 if (window.ENABLE_GOOGLE_GTAG && window.GTAG_ID && !document.querySelector('script[data-cookie-consent-gtag]')) { 350 const gtagScript = document.createElement('script'); 351 gtagScript.async = true; 352 gtagScript.src = 'https://www.googletagmanager.com/gtag/js?i
352d=' + encodeURIComponent(window.GTAG_ID); 353 gtagScript.setAttribute('data-cookie-consent-gtag', '1'); 354 document.head.appendChild(gtagScript); 355 window.dataLayer = window.dataLayer || []; 356 window.gtag = window.gtag || function () { window.dataLayer.push(arguments); }; 357 window.gtag('js', new Date()); 358 window.gtag('config', window.GTAG_ID); 359 } 360 }, 361 362 enableMarketing: function () { 363 if (window.FB_PIXEL_ID && !window.fbq) { 364 !(function (f, b, e, v, n, t, s) { 365 if (f.fbq) { 366 return; 367 } 368 n = f.fbq = function () { 369 n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments); 370 }; 371 if (!f._fbq) { 372 f._fbq = n; 373 } 374 n.push = n; 375 n.loaded = true; 376 n.version = '2.0'; 377 n.queue = []; 378 t = b.createElement(e); 379 t.async = true; 380 t.src = v; 381 s = b.getElementsByTagName(e)[0]; 382 s.parentNode.insertBefore(t, s); 383 })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js'); 384 window.fbq('init', window.FB_PIXEL_ID); 385 window.fbq('track', 'PageView'); 386 } 387 }, 388 389 setupEvents: function () { 390 const self = this; 391 392 window.openCookieSettings = function () { 393 self.showModal(); 394 }; 395 396 document.getElementById('cookie-consent-collapsed-trigger')?.addEventListener('click', function (event) { 397 event.preventDefault(); 398 self.showModal(); 399 }); 400 401 document.getElementById('cookie-accept-all')?.addEventListener('click', function () { 402 self.acceptAll(); 403 }); 404 405 document.getElementById('cookie-banner-close')?.addEventListener('click', function () { 406 self.closeBanner(); 407 }); 408 409 document.getElementById('cookie-reject-all')?.addEventListener('click', function () { 410 self.saveConsent({ 411 necessary: true, 412 preferences: false, 413 analytics: false, 414 marketing: false, 415 }); 416 }); 417 418 document.getElementById('cookie-settings-btn')?.addEventListener('click', function () { 419 self.showModal(); 420 }); 421 422 document.getElementById('cookie-settings-close')?.addEventListener('click', function () { 423 self.hideModal(); 424 }); 425 426 document.getElementById('cookie-save-settings')?.addEventListener('click', function () { 427 self.saveConsent({ 428 necessary: true, 429 preferences: document.getElementById('cookie-preferences')?.checked || false, 430 analytics: document.getElementById('cookie-analytics')?.checked || false, 431 marketing: document.getElementById('cookie-marketing')?.checked || false, 432 }); 433 }); 434 435 document.getElementById('cookie-settings-modal')?.addEventListener('click', function (event) { 436 if (event.target === this) { 437 self.hideModal(); 438 } 439 }); 440 441 document.addEventListener('keydown', function (event) { 442 if (!self.isBannerActive()) { 443 return; 444 } 445 446 if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { 447 return; 448 } 449 450 if (event.key === 'Escape') { 451 event.preventDefault(); 452 self.closeBanner(); 453 } 454 }); 455 }, 456 }; 457 458 window.AuraCookieConsent = CookieConsent; 459})(window, document);
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.