1/***************************************************** 2 * 3 * FnnCookieManager prototype system 4 * 5 ******************************************************/ 6var proto_methods = { 7 find: function(selector) { 8 const node = this.node; 9 return node.querySelectorAll(selector); 10 }, 11 12 /*************************************************** 13 * create a cookie (lifetime in days) 14 ***************************************************/ 15 setCookie: function(cookieName, cookieValue, lifetime) { 16 const d = new Date(); 17 d.setTime(d.getTime() + (lifetime*24*60*60*1000)); 18 const expires = "expires="+ d.toUTCString(); 19 document.cookie = cookieName + "=" + cookieValue + ";" + expires + ";path=/"; 20 }, 21 22 /*************************************************** 23 * get a cookie value 24 ***************************************************/ 25 getCookie: function(cookieName) { 26 const name = cookieName + "="; 27 const decodedCookie = decodeURIComponent(document.cookie); 28 const ca = decodedCookie.split(';'); 29 for(let i = 0; i <ca.length; i++) { 30 let c = ca[i]; 31 while (c.charAt(0) == ' ') { 32 c = c.substring(1); 33 } 34 if (c.indexOf(name) == 0) { 35 return c.substring(name.length, c.length); 36 } 37 } 38 return ""; 39 }, 40 41 /*************************************************** 42 * check if the cookie has value 43 ***************************************************/ 44 checkCookie: function(cookieName) { 45 return fnncm().getCookie(cookieName) == '' ? false : true; 46 }, 47 48 /*************************************************** 49 * remove cookie 50 ***************************************************/ 51 removeCookie: function(cookieName,cookieDomain) { 52 document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;domain='+cookieDomain; 53 }, 54 55 /*************************************************** 56 * Shows the cookie bar 57 ***************************************************/ 58 showCookiebar: function() { 59 document.querySelector('body').classList.add('fnn-cookiemanager-deactivate'); 60 document.querySelector('.fnn-cookiemanager').setAttribute('style', 'display:block;opacity:1;'); 61 if (document.querySelector('.fnn-cookiemanager').classList.contains('layout-middle-overlay')) { 62 fnncm().showCookieManager(); 63 } else { 64 document.querySelector('.fnn-cookiemanager .cm-cookiebar').setAttribute('style', 'display:block;'); 65 } 66 }, 67 68 /*************************************************** 69 * Shows the cookie manager 70 ***************************************************/ 71 showCookieManager: function() { 72 // Deactivate cookiebar 73 document.querySelector('.fnn-cookiemanager .cm-cookiebar').setAttribute('style', 'display:none;'); 74 document.querySelector('.fnn-cookiemanager .cm-cookiebar').setAttribute('aria-modal', 'false'); 75 76 77 // remove existing code elements 78 fnncm().removeCode(); 79 80 // set selected buttons 81 const cookieConsentData = fnncm().getCookie('fnncmCookieConsent'); 82 if (cookieConsentData !== '') { 83 const cookieConsent = JSON.parse(cookieConsentData); 84 let servicesGroupId; 85 for (servicesGroupId in cookieConsent) { 86 if (document.querySelector('[data-group-id="'+servicesGroupId+'"]') != null) { 87 if (cookieConsent[servicesGroupId] === 'yes') { 88 document.querySelector('[data-group-id="'+servicesGroupId+'"] [data-value="no"]').setAttribute('data-selected','no'); 89 document.querySelector('[data-group-id="'+servicesGroupId+'"] [data-value="yes"]').setAttribute('data-selected','yes'); 90 } else { 91 document.querySelector('[data-group-id="'+servicesGroupId+'"] [data-value="no"]').setAttribute('data-selected','yes'); 92 document.querySelector('[data-group-id="'+servicesGroupId+'"] [data-value="yes"]').setAttribute('data-selected','no'); 93 } 94 } 95 } 96 } 97 98 // scroll to top and show cookie manager form 99 window.scrollTo(0, 0); 100 document.querySelector('body').classList.add('fnn-cookiemanager-deactivate'); 101 document.querySelector('.fnn-cookiemanager').setAttribute('style', 'display:block;opacity:1;'); 102 document.querySelector('.fnn-cookiemanager .cm-modal').setAttribute('style', 'display:block;'); 103 document.querySelector('.fnn-cookiemanager .cm-modal .cm-modal-description a').focus(); 104 document.body.parentNode.style.overflow = 'hidden'; 105 }, 106 107 /*************************************************** 108 * hide cookie bar and cookie manager 109 ***************************************************/ 110 hide: function() { 111 document.body.parentNode.style.overflow = 'initial'; 112 document.querySelector('.fnn-cookiemanager .cm-cookiebar').setAttribute('style', 'display:none;'); 113 document.querySelector('.fnn-cookiemanager .cm-modal').setAttribute('style', 'display:none;'); 114 document.querySelector('.fnn-cookiemanager').setAttribute('style', 'display:none;'); 115 document.querySelector('body').classList.remove('fnn-cookiemanager-deactivate'); 116 }, 117 118 /*************************************************** 119 * hide cookie manager only 120 ***************************************************/ 121 hideCookieManager: function() { 122 document.querySelector('.fnn-cookiemanager .cm-modal').setAttribute('style', 'display:none;'); 123 if (fnncm().checkCookie('fnncmCookieConsent')) { 124 document.querySelector('.fnn-cookiemanager').setAttribute('style', 'display:none;'); 125 document.querySelector('body').classList.remove('fnn-cookiemanager-deactivate'); 126 } 127 }, 128 129 /*************************************************** 130 * prepend html into element 131 ***************************************************/ 132 prepend: function(html,tag) { 133 const node = this.node; 134 const s = document.createElement(tag); 135 s.async = true; 136 s.setAttribute('data-cm-code','fnncm'); 137 s.innerHTML = html; 138 node.insertBefore(s, node.firstChild); 139 }, 140 141 /*************************************************** 142 * append html into element 143 ***************************************************/ 144 append: function(html,tag) { 145 const node = this.node; 146 const s = document.createElement(tag); 147 s.async = true; 148 s.setAttribute('data-cm-code','fnncm'); 149 s.innerHTML = html; 150 node.appendChild(s); 151 }, 152 153 /*************************************************** 154 * get js from url and execute callback 155 ***************************************************/ 156 getScript: function(source, callback) { 157 const node = this.node; 158 let s = document.createElement('script'); 159 s.async = true; 160 s.onload = s.onreadystatechange = function( _, isAbort ) { 161 if(isAbort || !s.readyState || /loaded|complete/.test(s.readyState) ) { 162 s.onload = s.onreadystatechange = null; 163 s = undefined; 164 165 if(!isAbort && callback) setTimeout(callback, 0); 166 } 167 }; 168 s.src = source; 169 node.insertBefore(s, node.firstChild); 170 }, 171 172 /***************************************************
173 * remove all code blocks 174 ***************************************************/ 175 removeCode: function() { 176 const codeElements = document.querySelectorAll('[data-cm-code="fnncm"]'); 177 codeElements.forEach(function(currentElement, currentIndex, listObj) { 178 currentElement.remove(); 179 }); 180 }, 181 182 /*************************************************** 183 * remove all service cookies 184 ***************************************************/ 185 removeCookiesByServiceId: function(id) { 186 // remove cookies 187 const service = fnncm().getServiceData(null,id); 188 if (service === null) { return; } 189 190 service.cookies.forEach(function (cookie) { 191 fnncm().removeCookie(cookie.title,cookie.domain); 192 }); 193 }, 194 195 /*************************************************** 196 * check for new services 197 ***************************************************/ 198 checkForNewServices: function() { 199 const cookieConsent = JSON.parse(fnncm().getCookie('fnncmCookieConsent')); 200 for (let id in cmJsonData) { 201 const services = cmJsonData[id].services; 202 203 for (let s = 0; s < services.length; s++) { 204 if(cookieConsent[services[s].uid] === undefined) { 205 return false; 206 } 207 } 208 } 209 return true; 210 }, 211 212 //<editor-fold desc="Init elements functions"> 213 /*************************************************** 214 * initialize all tabs 215 ***************************************************/ 216 initTabs: function() { 217 const node = this.node; 218 // find all tabs 219 const elements = node.querySelectorAll('.cm-tab-item input'); 220 221 if (elements != null) { 222 // set height of tab content to 0 and register click events 223 elements.forEach(function(currentElement, currentIndex, listObj) { 224 // register expand click events 225 currentElement.addEventListener("click", function(clickEvent) { 226 const target = clickEvent.target || clickEvent.srcElement; 227 const tabContentId = target.getAttribute('data-cm-tab-content'); 228 229 if (tabContentId !== null) { 230 node.querySelectorAll('.cm-tab-content [data-tab-content]').forEach(function(tabItemContent) { 231 tabItemContent.setAttribute('style', 'display:none;'); 232 }); 233 234 node.querySelector('.cm-tab-content [data-tab-content="'+tabContentId+'"]').setAttribute('style', 'display:block'); 235 } 236 }, false); 237 238 // set all secondary tab content height to 0 239 const tabContentId = currentElement.getAttribute('data-cm-tab-content'); 240 if (tabContentId !== '1') { 241 node.querySelector('.cm-tab-content [data-tab-content="'+ tabContentId +'"]').setAttribute('style', 'display:none;'); 242 } 243 }); 244 } 245 return 'ok'; 246 }, 247 248 /*************************************************** 249 * initialize all expandable containers 250 ***************************************************/ 251 initExpandables: function() { 252 const node = this.node; 253 // find all expandable elements 254 const elements = node.querySelectorAll('[data-expandable]'); 255 256 if (elements != null) { 257 // set height of expandable elements to 0 and register click events 258 elements.forEach(function(currentElement, currentIndex, listObj) { 259 // register expand click events 260 currentElement.addEventListener("click", function(clickEvent){ 261 const target = clickEvent.target || clickEvent.srcElement; 262 const expandableNodeName = target.getAttribute('data-expandable'); 263 const expandableNode = target.parentNode.querySelector(expandableNodeName); 264 if (expandableNode.getAttribute('data-state') == 'minimized') { 265 target.classList.add('open'); 266 expandableNode.setAttribute('style', 'he
266ight:unset;display:bock;'); 267 expandableNode.setAttribute('data-state', 'expanded'); 268 } else { 269 target.classList.remove('open'); 270 expandableNode.setAttribute('style', 'height:0;display:none;'); 271 expandableNode.setAttribute('data-state', 'minimized'); 272 } 273 }, false); 274 275 const expandableNodeName = currentElement.getAttribute('data-expandable'); 276 const expandableNode = currentElement.parentNode.querySelector(expandableNodeName); 277 expandableNode.setAttribute('style', 'height:0;display:none;'); 278 expandableNode.setAttribute('data-state', 'minimized'); 279 }); 280 } 281 return 'ok'; 282 }, 283 284 /*************************************************** 285 * initialize all tri-state toggles 286 ***************************************************/ 287 initTrieStateToggles: function() { 288 const node = this.node; 289 290 // find all toggle buttons 291 const toggleButtons = node.querySelectorAll('.cm-tri-state-toggle:not([disabled])'); 292 293 if (toggleButtons != null) { 294 295 let cookieConsent; 296 // check cookie consent 297 if (fnncm().checkCookie('fnncmCookieConsent') && fnncm().checkForNewServices()) { 298 cookieConsent = JSON.parse(fnncm().getCookie('fnncmCookieConsent')); 299 } 300 301 // register click event for tri-state toggles 302 toggleButtons.forEach(function(currentElement, currentIndex, listObj) { 303 // set state by cookieconsent 304 if (cookieConsent !== undefined && currentElement.getAttribute('data-parent-id') !== '0') { 305 if (cookieConsent[currentElement.getAttribute('data-service-id')] === 1) { 306 currentElement.checked = true; 307 fnncm().toggleParentSwitch(currentElement.getAttribute('data-parent-id')); 308 } 309 } 310 311 312 // register change event 313 currentElement.addEventListener("change", function(clickEvent) { 314 const target = clickEvent.target || clickEvent.srcElement; 315 target.setAttribute('data-has-changed', '1'); 316 317 // Set parent state 318 let parentId = target.getAttribute('data-parent-id'); 319 fnncm().toggleParentSwitch(parentId); 320 321 // Set child states 322 parentId = target.getAttribute('data-service-id'); 323 document.querySelectorAll('[data-parent-id="' + parentId + '"]').forEach(function(groupedCheckBox) { 324 if (target.checked) { 325 groupedCheckBox.checked = true; 326 } else { 327 groupedCheckBox.checked = false; 328 } 329 }); 330 331 }, false); 332 }); 333 } 334 }, 335 //</editor-fold> 336 337 338 /***************************************************************** 339 * Sets switch to the state he should have based on his children. 340 ****************************************************************/ 341 toggleParentSwitch: function(parentId) { 342 if (parentId !== '0') { 343 // Count of selected checkboxes in same group 344 const selectedGroupedCheckboxes = document.querySelectorAll('[data-parent-id="' + parentId + '"]:checked').length; 345 346 // Total count of checkboxes in same group 347 const groupedCheckboxes = document.querySelectorAll('[data-parent-id="' + parentId + '"]').length; 348 349 // Parent toggle of grouped checkboxes 350 const parentToggle = document.getElementById('cm-tri-state-0-' + parentId); 351 352 if (selectedGroupedCheckboxes === 0) { 353 parentToggle.checked = false; 354 parentToggle.indeterminate = false; 355 } else if (selectedGroupedCheckboxes === groupedCheckboxes) { 356 parentToggle.checked = true; 357 parentToggle.indeterminate = false;
358 } else { 359 parentToggle.checked = true; 360 parentToggle.indeterminate = true; 361 } 362 } 363 }, 364 365 366 /*************************************************** 367 * Executes the service with the passed Uid 368 ***************************************************/ 369 acceptService: function(serviceId) { 370 // check cookie consent 371 if (fnncm().checkCookie('fnncmCookieConsent')) { 372 const consentLifetime = document.querySelector('[data-cookie-consent-lifetime]').getAttribute('data-cookie-consent-lifetime'); 373 let cookieConsent = JSON.parse(fnncm().getCookie('fnncmCookieConsent')); 374 375 // Set service as accepted 376 cookieConsent[serviceId] = 1; 377 378 // Parse cookie consent object to JSON and store in the consent cookie 379 fnncm('head').setCookie('fnncmCookieConsent', JSON.stringify(cookieConsent), consentLifetime); 380 } 381 382 // Find service data 383 const service = fnncm().getServiceData(null, serviceId); 384 if (service === null) { return; } 385 386 // Insert script blocks and replace placeholders 387 fnncm().insertCodeByService(service); 388 }, 389 390 /*************************************************** 391 * insert code blocks into the dom for a single service 392 ***************************************************/ 393 insertCodeByService: function(service) { 394 // Log runtime 395 let insertStart; 396 fnncmRuntime['service_' + service.uid] = {}; 397 398 // If it is a standalone service that does not depend on external services, its code sections are included in the dom. 399 if (!service.dataExternal) { 400 // For the external content, the placeholders and script blocks must also be converted back. 401 if (service.externalContent && service.elementIdentifier !== '') { 402 // Remove placeholders 403 fnncm('body').find('[data-cm-content-consent-type-id="' + service.uid + '"]').forEach(function (placeholderNode) { 404 placeholderNode.remove(); 405 }); 406 407 // Reset elements 408 fnncm('body').find('[cm-element-identifier="' + service.elementIdentifier.replace(/`/g,"'") + '"]').forEach(function (elementScriptNode) { 409 elementScriptNode.parentElement.innerHTML = elementScriptNode.innerText; 410 }); 411 fnncm('body').find('[cm-script-identifier="' + service.scriptIdentifier.replace(/`/g,"'") + '"]').forEach(function (elementScriptNode) { 412 fnncm('head').append(elementScriptNode.innerText); 413 }); 414 } 415 416 // Include the standard code blocks 417 if(service.dataHead !== '') { 418 insertStart = Date.now(); 419 fnncm('head').append(service.dataHead.replace(/`/g,"'"),'script'); 420 fnncmRuntime['service_' + service.uid]['head'] = (Date.now() - insertStart) + 'ms'; 421 } 422 if(service.dataBodyBegin !== '') { 423 insertStart = Date.now(); 424 fnncm('body').prepend(service.dataBodyBegin.replace(/`/g,"'"),'script'); 425 fnncmRuntime['service_' + service.uid]['bodyBegin'] = (Date.now() - insertStart) + 'ms'; 426 } 427 if(service.dataBodyEnd !== '') { 428 insertStart = Date.now(); 429 fnncm('body').append(service.dataBodyEnd.replace(/`/g,"'"),'script'); 430 fnncmRuntime['service_' + service.uid]['bodyEnd'] = (Date.now() - insertStart) + 'ms'; 431 } 432 if(service.noscript !== '') { 433 insertStart = Date.now(); 434 fnncm('body').append(service.noscript,'noscript'); 435 fnncmRuntime['service_' + service.uid]['noscript'] = (Date.now() - insertStart) + 'ms'; 436 } 437 438 } else { 439 440 } 441 }, 442 443 /*************************************************** 444 * insert code blocks into the dom for all accept services 445 ***************************************************/ 446 insertCode: function() { 447 // Get cookie consent from cookie 448 const cookieConsent = JSON.parse(fnncm().getCookie('fnncmCookieConsent')); 449 450 const serviceStart = Date.now(); 451 452 let serviceId; 453 for (serviceId in cookieConsent) { 454 // Gets service data from JSON object 455 const service = fnncm().getServiceData(null, serviceId); 456 457 // If there is no data for the service, further processing for this service is aborted. 458 if (service === null) { continue; } 459 460 // If the service was not accepted, further processing for this service will be cancelled. 461 if (cookieConsent[serviceId] !== 1) { 462 // Show placeholders from service 463 if (service.externalContent) { 464 fnncm('body').find('[data-cm-content-consent-type-id="' + service.uid + '"]').forEach(function (placeholderNode) { 465 placeholderNode.style.display = 'block'; 466 }); 467 } 468 469 continue; 470 } 471 472 // Insert code by service 473 fnncm().insertCodeByService(service); 474 475 fnncmRuntime['service_' + serviceId]['complete'] = (Date.now() - serviceStart) + 'ms'; 476 } 477 }, 478 479 /*************************************************** 480 * gets service data from cmJsonData 481 ***************************************************/ 482 getServiceData: function(serviceGroupUid, serviceUid) { 483 if (serviceGroupUid !== null) { 484 const services = cmJsonData[serviceGroupUid].services; 485 486 for (let i = 0; i < services.length; i++) { 487 if (services[i].uid === parseInt(serviceUid)) { 488 return services[i]; 489 } 490 } 491 492 console.warn('CookieManage - Data for service ' + serviceUid + ' not found! (1627374292)'); 493 return null; 494 } else { 495 // Gets the object keys 496 const serviceGroupsKeys = Object.keys(cmJsonData); 497 498 // Goes through the service groups 499 for (let sg = 0; sg < serviceGroupsKeys.length; sg++) { 500 //Goes through the services 501 for (let s = 0; s < cmJsonData[serviceGroupsKeys[sg]].services.length; s++) { 502 if (cmJsonData[serviceGroupsKeys[sg]].services[s].uid === parseInt(serviceUid)) { 503 return cmJsonData[serviceGroupsKeys[sg]].services[s]; 504 } 505 } 506 } 507 508 console.warn('CookieManage - Data for service ' + serviceUid + ' not found! (1627374340)'); 509 return null; 510 } 511 }, 512 513 /*************************************************** 514 * save cookie settings in cookie consent 515 ***************************************************/ 516 saveCookieSettings: function() { 517 // Get cookie consent lifetime 518 const consentLifetime = document.querySelector('[data-cookie-consent-lifetime]').getAttribute('data-cookie-consent-lifetime'); 519 520 let cookieConsent = {}; 521 let reload = false;
522 let disabledGroups = []; 523 524 // Find all service switches 525 const switches = fnncm('.cm-modal').find('.cm-service input[type="checkbox"].cm-tri-state-toggle'); 526 527 switches.forEach(function(service) { 528 // Set value to cookie consent object, this object is parsed to a JSON object and stored in the cookie. 529 cookieConsent[service.getAttribute('data-service-id')] = service.checked ? 1 : 0; 530 531 // If the status has changed and the service has been deactivated, the service will be remembered and a page reload will be scheduled 532 if (service.getAttribute('data-has-changed') === '1' && !service.checked) { 533 reload = true; 534 disabledGroups.push(service.getAttribute('data-service-id')); 535 } 536 537 // For external services (like GoogleTagManager) the specified code is inserted here 538 if (service.dataExternal && service.dataAfterApproval !== '') { 539 // The "setTimeout" should ensure that all dependencies are loaded and the code is executed only then. 540 setTimeout(function() { 541 fnncm('body').append(service.dataAfterApproval.replace(/`/g,"'"),'script'); 542 }, 200); 543 } 544 }); 545 546 // Parse cookie consent object to JSON and store in the consent cookie 547 fnncm().setCookie('fnncmCookieConsent', JSON.stringify(cookieConsent), consentLifetime); 548 549 if (reload) { 550 fnncm().removeCode(); 551 // Unnecessary or deselected cookies of the own domain are removed. 552 disabledGroups.forEach(function (groupId) { 553 fnncm().removeCookiesByServiceId(groupId); 554 }); 555 556 // If a page reload is needed this is initialized here 557 location.reload(); 558 } else { 559 fnncm().hide(); 560 // Inserts all necessary code blocks and replaces the placeholders of the external content 561 fnncm().insertCode(); 562 } 563 }, 564 565 /*************************************************** 566 * set all service groups to "yes" and save settings 567 ***************************************************/ 568 acceptAll: function() { 569 const switches = fnncm('.cm-modal').find('input[type="checkbox"].cm-tri-state-toggle'); 570 switches.forEach(function(currentElement, currentIndex, listObj) { 571 currentElement.checked = true; 572 }); 573 fnncm().saveCookieSettings(); 574 }, 575 576 /*************************************************** 577 * set all service groups to "no" and save settings 578 ***************************************************/ 579 rejectAll: function() { 580 const switches = fnncm('.cm-modal').find('input[type="checkbox"].cm-tri-state-toggle:not([disabled])'); 581 switches.forEach(function(currentElement, currentIndex, listObj) { 582 currentElement.checked = false; 583 }); 584 fnncm().saveCookieSettings(); 585 }, 586 587 /*************************************************** 588 * init fnn cookie manager 589 ***************************************************/ 590 init: function() { 591 fnncm('.cm-settings-popup').initTabs(); 592 fnncm('.cm-tab-content').initExpandables(); 593 fnncm('.cm-tab-content').initTrieStateToggles(); 594 595 // register click event for save button 596 document.querySelector('.cm-btn-save').addEventListener("click", function(clickEvent){ 597 const target = clickEvent.target || clickEvent.srcElement; 598 if (target.getAttribute('data-ready') === 'yes') { 599 fnncm().saveCookieSettings(); 600 return; 601 } 602 }, false); 603 604 // check cookie consent 605 if (fnncm().checkCookie('fnncmCookieConsent') && fnncm().checkForNewServices()) { 606 // cookie consent is set 607 fnncm().hide(); 608 fnncm().insertCode(); 609 } else { 610 // cookie consent not set 611 fnncm().showCookiebar(); 612 } 613 } 614}, fnncm, _fnncm; 615 616_fnncm = function(selector) { this.node = document.querySelector(selector); }; 617_fnncm.prototype = proto_methods; 618 619fnncm = function(selector) { 620 return new _fnncm(selector); 621}; 622 623let fnncmRuntime = {}; 624 625 626/***************************************************** 627 * 628 * FnnCookieManager init 629 * 630 ******************************************************/ 631document.addEventListener('DOMContentLoaded', function(event) { 632 633 // polyfill-ie11-nodelist-foreach 634 if ('NodeList' in window && !NodeList.prototype.forEach) { 635 NodeList.prototype.forEach = function (callback, thisArg) { 636 thisArg = thisArg || window; 637 for (var i = 0; i < this.length; i++) { 638 callback.call(thisArg, this[i], i, this); 639 } 640 }; 641 } 642 643 const fnncmOpeners = document.getElementsByClassName('fnn-cookiemanager-open'); 644 for (let i = 0; i < fnncmOpeners.length; i++) { 645 fnncmOpeners[i].addEventListener('click', function() { fnncm().showCookieManager(); }, false); 646 } 647 648 fnncm().init(); 649});
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.