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