1/*! 2 * CookieConsent v2.3 3 * https://www.github.com/orestbida/cookieconsent 4 * Author Orest Bida 5 * Released under the MIT License 6 */ 7(function () { 8 "use strict"; 9 /** 10 * @param {HTMLElement} root element where the cookieconsent will be appended (default :=> document.body) 11 * @returns {Object} 12 */ 13 var CookieConsent = function (root) { 14 // CHANGE THIS FLAG FALSE TO DISABLE console.log() 15 var ENABLE_LOGS = false; 16 17 var _config = { 18 current_lang: "en", 19 autorun: true, // run as soon as loaded 20 cookie_expiration: 182, // default: 6 months (in days) 21 }; 22 23 /** 24 * Object which holds the main methods (.show, .run, ...) 25 */ 26 var _cookieconsent = {}; 27 28 /** 29 * Internal state variables 30 */ 31 var _saved_cookie_content; 32 var consent_modal_exists = false; 33 var cookie_consent_accepted = false; 34 var consent_modal_visible = false; 35 var settings_modal_visible = false; 36 var clicked_inside_modal = false; 37 var current_modal_focusable; 38 39 /** 40 * Save reference to the last focused element on the page 41 * (used later on to restore focus when both modals are closed) 42 */ 43 var last_elem_before_modal; 44 var last_consent_modal_btn_focus; 45 46 /** 47 * Both of the array below have the same structure: 48 * [0] => holds reference to FIRST focusable element inside modal 49 * [1] => holds reference to LAST focusable element inside modal 50 */ 51 var consent_modal_focusable = []; 52 var settings_modal_focusable = []; 53 54 /** 55 * Array of booleans used to keep track of enabled/disabled preferences 56 */ 57 var toggle_states = []; 58 59 /** 60 * Pointers to main dom elements (to avoid retrieving them later using document.getElementById) 61 */ 62 var html_dom = document.documentElement; 63 var main_container; 64 var consent_modal; 65 var settings_container, settings_inner; 66 67 /** 68 * Update config settings 69 * @param {Object} conf_params 70 */ 71 var _setConfig = function (conf_params) { 72 _log("CookieConsent [CONFIG]: recieved_config_settings ", conf_params); 73 74 if (typeof conf_params["cookie_expiration"] === "number") { 75 _config.cookie_expiration = conf_params["cookie_expiration"]; 76 } 77 78 if (typeof conf_params["autorun"] === "boolean") { 79 _config.autorun = conf_params["autorun"]; 80 } 81 82 if (conf_params["auto_language"]) { 83 _config.current_lang = _getValidatedLanguage( 84 _getBrowserLang(), 85 conf_params.languages 86 ); 87 } else { 88 if (typeof conf_params["current_lang"] === "string") { 89 _config.current_lang = _getValidatedLanguage( 90 conf_params["current_lang"], 91 conf_params.languages 92 ); 93 } 94 } 95 96 if (conf_params["force_consent"] === true) { 97 _addClass(html_dom, "force--consent"); 98 } 99 100 _log( 101 "CookieConsent [LANG]: setting current_lang = '" + 102 _config.current_lang + 103 "'" 104 ); 105 }; 106 107 /** 108 * Search for all occurrences in webpage and add an onClick listener : 109 * when clicked => open settings modal 110 */ 111 var _addCookieSettingsButtonListener = function () { 112 var all_links = document.querySelectorAll( 113 'a[data-cc="c-settings"], button[data-cc="c-settings"]' 114 ); 115 for (var x = 0; x < all_links.length; x++) { 116 all_links[x].setAttribute("aria-haspopup", "dialog"); 117 _addEvent(all_links[x], "click", function (event) { 118 _cookieconsent.showSettings(0); 119 event.preventDefault 120 ? event.preventDefault() 121 : (event.returnValue = false); 122 }); 123 } 124 }; 125 126 /** 127 * Check if given lang index exists as a defined property inside _config object 128 * If it exists -> desired language is implemented for cookieconsent 129 * Otherwise switch back to default current_lang 130 * @param {String} lang 131 * @param {Object} all_languages 132 */ 133 var _getValidatedLanguage = function (lang, all_languages) { 134 if (all_languages.hasOwnProperty(lang)) { 135 return lang; 136 } else if (_getKeys(all_languages).length > 0) { 137 if (all_languages.hasOwnProperty(_config.current_lang)) { 138 return _config.current_lang; 139 } else { 140 return _getKeys(all_languages)[0]; 141 } 142 } 143 }; 144 145 /** 146 * Save reference to first and last focusable elements inside each modal 147 * to prevent losing focus while navigating with TAB 148 * @param {HTMLElement} modal_dom 149 */ 150 var _getModalFocusableData = function () { 151 /** 152 * Note: any of the below focusable elements, which has the attribute tabindex="-1" AND is either 153 * the first or last element of the modal, won't recieve focus during "open/close" modal 154 */ 155 var allowed_focusable_types = [ 156 "[href]", 157 "button", 158 "input", 159 "details", 160 '[tabindex="0"]', 161 ]; 162 var focus_later, focus_first; 163 164 function _getAllFocusableElements(modal) { 165 focus_later = focus_first = false;
166 167 // ie might throw exception due to complex unsupported selector => a:not([tabindex="-1"]) 168 try { 169 var elems = modal.querySelectorAll( 170 allowed_focusable_types.join(':not([tabindex="-1"]), ') 171 ); 172 var attr, 173 len = elems.length, 174 i = 0; 175 176 while (i < len) { 177 attr = elems[i].getAttribute("data-focus"); 178 179 if (!focus_first && attr === "1") { 180 focus_first = elems[i]; 181 } else if (attr === "0") { 182 focus_later = elems[i]; 183 if ( 184 !focus_first && 185 elems[i + 1].getAttribute("data-focus") !== "0" 186 ) { 187 focus_first = elems[i + 1]; 188 } 189 } 190 191 i++; 192 } 193 194 return elems; 195 } catch (e) { 196 return modal.querySelectorAll(allowed_focusable_types.join(", ")); 197 } 198 } 199 200 /** 201 * Get settings modal'S all focusable elements 202 */ 203 var focusableContent = _getAllFocusableElements(settings_inner); 204 205 /** 206 * Save first and last elements (used to lock/trap focus inside modal) 207 */ 208 settings_modal_focusable.push(focusableContent[0]); 209 settings_modal_focusable.push( 210 focusableContent[focusableContent.length - 1] 211 ); 212 settings_modal_focusable[2] = focus_later || false; 213 settings_modal_focusable[3] = focus_first || false; 214 215 /** 216 * If consent modal exists, do the same 217 */ 218 if (consent_modal_exists) { 219 focusableContent = _getAllFocusableElements(consent_modal); 220 consent_modal_focusable.push(focusableContent[0]); 221 consent_modal_focusable.push( 222 focusableContent[focusableContent.length - 1] 223 ); 224 consent_modal_focusable[2] = focus_later || false; 225 consent_modal_focusable[3] = focus_first || false; 226 } 227 228 focus_later = focus_first = focusableContent = null; 229 }; 230 231 /** 232 * Generate cookie consent html based on config settings 233 */ 234 var _createCookieConsentHTML = function (never_accepted, conf_params) { 235 // Create main container which holds both consent modal & settings modal 236 main_container = _createNode("div"); 237 main_container.id = "cc--main"; 238 239 // Fix layout flash 240 main_container.style.position = "fixed"; 241 main_container.style.zIndex = "1000000"; 242 main_container.innerHTML = 243 '<!--[if lt IE 9 ]><div id="cc_div" class="ie"></div><![endif]--><!--[if (gt IE 8)|!(IE)]><!--><div id="cc_div"></div><!--<![endif]-->'; 244 var all_modals_container = main_container.children[0]; 245 246 // Get current language 247 var lang = _config.current_lang; 248 249 // Feature detection :=> avoid IE exception since .textContent is not always supported 250 var innerText = 251 typeof html_dom.textContent === "string" ? "textContent" : "innerText"; 252 253 // If never_accepted => create consent-modal 254 if (!never_accepted) { 255 consent_modal = _createNode("div"); 256 var consent_modal_inner = _createNode("div"); 257 var consent_title = _createNode("div"); 258 var consent_text = _createNode("div"); 259 var consent_buttons = _createNode("div"); 260 var consent_primary_btn = _createNode("button"); 261 var consent_secondary_btn = _createNode("button"); 262 263 consent_modal.id = "cm"; 264 consent_modal_inner.id = "cm_inner"; 265 consent_title.id = "cm_title"; 266 consent_text.id = "cm_text"; 267 consent_buttons.id = "cm_btns"; 268 consent_primary_btn.id = "cm_primary_btn"; 269 consent_secondary_btn.id = "cm_secondary_btn"; 270 consent_primary_btn.className = "c_button"; 271 consent_secondary_btn.className = "c_button c_link"; 272 273 consent_title.setAttribute("role", "heading"); 274 consent_title.setAttribute("aria-level", "2"); 275 consent_modal.setAttribute("role", "dialog"); 276 consent_modal.setAttribute("aria-modal", "true"); 277 consent_modal.setAttribute("aria-hidden", "false"); 278 consent_modal.setAttribute("aria-labelledby", "cm_title"); 279 consent_modal.setAttribute("aria-describedby", "cm_text"); 280 consent_primary_btn.setAttribute("type", "button"); 281 consent_secondary_btn.setAttribute("type", "button"); 282 283 /** 284 * Make modal by default hidden to prevent weird page jumps/flashes (shown only once css is loaded) 285 */ 286 consent_modal.style.visibility = "hidden"; 287 288 // Use insertAdjacentHTML instead of innerHTML 289 consent_title.insertAdjacentHTML( 290 "beforeend", 291 conf_params.languages[lang]["consent_modal"]["title"] 292 ); 293 consent_text.insertAdjacentHTML( 294 "beforeend", 295 conf_params.languages[lang]["consent_modal"]["description"] 296 ); 297 298 consent_primary_btn[innerText] = 299 conf_params.languages[lang]["consent_modal"]["primary_btn"]["text"]; 300 consent_secondary_btn[innerText] = 301 conf_params.languages[lang]["consent_modal"]["secondary_btn"]["text"]; 302 303 /** 304 * Add click event listeners 305 */ 306 if ( 307 conf_params.languages[lang]["consent_modal"]["primary_btn"]["role"] == 308 "accept_all" 309 ) { 310 _addEvent(consent_primary_btn, "click", function () { 311 _cookieconsent.hide(); 312 _log("CookieConsent [ACCEPT]: cookie_consent was accepted!"); 313 _saveCookiePreferences(conf_params, 1); // 1 => accept all 314 }); 315 } else { 316 _addEvent(consent_primary_btn, "click", function () { 317 _cookieconsent.hide(); 318 _log( 319 "CookieConsent [ACCEPT]: cookie_consent was accepted (necessary only)!" 320 ); 321 _saveCookiePreferences(conf_params, -1); // -1 => accept current selection 322 }); 323 } 324 325 if ( 326 conf_params.languages[lang]["consent_modal"]["secondary_btn"][ 327 "role" 328 ] == "accept_necessary" 329 ) { 330 _addEvent(consent_secondary_btn, "click", function () { 331 _cookieconsent.hide(); 332 _saveCookiePreferences(conf_params, 0); // 0 => accept necessary only 333 }); 334 } else { 335 _addEvent(consent_secondary_btn, "click", function () { 336 _cookieconsent.showSettings(0); 337 }); 338 } 339 340 consent_modal_inner.appendChild(consent_title); 341 consent_modal_inner.appendChild(consent_text); 342 consent_buttons.appendChild(consent_primary_btn); 343 consent_buttons.appendChild(consent_secondary_btn); 344 consent_modal_inner.appendChild(consent_buttons); 345 consent_modal.appendChild(consent_modal_inner); 346 347 // Append consent modal to main container 348 all_modals_container.appendChild(consent_modal); 349 } 350 351 /** 352 * Create all consent_modal elements 353 */ 354 settings_container = _createNode("div"); 355 var settings_container_valign = _createNode("div"); 356 var settings = _createNode("div"); 357 var settings_container_inner = _createNode("div"); 358 settings_inner = _createNode("div"); 359 var settings_title = _createNode("div"); 360 var settings_header = _createNode("div"); 361 var settings_close_btn = _createNode("button"); 362 var settings_close_btn_container = _createNode("div"); 363 var settings_blocks = _createNode("div"); 364 settings_close_btn.setAttribute("type", "button"); 365 366 /** 367 * Set ids 368 */ 369 settings_container.id = "cs_cont"; 370 settings_container_valign.id = "cs_valign"; 371 settings_container_inner.id = "cs_cont_inner"; 372 settings.id = "cs"; 373 settings_title.id = "cs_title"; 374 settings_inner.id = "cs_inner"; 375 settings_header.id = "cs_header"; 376 settings_blocks.id = "cs_blocks"; 377 settings_close_btn.id = "cs_close_btn"; 378 settings_close_btn_container.id = "cs_close_btn_cont"; 379 settings_close_btn.className = "c_button"; 380 381 settings_close_btn.setAttribute( 382 "aria-label", 383 conf_params.languages[lang]["settings_modal"]["close_btn_label"] || 384 "Close" 385 ); 386 settings_container.setAttribute("role", "dialog"); 387 settings_container.setAttribute("aria-modal", "true"); 388 settings_container.setAttribute("aria-hidden", "true"); 389 settings_container.setAttribute("aria-labelledby", "cs_title"); 390 settings_blocks.setAttribute("tabindex", "-1"); 391 settings_title.setAttribute("role", "heading"); 392 settings_container.style.visibility = "hidden"; 393 394 settings_close_btn_container.appendChild(settings_close_btn); 395 396 // If 'esc' key is pressed inside settings_container div => hide settings 397 _addEvent( 398 settings_container_valign, 399 "keydown", 400 function (evt) { 401 evt = evt || window.event; 402 if (evt.keyCode == 27) { 403 _cookieconsent.hideSettings(0); 404 } 405 }, 406 true 407 ); 408 409 _addEvent(settings_close_btn, "click", function () { 410 _cookieconsent.hideSettings(0); 411 }); 412 413 var all_blocks = 414 conf_params.languages[_config.current_lang]["settings_modal"]["blocks"]; 415 var n_blocks = all_blocks.length; 416 417 // Set settings modal title 418 settings_title.insertAdjacentHTML( 419 "beforeend", 420 conf_params.languages[_config.current_lang]["settings_modal"]["title"] 421 ); 422 423 // Create settings modal content (blocks) 424 for (var i = 0; i < n_blocks; ++i) { 425 // Create title 426 var block_section = _createNode("div"); 427 var block_table_container = _createNode("div");
428 var block_desc = _createNode("div"); 429 var block_title_container = _createNode("div"); 430 431 block_section.className = "cs_block"; 432 block_table_container.className = "desc"; 433 block_desc.className = "p"; 434 block_title_container.className = "title"; 435 436 // Sset title and description for each block 437 block_desc.insertAdjacentHTML( 438 "beforeend", 439 all_blocks[i]["description"] 440 ); 441 442 // Create toggle if specified (opt in/out) 443 if (typeof all_blocks[i]["toggle"] !== "undefined") { 444 var accordion_id = "acc__" + i; 445 446 // Create button (to collapse/expand block description) 447 var block_title_btn = _createNode("button"); 448 var block_switch_label = _createNode("label"); 449 var block_switch = _createNode("input"); 450 var block_switch_span = _createNode("span"); 451 var label_text_span = _createNode("span"); 452 453 // These 2 spans will contain each 2 pseudo-elements to generate 'tick' and 'x' icons 454 var block_switch_span_on_icon = _createNode("span"); 455 var block_switch_span_off_icon = _createNode("span"); 456 457 block_title_btn.className = "b_title"; 458 block_switch_label.className = "c_b_toggle"; 459 block_switch.className = "c_toggle"; 460 block_switch_span_on_icon.className = "c_on_icon"; 461 block_switch_span_off_icon.className = "c_off_icon"; 462 block_switch_span.className = "sc_toggle"; 463 label_text_span.className = "toggle_label"; 464 465 block_title_btn.setAttribute("type", "button"); 466 block_title_btn.setAttribute("aria-expanded", "false"); 467 block_title_btn.setAttribute("aria-controls", accordion_id); 468 469 block_switch.setAttribute("type", "checkbox"); 470 block_switch_span.setAttribute("aria-hidden", "true"); 471 472 var cookie_category = all_blocks[i]["toggle"].value; 473 block_switch.value = cookie_category; 474 475 label_text_span[innerText] = all_blocks[i]["title"]; 476 block_title_btn.insertAdjacentHTML( 477 "beforeend", 478 all_blocks[i]["title"] 479 ); 480 481 block_title_container.appendChild(block_title_btn); 482 block_switch_span.appendChild(block_switch_span_on_icon); 483 block_switch_span.appendChild(block_switch_span_off_icon); 484 485 /** 486 * If never accepted => generate toggles with the states defined in the config. object 487 * Otherwise, retrieve values from saved cookie 488 */ 489 if (never_accepted) { 490 if ( 491 _arrayContains( 492 JSON.parse(_saved_cookie_content).level, 493 cookie_category 494 ) 495 ) { 496 block_switch.checked = true; 497 toggle_states.push(true); 498 } else { 499 toggle_states.push(false); 500 } 501 } else if (all_blocks[i]["toggle"]["enabled"]) { 502 block_switch.checked = true; 503 } 504 505 /** 506 * Set toggle as readonly if true (disable checkbox) 507 */ 508 if (all_blocks[i]["toggle"]["readonly"]) { 509 block_switch.disabled = true; 510 block_switch.setAttribute("aria-readonly", "true"); 511 _addClass(block_switch_span, "sc_readonly"); 512 } 513 514 _addClass(block_table_container, "accordion"); 515 _addClass(block_title_container, "block_button"); 516 517 block_table_container.id = accordion_id; 518 block_table_container.setAttribute("aria-hidden", "true"); 519 520 block_switch_label.appendChild(block_switch); 521 block_switch_label.appendChild(block_switch_span); 522 block_switch_label.appendChild(label_text_span); 523 block_title_container.appendChild(block_switch_label); 524 525 _addClass(block_section, "block__expand"); 526 527 /** 528 * On button click handle the following :=> aria-expanded, aria-hidden and _active class for current block 529 */ 530 (function (accordion, block_section, btn) { 531 _addEvent( 532 block_title_btn, 533 "click", 534 function () { 535 if (!_hasClass(block_section, "_active")) { 536 _addClass(block_section, "_active"); 537 btn.setAttribute("aria-expanded", "true"); 538 accordion.setAttribute("aria-hidden", "false"); 539 } else { 540 _removeClass(block_section, "_active"); 541 btn.setAttribute("aria-expanded", "false"); 542 accordion.setAttribute("aria-hidden", "true"); 543 } 544 }, 545 false 546 ); 547 })(block_table_container, block_section, block_title_btn); 548 } else { 549 /** 550 * If block is not a button (no toggle defined), 551 * create a simple div instead 552 */ 553 var block_title = _createNode("div"); 554 block_title.className = "b_title"; 555 block_title.setAttribute("role", "heading"); 556 block_title.setAttribute("aria-level", "3"); 557 block_title.insertAdjacentHTML("beforeend", all_blocks[i]["title"]); 558 block_title_container.appendChild(block_title); 559 } 560 561 block_section.appendChild(block_title_container); 562 block_table_container.appendChild(block_desc); 563 564 // if cookie table found, generate table for this block 565 if (typeof all_blocks[i]["cookie_table"] !== "undefined") { 566 var tr_tmp_fragment = document.createDocumentFragment(); 567 var all_table_headers = 568 conf_params.languages[_config.current_lang]["settings_modal"][ 569 "cookie_table_headers" 570 ]; 571 572 /** 573 * Use custom table headers 574 */ 575 for (var p = 0; p < all_table_headers.length; ++p) { 576 // create new header 577 var th1 = _createNode("th"); 578 th1.setAttribute("scope", "col"); 579 580 // get custom header content 581 var new_column_key = _getKeys(all_table_headers[p])[0]; 582 583 th1[innerText] = all_table_headers[p][new_column_key]; 584 tr_tmp_fragment.appendChild(th1); 585 } 586 587 var tr_tmp = _createNode("tr"); 588 tr_tmp.appendChild(tr_tmp_fragment); 589 590 // create table header & append fragment 591 var thead = _createNode("thead"); 592 thead.appendChild(tr_tmp); 593 594 // append header to table 595 var block_table = _createNode("table"); 596 block_table.appendChild(thead); 597 598 var tbody_fragment = document.createDocumentFragment(); 599 600 // create table content 601 for (var n = 0; n < all_blocks[i]["cookie_table"].length; n++) { 602 var tr = _createNode("tr"); 603 604 for (var g = 0; g < all_table_headers.length; ++g) { 605 // get custom header content 606 var new_column_key = _getKeys(all_table_headers[g])[0]; 607 608 var td_tmp = _createNode("td"); 609 td_tmp[innerText] = 610 all_blocks[i]["cookie_table"][n][new_column_key]; 611 td_tmp.setAttribute( 612 "data-column", 613 all_table_headers[g][new_column_key] 614 ); 615 616 tr.appendChild(td_tmp); 617 } 618 619 tbody_fragment.appendChild(tr); 620 } 621 622 // append tbody_fragment to tbody & append the latter into the table 623 var tbody = _createNode("tbody"); 624 tbody.appendChild(tbody_fragment); 625 block_table.appendChild(tbody); 626 627 //block_section.appendChild(block_table); 628 block_table_container.appendChild(block_table); 629 } 630 631 block_section.appendChild(block_table_container); 632
633 // append block inside settings dom 634 settings_blocks.appendChild(block_section); 635 } 636 637 // Create settings buttons 638 var settings_buttons = _createNode("div"); 639 var settings_save_btn = _createNode("button"); 640 var settings_accept_all_btn = _createNode("button"); 641 642 settings_buttons.id = "cs_buttons"; 643 settings_save_btn.id = "cs_save__btn"; 644 settings_accept_all_btn.id = "cs_acceptall_btn"; 645 settings_save_btn.setAttribute("type", "button"); 646 settings_accept_all_btn.setAttribute("type", "button"); 647 settings_save_btn.className = "c_button"; 648 settings_accept_all_btn.className = "c_button"; 649 settings_save_btn.insertAdjacentHTML( 650 "beforeend", 651 conf_params.languages[_config.current_lang]["settings_modal"][ 652 "save_settings_btn" 653 ] 654 ); 655 settings_accept_all_btn.insertAdjacentHTML( 656 "beforeend", 657 conf_params.languages[_config.current_lang]["settings_modal"][ 658 "accept_all_btn" 659 ] 660 ); 661 662 settings_buttons.appendChild(settings_accept_all_btn); 663 settings_buttons.appendChild(settings_save_btn); 664 665 // Add save preferences button onClick event 666 // Hide both settings modal and consent modal 667 _addEvent(settings_save_btn, "click", function () { 668 _cookieconsent.hideSettings(); 669 _cookieconsent.hide(); 670 _saveCookiePreferences(conf_params, -1); 671 }); 672 673 _addEvent(settings_accept_all_btn, "click", function () { 674 _cookieconsent.hideSettings(); 675 _cookieconsent.hide(); 676 _saveCookiePreferences(conf_params, 1); 677 }); 678 679 settings_header.appendChild(settings_title); 680 settings_header.appendChild(settings_close_btn_container); 681 682 settings_inner.appendChild(settings_header); 683 settings_inner.appendChild(settings_blocks); 684 settings_inner.appendChild(settings_buttons); 685 settings_container_inner.appendChild(settings_inner); 686 687 settings.appendChild(settings_container_inner); 688 settings_container_valign.appendChild(settings); 689 settings_container.appendChild(settings_container_valign); 690 691 all_modals_container.appendChild(settings_container); 692 693 // Finally append everything to body (main_container holds both modals) 694 (root || document.body).appendChild(main_container); 695 }; 696 697 /** 698 * Save cookie preferences 699 * accept_type = 0: accept necessary only 700 * accept_type = 1: accept all 701 * accept_type = -1: accept selection 702 */ 703 var _saveCookiePreferences = function (conf_params, accept_type) { 704 // Get all cookiepreferences values saved in cookieconsent settings modal 705 var category_toggles = document.querySelectorAll(".c_toggle"); 706 var c_cookie_level = "", 707 changedSettings = false; 708 709 // If there are opt in/out toggles ... 710 if (typeof category_toggles.length === "number") { 711 switch (accept_type) { 712 case -1: 713 //accept current selection 714 for (var i = 0; i < category_toggles.length; i++) { 715 if (category_toggles[i].checked) { 716 c_cookie_level += '"' + category_toggles[i].value + '",'; 717 if (!toggle_states[i]) { 718 changedSettings = true; 719 toggle_states[i] = true; 720 } 721 } else { 722 if (toggle_states[i]) { 723 changedSettings = true; 724 toggle_states[i] = false; 725 } 726 } 727 } 728 break; 729 case 0: 730 // disable all except necessary 731 for (var i = 0; i < category_toggles.length; i++) { 732 if (category_toggles[i].disabled) { 733 c_cookie_level += '"' + category_toggles[i].value + '",'; 734 toggle_states[i] = true; 735 } else { 736 category_toggles[i].checked = false; 737 if (toggle_states[i]) { 738 changedSettings = true; 739 toggle_states[i] = false; 740 } 741 } 742 } 743 break; 744 case 1: 745 // enable all 746 for (var i = 0; i < category_toggles.length; i++) { 747 category_toggles[i].checked = true;
748 c_cookie_level += '"' + category_toggles[i].value + '",'; 749 if (!toggle_states[i]) { 750 changedSettings = true; 751 } 752 753 toggle_states[i] = true; 754 } 755 break; 756 } 757 758 // remove last ',' character 759 c_cookie_level = c_cookie_level.slice(0, -1); 760 761 /** 762 * If autoclear_cookies==true -> delete all cookies which are unused (based on selected preferences) 763 */ 764 if (conf_params["autoclear_cookies"] && cookie_consent_accepted) { 765 // Get array of all blocks defined inside settings 766 var all_blocks = 767 conf_params.languages[_config.current_lang]["settings_modal"][ 768 "blocks" 769 ]; 770 771 // Get number of blocks 772 var len = all_blocks.length; 773 774 // For each block 775 var count = -1; 776 for (var jk = 0; jk < len; jk++) { 777 // Save current block (local scope & less accesses -> ~faster value retrieval) 778 var curr_block = all_blocks[jk]; 779 780 // If current block has a toggle for opt in/out 781 if (curr_block.hasOwnProperty("toggle")) { 782 // if current block has a cookie table with toggle off => delete cookies 783 if ( 784 !toggle_states[++count] && 785 curr_block.hasOwnProperty("cookie_table") 786 ) { 787 var ckey = _getKeys( 788 conf_params.languages[_config.current_lang]["settings_modal"][ 789 "cookie_table_headers" 790 ][0] 791 )[0]; 792 793 // Get number of cookies defined in cookie_table 794 var clen = curr_block["cookie_table"].length; 795 796 // Delete each cookie defined in ccb_cookie_table of current block 797 for (var hk = 0; hk < clen; hk++) { 798 // Get current row of table (corresponds to all cookie params) 799 var curr_row = curr_block["cookie_table"][hk]; 800 801 // If cookie exists -> delete it 802 if (_getCookie(curr_row[ckey]) != "") { 803 _eraseCookie(curr_row[ckey]); 804 _log( 805 "CookieConsent [AUTOCLEAR]: deleting cookie: '" + 806 curr_row[ckey] + 807 "'" 808 ); 809 } 810 } 811 } 812 } 813 } 814 } 815 } 816 817 _saved_cookie_content = '{"level": [' + c_cookie_level + "]}"; 818 819 // save cookie with preferences 'level' (only if never accepted or settings were updated) 820 if (!cookie_consent_accepted || changedSettings) 821 _setCookie( 822 "cc_cookie", 823 _saved_cookie_content, 824 _config.cookie_expiration, 825 0, 826 0 827 ); 828 829 if ( 830 typeof conf_params["onAccept"] === "function" && 831 !cookie_consent_accepted 832 ) { 833 cookie_consent_accepted = true; 834 return conf_params["onAccept"](JSON.parse(_saved_cookie_content)); 835 } 836 837 // fire onChange only if settings were changed 838 if (typeof conf_params["onChange"] === "function" && changedSettings) { 839 conf_params["onChange"](JSON.parse(_saved_cookie_content)); 840 } 841 }; 842 843 /** 844 * Load style via ajax in background (and then show modal) 845 * @param {Object} conf_params 846 * @param {Function} callback 847 */ 848 var _loadCSS = function (conf_params, callback) { 849 if ( 850 conf_params["autoload_css"] && 851 !document.getElementById("cc--style") 852 ) { 853 // Create style tag 854 var style = _createNode("style"); 855 856 // ad an id so that in SPA apps (react-like) the style doesn't get loaded multiple times when plugin is called 857 style.id = "cc--style"; 858 859 var xhr = new XMLHttpRequest(); 860 861 xhr.onreadystatechange = function () { 862 if (this.readyState == 4 && this.status == 200) { 863 // Necessary for <IE9 864 style.setAttribute("type", "text/css"); 865 866 if (style.styleSheet) { 867 // if <IE9 868 style.styleSheet.cssText = this.responseText; 869 } else { 870 // all other browsers 871 style.appendChild(document.createTextNode(this.responseText)); 872 } 873 874 // Append css text content 875 document.getElementsByTagName("head")[0].appendChild(style); 876 _log( 877 "CookieConsent [AUTOLOAD_CSS]: loaded style = '" + 878 conf_params["theme_css"] + 879 "'" 880 ); 881 882 // Call function with minimal delay (to make sure that initial fade-zoom-in animations dont get skipped) 883 setTimeout(function () { 884 callback(); 885 }, 10); 886 } 887 }; 888 889 xhr.open("GET", conf_params["theme_css"]); 890 xhr.send(); 891 } else { 892 callback(); 893 } 894 }; 895 896 /** 897 * Returns true if value is found inside array 898 * @param {Array} arr 899 * @param {Object} value 900 * @returns {Boolean} 901 */ 902 var _arrayContains = function (arr, value) { 903 var len = arr.length; 904 for (var i = 0; i < len; i++) { 905 if (arr[i] == value) return true; 906 } 907 return false;
908 }; 909 910 /** 911 * Helper function which prints info (console.log()) 912 * @param {Object} print_msg 913 * @param {Object} optional_param 914 */ 915 var _log = function (print_msg, optional_param, error) { 916 ENABLE_LOGS && 917 (!error 918 ? console.log(print_msg, optional_param || " ") 919 : console.error(print_msg, optional_param || "")); 920 }; 921 922 /** 923 * Helper function which creates an HTMLElement object based on 'type' and returns it. 924 * @param {String} type 925 * @returns {HTMLElement} 926 */ 927 var _createNode = function (type) { 928 return document.createElement(type); 929 }; 930 931 /** 932 * Get current client - browser language 933 * Used when 'auto_language' config property is set to 'true' (boolean) 934 * @returns {String} 935 */ 936 var _getBrowserLang = function () { 937 var browser_lang = navigator.language || navigator.browserLanguage; 938 browser_lang.length > 2 && 939 (browser_lang = browser_lang[0] + browser_lang[1]); 940 _log( 941 "CookieConsent [LANG]: detected_browser_lang = '" + browser_lang + "'" 942 ); 943 return browser_lang.toLowerCase(); 944 }; 945 946 /** 947 * Traps focus inside modal && focuses the first focusable element of current active modal 948 */ 949 var _handleFocusOutline = function () { 950 var tabbedOutsideDiv = false; 951 var tabbedInsideModal = false; 952 953 _addEvent(document, "keydown", function (e) { 954 e = e || window.event; 955 956 // If is tab key => ok 957 if (e.key !== "Tab") return; 958 959 // If there is any modal to focus 960 if (current_modal_focusable) { 961 // If reached natural end of the tab sequence => restart 962 if (e.shiftKey) { 963 if (document.activeElement === current_modal_focusable[0]) { 964 current_modal_focusable[1].focus(); 965 e.preventDefault(); 966 } 967 } else { 968 if (document.activeElement === current_modal_focusable[1]) { 969 current_modal_focusable[0].focus(); 970 e.preventDefault(); 971 } 972 } 973 974 // If have not yet used tab (or shift+tab) and modal is open ... 975 // Focus the first focusable element 976 if (!tabbedInsideModal && !clicked_inside_modal) { 977 tabbedInsideModal = true; 978 !tabbedOutsideDiv && e.preventDefault(); 979 980 if (e.shiftKey) { 981 if (current_modal_focusable[3]) { 982 if (!current_modal_focusable[2]) { 983 current_modal_focusable[0].focus(); 984 } else { 985 current_modal_focusable[2].focus(); 986 } 987 } else { 988 current_modal_focusable[1].focus(); 989 } 990 } else { 991 if (current_modal_focusable[3]) { 992 current_modal_focusable[3].focus(); 993 } else { 994 current_modal_focusable[0].focus(); 995 } 996 } 997 } 998 } 999 1000 !tabbedInsideModal && (tabbedOutsideDiv = true); 1001 }); 1002 1003 if (document.contains) { 1004 _addEvent( 1005 main_container, 1006 "click", 1007 function (e) { 1008 e = e || window.event; 1009 /** 1010 * If click is on the foreground overlay (and not inside settings_modal), 1011 * hide settings modal 1012 * 1013 * Notice: click on div is not supported in IE 1014 */ 1015 if (settings_modal_visible) { 1016 if (!settings_inner.contains(e.target)) { 1017 _cookieconsent.hideSettings(0); 1018 clicked_inside_modal = false; 1019 } else { 1020 clicked_inside_modal = true; 1021 } 1022 } else if (consent_modal_visible) { 1023 if (consent_modal.contains(e.target)) { 1024 clicked_inside_modal = true; 1025 } 1026 } 1027 }, 1028 true 1029 ); 1030 } 1031 }; 1032 1033 /** 1034 * Returns true cookie category is saved into cc_cookie 1035 * @param {String} cookie_name 1036 * @returns {Boolean} 1037 */ 1038 _cookieconsent.allowedCategory = function (cookie_name) { 1039 return _arrayContains( 1040 JSON.parse(_getCookie("cc_cookie") || "{}")["level"] || [], 1041 cookie_name 1042 ); 1043 }; 1044 1045 /** 1046 * Check if cookieconsent is alredy attached to dom 1047 * If not, create one, configure it and attach it to body 1048 */ 1049 _cookieconsent.run = function (conf_params) { 1050 if (!main_container) { 1051 // configure all parameters 1052 _setConfig(conf_params); 1053 1054 // Retrieve cookie value (if set) 1055 _saved_cookie_content = _getCookie("cc_cookie"); 1056 1057 // If cookie is empty => create consent modal 1058 consent_modal_exists = _saved_cookie_content == ""; 1059 1060 _loadCSS(conf_params, function () {
1061 // Generate cookie-settings dom (& consent modal) 1062 _createCookieConsentHTML(!consent_modal_exists, conf_params); 1063 _addCookieSettingsButtonListener(); 1064 _getModalFocusableData(); 1065 1066 if (!_saved_cookie_content && _config.autorun) { 1067 _cookieconsent.show(conf_params["delay"] || 0); 1068 } 1069 1070 // Add class to enable animations 1071 setTimeout(function () { 1072 _addClass(main_container, "c--anim"); 1073 }, 15); 1074 1075 // Accessibility :=> if tab pressed => show outline focus 1076 setTimeout(function () { 1077 _handleFocusOutline(); 1078 }, 100); 1079 }); 1080 1081 // if cookie accepted => fire once onAccept method (if defined) 1082 if ( 1083 _saved_cookie_content && 1084 typeof conf_params["onAccept"] === "function" && 1085 !cookie_consent_accepted 1086 ) { 1087 cookie_consent_accepted = true; 1088 conf_params["onAccept"](JSON.parse(_saved_cookie_content || "{}")); 1089 } 1090 } else { 1091 _log("CookieConsent [NOTICE]: cookie consent alredy attached to body!"); 1092 } 1093 }; 1094 1095 /** 1096 * Show settings modal 1097 * @param {Number} delay 1098 */ 1099 _cookieconsent.showSettings = function (delay) { 1100 setTimeout( 1101 function () { 1102 _addClass(html_dom, "show--settings"); 1103 settings_container.setAttribute("aria-hidden", "false"); 1104 settings_modal_visible = true; 1105 1106 // If there is no consent-modal, keep track of last focused elem 1107 if (!consent_modal_visible) { 1108 last_elem_before_modal = document.activeElement; 1109 } else { 1110 last_consent_modal_btn_focus = document.activeElement; 1111 } 1112 1113 /** 1114 * Set focus to first focusable element inside settings modal 1115 */ 1116 setTimeout(function () { 1117 if (settings_modal_focusable[3]) { 1118 settings_modal_focusable[3].focus(); 1119 } else { 1120 settings_modal_focusable[0].focus(); 1121 } 1122 current_modal_focusable = settings_modal_focusable; 1123 }, 100); 1124 1125 _log("CookieConsent [SETTINGS]: show settings_modal"); 1126 }, 1127 delay > 0 ? delay : 0 1128 ); 1129 }; 1130 1131 /** 1132 * Dynamically load script 1133 * @param {String} src 1134 * @param {Function} callback 1135 * @param {Array} attrs 1136 */ 1137 _cookieconsent.loadScript = function (src, callback, attrs) { 1138 // Load script only if not alredy loaded 1139 if (!document.querySelector('script[src="' + src + '"]')) { 1140 var script = _createNode("script"); 1141 1142 // if an array is provided => add custom attributes 1143 if (attrs && attrs.length > 0) { 1144 for (var i = 0; i < attrs.length; ++i) { 1145 attrs[i] && 1146 script.setAttribute(attrs[i]["name"], attrs[i]["value"]); 1147 } 1148 } 1149 1150 if (script.readyState) { 1151 // only required for IE <9 1152 script.onreadystatechange = function () { 1153 if ( 1154 script.readyState === "loaded" || 1155 script.readyState === "complete" 1156 ) { 1157 script.onreadystatechange = null; 1158 callback(); 1159 } 1160 }; 1161 } else { 1162 //Others 1163 script.onload = callback; 1164 } 1165 1166 script.src = src; 1167 1168 /** 1169 * Append script to head 1170 */ 1171 (document.head 1172 ? document.head 1173 : document.getElementsByTagName("head")[0] 1174 ).appendChild(script); 1175 } else { 1176 callback(); 1177 } 1178 }; 1179 1180 /** 1181 * Show cookie consent modal (with delay parameter) 1182 * @param {Number} delay 1183 */ 1184 _cookieconsent.show = function (delay) { 1185 if (consent_modal_exists) { 1186 setTimeout( 1187 function () { 1188 _addClass(html_dom, "show--consent"); 1189 1190 /** 1191 * Update attributes/internal statuses 1192 */ 1193 consent_modal.setAttribute("aria-hidden", "false"); 1194 consent_modal_visible = true; 1195 last_elem_before_modal = document.activeElement; 1196 current_modal_focusable = consent_modal_focusable; 1197 1198 _log("CookieConsent [MODAL]: show consent_modal"); 1199 }, 1200 delay > 0 ? delay : 0 1201 ); 1202 } 1203 }; 1204 1205 /** 1206 * Hide consent modal 1207 */ 1208 _cookieconsent.hide = function () { 1209 if (consent_modal_exists) { 1210 _removeClass(html_dom, "show--consent"); 1211 consent_modal.setAttribute("aria-hidden", "true"); 1212 consent_modal_visible = false;
1213 1214 //restore focus to last page element which had focus before modal opening 1215 last_elem_before_modal.focus(); 1216 current_modal_focusable = null; 1217 _log("CookieConsent [MODAL]: hide"); 1218 } 1219 }; 1220 1221 /** 1222 * Hide settings modal 1223 */ 1224 _cookieconsent.hideSettings = function () { 1225 _removeClass(html_dom, "show--settings"); 1226 settings_modal_visible = false; 1227 settings_container.setAttribute("aria-hidden", "true"); 1228 1229 /** 1230 * If consent modal is visible, focus him (instead of page document) 1231 */ 1232 if (consent_modal_visible) { 1233 last_consent_modal_btn_focus.focus(); 1234 current_modal_focusable = consent_modal_focusable; 1235 } else { 1236 /** 1237 * Restore focus to last page element which had focus before modal opening 1238 */ 1239 last_elem_before_modal.focus(); 1240 current_modal_focusable = null; 1241 } 1242 1243 clicked_inside_modal = false; 1244 _log("CookieConsent [SETTINGS]: hide settings_modal"); 1245 }; 1246 1247 /** 1248 * Set cookie, specifying name, value and expiration time 1249 * @param {String} name 1250 * @param {String} value 1251 * @param {Number} days 1252 */ 1253 var _setCookie = function (name, value, days) { 1254 var expires = ""; 1255 1256 var date = new Date(); 1257 date.setTime(date.getTime() + 1000 * (days * 24 * 60 * 60)); 1258 expires = "; expires=" + date.toUTCString(); 1259 1260 /** 1261 * Set secure cookie if https found 1262 */ 1263 if (location.protocol === "https:") { 1264 document.cookie = 1265 name + 1266 "=" + 1267 (value || "") + 1268 expires + 1269 "; path=/; Domain=" + 1270 window.location.hostname + 1271 "; SameSite=Lax; Secure"; 1272 } else { 1273 document.cookie = 1274 name + "=" + (value || "") + expires + "; path=/; SameSite=Lax;"; 1275 } 1276 1277 _log( 1278 "CookieConsent [SET_COOKIE]: cookie " + 1279 name + 1280 "='" + 1281 value + 1282 "' was set!" 1283 ); 1284 }; 1285 1286 /** 1287 * Get cookie value by name, 1288 * returns cookie value if found, otherwise empty string: "" 1289 * @param {String} name 1290 * @returns {String} 1291 */ 1292 var _getCookie = function (name) { 1293 return (name = document.cookie.match( 1294 "(^|;)\\s*" + name + "\\s*=\\s*([^;]+)" 1295 )) 1296 ? name.pop() 1297 : ""; 1298 }; 1299 1300 /** 1301 * Delete cookie by name 1302 * @param {String} name 1303 */ 1304 var _eraseCookie = function (name) { 1305 document.cookie = 1306 name + 1307 "=; Path=/; Domain=" + 1308 window.location.hostname + 1309 "; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; 1310 document.cookie = 1311 name + 1312 "=; Path=/; Domain=." + 1313 window.location.hostname + 1314 "; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; 1315 }; 1316 1317 /** 1318 * Return true if cookie was found and has valid value (not empty string) 1319 * @param {String} cookie_name 1320 * @returns {Boolean} 1321 */ 1322 _cookieconsent.validCookie = function (cookie_name) { 1323 return _getCookie(cookie_name) != ""; 1324 }; 1325 1326 /** 1327 * Add event listener to dom object (cross browser function) 1328 * @param {Object} elem 1329 * @param {String} event //event type 1330 * @param {Object } fn 1331 * @param {Boolean} passive 1332 */ 1333 var _addEvent = function (elem, event, fn, passive) { 1334 var passive = passive || false; 1335 1336 if (elem.addEventListener) { 1337 passive 1338 ? elem.addEventListener(event, fn, { passive: true }) 1339 : elem.addEventListener(event, fn, false); 1340 } else { 1341 /** 1342 * For old browser, add 'on' before event: 1343 * 'click':=> 'onclick' 1344 */ 1345 elem.attachEvent("on" + event, fn); 1346 } 1347 }; 1348 1349 /** 1350 * Get all prop. keys defined inside object 1351 * @param {Object} obj 1352 */ 1353 var _getKeys = function (obj) { 1354 if (typeof obj === "object") { 1355 var keys = [], 1356 i = 0; 1357 for (keys[i++] in obj) { 1358 } 1359 return keys; 1360 } 1361 }; 1362 1363 /** 1364 * Append class to specified dom element 1365 * @param {Object} elem 1366 * @param {String} classname 1367 */ 1368 var _addClass = function (elem, classname) { 1369 if (elem.classList) elem.classList.add(classname); 1370 else { 1371 if (!_hasClass(elem, classname)) elem.className += " " + classname; 1372 } 1373 }; 1374 1375 /** 1376 * Remove specified class from dom element 1377 * @param {HTMLElement} elem 1378 * @param {String} classname 1379 */ 1380 var _removeClass = function (el, className) { 1381 el.classList 1382 ? el.classList.remove(className) 1383 : (el.className = el.className.replace( 1384 new RegExp("(\\s|^)" + className + "(\\s|$)"), 1385 " " 1386 )); 1387 }; 1388 1389 /** 1390 * Check if html element has classname 1391 * @param {HTMLElement} el 1392 * @param {String} className 1393 */ 1394 var _hasClass = function (el, className) { 1395 if (el.classList) { 1396 return el.classList.contains(className); 1397 } 1398 return !!el.className.match( 1399 new RegExp("(\\s|^)" + className + "(\\s|$)") 1400 ); 1401 }; 1402 1403 return _cookieconsent; 1404 }; 1405 1406 /** 1407 * Make CookieConsent object accessible globally 1408 */ 1409 if (typeof window["initCookieConsent"] !== "function") { 1410 window["initCookieConsent"] = CookieConsent; 1411 } 1412})();
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.