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