1/** 2 * Cookie popup v2.3 3 * 4 * Cookie settings are send to server endpoint. 5 * 6 * Update v2.3 7 * - added support for Consent Mode 2.0 8 * - UserData and Personalization cookies added 9 * - switches for UserData and Personalization cookies are hidden , it's controled by marketing switch 10 * - modal width responsivity was adjusted 11 * 12 * Update v2.2 13 * - added events consentmodalclose and consentmodalwillnotopen 14 */ 15import THEMES from './cookies-consent-themes.js'; 16import { chevronDown, chevronLeft } from './svg-icons.js'; 17 18const getCookie = (cname) => { 19 var name = cname + "="; 20 var decodedCookie = decodeURIComponent(document.cookie); 21 var ca = decodedCookie.split(';'); 22 for (var i = 0; i < ca.length; i++) { 23 var c = ca[i]; 24 while (c.charAt(0) == ' ') { 25 c = c.substring(1); 26 } 27 if (c.indexOf(name) == 0) { 28 return c.substring(name.length, c.length); 29 } 30 } 31 return ""; 32} 33 34class CookiesConsent extends HTMLElement { 35 constructor() { 36 super(); 37 38 this.debug = this.getAttribute("debug") === "true" ? true : false; 39 40 this.shadow = this.attachShadow({ mode: "closed" }); 41 42 // style props 43 this.popupTheme = this.getAttribute("theme"); 44 45 this.delay = this.hasAttribute("delay") && !isNaN(parseInt(this.getAttribute("delay"), 10)) ? parseInt(this.getAttribute("delay"), 10) : 2000; 46 47 // better props check , hasattribute , attribute names are case insensitive 48 const url = this.getAttribute("url"); 49 const cookie = parseInt(this.getAttribute("cookie"), 10); 50 //const cookie = this.hasAttribute("cookie") ? parseInt(this.getAttribute("cookie"), 10) : parseInt(getCookie("CMSCookieLevel"), 10); 51 const isCookieValid = (() => { 52 if (isNaN(cookie)) return false; 53 if (cookie === 0 || cookie === 1000) return true; 54 if (cookie > 300 && cookie < 315) return true; 55 return false; 56 })(); 57 const loadedConsent = isCookieValid ? cookie : 0; 58 59 this.dlog('cookie', cookie); 60 this.dlog('consent', loadedConsent); 61 62 let [analytics_storage, ad_storage, ad_user_data, ad_personalization] = (() => { 63 if (loadedConsent === 0) return [false, false, false, false]; 64 if (loadedConsent === 1000) return [true, true, true, true]; 65 const tempConsent = loadedConsent - 300; 66 return [ 67 (tempConsent & 1) ? true : false, 68 (tempConsent & 2) ? true : false, 69 (tempConsent & 4) ? true : false, 70 (tempConsent & 8) ? true : false, 71 ]; 72 })(); 73 74 const getConsentLevel = () => { 75 if (!analytics_storage && !ad_storage && !ad_user_data && !ad_personalization) return 0; 76 if (analytics_storage && ad_storage && ad_user_data && ad_personalization) return 1000; 77 const tempResult = [ 78 analytics_storage ? 1 : 0, 79 ad_storage ? 2 : 0, 80 ad_user_data ? 4 : 0, 81 ad_personalization ? 8 : 0 82 ]; 83 return 300 + tempResult.reduce((acc, n) => acc + n, 0); 84 } 85 86 this.dlog('consent constructed'); 87 88 this.render(); 89 90 const $cookieModal = this.shadow.querySelector("#cookie-modal"); 91 const $optionsButton = $cookieModal.querySelector(`[data-control="open-options"]`); 92 const $backButton = $cookieModal.querySelector(`[data-control="back-button"]`); 93 const $necessaryButton = $cookieModal.querySelector(`[data-control="necessary-button"]`); 94 const $moreOptionsSection = $cookieModal.querySelector(`[data-control="more-options-section"]`); 95 const $acceptAllButton = $cookieModal.querySelector(`[data-control="accept-all"]`); 96 const $denyAllButton = $cookieModal.querySelector(`[data-control="deny-all"]`); 97 const $acceptButton = $cookieModal.querySelector(`[data-control="accept"]`); 98 const $acceptTechnicaOnlylButton = $cookieModal.querySelector(`[data-control="accept-technical-only"]`); 99 const $switchAnalyticsButton = $cookieModal.querySelector(`[data-control="switch-analytics"]`); 100 const $switchMarketingButton = $cookieModal.querySelector(`[data-control="switch-marketing"]`); 101 const $switchUserDataButton = $cookieModal.querySelector(`[data-control="switch-user-data"]`); 102 const $switchPersonalizationButton = $cookieModal.querySelector(`[data-control
102="switch-personalization"]`); 103 104 const openModal = () => $cookieModal.showModal(); 105 const closeModal = () => { 106 $cookieModal.close(); 107 const event = new CustomEvent("consentmodalclose", { bubbles: false }); 108 window.dispatchEvent(event); 109 this.dlog('dispatch consentmodalclose'); 110 } 111 112 if (!isCookieValid) { 113 setTimeout(openModal, this.delay); 114 } else { 115 const event = new CustomEvent("consentmodalwillnotopen", { bubbles: false }); 116 window.dispatchEvent(event); 117 this.dlog('dispatch consentmodalwillnotopen'); 118 } 119 120 // open on event dispatch, mostly settings button 121 window.addEventListener('openconsentmodal', () => { 122 openModal(); 123 }); 124 125 if (analytics_storage) $switchAnalyticsButton.classList.add("granted"); 126 if (ad_storage) $switchMarketingButton.classList.add("granted"); 127 if (ad_user_data) $switchUserDataButton.classList.add("granted"); 128 if (ad_personalization) $switchPersonalizationButton.classList.add("granted"); 129 130 $cookieModal.addEventListener("click", (e) => { 131 if (e.target === $optionsButton) { 132 $moreOptionsSection.classList.toggle("open"); 133 $acceptAllButton.classList.toggle("disabled"); 134 $denyAllButton.classList.toggle("disabled"); 135 $optionsButton.classList.toggle("disabled"); 136 $backButton.classList.toggle("disabled"); 137 $necessaryButton.classList.toggle("disabled"); 138 return; 139 } 140 141 if (e.target === $backButton) { 142 $moreOptionsSection.classList.toggle("open"); 143 $acceptAllButton.classList.toggle("disabled"); 144 $denyAllButton.classList.toggle("disabled"); 145 $optionsButton.classList.toggle("disabled"); 146 $backButton.classList.toggle("disabled"); 147 $necessaryButton.classList.toggle("disabled"); 148 return; 149 } 150 151 if (e.target === $acceptAllButton) { 152 postConsentCookies(1000); 153 analytics_storage = true; 154 ad_storage = true; 155 ad_user_data = true; 156 ad_personalization = true; 157 $switchAnalyticsButton.classList.add("granted"); 158 $switchMarketingButton.classList.add("granted"); 159 $switchUserDataButton.classList.add("granted"); 160 $switchPersonalizationButton.classList.add("granted"); 161 closeModal(); 162 return; 163 } 164 if (e.target === $acceptButton) { 165 postConsentCookies(getConsentLevel()); 166 closeModal(); 167 return; 168 } 169 if (e.target === $switchAnalyticsButton) { 170 $switchAnalyticsButton.classList.toggle("granted"); 171 analytics_storage = !analytics_storage; 172 return; 173 } 174 if (e.target === $switchMarketingButton) { 175 $switchMarketingButton.classList.toggle("granted"); 176 ad_storage = !ad_storage; 177 178 // control of user data and personalization cookies is handled by marketing switch 179 $switchUserDataButton.classList.toggle("granted"); 180 $switchPersonalizationButton.classList.toggle("granted"); 181 ad_user_data = !ad_user_data; 182 ad_personalization = !ad_personalization; 183 return; 184 } 185 // if (e.target === $switchUserDataButton) { 186 // $switchUserDataButton.classList.toggle("granted"); 187 // ad_user_data = !ad_user_data; 188 // return; 189 // } 190 // if (e.target === $switchPersonalizationButton) { 191 // $switchPersonalizationButton.classList.toggle("granted"); 192 // ad_personalization = !ad_personalization; 193 // return; 194 // } 195 if (e.target === $acceptTechnicaOnlylButton || e.target === $necessaryButton || e.target === $denyAllButton) { 196 postConsentCookies(0); 197 analytics_storage = false; 198 ad_storage = false; 199 ad_user_data = false; 200 ad_personalization = false; 201 $switchAnalyticsButton.classList.remove("granted"); 202 $switchMarketingButton.classList.remove("granted"); 203 $switchUserDataButton.classList.remove("granted"); 204 $switchPersonalizationButton.classList.remove("granted"); 205 closeModal(); 206 return; 207 } 208 209 const $moreButtonElement = e.target.closest(`[data-control
209="more-button"]`); 210 if ($moreButtonElement) { 211 $moreButtonElement.nextElementSibling.classList.toggle("disabled"); 212 $moreButtonElement.classList.toggle("open"); 213 return; 214 } 215 }); 216 217 async function postData(url = "", data = {}) { 218 const response = await fetch(url, { 219 method: "POST", 220 headers: { 221 "Content-Type": "application/json", 222 }, 223 body: JSON.stringify(data), 224 }); 225 return response.json(); 226 } 227 228 async function postConsentCookies(newLevel = -1000) { 229 const response = await postData(url, { "newCookiesLevel": newLevel }); 230 231 // handle response.ok is false 232 233 const event = new CustomEvent("consentupdate", { bubbles: false, detail: { consentLevel: newLevel } }); 234 window.dispatchEvent(event); 235 } 236 } 237 238 dlog = (...args) => this.debug && console.log(...args); 239 240 getText(str) { 241 return this.getAttribute(str) || ""; 242 } 243 244 placeText(template) { 245 const replacer = (match, p1, p2, p3, offset, string) => { 246 const str = this.getText(p2); 247 if (str === "") return ""; 248 return `${p1}${str}${p3}`; 249 } 250 return template.replace(/([^\{\{]*)\{\{ ([^\}\}]*) \}\}(.*)/, replacer); 251 } 252 253 render() { 254 this.shadow.innerHTML = ` 255 <dialog id="cookie-modal" class="cookie-modal"> 256 <button data-control="back-button" class="cookie-modal__content_back-text disabled" type="button">${chevronLeft}${this.getText("BackText")}</button> 257 <div class="cookie-modal__content"> 258 <button data-control="necessary-button" class="cookie-modal__content_necessary-text" type="button" style="display:none">${this.getText("OnlyNecessaryText")}</button> 259 <h2>${this.getText("Name")}</h2> 260 <div class="small-text"> 261 ${this.placeText('<h3>{{ Subtitle }}</h3>')} 262 ${this.placeText('<p>{{ Text }}</p>')} 263 ${this.placeText('<h4>{{ Subtitle2 }}</h2>')} 264 ${this.placeText('<p>{{ Text2 }}</p>')} 265 </div> 266 <div class="buttons u-center"> 267 <button data-control="open-options" class="more-options-btn btn btn-more btn-secondary" type="button">${this.getText("MoreOptionsBTN")}</button> 268 <button data-control="deny-all" class="btn btn-more" type="button">${this.getText("DenyButton")}</button> 269 <button data-control="accept-all" class="btn btn-more" type="button" autofocus>${this.getText("AcceptButton")}</button> 270 </div> 271 <div class="more-options" data-control="more-options-section"> 272 <div class="more-options__option"> 273 <div class="more-options__option--text"> 274 <p data-control="more-button" class="more-options__option--name">${chevronDown}${this.getText("Technical")}</p> 275 <p class="more-options__option--desc disabled">${this.getText("TechnicalDesc")}</p> 276 </div> 277 <span class="switch tech-cookies granted"></span> 278 </div> 279 <div class="more-options__option"> 280 <div class="more-options__option--text"> 281 <p data-control="more-button" class="more-options__option--name">${chevronDown}${this.getText("Analytical")}</p> 282 <p class="more-options__option--desc disabled">${this.getText("AnalyticalDesc")}</p> 283 </div> 284 <button class="switch" data-control="switch-analytics" type="button"></button> 285 </div> 286 <div class="more-options__option"> 287 <div class="more-options__option--text"> 288 <p data-control="more-button" class="more-options__option--name">${chevronDown}${this.getText("Marketing")}</p> 289 <p class="more-options__option--desc disabled">${this.getText("MarketingDesc")}</p> 290 </div> 291 <button class="switch" data-control="switch-marketing" type="button"></button> 292 </div> 293 <div class="more-options__option" style="display:none"> 294 <div class="more-options__option--text"> 295 <p data-control="more-button" class="more-options__option--name">${chevronDown}${this.getText("UserData")}</p> 296 <p class="more-options__option--desc disabled">${this.getText("UserDataDesc")}</p> 297 </div>
298 <button class="switch" data-control="switch-user-data" type="button"></button> 299 </div> 300 <div class="more-options__option" style="display:none"> 301 <div class="more-options__option--text"> 302 <p data-control="more-button" class="more-options__option--name">${chevronDown}${this.getText("Personalization")}</p> 303 <p class="more-options__option--desc disabled">${this.getText("PersonalizationDesc")}</p> 304 </div> 305 <button class="switch" data-control="switch-personalization" type="button"></button> 306 </div> 307 <div class="buttons u-center"> 308 ${this.placeText(`<button data-control="accept-technical-only" class="cookie-btn-text" type="button">{{ OnlyNecessary }}</button>`)} 309 <button data-control="accept" class="btn" type="button">${this.getText("Accept")}</button> 310 </div> 311 </div> 312 </div> 313</dialog> 314 315 316 317<style> 318/*# Cookie modal #*/ 319 320*, *:before, *:after { 321 box-sizing: border-box; 322} 323 324.cookie-modal{ 325 border: none; 326 overflow: auto; 327 border-radius: 25.5px; 328 background: #fff; 329 padding: 20px 25px; 330 z-index: 11000; 331} 332 333::backdrop { 334 backdrop-filter: blur(3px); 335 background-color: rgba(0,0,0,0.5); 336} 337 338.cookie-modal__content{ 339 font-family: 'Montserrat',sans-serif; 340 font-size: 16px; 341 font-weight: normal; 342 color: #000; 343 /*width: 60ch; */ 344 max-width: min(65ch, 90vw); 345 margin: auto; 346 position: relative; 347} 348 349.cookie-modal__content h2{ 350 margin-top: 30px; 351} 352 353.cookie-modal__content a{ 354 text-decoration: none; 355 font-weight: 700; 356 color: #0076ff; 357} 358 359.cookie-modal__content .small-text p{ 360 color:#3f3f3f; 361} 362 363.cookie-modal__content_back-text, 364.cookie-modal__content_necessary-text { 365 background: none; 366 color: #000; 367 border: 0; 368 position: absolute; 369 right: 1.5rem; 370 transition: all .15s; 371 cursor: pointer; 372} 373 374.cookie-modal__content_necessary-text { 375 color: #757575; 376} 377 378.cookie-modal__content_back-text svg{ 379 width: 1em; 380 height: 0.7em; 381} 382 383.cookie-modal__content_back-text:hover, 384.cookie-modal__content_necessary-text:hover, 385.cookie-modal__content_back-text:hover:after, 386.cookie-modal__content_back-text:hover:before{ 387 text-shadow: 1px 0px; 388} 389 390.more-options{ 391 height: 0; 392 visibility: hidden; 393 transition: all 1s; 394 overflow: hidden; 395} 396 397.more-options.open{ 398 height: auto; 399 visibility: visible; 400 /*padding-top: 1rem;*/ 401} 402 403.more-options__option{ 404 display: flex; 405 flex-direction: row; 406 justify-content: space-between; 407 background: #e8e8e8; 408 padding: 0 0.5rem 0 1.5rem; 409 margin-bottom: 0.5rem; 410 border-radius: 15px; 411} 412 413.more-options__option--name { 414 font-weight: 600; 415 cursor: pointer; 416 position: relative; 417 margin-top: 1.1rem; 418} 419 420.more-options__option--name svg{ 421 width: 0.7em; 422 margin-right: 0.25em; 423 transition: transform 0.2s 424} 425 426.more-options__option--name.open svg { 427 transform: rotate(180deg); 428} 429 430.more-options__option .switch{ 431 width: 55px; 432 height: 25px; 433 background: rgba(172, 168, 168, .8); 434 border-radius: 20px; 435 border: 1px solid rgb(172, 168, 168); 436 display: block; 437 position: relative; 438 cursor: pointer; 439 flex-shrink: 0; 440 margin-top: 0.8rem; 441} 442 443.more-options__option .switch.granted { 444 background: #08768c; 445} 446 447.more-options__option span.switch.granted{ 448 background: rgba(8,118,140, 0.5); 449} 450 451 452.more-options__option .switch:after{ 453 content: ""; 454 height: 25px; 455 width: 25px; 456 border-radius: 20px; 457 border: 1px solid #fff; 458 background: #fff; 459 position: absolute; 460 top: -1px; 461 left: 0; 462 right: 26px; 463 transition: all .45s; 464} 465 466.more-options__option .switch.granted:after { 467 border: 1px solid #08768c; 468 left: 26px; 469 right: 0; 470} 471 472.more-options__option span.switch.granted:after { 473 border: 1px solid #dfdddd; 474 background: #dfdddd; 475} 476 477.btn { 478 background: #000; 479 color: #fff; 480 display: inline-block; 481 cursor: pointer; 482 font-size: 16px; 483 padding: 1rem 1.9rem; 484 border: none; 485} 486 487.btn-secondary { 488 color: #000; 489 background: #fff; 490} 491 492${this.popupTheme ? THEMES[this.popupTheme] : THEMES.default} 493 494.btn-more { 495 margin-top: 1.25rem; 496 margin-bottom: 1.25rem; 497} 498 499.more-options .buttons, 500.cookie-modal__content .buttons{ 501 margin-top: 1rem; 502 display: flex; 503 gap: 0.7rem; 504 flex-wrap: wrap; 505 justify-content: flex-end; 506} 507 508.more-options .buttons button { 509 margin-bottom: 1rem; 510} 511 512.cookie-btn-text{ 513 border: none; 514 background: transparent; 515 text-decoration: underline; 516 color: #08768c; 517 text-shadow: 0px 0px; 518 transition: all .35s; 519 margin-right: 1rem; 520} 521 522.cookie-btn-text:hover{ 523 text-shadow: 1px 0px; 524} 525 526@media (max-width: 767px) { 527 .cookie-modal { 528 padding: 2rem; 529 } 530 .cookie-modal__content{ 531 font-size: 14px; 532 } 533 .cookie-modal__content .buttons, .more-options .buttons { 534 display: block; 535 } 536 .btn { 537 display: block; 538 width: 100%; 539 } 540 .more-options__option p, .more-options__option strong .more-options__option a { 541 font-size: 11px; 542 } 543 .more-options__option { 544 padding-left: 1rem; 545 } 546} 547 548.disabled { 549 display: none; 550} 551</style> 552 `; 553 } 554} 555 556customElements.define("cookies-consent", CookiesConsent); 557 558window.addEventListener('load', () => {
559 Array.from(document.querySelectorAll('[data-control="open-consent-modal"]')).map(e => e.addEventListener('click', (e) => { 560 e.preventDefault(); 561 const event = new CustomEvent("openconsentmodal", { bubbles: false }); 562 window.dispatchEvent(event); 563 })); 564}); 565
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.