1/*! 2 * Forked from CookieConsent v2.8.9 3 * https://www.github.com/orestbida/cookieconsent 4 * Author Orest Bida 5 * Released under the MIT License 6 * Modified by Codingfreaks for Typo3 CMS integration 7 */ 8(function () { 9 'use strict'; 10 /** 11 * @param {HTMLElement} [root] - [optional] element where the cookieconsent will be appended 12 * @returns {Object} cookieconsent object with API 13 */ 14 var CookieConsent = function (root) { 15 16 /** 17 * CHANGE THIS FLAG FALSE TO DISABLE console.log() 18 */ 19 var ENABLE_LOGS = false; 20 21 var _config = { 22 'mode': 'opt-in', // 'opt-in', 'opt-out' 23 'current_lang': 0, // Set Default SySLanguage UID (0 = System Default Language) 24 'auto_language': null, 25 'autorun': true, // run as soon as loaded 26 'page_scripts': true, 27 'hide_from_bots': true, 28 'cookie_name': 'cf_cookie', 29 'cookie_expiration': 182, // default: 6 months (in days) 30 'cookie_domain': window.location.hostname, // default: current domain 31 'cookie_path': '/', 32 'cookie_same_site': 'Lax', 33 'use_rfc_cookie': false, 34 'autoclear_cookies': true, 35 'revision': 0, 36 'script_selector': 'data-service' 37 }; 38 39 var 40 /** 41 * Object which holds the main methods/API (.show, .run, ...) 42 */ 43 _cookieconsent = {}, 44 /** 45 * Object which holds the main Categorys selected) 46 */ 47 _categorysSelected = {}, 48 49 /** 50 * Global user configuration object 51 */ 52 user_config, 53 54 /** 55 * Internal state variables 56 */ 57 saved_cookie_content = {}, 58 cookie_data = null, 59 60 /** 61 * @type {Date} 62 */ 63 consent_date, 64 65 /** 66 * @type {Date} 67 */ 68 last_consent_update, 69 70 /** 71 * @type {string} 72 */ 73 consent_uuid, 74 75 /** 76 * @type {boolean} 77 */ 78 invalid_consent = true, 79 80 consent_modal_exists = false, 81 consent_modal_visible = false, 82 83 settings_modal_visible = false, 84 clicked_inside_modal = false, 85 current_modal_focusable, 86 87 all_table_headers, 88 all_blocks, 89 90 // Helper callback functions 91 // (avoid calling "user_config['onAccept']" all the time) 92 onAccept, 93 onChange, 94 onFirstAction, 95 96 revision_enabled = false, 97 valid_revision = true, 98 revision_message = '', 99 100 // State variables for the autoclearCookies function 101 changed_settings = [], 102 reload_page = false; 103 104 /** 105 * Accept type: 106 * - "all" 107 * - "necessary" 108 * - "custom" 109 * @type {string} 110 */ 111 var accept_type; 112 113 /** 114 * Contains all accepted categories 115 * @type {string[]} 116 */ 117 var accepted_categories = []; 118 119 /** 120 * Contains all non-accepted (rejected) categories 121 * @type {string[]} 122 */ 123 var rejected_categories = []; 124 125 /** 126 * Contains all categories enabled by default 127 * @type {string[]} 128 */ 129 var default_enabled_categories = []; 130 131 // Don't run plugin (to avoid indexing its text content) if bot detected 132 var is_bot = false; 133 134 /** 135 * Save reference to the last focused element on the page 136 * (used later to restore focus when both modals are closed) 137 */ 138 var last_elem_before_modal; 139 var last_consent_modal_btn_focus; 140 141 /** 142 * Both of the arrays below have the same structure: 143 * [0] => holds reference to the FIRST focusable element inside modal 144 * [1] => holds reference to the LAST focusable element inside modal 145 */ 146 var consent_modal_focusable = []; 147 var settings_modal_focusable = []; 148 149 /** 150 * Keep track of enabled/disabled categories 151 * @type {boolean[]} 152 */ 153 var toggle_states = []; 154 155 /** 156 * Stores all available categories 157 * @type {string[]} 158 */ 159 var all_categories = []; 160 161 /** 162 * Keep track of readonly toggles 163 * @type {boolean[]} 164 */ 165 var readonly_categories = []; 166 167 /** 168 * Pointers to main dom elements (to avoid retrieving them later using document.getElementById) 169 */ 170 var 171 /** @type {HTMLElement} */ html_dom = document.documentElement, 172 /** @type {HTMLElement} */ main_container, 173 /** @type {HTMLElement} */ all_modals_container, 174 /** @type {HTMLElement} */ consent_modal, 175 /** @type {HTMLElement} */ settings_container, 176 /** @type {HTMLElement} */ new_settings_blocks; 177 178 179 180 /** 181 * Update config settings 182 * @param {Object} user_config 183 */ 184 var _setConfig = function (_user_config) { 185 /** 186 * Make user configuration globally available 187 */ 188 user_config = _user_config; 189 190 _log("CookieConsent [CONFIG]: received_config_settings ", user_config); 191 192 if (typeof user_config['cookie_expiration'] === "number") 193 _config.cookie_expiration = user_config['cookie_expiration']; 194 195 if (typeof user_config['cookie_necessary_only_expiration'] === "number") 196 _config.cookie_necessary_only_expiration = user_config['cookie_necessary_only_expiration']; 197 198 if (typeof user_config['autorun'] === "boolean") 199 _config.autorun = user_config['autorun']; 200 201 if (typeof user_config['cookie_domain'] === "string") 202 _config.cookie_domain = user_config['cookie_domain']; 203 204 if (typeof user_config['cookie_same_site'] === "string") 205 _config.cookie_same_site = user_config['cookie_same_site']; 206 207 if (typeof user_config['cookie_path'] === "string") 208 _config.cookie_path = user_config['cookie_path']; 209 210 if (typeof user_config['cookie_name'] === "string") 211 _config.cookie_name = user_config['cookie_name']; 212 213 if (typeof user_config['onAccept'] === "function") 214 onAccept = user_config['onAccept']; 215 216 if (typeof user_config['onFirstAction'] === "function") 217 onFirstAction = user_config['onFirstAction']; 218 219 if (typeof user_config['onChange'] === "function") 220 onChange = user_config['onChange']; 221 222 if (user_config['mode'] === 'opt-out') 223 _config.mode = 'opt-out'; 224
225 if (typeof user_config['revision'] === "number") { 226 user_config['revision'] > -1 && (_config.revision = user_config['revision']); 227 revision_enabled = true; 228 } 229 230 if (typeof user_config['autoclear_cookies'] === "boolean") 231 _config.autoclear_cookies = user_config['autoclear_cookies']; 232 233 if (user_config['use_rfc_cookie'] === true) 234 _config.use_rfc_cookie = true; 235 236 if (typeof user_config['hide_from_bots'] === "boolean") { 237 _config.hide_from_bots = user_config['hide_from_bots']; 238 } 239 240 if (_config.hide_from_bots) { 241 is_bot = navigator && 242 ((navigator.userAgent && /bot|crawl|spider|slurp|teoma|lighthouse/i.test(navigator.userAgent)) || ( navigator.webdriver && navigator.userAgent.indexOf("CF-CookieScanner") === -1)); 243 } 244 245 _config.page_scripts = user_config['page_scripts'] === true; 246 247 if (user_config['auto_language'] === 'browser' || user_config['auto_language'] === true) { 248 _config.auto_language = 'browser'; 249 } else if (user_config['auto_language'] === 'document') { 250 _config.auto_language = 'document'; 251 } 252 253 _log("CookieConsent [LANG]: auto_language strategy is '" + _config.auto_language + "'"); 254 255 _config.current_lang = _resolveCurrentLang(user_config.languages, user_config['current_lang']); 256 } 257 258 /** 259 * Add an onClick listeners to all html elements with data-cc attribute 260 */ 261 var _addDataButtonListeners = function (elem) { 262 263 var _a = 'accept-'; 264 265 var show_settings = _getElements('c-settings'); 266 var accept_all = _getElements(_a + 'all'); 267 var accept_necessary = _getElements(_a + 'necessary'); 268 var accept_custom_selection = _getElements(_a + 'custom'); 269 270 for (var i = 0; i < show_settings.length; i++) { 271 show_settings[i].setAttribute('aria-haspopup', 'dialog'); 272 _addEvent(show_settings[i], 'click', function (event) { 273 event.preventDefault(); 274 _cookieconsent.showSettings(0); 275 }); 276 } 277 278 for (i = 0; i < accept_all.length; i++) { 279 _addEvent(accept_all[i], 'click', function (event) { 280 _acceptAction(event, 'all'); 281 }); 282 } 283 284 for (i = 0; i < accept_custom_selection.length; i++) { 285 _addEvent(accept_custom_selection[i], 'click', function (event) { 286 _acceptAction(event); 287 }); 288 } 289 290 for (i = 0; i < accept_necessary.length; i++) { 291 _addEvent(accept_necessary[i], 'click', function (event) { 292 _acceptAction(event, []); 293 }); 294 } 295 296 /** 297 * Return all elements with given data-cc role 298 * @param {string} data_role 299 * @returns {NodeListOf<Element>} 300 */ 301 function _getElements(data_role) { 302 return (elem || document).querySelectorAll('a[data-cc="' + data_role + '"], button[data-cc="' + data_role + '"]'); 303 } 304 305 /** 306 * Helper function: accept and then hide modals 307 * @param {PointerEvent} e source event 308 * @param {string} [accept_type] 309 */ 310 function _acceptAction(e, accept_type) { 311 e.preventDefault(); 312 _cookieconsent.accept(accept_type); 313 _cookieconsent.hideSettings(); 314 _cookieconsent.hide(); 315 } 316 } 317 318 /** 319 * Get a valid language (at least 1 must be defined) 320 * @param {string} lang - desired language 321 * @param {Object} all_languages - all defined languages 322 * @returns {string} validated language 323 */ 324 var _getValidatedLanguage = function (lang, all_languages) { 325 if (Object.prototype.hasOwnProperty.call(all_languages, lang)) { 326 return lang; 327 } else if (_getKeys(all_languages).length > 0) { 328 if (Object.prototype.hasOwnProperty.call(all_languages, _config.current_lang)) { 329 return _config.current_lang; 330 } else { 331 return _getKeys(all_languages)[0]; 332 } 333 } 334 } 335 336 /** 337 * Save reference to first and last focusable elements inside each modal 338 * to prevent losing focus while navigating with TAB 339 */ 340 var _getModalFocusableData = function () { 341 342 /** 343 * Note: any of the below focusable elements, which has the attribute tabindex="-1" AND is either 344 * the first or last element of the modal, won't receive focus during "open/close" modal 345 */ 346 var allowed_focusable_types = ['[href]', 'button', 'input', 'details', '[tabindex="0"]']; 347 348 function _getAllFocusableElements(modal, _array) { 349 var focus_later = false, focus_first = false;
350 // ie might throw exception due to complex unsupported selector => a:not([tabindex="-1"]) 351 try { 352 var focusable_elems = modal.querySelectorAll(allowed_focusable_types.join(':not([tabindex="-1"]), ')); 353 var attr, len = focusable_elems.length, i = 0; 354 355 while (i < len) { 356 357 attr = focusable_elems[i].getAttribute('data-focus'); 358 359 if (!focus_first && attr === "1") { 360 focus_first = focusable_elems[i]; 361 362 } else if (attr === "0") { 363 focus_later = focusable_elems[i]; 364 if (!focus_first && focusable_elems[i + 1].getAttribute('data-focus') !== "0") { 365 focus_first = focusable_elems[i + 1]; 366 } 367 } 368 369 i++; 370 } 371 372 } catch (e) { 373 return modal.querySelectorAll(allowed_focusable_types.join(', ')); 374 } 375 376 /** 377 * Save first and last elements (used to lock/trap focus inside modal) 378 */ 379 _array[0] = focusable_elems[0]; 380 _array[1] = focusable_elems[focusable_elems.length - 1]; 381 _array[2] = focus_later; 382 _array[3] = focus_first; 383 } 384 385 /** 386 * Get settings modal'S all focusable elements 387 * Save first and last elements (used to lock/trap focus inside modal) 388 */ 389 _getAllFocusableElements(all_modals_container.querySelector("#s-inr"), settings_modal_focusable); 390 391 /** 392 * If consent modal exists, do the same 393 */ 394 if (consent_modal_exists) { 395 _getAllFocusableElements(all_modals_container, consent_modal_focusable); 396 } 397 } 398 399 var _createConsentModal = function (lang) { 400 401 if (user_config['force_consent'] === true) 402 _addClass(html_dom, 'force--consent'); 403 404 // Create modal if it doesn't exist 405 if (!consent_modal) { 406 const consent_modal = _createNode('div'); 407 408 if(typeof CF_CONSENTMODAL_TEMPLATE !== "undefined"){ 409 consent_modal.innerHTML = CF_CONSENTMODAL_TEMPLATE; 410 }else{ 411 consent_modal.innerHTML = ` 412 <div id="cm" role="dialog" aria-modal="true" aria-hidden="false" aria-labelledby="c-ttl" aria-describedby="c-txt" style="visibility: hidden;"> 413 <div id="c-inr"> 414 <div id="c-inr-i"> 415 <div id="c-ttl" role="heading" aria-level="2"></div> 416 <div id="c-txt"></div> 417 </div> 418 <div id="c-bns"><button type="button" id="c-p-bn" class="c-bn"></button><button type="button" id="c-s-bn" class="c-bn c_link"></button><button type="button" id="c-t-bn" class="c-bn c_settings"></button></div> 419 </div> 420 <div id="c-footer"><div class="c-links"><div class="c-link-group">[##linkPrivacy##] [##linkImpress##]</div></div></div> 421 </div>`; 422 423 } 424 425 var overlay = _createNode('div'); 426 overlay.id = 'cm-ov'; 427 // Append consent modal to main container 428 all_modals_container.appendChild(consent_modal); 429 all_modals_container.appendChild(overlay); 430 /** 431 * Make modal by default hidden to prevent weird page jumps/flashes (shown only once css is loaded) 432 */ 433 consent_modal.style.visibility = overlay.style.visibility = "hidden"; 434 overlay.style.opacity = 0; 435 } 436 437 // Use insertAdjacentHTML instead of innerHTML 438 var consent_modal_title_value = user_config.languages[lang]['consent_modal']['title']; 439 440 // Add title (if valid) 441 if (consent_modal_title_value) { 442 all_modals_container.querySelector("#c-ttl").innerHTML = consent_modal_title_value; 443 } 444 445 var description = user_config.languages[lang]['consent_modal']['description']; 446 var impress_link = user_config.languages[lang]['consent_modal']['impress_link']; 447 var data_policy_link = user_config.languages[lang]['consent_modal']['data_policy_link']; 448 449 450 if(data_policy_link.length === 0 && impress_link.length === 0){ 451 all_modals_container.querySelector("#c-footer").style.display = "none"; 452 } 453 454 if (data_policy_link.length > 0) { 455 _replaceLink("[##linkPrivacy##]", data_policy_link); 456 } else { 457 _replaceLink("[##linkPrivacy##]", ""); 458 } 459 460 461 if (impress_link.length > 0) { 462 _replaceLink("[##linkImpress##]", impress_link); 463 } else { 464 _replaceLink("[##linkImpress##]", ""); 465 } 466 467 468 if (revision_enabled) { 469 if (!valid_revision) { 470 description = description.replace("{{revision_message}}", revision_message || user_config.languages[lang]['consent_modal']['revision_message'] || ""); 471 }else{ 472 description = description.replace("{{revision_message}}", ""); 473 } 474 }else{ 475 description = description.replace("{{revision_message}}", ""); 476 } 477 478 // Set description content 479 all_modals_container.querySelector("#c-txt").innerHTML = description; 480 481 var primary_btn_data = user_config.languages[lang]['consent_modal']['primary_btn'], // accept current selection 482 secondary_btn_data = user_config.languages[lang]['consent_modal']['secondary_btn'], 483 tertiary_btn_data = user_config.languages[lang]['consent_modal']['tertiary_btn']; 484 485 // Add primary button if not falsy 486 if (primary_btn_data) { 487 if (primary_btn_data['role'] === 'display_none') { 488 all_modals_container.querySelector("#c-p-bn").style.display = "none"; 489 }else if(primary_btn_data['role'] === "accept_all"){ 490 _addEvent(all_modals_container.querySelector("#c-p-bn"), "click", function () { 491 _cookieconsent.hide(); 492 _cookieconsent.accept("all"); 493 }); 494 } else if (primary_btn_data['role'] === 'accept_necessary') { 495 _addEvent(all_modals_container.querySelector("#c-p-bn"), 'click', function () { 496 _cookieconsent.hide(); 497 _cookieconsent.accept([]); // accept necessary only 498 }); 499 } else { 500 _addEvent(all_modals_container.querySelector("#c-p-bn"), 'click', function () { 501 _cookieconsent.showSettings(0); 502 }); 503 } 504 all_modals_container.querySelector("#c-p-bn").innerHTML = user_config.languages[lang]['consent_modal']['primary_btn']['text']; 505 } 506 507 // Add secondary button if not falsy 508 if (secondary_btn_data) { 509 if (secondary_btn_data['role'] === 'display_none') { 510 all_modals_container.querySelector("#c-s-bn").style.display = "none"; 511 }else if(secondary_btn_data['role'] === "accept_all"){ 512 _addEvent(all_modals_container.querySelector("#c-s-bn"), "click", function () { 513 _cookieconsent.hide(); 514 _cookieconsent.accept("all"); 515 }); 516 } else if (secondary_btn_data['role'] === 'accept_necessary') { 517 _addEvent(all_modals_container.querySelector("#c-s-bn"), 'click', function () { 518 _cookieconsent.hide(); 519 _cookieconsent.accept([]); // accept necessary only 520 }); 521 } else { 522 _addEvent(all_modals_container.querySelector("#c-s-bn"), 'click', function () { 523 _cookieconsent.showSettings(0); 524 }); 525 } 526 all_modals_container.querySelector("#c-s-bn").innerHTML = user_config.languages[lang]['consent_modal']['secondary_btn']['text']; 527 } 528 529 530 531 // Add tertiary button if not falsy c-t-bn 532 if(tertiary_btn_data){ 533 if (tertiary_btn_data['role'] === 'display_none') { 534 all_modals_container.querySelector("#c-t-bn").style.display = "none"; 535 }else if(tertiary_btn_data['role'] === "accept_all"){ 536 _addEvent(all_modals_container.querySelector("#c-t-bn"), "click", function () { 537 _cookieconsent.hide(); 538 _cookieconsent.accept("all"); 539 }); 540 } else if (tertiary_btn_data['role'] === 'accept_necessary') { 541 _addEvent(all_modals_container.querySelector("#c-t-bn"), 'click', function () { 542 _cookieconsent.hide(); 543 _cookieconsent.accept([]); // accept necessary only 544 }); 545 } else { 546 _addEvent(all_modals_container.querySelector("#c-t-bn"), 'click', function () { 547 _cookieconsent.showSettings(0); 548 }); 549 } 550 all_modals_container.querySelector("#c-t-bn").innerHTML = user_config.languages[lang]['consent_modal']['tertiary_btn']['text']; 551 552 } 553 554 555 consent_modal_exists = true; 556 557 _addDataButtonListeners(all_modals_container); 558 } 559 560 561 var _createSettingsModal = function (lang) { 562 /** 563 * Create all consent_modal elements 564 */ 565 566 if(typeof CF_SETTINGSMODAL_TEMPLATE !== "undefined"){ 567 var settingsHTML = CF_SETTINGSMODAL_TEMPLATE; 568 }else{ 569 var settingsHTML = ` 570 <div id="s-cnt" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="s-ttl" style="visibility: hidden;"> 571 <div id="c-vln"> 572 <div id="cs"> 573 <div id="c-s-in"> 574 <div id="s-inr" class="bns-t"> 575 <div id="s-hdr"> 576 <div id="s-ttl" role="heading">Cookie Settings</div> 577 <div id="s-c-bnc"> 578 <button type="button" id="s-c-bn" class="c-bn" aria-label=""></button> 579 </div> 580 </div> 581 <div id="s-bl"> 582 <div id="cf-category-wrapper" class="cf-category-wrapper"> 583 584 </div> 585 </div> 586 <div id="s-bns"> 587 <button type="button" id="s-all-bn" class="c-bn">Accept All</button> 588 <button type="button" id="s-rall-bn" class="c-bn">Reject All</button> 589 <button type="button" id="s-sv-bn" class="c-bn">Save</button> 590 </div> 591 </div> 592 </div> 593 </div> 594 </div> 595 </div> 596 `; 597 } 598 599 const settings_modal = _createNode('div'); 600 settings_modal.innerHTML = settingsHTML; 601 all_modals_container.appendChild(settings_modal); 602 603 var impress_link = user_config.languages[lang]['consent_modal']['impress_link']; 604 var data_policy_link = user_config.languages[lang]['consent_modal']['data_policy_link']; 605 606 607 608 if (data_policy_link.length > 0) { 609 _replaceLink("[##linkPrivacy##]", data_policy_link); 610 } else { 611 _replaceLink("[##linkPrivacy##]", ""); 612 } 613 614 615 if (impress_link.length > 0) { 616 _replaceLink("[##linkImpress##]", impress_link); 617 } else { 618 _replaceLink("[##linkImpress##]", ""); 619 } 620 621 if (!settings_container) { 622 // If 'esc' key is pressed inside settings_container div => hide settings 623 _addEvent(all_modals_container.querySelector("#c-vln"), 'keydown', function (evt) { 624 evt = evt || window.event; 625 if (evt.keyCode === 27) { 626 _cookieconsent.hideSettings(0); 627 } 628 }, true); 629 630 _addEvent(all_modals_container.querySelector("#s-c-bn"), 'click', function () { 631 _cookieconsent.hideSettings(0); 632 }); 633 } else { 634 new_settings_blocks = _createNode('div'); 635 new_settings_blocks.id = 's-bl'; 636 } 637 638 // Add label to close button 639 all_modals_container.querySelector("#s-c-bn").setAttribute('aria-label', user_config.languages[lang]['settings_modal']['close_btn_label'] || 'Close'); 640 641 all_blocks = user_config.languages[lang]['settings_modal']['blocks']; 642 all_table_headers = user_config.languages[lang]['settings_modal']['co
642okie_table_headers']; 643 644 var n_blocks = all_blocks.length; 645 // Set settings modal title 646 all_modals_container.querySelector("#s-ttl").innerHTML = user_config.languages[lang]['settings_modal']['title']; 647 /* CF HOOK */ 648 649 if(typeof CF_SETTINGSMODAL_CATEGORY_TEMPLATE !== "undefined"){ 650 var categoryHTML = CF_SETTINGSMODAL_CATEGORY_TEMPLATE; 651 }else{ 652 var categoryHTML = ` 653 <div class="titlecategory b-bn"> 654 <button type="button" class="b-tl exp" aria-expanded="false" aria-controls="">Externe Medien</button> 655 <label class="expand-button-cfwrapper b-tg"><input class="expand-button " type="checkbox" value=""> 656 <span class="c-tg" aria-hidden="true"> 657 <span class="on-i"></span> 658 <span class="off-i"></span> 659 </span> 660 <span class="t-lb"></span> 661 </label> 662 </div> 663 <div class="block-section b-ex"> 664 <div class="cf-category-description"></div> 665 </div> 666 `; 667 } 668 669 670 var categories = user_config.languages[lang]['settings_modal']['categories']; 671 var n_categories = categories.length; 672 var cf_category_list_full = []; 673 for (var ii = 0; ii < n_categories; ++ii) { 674 var category; 675 category = _createNode('div'); 676 category.id = categories[ii]["category"]; 677 category.className = categories[ii]["category"] + "-cfwrapper cfwrapper"; 678 category.innerHTML = categoryHTML; 679 680 category.querySelector(".cf-category-description").insertAdjacentHTML('beforeend', categories[ii]["description"]); 681 // Create toggle if specified (opt in/out) 682 isExpandable = true; 683 if (true) { 684 var accordion_id2 = "c-acc-" + ii; 685 686 // Create button (to collapse/expand block description) 687 var block_title_btn2 = category.querySelector(".b-tl"); 688 block_title_btn2.setAttribute('role', 'button'); 689 var block_switch2 = category.querySelector(".expand-button"); 690 block_switch2.setAttribute('role', 'checkbox'); 691 692 var block_switch_span2 = category.querySelector(".c-tg"); 693 694 // These 2 spans will contain each 2 pseudo-elements to generate 'tick' and 'x' icons 695 block_title_btn2.innerText = categories[ii]["title"]; 696 697 if (isExpandable) { 698 block_title_btn2.setAttribute('aria-expanded', 'false'); 699 block_title_btn2.setAttribute('aria-controls', accordion_id2); 700 } 701 702 //block_switch2.type = 'checkbox'; 703 //block_switch_span2.setAttribute('aria-hidden', 'true'); 704 block_switch2.value = categories[ii]["category"]; 705 706 /** 707 * Set toggle as readonly if true (disable checkbox) 708 */ 709 if (categories[ii]["toggle"]["readonly"]) { 710 block_switch2.disabled = true; 711 _addClass(block_switch_span2, 'c-ro'); 712 } 713 714 /** 715 * Set toggle as required if true 716 */ 717 if(categories[ii]["toggle"]["enabled"]){ 718 block_switch2.checked = true; 719 } 720 721 // Add label for toggle 722 var block_switchlabel = category.querySelector(".t-lb"); 723 block_switchlabel.innerText = categories[ii]["title"]; 724 725 //category.setAttribute('aria-hidden', 'true'); 726 727 /** 728 * On button click handle the following :=> aria-expanded, aria-hidden and act class for current block 729 */ 730 (function (accordion2, block_section2, btn2) { 731 _addEvent(block_title_btn2, 'click', function () { 732 if (!_hasClass(block_section2, 'opencategory')) { 733 _addClass(block_section2, 'opencategory'); 734 _addClass(btn2, 'act'); 735 btn2.setAttribute('aria-expanded', 'true'); 736 accordion2.setAttribute('aria-hidden', 'false'); 737 } else { 738 _removeClass(block_section2, 'opencategory'); 739 _removeClass(btn2, 'act'); 740 btn2.setAttribute('aria-expanded', 'false'); 741 accordion2.setAttribute('aria-hidden', 'true'); 742 } 743 }, false); 744 }
744)(category, category.querySelector(".block-section"), block_title_btn2); 745 746 } 747 748 cf_category_list_full.push(category); 749 } 750 /* CF HOOK END*/ 751 752 753 // Create settings modal content (blocks) 754 for (var i = 0; i < n_blocks; ++i) { 755 756 var title_data = all_blocks[i]['title'], 757 description_data = all_blocks[i]['description'], 758 toggle_data = all_blocks[i]['toggle'], 759 cookie_table_data = all_blocks[i]['cookie_table'], 760 remove_cookie_tables = user_config['remove_cookie_tables'] === true, 761 isExpandable = (description_data && 'truthy') || (!remove_cookie_tables && (cookie_table_data && 'truthy')); 762 763 764 // Create title 765 var block_section = _createNode('div'); 766 var block_table_container = _createNode('div'); 767 768 // Create description 769 if (description_data) { 770 var block_desc = _createNode('div'); 771 block_desc.className = 'p'; 772 block_desc.insertAdjacentHTML('beforeend', description_data); 773 } 774 775 var block_title_container = _createNode('div'); 776 block_title_container.className = 'title'; 777 778 block_section.className = 'c-bl'; 779 block_table_container.className = 'desc'; 780 781 782 // Create toggle if specified (opt in/out) 783 if (typeof toggle_data !== 'undefined') { 784 var accordion_id = "c-ac-" + i; 785 786 // Create button (to collapse/expand block description) 787 var block_title_btn = isExpandable ? _createNode('button') : _createNode('div'); 788 var block_switch_label = _createNode('label'); 789 var block_switch = _createNode('input'); 790 var block_switch_span = _createNode('span'); 791 var label_text_span = _createNode('span'); 792 793 // These 2 spans will contain each 2 pseudo-elements to generate 'tick' and 'x' icons 794 var block_switch_span_on_icon = _createNode('span'); 795 var block_switch_span_off_icon = _createNode('span'); 796 797 block_title_btn.className = isExpandable ? 'b-tl exp' : 'b-tl'; 798 block_title_btn.setAttribute('role', 'button'); 799 block_switch_label.className = 'b-tg'; 800 block_switch.className = 'c-tgl'; 801 block_switch_span_on_icon.className = 'on-i'; 802 block_switch_span_off_icon.className = 'off-i'; 803 block_switch_span.className = 'c-tg'; 804 label_text_span.className = "t-lb"; 805 806 if (isExpandable) { 807 block_title_btn.setAttribute('aria-expanded', 'false'); 808 block_title_btn.setAttribute('aria-controls', accordion_id); 809 } 810 811 block_switch.type = 'checkbox'; 812 block_switch_span.setAttribute('aria-hidden', 'true'); 813 814 var cookie_category = toggle_data.value; 815 block_switch.value = cookie_category; 816 817 label_text_span.textContent = title_data; 818 block_title_btn.insertAdjacentHTML('beforeend', title_data); 819 820 block_title_container.appendChild(block_title_btn); 821 block_switch_span.appendChild(block_switch_span_on_icon); 822 block_switch_span.appendChild(block_switch_span_off_icon); 823 824 /** 825 * If consent is valid => retrieve category states from cookie 826 * Otherwise use states defined in the user_config. object 827 */ 828 829 if (!invalid_consent) { 830 if (_inArray(saved_cookie_content['categories'], cookie_category) > -1) { 831 block_switch.checked = true; 832 !new_settings_blocks && toggle_states.push(true); 833 } else { 834 !new_settings_blocks && toggle_states.push(false); 835 } 836 } else if (toggle_data['enabled']) { 837 838 block_switch.checked = true; 839 !new_settings_blocks && toggle_states.push(true); 840 841 /** 842 * Keep track of categories enabled by default (useful when mode=='opt-out') 843 */ 844 if (toggle_data['enabled']) 845 !new_settings_blocks && default_enabled_categories.push(cookie_category); 846 847 } else if (toggle_data["enabled_by_default"]){ 848 block_switch.checked = true; 849 !new_settings_blocks && toggle_states.push(true); 850 851 /** 852 * Keep track of categories enabled by default (useful when mode=='opt-out') 853 */ 854 if (toggle_data['enabled']) 855 !new_settings_blocks && default_enabled_categories.push(cookie_category); 856 } else { 857 !new_settings_blocks && toggle_states.push(false); 858 } 859 860 !new_settings_blocks && all_categories.push(cookie_category); 861 862 /** 863 * Set toggle as readonly if true (disable checkbox) 864 */ 865 if (toggle_data['readonly']) { 866 block_switch.disabled = true; 867 _addClass(block_switch_span, 'c-ro'); 868 !new_settings_blocks && readonly_categories.push(true); 869 } else { 870 !new_settings_blocks && readonly_categories.push(false); 871 } 872 873 if(toggle_data["enabled"]){ 874 block_switch.checked = true; 875 } 876 877 block_switch.setAttribute("data-cfcookie-category", all_blocks[i]["category"]); 878 if (_categorysSelected[all_blocks[i]["category"]] === false || _categorysSelected[all_blocks[i]["category"]] === undefined) { 879 _categorysSelected[all_blocks[i]["category"]] = block_switch.checked; 880 } 881 882 883 _addClass(block_table_container, 'b-acc'); 884 _addClass(block_title_container, 'b-bn'); 885 _addClass(block_section, 'b-ex'); 886 _addClass(block_section, all_blocks[i]["category"]); 887
888 block_table_container.id = accordion_id; 889 block_table_container.setAttribute('aria-hidden', 'true'); 890 891 block_switch_label.appendChild(block_switch); 892 block_switch_label.appendChild(block_switch_span); 893 block_switch_label.appendChild(label_text_span); 894 block_title_container.appendChild(block_switch_label); 895 896 /** 897 * On button click handle the following :=> aria-expanded, aria-hidden and act class for current block 898 */ 899 900 isExpandable && (function (accordion, block_section, btn) { 901 _addEvent(block_title_btn, 'click', function () { 902 if (!_hasClass(block_section, 'act')) { 903 _addClass(block_section, 'act'); 904 btn.setAttribute('aria-expanded', 'true'); 905 accordion.setAttribute('aria-hidden', 'false'); 906 } else { 907 _removeClass(block_section, 'act'); 908 btn.setAttribute('aria-expanded', 'false'); 909 accordion.setAttribute('aria-hidden', 'true'); 910 } 911 }, false); 912 })(block_table_container, block_section, block_title_btn); 913 914 } else { 915 /** 916 * If block is not a button (no toggle defined), 917 * create a simple div instead 918 */ 919 if (title_data) { 920 var block_title = _createNode('div'); 921 block_title.className = 'b-tl freaking'; 922 block_title.setAttribute('role', 'heading'); 923 block_title.setAttribute('aria-level', '3'); 924 block_title.insertAdjacentHTML('beforeend', title_data); 925 block_title_container.appendChild(block_title); 926 } 927 } 928 929 title_data && block_section.appendChild(block_title_container); 930 description_data && block_table_container.appendChild(block_desc); 931 all_modals_container.querySelector("#cf-category-wrapper").appendChild(block_section); 932 // if cookie table found, generate table for this block 933 if (!remove_cookie_tables && typeof cookie_table_data !== 'undefined' && cookie_table_data.length > 0) { 934 var tr_tmp_fragment = document.createDocumentFragment(); 935 936 /** 937 * Use custom table headers 938 */ 939 for (var p = 0; p < all_table_headers.length; ++p) { 940 // create new header 941 var th1 = _createNode('th'); 942 var obj = all_table_headers[p]; 943 th1.setAttribute('scope', 'col'); 944 945 // get custom header content 946 if (obj) { 947 var new_column_key = obj && _getKeys(obj)[0]; 948 th1.textContent = all_table_headers[p][new_column_key]; 949 tr_tmp_fragment.appendChild(th1); 950 } 951 } 952 953 var tr_tmp = _createNode('tr'); 954 tr_tmp.appendChild(tr_tmp_fragment); 955 956 // create table header & append fragment 957 var thead = _createNode('thead'); 958 thead.appendChild(tr_tmp); 959 960 // append header to table 961 var block_table = _createNode('table'); 962 block_table.appendChild(thead); 963 964 var tbody_fragment = document.createDocumentFragment(); 965 966 // create table content 967 for (var n = 0; n < cookie_table_data.length; n++) { 968 var tr = _createNode('tr'); 969 tr.setAttribute("class","cookie-item"); 970 971 var tr_description = _createNode('tr'); 972 tr_description.setAttribute("class", "cookie-additional-description"); 973 tr_description.setAttribute("aria-hidden", "true"); 974 tr_description.setAttribute("id", 'c-cookie-desc-' + Math.random().toString(36).substr(2)); 975 976 for (var g = 0; g < all_table_headers.length; ++g) { 977 // get custom header content 978 obj = all_table_headers[g]; 979 if (obj) { 980 new_column_key = _getKeys(obj)[0]; 981 982 var td_tmp = _createNode('td'); 983 // Allow html inside table cells 984 td_tmp.insertAdjacentHTML('beforeend', cookie_table_data[n][new_column_key]); 985 td_tmp.setAttribute('data-column', obj[new_column_key]); 986 987 tr.appendChild(td_tmp); 988 // hydrate cookie info button 989 let cookieInfoBtn = tr.getElementsByClassName('cookie-info-btn')[0]; 990 if (cookieInfoBtn) { 991 cookieInfoBtn.setAttribute("aria-expanded", "false"); 992 cookieInfoBtn.setAttribute("aria-controls", tr_description.getAttribute("id")); 993 _addEvent(cookieInfoBtn, 'click', function () { 994 // get table row 995 let tr = this.parentNode.parentNode; 996 if (_hasClass(tr.nextSibling, "cookie-description-active") === false) { 997 tr.setAttribute("class", "cookie-additional-header cookie-item"); 998 tr.nextSibling.setAttribute("aria-hidden", "false"); 999 this.setAttribute("aria-expanded", "true"); 1000 _addClass(tr.nextSibling, "cookie-description-active"); 1001 } else { 1002 _removeClass(tr.nextSibling, "cookie-description-active"); 1003 tr.nextSibling.setAttribute("aria-hidden", "true"); 1004 this.setAttribute("aria-expanded", "false"); 1005 } 1006 }); 1007 } 1008 } 1009 } 1010 1011 var td_tmp_description = _createNode('td'); 1012 td_tmp_description.setAttribute('colspan', all_table_headers.length); 1013 td_tmp_description.setAttribute('class', "cookie-informations"); 1014 1015 var description_html_box = _createNode('div'); 1016 for (const key in cookie_table_data[n]["additional_information"]) { 1017 if(cookie_table_data[n]["additional_information"][key]["value"] === "" || cookie_table_data[n]["additional_information"][key]["value"] === 0) continue; 1018 let text = _createNode('p'); 1019 text.setAttribute('data-key', key); 1020 text.insertAdjacentHTML('beforeend', "<span class='cookie-title'>" + cookie_table_data[n]["additional_information"][key]["title"] + ":</span> " + cookie_table_data[n]["additional_information"][key]["value"]); 1021 description_html_box.appendChild(text); 1022 } 1023 1024 td_tmp_description.insertAdjacentHTML("beforeend", description_html_box.innerHTML); 1025 tr_description.appendChild(td_tmp_description); 1026 1027 tbody_fragment.appendChild(tr); 1028 tbody_fragment.appendChild(tr_description); 1029 } 1030 1031 // append tbody_fragment to tbody & append the latter into the table 1032 var tbody = _createNode('tbody'); 1033 tbody.appendChild(tbody_fragment); 1034 block_table.appendChild(tbody); 1035 block_table_container.appendChild(block_table); 1036 1037 } 1038 1039 /** 1040 * Append only if is either: 1041 * - togglable div with title 1042 * - a simple div with at least a title or description 1043 */ 1044 if (toggle_data && title_data || (!toggle_data && (title_data || description_data))) { 1045 block_section.appendChild(block_table_container); 1046 1047 /*CF HOOK */ 1048 for (var iii = 0; iii < cf_category_list_full.length; ++iii) { 1049 1050 if (block_section.classList.contains(cf_category_list_full[iii].id)) { 1051 cf_category_list_full[iii].children[1].appendChild(block_section); 1052 } 1053 } 1054 1055 for (var iiii = 0; iiii < cf_category_list_full.length; ++iiii) { 1056 1057 all_modals_container.querySelector("#cf-category-wrapper").appendChild(cf_category_list_full[iiii]); 1058 1059 } 1060 1061 /*CF HOOK END */ 1062 //if(new_settings_blocks) 1063 // new_settings_blocks.appendChild(cf_category_container); 1064 // else 1065 // settings_blocks.appendChild(cf_category_container); 1066 } 1067 } 1068 1069 1070 _addEvent(all_modals_container.querySelector("#s-all-bn"), 'click', function () { 1071 _cookieconsent.hideSettings(); 1072 _cookieconsent.hide(); 1073 _cookieconsent.accept('all'); 1074 }); 1075 1076 1077 all_modals_container.querySelector("#s-all-bn").innerHTML = user_config.languages[lang]['settings_modal']['accept_all_btn']; 1078 1079 var reject_all_btn_text = user_config.languages[lang]['settings_modal']['reject_all_btn']; 1080 1081 // Add third [optional] reject all button if provided 1082 _addEvent(all_modals_container.querySelector("#s-rall-bn"), 'click', function () { 1083 _cookieconsent.hideSettings(); 1084 _cookieconsent.hide(); 1085 _cookieconsent.accept([]); 1086 }); 1087 all_modals_container.querySelector("#s-rall-bn").innerHTML = reject_all_btn_text; 1088 1089 1090 // Add save preferences button onClick event 1091 // Hide both settings modal and consent modal 1092 _addEvent(all_modals_container.querySelector("#s-sv-bn"), 'click', function () { 1093 _cookieconsent.hideSettings(); 1094 _cookieconsent.hide(); 1095 _cookieconsent.accept(); 1096 }); 1097 1098 all_modals_container.querySelector("#s-sv-bn").innerHTML = user_config.languages[lang]['settings_modal']['s
1098ave_settings_btn']; 1099 1100 /* 1101 if(new_settings_blocks) { 1102 // replace entire existing cookie category blocks with the new cookie categories new blocks (in a different language) 1103 settings_inner.replaceChild(new_settings_blocks, settings_blocks); 1104 settings_blocks = new_settings_blocks; 1105 return; 1106 }; 1107 */ 1108 } 1109 1110 /** 1111 * Generate cookie consent html markup 1112 */ 1113 var _createCookieConsentHTML = function () { 1114 1115 // Create main container which holds both consent modal & settings modal 1116 main_container = _createNode('div'); 1117 main_container.id = 'cc--main'; 1118 1119 // Fix layout flash 1120 main_container.style.position = "fixed"; 1121 main_container.style.zIndex = "2147483647"; 1122 main_container.innerHTML = '<!--[if lt IE 9 ]><div id="cc_div" class="cc_div ie"></div><![endif]--><!--[if (gt IE 8)|!(IE)]><!--><div id="cc_div" class="cc_div"></div><!--<![endif]-->' 1123 all_modals_container = main_container.children[0]; 1124 1125 // Get current language 1126 var lang = _config.current_lang; 1127 1128 // Create consent modal 1129 if (consent_modal_exists) 1130 _createConsentModal(lang); 1131 1132 // Always create settings modal 1133 _createSettingsModal(lang); 1134 1135 for (const key in _categorysSelected) { 1136 if (_categorysSelected[key] === true || _categorysSelected[key] === "true") { 1137 main_container.querySelector(".expand-button[value=" + key + "]").setAttribute("checked", "checked"); 1138 } 1139 1140 } 1141 // Finally append everything (main_container holds both modals) 1142 (root || document.body).appendChild(main_container); 1143 } 1144 1145 /** 1146 * Update/change modals language 1147 * @param {String} lang new language 1148 * @param {Boolean} [force] update language fields forcefully 1149 * @returns {Boolean} 1150 */ 1151 _cookieconsent.updateLanguage = function (lang, force) { 1152 1153 if (typeof lang !== 'string') return; 1154 1155 /** 1156 * Validate language to avoid errors 1157 */ 1158 var new_validated_lang = _getValidatedLanguage(lang, user_config.languages); 1159 1160 /** 1161 * Set language only if it differs from current 1162 */ 1163 if (new_validated_lang !== _config.current_lang || force === true) { 1164 _config.current_lang = new_validated_lang; 1165 1166 if (consent_modal_exists) { 1167 _createConsentModal(new_validated_lang); 1168 } 1169 1170 _createSettingsModal(new_validated_lang); 1171 1172 _log("CookieConsent [LANGUAGE]: curr_lang: '" + new_validated_lang + "'"); 1173 1174 return true; 1175 } 1176 1177 return false; 1178 } 1179 1180 /** 1181 * Delete all cookies which are unused (based on selected preferences) 1182 * 1183 * @param {boolean} [clearOnFirstAction] 1184 */ 1185 var _autoclearCookies = function (clearOnFirstAction) { 1186 1187 // Get number of blocks 1188 var len = all_blocks.length; 1189 var count = -1; 1190 1191 // reset reload state 1192 reload_page = false; 1193 1194 // Retrieve all cookies 1195 var all_cookies_array = _getCookie('', 'all'); 1196 1197 // delete cookies on 'www.domain.com' and '.www.domain.com' (can also be without www) 1198 var domains = [_config.cookie_domain, '.' + _config.cookie_domain]; 1199 1200 1201 1202 // if domain has www, delete cookies also for 'domain.com' and '.domain.com' 1203 if (_config.cookie_domain.slice(0, 4) === 'www.') { 1204 var non_www_domain = _config.cookie_domain.substr(4); // remove first 4 chars (www.) 1205 domains.push(non_www_domain); 1206 domains.push('.' + non_www_domain); 1207 } 1208 1209 1210 // For each block 1211 for (var i = 0; i < len; i++) { 1212 1213 // Save current block (local scope & less accesses -> ~faster value retrieval) 1214 var curr_block = all_blocks[i]; 1215 1216 // If current block has a toggle for opt in/out 1217 if (Object.prototype.hasOwnProperty.call(curr_block, "toggle")) { 1218 1219 // if current block has a cookie table, an off toggle, 1220 // and its preferences were just changed => delete cookies 1221 var category_just_disabled = _inArray(changed_settings, curr_block['toggle']['value']) > -1; 1222 if ( 1223 !toggle_states[++count] && 1224 Object.prototype.hasOwnProperty.call(curr_block, "cookie_table") && 1225 (clearOnFirstAction || category_just_disabled) 1226 ) { 1227 var curr_cookie_table = curr_block['cookie_table']; 1228 1229 // Get first property name 1230 var ckey = _getKeys(all_table_headers[0])[0]; 1231 1232 // Get number of cookies defined in cookie_table 1233 var clen = curr_cookie_table.length; 1234 1235 // set "reload_page" to true if reload=on_disable 1236 if (curr_block['toggle']['reload'] === 'on_disable') 1237 category_just_disabled && (reload_page = true); 1238 1239 // for each row defined in the cookie table 1240 for (var j = 0; j < clen; j++) { 1241 var curr_domains = domains; 1242 1243 // Get current row of table (corresponds to all cookie params) 1244 var curr_row = curr_cookie_table[j], found_cookies = []; 1245 var curr_cookie_name = curr_row[ckey]; 1246 var is_regex = curr_row['is_regex'] || false; 1247 var curr_cookie_domain = curr_row['domain'] || null; 1248 var curr_cookie_path = curr_row['path'] || false; 1249 1250 // set domain to the specified domain 1251 curr_cookie_domain && (curr_domains = [curr_cookie_domain, '.' + curr_cookie_domain]); 1252 1253 1254 // If regex provided => filter cookie array 1255 if (is_regex) { 1256 for (var n = 0; n < all_cookies_array.length; n++) { 1257 if (all_cookies_array[n].match(curr_cookie_name)) { 1258 found_cookies.push(all_cookies_array[n]); 1259 } 1260 } 1261 } else { 1262 var found_index = _inArray(all_cookies_array, curr_cookie_name); 1263 if (found_index > -1) found_cookies.push(all_cookies_array[found_index]); 1264 } 1265 1266 _log("CookieConsent [AUTOCLEAR]: search cookie: '" + curr_cookie_name + "', found:", found_cookies); 1267 1268 // If cookie exists -> delete it 1269 if (found_cookies.length > 0) { 1270 _eraseCookies(found_cookies, curr_cookie_path, curr_domains); 1271 curr_block['toggle']['reload'] === 'on_clear' && (reload_page = true); 1272 } 1273 } 1274 } 1275 } 1276 } 1277 } 1278 1279 /** 1280 * Set toggles/checkboxes based on accepted categories and save cookie 1281 * @param {string[]} accepted_categories - Array of categories to accept 1282 */ 1283 var _saveCookiePreferences = function (accepted_categories) { 1284 1285 changed_settings = []; 1286 1287 // Retrieve all toggle/checkbox values 1288 var category_toggles = document.querySelectorAll('.c-tgl') || []; 1289 1290 // If there are opt in/out toggles ... 1291 if (category_toggles.length > 0) { 1292 for (var i = 0; i < category_toggles.length; i++) { 1293 var categoryContainer = all_modals_container.querySelector('#'+category_toggles[i].getAttribute("data-cfcookie-category")).querySelector(".expand-button"); 1294 if (_inArray(accepted_categories, all_categories[i]) !== -1) { 1295 categoryContainer.checked = true; 1296 category_toggles[i].checked = true; 1297 if (!toggle_states[i]) { 1298 changed_settings.push(all_categories[i]); 1299 toggle_states[i] = true; 1300 } 1301 } else { 1302 category_toggles[i].checked = false;
1303 if (toggle_states[i]) { 1304 changed_settings.push(all_categories[i]); 1305 toggle_states[i] = false; 1306 } 1307 1308 var isActive = all_modals_container.querySelector('#'+category_toggles[i].getAttribute("data-cfcookie-category")).querySelectorAll(".block-section input:checked"); 1309 if(!!isActive){ 1310 //categoryContainer.checked = true; 1311 if(isActive.length === 0){ 1312 categoryContainer.checked = false; 1313 } 1314 } 1315 } 1316 } 1317 } 1318 1319 1320 1321 1322 /** 1323 * Clear cookies when settings/preferences change 1324 */ 1325 if (!invalid_consent && _config.autoclear_cookies && changed_settings.length > 0) 1326 _autoclearCookies(); 1327 1328 if (!consent_date) consent_date = new Date(); 1329 if (!consent_uuid) consent_uuid = _uuidv4(); 1330 1331 saved_cookie_content = { 1332 "categories": accepted_categories, 1333 "level": accepted_categories, // Copy of the `categories` property for compatibility purposes with version v2.8.0 and below. 1334 "revision": _config.revision, 1335 "data": cookie_data, 1336 "rfc_cookie": _config.use_rfc_cookie, 1337 "consent_date": consent_date.toISOString(), 1338 "consent_uuid": consent_uuid 1339 } 1340 1341 // save cookie with preferences 'categories' (only if never accepted or settings were updated) 1342 if (invalid_consent || changed_settings.length > 0) { 1343 valid_revision = true; 1344 1345 1346 /** 1347 * Update "last_consent_update" only if it is invalid (after t) 1348 */ 1349 if (!last_consent_update) 1350 last_consent_update = consent_date; 1351 else 1352 last_consent_update = new Date(); 1353 1354 saved_cookie_content['last_consent_update'] = last_consent_update.toISOString(); 1355 1356 /** 1357 * Update accept type 1358 */ 1359 accept_type = _getAcceptType(_getCurrentCategoriesState()); 1360 1361 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1362 _manageExistingScripts(); 1363 } 1364 1365 if (invalid_consent) { 1366 1367 /** 1368 * Delete unused/"zombie" cookies if consent is not valid (not yet expressed or cookie has expired) 1369 */ 1370 if (_config.autoclear_cookies) 1371 _autoclearCookies(true); 1372 1373 if (typeof onFirstAction === 'function') 1374 onFirstAction(_cookieconsent.getUserPreferences(), saved_cookie_content); 1375 1376 if (typeof onAccept === 'function') 1377 onAccept(saved_cookie_content); 1378 1379 /** 1380 * Set consent as valid 1381 */ 1382 invalid_consent = false; 1383 1384 if (_config.mode === 'opt-in') return; 1385 } 1386 1387 // fire onChange only if settings were changed 1388 if (typeof onChange === "function" && changed_settings.length > 0) 1389 onChange(saved_cookie_content, changed_settings); 1390 1391 /** 1392 * reload page if needed 1393 */ 1394 if (reload_page) 1395 window.location.reload(); 1396 } 1397 1398 /** 1399 * Returns index of found element inside array, otherwise -1 1400 * @param {Array} arr 1401 * @param {Object} value 1402 * @returns {number} 1403 */ 1404 var _inArray = function (arr, value) { 1405 return arr.indexOf(value); 1406 } 1407 1408 /** 1409 * Helper function which prints info (console.log()) 1410 * @param {Object} print_msg 1411 * @param {Object} [optional_param] 1412 */ 1413 var _log = function (print_msg, optional_param, error) { 1414 ENABLE_LOGS && (!error ? console.log(print_msg, optional_param !== undefined ? optional_param : ' ') : console.error(print_msg, optional_param || "")); 1415 } 1416 1417 /** 1418 * Helper function which creates an HTMLElement object based on 'type' and returns it. 1419 * @param {string} type 1420 * @returns {HTMLElement} 1421 */ 1422 var _createNode = function (type) { 1423 var el = document.createElement(type); 1424 if (type === 'button') { 1425 el.setAttribute('type', type); 1426 } 1427 return el; 1428 } 1429 1430 /** 1431 * Generate RFC4122-compliant UUIDs. 1432 * https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid?page=1&tab=votes#tab-top 1433 * @returns {string} 1434 */ 1435 var _uuidv4 = function () { 1436 return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, function (c) { 1437 try { 1438 return (c ^ (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) 1439 } catch (e) { 1440 return ''; 1441 } 1442 }); 1443 } 1444 1445 /** 1446 * Resolve which language should be used. 1447 * 1448 * @param {Object} languages Object with language translations 1449 * @param {string} [requested_language] Language specified by given configuration parameters 1450 * @returns {string} 1451 */ 1452 var _resolveCurrentLang = function (languages, requested_language) { 1453 1454 if (_config.auto_language === 'browser') { 1455 return _getValidatedLanguage(_getBrowserLang(), languages); 1456 } else if (_config.auto_language === 'document') { 1457 return _getValidatedLanguage(document.documentElement.lang, languages); 1458 } else { 1459 if (typeof requested_language === 'string') { 1460 return _config.current_lang = _getValidatedLanguage(requested_language, languages); 1461 } 1462 } 1463 1464 _log("CookieConsent [LANG]: setting current_lang = '" + _config.current_lang + "'"); 1465 return _config.current_lang; // otherwise return default 1466 } 1467 1468 /** 1469 * Get current client's browser language 1470 * @returns {string} 1471 */ 1472 var _getBrowserLang = function () { 1473 var browser_lang = navigator.language || navigator.browserLanguage; 1474 browser_lang.length > 2 && (browser_lang = browser_lang[0] + browser_lang[1]); 1475 _log("CookieConsent [LANG]: detected_browser_lang = '" + browser_lang + "'"); 1476 return browser_lang.toLowerCase() 1477 } 1478 1479 /** 1480 * Trap focus inside modal and focus the first 1481 * focusable element of current active modal 1482 */ 1483 var _handleFocusTrap = function () { 1484 var tabbedOutsideDiv = false;
1485 var tabbedInsideModal = false; 1486 1487 _addEvent(document, 'keydown', function (e) { 1488 e = e || window.event; 1489 1490 // If is tab key => ok 1491 if (e.key !== 'Tab') return; 1492 1493 // If there is any modal to focus 1494 if (current_modal_focusable) { 1495 // If reached natural end of the tab sequence => restart 1496 if (e.shiftKey) { 1497 if (document.activeElement === current_modal_focusable[0]) { 1498 current_modal_focusable[1].focus(); 1499 e.preventDefault(); 1500 } 1501 } else { 1502 if (document.activeElement === current_modal_focusable[1]) { 1503 current_modal_focusable[0].focus(); 1504 e.preventDefault(); 1505 } 1506 } 1507 1508 // If have not yet used tab (or shift+tab) and modal is open ... 1509 // Focus the first focusable element 1510 if (!tabbedInsideModal && !clicked_inside_modal) { 1511 tabbedInsideModal = true; 1512 !tabbedOutsideDiv && e.preventDefault(); 1513 1514 if (e.shiftKey) { 1515 if (current_modal_focusable[3]) { 1516 if (!current_modal_focusable[2]) { 1517 current_modal_focusable[0].focus(); 1518 } else { 1519 current_modal_focusable[2].focus(); 1520 } 1521 } else { 1522 current_modal_focusable[1].focus(); 1523 } 1524 } else { 1525 if (current_modal_focusable[3]) { 1526 current_modal_focusable[3].focus(); 1527 } else { 1528 current_modal_focusable[0].focus(); 1529 } 1530 } 1531 } 1532 } 1533 1534 !tabbedInsideModal && (tabbedOutsideDiv = true); 1535 }); 1536 1537 1538 if (document.contains) { 1539 _addEvent(main_container, 'click', function (e) { 1540 e = e || window.event; 1541 /** 1542 * If click is on the foreground overlay (and not inside settings_modal), 1543 * hide settings modal 1544 * 1545 * Notice: click on div is not supported in IE 1546 */ 1547 1548 if (_hasClass(e.target, "c-tgl")) { 1549 var categoryIdentifier = e.target.getAttribute("data-cfcookie-category"); 1550 var categoriesSwitchButton = main_container.querySelector("#" + e.target.getAttribute("data-cfcookie-category")).querySelector(".expand-button"); 1551 var services = main_container.querySelector("#" + categoryIdentifier).querySelectorAll(".block-section ." + categoryIdentifier); 1552 1553 1554 var isServiceActive = false; 1555 for (var i = 0; i < services.length; i++) { 1556 if (services[i].querySelector("input").checked === true) { 1557 isServiceActive = true; 1558 } 1559 } 1560 1561 if (isServiceActive === false) { 1562 /* All Services are Disabled */ 1563 categoriesSwitchButton.checked = false; 1564 } else { 1565 categoriesSwitchButton.checked = true; 1566 } 1567 1568 1569 } 1570 1571 if (_hasClass(e.target, "expand-button")) { 1572 /** IF Category Switch button **/ 1573 var services = main_container.querySelector("#" + e.target.value).querySelectorAll(".block-section ." + e.target.value); 1574 for (var i = 0; i < services.length; i++) { 1575 if (e.target.checked === true && services[i].querySelector("input").disabled === false) { 1576 services[i].querySelector("input").checked = true; 1577 } else if(services[i].querySelector("input").disabled === false){ 1578 services[i].querySelector("input").checked = false;
1579 } 1580 } 1581 } 1582 1583 if (settings_modal_visible) { 1584 if (!all_modals_container.querySelector("#s-inr").contains(e.target)) { 1585 _cookieconsent.hideSettings(0); 1586 clicked_inside_modal = false; 1587 } else { 1588 clicked_inside_modal = true; 1589 } 1590 } else if (consent_modal_visible) { 1591 if (all_modals_container.querySelector("#cm").contains(e.target)) { 1592 clicked_inside_modal = true; 1593 } 1594 } 1595 1596 }, true); 1597 } 1598 } 1599 1600 /** 1601 * Manage each modal's layout 1602 * @param {Object} gui_options 1603 */ 1604 var _guiManager = function (gui_options, only_consent_modal) { 1605 1606 // If gui_options is not object => exit 1607 if (typeof gui_options !== 'object') return; 1608 1609 var consent_modal_options = gui_options['consent_modal']; 1610 var settings_modal_options = gui_options['settings_modal']; 1611 1612 /** 1613 * Helper function which adds layout and 1614 * position classes to given modal 1615 * 1616 * @param {HTMLElement} modal 1617 * @param {string[]} allowed_layouts 1618 * @param {string[]} allowed_positions 1619 * @param {string} layout 1620 * @param {string[]} position 1621 */ 1622 function _setLayout(modal, allowed_layouts, allowed_positions, allowed_transitions, layout, position, transition) { 1623 position = (position && position.split(" ")) || []; 1624 // Check if specified layout is valid 1625 if (_inArray(allowed_layouts, layout) > -1) { 1626 1627 // Add layout classes 1628 _addClass(modal, layout); 1629 1630 // Add position class (if specified) 1631 if (!(layout === 'bar' && position[0] === 'middle') && _inArray(allowed_positions, position[0]) > -1) { 1632 for (var i = 0; i < position.length; i++) { 1633 _addClass(modal, position[i]); 1634 } 1635 } 1636 } 1637 1638 // Add transition class 1639 (_inArray(allowed_transitions, transition) > -1) && _addClass(modal, transition); 1640 } 1641 1642 if (consent_modal_exists && consent_modal_options) { 1643 _setLayout( 1644 all_modals_container.querySelector("#cm"), 1645 ['box', 'bar', 'cloud'], 1646 ['top', 'middle', 'bottom'], 1647 ['zoom', 'slide'], 1648 consent_modal_options['layout'], 1649 consent_modal_options['position'], 1650 consent_modal_options['transition'] 1651 ); 1652 } 1653 1654 if (!only_consent_modal && settings_modal_options) { 1655 _setLayout( 1656 all_modals_container.querySelector("#s-cnt"), 1657 ['bar'], 1658 ['left', 'right'], 1659 ['zoom', 'slide'], 1660 settings_modal_options['layout'], 1661 settings_modal_options['position'], 1662 settings_modal_options['transition'] 1663 ); 1664 } 1665 } 1666 1667 /** 1668 * Returns true if cookie category is accepted by the user 1669 * @param {string} cookie_category 1670 * @returns {boolean} 1671 */ 1672 _cookieconsent.allowedCategory = function (cookie_category) { 1673 1674 if (!invalid_consent || _config.mode === 'opt-in') 1675 var allowed_categories = JSON.parse(_getCookie(_config.cookie_name, 'one', true) || '{}')['categories'] || [] 1676 else // mode is 'opt-out' 1677 var allowed_categories = default_enabled_categories; 1678 1679 return _inArray(allowed_categories, cookie_category) > -1; 1680 } 1681 1682 /** 1683 * "Init" method. Will run once and only if modals do not exist 1684 */ 1685 _cookieconsent.run = function (user_config) { 1686 1687 if (!document.getElementById('cc_div')) { 1688 1689 // configure all parameters 1690 _setConfig(user_config); 1691 1692 // if is bot, don't run plugin 1693 if (is_bot) return; 1694 1695 // Retrieve cookie value (if set) 1696 saved_cookie_content = JSON.parse(_getCookie(_config.cookie_name, 'one', true) || "{}"); 1697 1698 1699 // Retrieve "consent_uuid" 1700 consent_uuid = saved_cookie_content['consent_uuid']; 1701 1702 // If "consent_uuid" is present => assume that consent was previously given 1703 var cookie_consent_accepted = consent_uuid !== undefined; 1704 1705 // Retrieve "consent_date" 1706 consent_date = saved_cookie_content['consent_date']; 1707 consent_date && (consent_date = new Date(consent_date)); 1708 1709 // Retrieve "last_consent_update" 1710 last_consent_update = saved_cookie_content['last_consent_update']; 1711 last_consent_update && (last_consent_update = new Date(last_consent_update)); 1712 1713 // Retrieve "data" 1714 cookie_data = saved_cookie_content['data'] !== undefined ? saved_cookie_content['data'] : null; 1715 1716 // If revision is enabled and current value !== saved value inside the cookie =>
1716 revision is not valid 1717 if(typeof saved_cookie_content['revision'] !== "undefined" && revision_enabled && saved_cookie_content['revision'] !== _config.revision){ 1718 //user has a cookie 1719 valid_revision = false; 1720 }else if(saved_cookie_content['revision'] !== _config.revision && typeof saved_cookie_content['revision'] !== "undefined"){ 1721 valid_revision = true; 1722 } 1723 1724 // If consent is not valid => create consent modal 1725 consent_modal_exists = invalid_consent = (!cookie_consent_accepted || !valid_revision || !consent_date || !last_consent_update || !consent_uuid); 1726 1727 // Generate cookie-settings dom (& consent modal) 1728 _createCookieConsentHTML(); 1729 1730 _getModalFocusableData(); 1731 1732 _guiManager(user_config['gui_options']); 1733 _addDataButtonListeners(); 1734 1735 if (_config.autorun && consent_modal_exists) { 1736 _cookieconsent.show(user_config['delay'] || 0); 1737 } 1738 1739 // Add class to enable animations/transitions 1740 setTimeout(function () { 1741 _addClass(main_container, 'c--anim'); 1742 }, 30); 1743 1744 // Accessibility :=> if tab pressed => trap focus inside modal 1745 setTimeout(function () { 1746 _handleFocusTrap(); 1747 }, 100); 1748 1749 // If consent is valid 1750 if (!invalid_consent) { 1751 var rfc_prop_exists = typeof saved_cookie_content['rfc_cookie'] === "boolean"; 1752 1753 /* 1754 * Convert cookie to rfc format (if `use_rfc_cookie` is enabled) 1755 */ 1756 if (!rfc_prop_exists || (rfc_prop_exists && saved_cookie_content['rfc_cookie'] !== _config.use_rfc_cookie)) { 1757 saved_cookie_content['rfc_cookie'] = _config.use_rfc_cookie; 1758 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1759 } 1760 1761 /** 1762 * Update accept type 1763 */ 1764 accept_type = _getAcceptType(_getCurrentCategoriesState()); 1765 1766 _manageExistingScripts(); 1767 1768 if (typeof onAccept === 'function') 1769 onAccept(saved_cookie_content); 1770 1771 _log("CookieConsent [NOTICE]: consent already given!", saved_cookie_content); 1772 1773 } else { 1774 if (_config.mode === 'opt-out') { 1775 _log("CookieConsent [CONFIG] mode='" + _config.mode + "', default enabled categories:", default_enabled_categories); 1776 _manageExistingScripts(default_enabled_categories); 1777 } 1778 _log("CookieConsent [NOTICE]: ask for consent!"); 1779 } 1780 } else { 1781 _log("CookieConsent [NOTICE]: cookie consent already attached to body!"); 1782 } 1783 } 1784 1785 /** 1786 * Show settings modal (with optional delay) 1787 * @param {number} delay 1788 */ 1789 _cookieconsent.showSettings = function (delay) { 1790 setTimeout(function () { 1791 _addClass(html_dom, "show--settings"); 1792 all_modals_container.querySelector("#s-cnt").setAttribute('aria-hidden', 'false'); 1793 settings_modal_visible = true; 1794 /** 1795 * Set focus to the first focusable element inside settings modal 1796 */ 1797 setTimeout(function () { 1798 // If there is no consent-modal, keep track of the last focused elem. 1799 if (!consent_modal_visible) { 1800 last_elem_before_modal = document.activeElement; 1801 } else { 1802 last_consent_modal_btn_focus = document.activeElement; 1803 } 1804 1805 if (settings_modal_focusable.length === 0) return; 1806 1807 if (settings_modal_focusable[3]) { 1808 settings_modal_focusable[3].focus(); 1809 } else { 1810 settings_modal_focusable[0].focus(); 1811 } 1812 current_modal_focusable = settings_modal_focusable; 1813 }, 200); 1814 1815 _log("CookieConsent [SETTINGS]: show settings_modal"); 1816 }, delay > 0 ? delay : 0); 1817 } 1818 1819 /** 1820 * This function handles the loading/activation logic of the already 1821 * existing scripts based on the current accepted cookie categories 1822 * 1823 * @param {string[]} [must_enable_categories] 1824 */ 1825 var _manageExistingScripts = function (must_enable_categories) { 1826 1827 if (!_config.page_scripts) return; 1828 1829 // get all the scripts with "cookie-category" attribute 1830 var scripts = document.querySelectorAll('script[' + _config.script_selector + ']'); 1831 var accepted_categories = must_enable_categories || saved_cookie_content['categories'] || []; 1832 1833 /** 1834 * Load scripts (sequentially), using a recursive function 1835 * which loops through the scripts array 1836 * @param {Element[]} scripts scripts to load 1837 * @param {number} index current script to load 1838 */ 1839 var _loadScripts = function (scripts, index) { 1840 if (index < scripts.length) { 1841 1842 var curr_script = scripts[index]; 1843 var curr_script_category = curr_script.getAttribute(_config.script_selector); 1844 1845 /** 1846 * If current script's category is on the array of categories 1847 * accepted by the user => load script 1848 */ 1849 1850 if (_inArray(accepted_categories, curr_script_category) > -1) { 1851 1852 curr_script.type = 'text/javascript'; 1853 curr_script.removeAttribute(_config.script_selector); 1854 1855 // get current script data-src 1856 var src = curr_script.getAttribute('data-src'); 1857 1858 // some scripts (like ga) might throw warning if data-src is present 1859 src && curr_script.removeAttribute('data-src'); 1860 1861 // create fresh script (with the same code) 1862 var fresh_script = _createNode('script'); 1863 fresh_script.textContent = curr_script.innerHTML; 1864 1865 // Copy attributes over to the new "revived" script 1866 (function (destination, source) { 1867 var attributes = source.attributes; 1868 var len = attributes.length; 1869 for (var i = 0; i < len; i++) { 1870 var attr_name = attributes[i].nodeName; 1871 destination.setAttribute(attr_name, source[attr_name] || source.getAttribute(attr_name)); 1872 } 1873 })(fresh_script, curr_script); 1874 1875 // set src (if data-src found) 1876 src ? (fresh_script.src = src) : (src = curr_script.src); 1877 1878 // if script has "src" attribute 1879 // try loading it sequentially 1880 if (src) { 1881 // load script sequentially => the next script will not be loaded 1882 // until the current's script onload event triggers 1883 if (fresh_script.readyState) { // only required for IE <9 1884 fresh_script.onreadystatechange = function () { 1885 if (fresh_script.readyState === "loaded" || fresh_script.readyState === "complete") { 1886 fresh_script.onreadystatechange = null; 1887 _loadScripts(scripts, ++index); 1888 } 1889 }; 1890 } else { // others 1891 fresh_script.onload = function () { 1892 fresh_script.onload = null; 1893 _loadScripts(scripts, ++index); 1894 }; 1895 } 1896 } 1897 1898 1899 // Replace current "sleeping" script with the new "revived" one 1900 curr_script.parentNode.replaceChild(fresh_script, curr_script); 1901 1902 /** 1903 * If we managed to get here and scr is still set, it means that 1904 * the script is loading/loaded sequentially so don't go any further 1905 */ 1906 if (src) return; 1907 } 1908 1909 // Go to next script right away 1910 _loadScripts(scripts, ++index); 1911 } 1912 } 1913 1914 _loadScripts(scripts, 0); 1915 } 1916 1917 /** 1918 * Save custom data inside cookie 1919 * @param {object|string} new_data 1920 * @param {string} [mode] 1921 * @returns {boolean} 1922 */
1923 var _setCookieData = function (new_data, mode) { 1924 1925 var set = false; 1926 /** 1927 * If mode is 'update': 1928 * add/update only the specified props. 1929 */ 1930 if (mode === 'update') { 1931 cookie_data = _cookieconsent.get('data'); 1932 var same_type = typeof cookie_data === typeof new_data; 1933 1934 if (same_type && typeof cookie_data === "object") { 1935 !cookie_data && (cookie_data = {}); 1936 1937 for (var prop in new_data) { 1938 if (cookie_data[prop] !== new_data[prop]) { 1939 cookie_data[prop] = new_data[prop] 1940 set = true; 1941 } 1942 } 1943 } else if ((same_type || !cookie_data) && cookie_data !== new_data) { 1944 cookie_data = new_data; 1945 set = true; 1946 } 1947 } else { 1948 cookie_data = new_data; 1949 set = true; 1950 } 1951 1952 if (set) { 1953 saved_cookie_content['data'] = cookie_data; 1954 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1955 } 1956 1957 return set; 1958 } 1959 1960 /** 1961 * Helper method to set a variety of fields 1962 * @param {string} field 1963 * @param {object} data 1964 * @returns {boolean} 1965 */ 1966 _cookieconsent.set = function (field, data) { 1967 switch (field) { 1968 case 'data': 1969 return _setCookieData(data['value'], data['mode']); 1970 default: 1971 return false; 1972 } 1973 } 1974 1975 /** 1976 * Retrieve data from existing cookie 1977 * @param {string} field 1978 * @param {string} [cookie_name] 1979 * @returns {any} 1980 */ 1981 _cookieconsent.get = function (field, cookie_name) { 1982 var cookie = JSON.parse(_getCookie(cookie_name || _config.cookie_name, 'one', true) || "{}"); 1983 1984 return cookie[field]; 1985 } 1986 1987 /** 1988 * Read current configuration value 1989 * @returns {any} 1990 */ 1991 _cookieconsent.getConfig = function (field) { 1992 return _config[field] || user_config[field]; 1993 } 1994 1995 /** 1996 * Obtain accepted and rejected categories 1997 * @returns {{accepted: string[], rejected: string[]}} 1998 */ 1999 var _getCurrentCategoriesState = function () { 2000 2001 // get accepted categories 2002 accepted_categories = saved_cookie_content['categories'] || []; 2003 2004 // calculate rejected categories (all_categories - accepted_categories) 2005 rejected_categories = all_categories.filter(function (category) { 2006 return (_inArray(accepted_categories, category) === -1); 2007 }); 2008 2009 return { 2010 accepted: accepted_categories, 2011 rejected: rejected_categories 2012 } 2013 } 2014 2015 /** 2016 * Calculate "accept type" given current categories state 2017 * @param {{accepted: string[], rejected: string[]}} currentCategoriesState 2018 * @returns {string} 2019 */ 2020 var _getAcceptType = function (currentCategoriesState) { 2021 2022 var type = 'custom'; 2023 2024 // number of categories marked as necessary/readonly 2025 var necessary_categories_length = readonly_categories.filter(function (readonly) { 2026 return readonly === true; 2027 }).length; 2028 2029 // calculate accept type based on accepted/rejected categories 2030 if (currentCategoriesState.accepted.length === all_categories.length) 2031 type = 'all'; 2032 else if (currentCategoriesState.accepted.length === necessary_categories_length) 2033 type = 'necessary' 2034 2035 return type; 2036 } 2037 2038 /** 2039 * @typedef {object} userPreferences 2040 * @property {string} accept_type 2041 * @property {string[]} accepted_categories 2042 * @property {string[]} rejected_categories 2043 */ 2044 2045 /** 2046 * Retrieve current user preferences (summary) 2047 * @returns {userPreferences} 2048 */ 2049 _cookieconsent.getUserPreferences = function () { 2050 var currentCategoriesState = _getCurrentCategoriesState(); 2051 var accept_type = _getAcceptType(currentCategoriesState); 2052 2053 return { 2054 'accept_type': accept_type, 2055 'accepted_categories': currentCategoriesState.accepted, 2056 'rejected_categories': currentCategoriesState.rejected 2057 } 2058 } 2059 2060 /** 2061 * Function which will run after script load 2062 * @callback scriptLoaded 2063 */ 2064 2065 /** 2066 * Dynamically load script (append to head) 2067 * @param {string} src 2068 * @param {scriptLoaded} callback 2069 * @param {object[]} [attrs] Custom attributes 2070 */ 2071 _cookieconsent.loadScript = function (src, callback, attrs) { 2072 2073 var function_defined = typeof callback === 'function'; 2074 2075 // Load script only if not already loaded 2076 if (!document.querySelector('script[src="' + src + '"]')) { 2077 2078 var script = _createNode('script'); 2079 2080 // if an array is provided => add custom attributes 2081 if (attrs && attrs.length > 0) { 2082 for (var i = 0; i < attrs.length; ++i) { 2083 attrs[i] && script.setAttribute(attrs[i]['name'], attrs[i]['value']); 2084 } 2085 } 2086 2087 // if callback function defined => run callback onload 2088 if (function_defined) { 2089 script.onload = callback; 2090 } 2091 2092 script.src = src; 2093 2094 /** 2095 * Append script to head 2096 */ 2097 document.head.appendChild(script); 2098 } else { 2099 function_defined && callback(); 2100 } 2101 } 2102 2103 /** 2104 * Manage dynamically loaded scripts: https://github.com/orestbida/cookieconsent/issues/101 2105 * If plugin has already run, call this method to enable 2106 * the newly added scripts based on currently selected preferences 2107 */ 2108 _cookieconsent.updateScripts = function () { 2109 _manageExistingScripts(); 2110 } 2111 2112 /** 2113 * Show cookie consent modal (with delay parameter) 2114 * @param {number} [delay] 2115 * @param {boolean} [create_modal] create modal if it doesn't exist 2116 */ 2117 _cookieconsent.show = function (delay, create_modal) { 2118 2119 if (create_modal === true) 2120 _createConsentModal(_config.current_lang); 2121 2122 if (consent_modal_exists) { 2123 setTimeout(function () { 2124 _addClass(html_dom, "show--consent"); 2125 2126 /** 2127 * Update attributes/internal statuses 2128 */ 2129 all_modals_container.querySelector("#cm").setAttribute('aria-hidden', 'false'); 2130 consent_modal_visible = true; 2131 2132 setTimeout(function () { 2133 last_elem_before_modal = document.activeElement; 2134 current_modal_focusable = consent_modal_focusable; 2135 }, 200); 2136 2137 _log("CookieConsent [MODAL]: show consent_modal"); 2138 }, delay > 0 ? delay : (create_modal ? 30 : 0)); 2139 } 2140 } 2141 2142 /** 2143 * Hide consent modal 2144 */ 2145 _cookieconsent.hide = function () { 2146 if (consent_modal_exists) { 2147 _removeClass(html_dom, "show--consent"); 2148 all_modals_container.querySelector("#cm").setAttribute('aria-hidden', 'true'); 2149 consent_modal_visible = false;
2150 2151 setTimeout(function () { 2152 //restore focus to the last page element which had focus before modal opening 2153 last_elem_before_modal.focus(); 2154 current_modal_focusable = null; 2155 }, 200); 2156 2157 _log("CookieConsent [MODAL]: hide"); 2158 } 2159 } 2160 2161 /** 2162 * Hide settings modal 2163 */ 2164 _cookieconsent.hideSettings = function () { 2165 _removeClass(html_dom, "show--settings"); 2166 settings_modal_visible = false; 2167 all_modals_container.querySelector("#s-cnt").setAttribute('aria-hidden', 'true'); 2168 2169 2170 setTimeout(function () { 2171 /** 2172 * If consent modal is visible, focus him (instead of page document) 2173 */ 2174 if (consent_modal_visible) { 2175 last_consent_modal_btn_focus && last_consent_modal_btn_focus.focus(); 2176 current_modal_focusable = consent_modal_focusable; 2177 } else { 2178 /** 2179 * Restore focus to last page element which had focus before modal opening 2180 */ 2181 last_elem_before_modal && last_elem_before_modal.focus(); 2182 current_modal_focusable = null; 2183 } 2184 2185 clicked_inside_modal = false; 2186 }, 200); 2187 2188 _log("CookieConsent [SETTINGS]: hide settings_modal"); 2189 } 2190 2191 /** 2192 * Accept cookieconsent function API 2193 * @param {string[]|string} _categories - Categories to accept 2194 * @param {string[]} [_exclusions] - Excluded categories [optional] 2195 */ 2196 _cookieconsent.accept = function (_categories, _exclusions) { 2197 var categories = _categories || undefined; 2198 var exclusions = _exclusions || []; 2199 var to_accept = []; 2200 2201 /** 2202 * Get all accepted categories 2203 * @returns {string[]} 2204 */ 2205 var _getCurrentPreferences = function () { 2206 var toggles = document.querySelectorAll('.c-tgl') || []; 2207 var states = []; 2208 2209 for (var i = 0; i < toggles.length; i++) { 2210 if (toggles[i].checked) { 2211 states.push(toggles[i].value); 2212 } 2213 } 2214 return states; 2215 } 2216 2217 if (!categories) { 2218 to_accept = _getCurrentPreferences(); 2219 } else { 2220 if ( 2221 typeof categories === "object" && 2222 typeof categories.length === "number" 2223 ) { 2224 for (var i = 0; i < categories.length; i++) { 2225 if (_inArray(all_categories, categories[i]) !== -1) 2226 to_accept.push(categories[i]); 2227 } 2228 } else if (typeof categories === "string") { 2229 if (categories === 'all') 2230 to_accept = all_categories.slice(); 2231 else { 2232 if (_inArray(all_categories, categories) !== -1) 2233 to_accept.push(categories); 2234 } 2235 } 2236 } 2237 2238 // Remove excluded categories 2239 if (exclusions.length >= 1) { 2240 for (i = 0; i < exclusions.length; i++) { 2241 to_accept = to_accept.filter(function (item) { 2242 return item !== exclusions[i] 2243 }) 2244 } 2245 } 2246 2247 // Add back all the categories set as "readonly/required" 2248 for (i = 0; i < all_categories.length; i++) { 2249 if ( 2250 readonly_categories[i] === true && 2251 _inArray(to_accept, all_categories[i]) === -1 2252 ) { 2253 to_accept.push(all_categories[i]); 2254 } 2255 } 2256 2257 _saveCookiePreferences(to_accept); 2258 } 2259 2260 /** 2261 * API function to easily erase cookies 2262 * @param {(string|string[])} _cookies 2263 * @param {string} [_path] - optional 2264 * @param {string} [_domain] - optional 2265 */
2266 _cookieconsent.eraseCookies = function (_cookies, _path, _domain) { 2267 var cookies = []; 2268 var domains = _domain 2269 ? [_domain, "." + _domain] 2270 : [_config.cookie_domain, "." + _config.cookie_domain]; 2271 2272 if (typeof _cookies === "object" && _cookies.length > 0) { 2273 for (var i = 0; i < _cookies.length; i++) { 2274 this.validCookie(_cookies[i]) && cookies.push(_cookies[i]); 2275 } 2276 } else { 2277 this.validCookie(_cookies) && cookies.push(_cookies); 2278 } 2279 2280 _eraseCookies(cookies, _path, domains); 2281 } 2282 2283 /** 2284 * Set cookie, by specifying name and value 2285 * @param {string} name 2286 * @param {string} value 2287 */ 2288 var _setCookie = function (name, value) { 2289 2290 var cookie_expiration = _config.cookie_expiration; 2291 2292 if (typeof _config.cookie_necessary_only_expiration === 'number' && accept_type === 'necessary') 2293 cookie_expiration = _config.cookie_necessary_only_expiration; 2294 2295 value = _config.use_rfc_cookie ? encodeURIComponent(value) : value; 2296 2297 var date = new Date(); 2298 date.setTime(date.getTime() + (1000 * (cookie_expiration * 24 * 60 * 60))); 2299 var expires = "; expires=" + date.toUTCString(); 2300 2301 var cookieStr = name + "=" + (value || "") + expires + "; Path=" + _config.cookie_path + ";"; 2302 cookieStr += " SameSite=" + _config.cookie_same_site + ";"; 2303 2304 // assures cookie works with localhost (=> don't specify domain if on localhost) 2305 if (window.location.hostname.indexOf(".") > -1) { 2306 cookieStr += " Domain=" + _config.cookie_domain + ";"; 2307 } 2308 2309 if (window.location.protocol === "https:") { 2310 cookieStr += " Secure;"; 2311 } 2312 2313 document.cookie = cookieStr; 2314 _log("CookieConsent [SET_COOKIE]: '" + name + "' expires after " + cookie_expiration + " day(s)"); 2315 } 2316 2317 /** 2318 * Get cookie value by name, 2319 * returns the cookie value if found (or an array 2320 * of cookies if filter provided), otherwise empty string: "" 2321 * @param {string} name 2322 * @param {string} filter 'one' or 'all' 2323 * @param {boolean} [get_value] set to true to obtain its value 2324 * @returns {string|string[]} 2325 */ 2326 var _getCookie = function (name, filter, get_value) { 2327 var found; 2328 2329 if (filter === 'one') { 2330 found = document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)"); 2331 found = found ? (get_value ? found.pop() : name) : ""; 2332 2333 if (found && name === _config.cookie_name) { 2334 try { 2335 found = JSON.parse(found) 2336 } catch (e) { 2337 try { 2338 found = JSON.parse(decodeURIComponent(found)) 2339 } catch (e) { 2340 // if I got here => cookie value is not a valid json string 2341 found = {}; 2342 } 2343 } 2344 found = JSON.stringify(found); 2345 } else { 2346 //Undetected Cookies 2347 } 2348 } else if (filter === 'all') { 2349 // array of names of all existing cookies 2350 var cookies = document.cookie.split(/;\s*/); 2351 found = []; 2352 for (var i = 0; i < cookies.length; i++) { 2353 found.push(cookies[i].split("=")[0]); 2354 } 2355 } 2356 2357 return found; 2358 } 2359 2360 /** 2361 * Delete cookie by name & path 2362 * @param {string[]} cookies 2363 * @param {string} [custom_path] - optional 2364 * @param {string[]} domains - example: ['domain.com', '.domain.com'] 2365 */ 2366 var _eraseCookies = function (cookies, custom_path, domains) { 2367 var path = custom_path ? custom_path : '/'; 2368 var expires = 'Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 2369 2370 for (var i = 0; i < cookies.length; i++) { 2371 for (var j = 0; j < domains.length; j++) { 2372 var d = window.location.hostname.split("."); 2373 while (d.length > 0) { 2374 var cookieBase = encodeURIComponent(cookie
2374s[i].split(";")[0].split("=")[0]) + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT; domain=' + d.join('.') + ' ;path='; 2375 var p = location.pathname.split('/'); 2376 document.cookie = cookieBase + '/'; 2377 while (p.length > 0) { 2378 document.cookie = cookieBase + p.join('/'); 2379 p.pop(); 2380 }; 2381 d.shift(); 2382 } 2383 document.cookie = cookies[i] + '=; path=' + path + (domains[j].indexOf('.') == 0 ? '; domain=' + domains[j] : "") + '; ' + expires; 2384 } 2385 _log("CookieConsent [AUTOCLEAR]: deleting cookie: '" + cookies[i] + "' path: '" + path + "' domain:", domains); 2386 } 2387 } 2388 2389 /** 2390 * Returns true if cookie was found and has valid value (not empty string) 2391 * @param {string} cookie_name 2392 * @returns {boolean} 2393 */ 2394 _cookieconsent.validCookie = function (cookie_name) { 2395 return _getCookie(cookie_name, 'one', true) !== ""; 2396 } 2397 2398 /** 2399 * Function to run when event is fired 2400 * @callback eventFired 2401 */ 2402 2403 /** 2404 * Add event listener to dom object (cross browser function) 2405 * @param {Element} elem 2406 * @param {string} event 2407 * @param {eventFired} fn 2408 * @param {boolean} [isPassive] 2409 */ 2410 var _addEvent = function (elem, event, fn, isPassive) { 2411 elem.addEventListener(event, fn, isPassive === true ? {passive: true} : false); 2412 } 2413 2414 /** 2415 * Get all prop. keys defined inside object 2416 * @param {Object} obj 2417 */ 2418 var _getKeys = function (obj) { 2419 if (typeof obj === "object") { 2420 return Object.keys(obj); 2421 } 2422 } 2423 2424 /** 2425 * Append class to the specified dom element 2426 * @param {HTMLElement} elem 2427 * @param {string} classname 2428 */ 2429 var _addClass = function (elem, classname) { 2430 if(typeof elem !== "undefined"){ 2431 elem.classList.add(classname); 2432 } 2433 } 2434 2435 /** 2436 * Remove specified class from dom element 2437 * @param {HTMLElement} elem 2438 * @param {string} classname 2439 */ 2440 var _removeClass = function (el, className) { 2441 el.classList.remove(className); 2442 } 2443 2444 /** 2445 * Check if html element has class 2446 * @param {HTMLElement} el 2447 * @param {string} className 2448 */ 2449 var _hasClass = function (el, className) { 2450 return el.classList.contains(className); 2451 } 2452 2453 2454 /** 2455 * Replace all link placeholders in the cookiemanager temp html 2456 * @param token 2457 * @param link 2458 */ 2459 function _replaceLink(token, link) { 2460 for (const element of all_modals_container.querySelectorAll("#s-cnt *, #cm *")) { 2461 if (element.textContent.includes(token)) { 2462 element.innerHTML = element.innerHTML.replace(token, link); 2463 } 2464 } 2465 } 2466 2467 2468 return _cookieconsent; 2469 }; 2470 2471 var init = 'initCookieConsent'; 2472 /** 2473 * Make CookieConsent object accessible globally 2474 */ 2475 if (typeof window !== 'undefined' && typeof window[init] !== 'function') { 2476 window[init] = CookieConsent 2477 } 2478})();
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.