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