1/*! 2 * CookieConsent v2.8.6 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 558 var _createSettingsModal = function(lang){ 559 560 /** 561 * Create all consent_modal elements 562 */ 563 if(!settings_container){ 564 settings_container = _createNode('div'); 565 var settings_container_valign = _createNode('div'); 566 var settings = _createNode('div'); 567 var settings_container_inner = _createNode('div'); 568 settings_inner = _createNode('div'); 569 settings_title = _createNode('div'); 570 var settings_header = _createNode('div'); 571 settings_close_btn = _createNode('button'); 572 var settings_close_btn_container = _createNode('div'); 573 settings_blocks = _createNode('div');
574 var overlay = _createNode('div'); 575 576 /** 577 * Set ids 578 */ 579 settings_container.id = 's-cnt'; 580 settings_container_valign.id = "c-vln"; 581 settings_container_inner.id = "c-s-in"; 582 settings.id = "cs"; 583 settings_title.id = 's-ttl'; 584 settings_inner.id = 's-inr'; 585 settings_header.id = "s-hdr"; 586 settings_blocks.id = 's-bl'; 587 settings_close_btn.id = 's-c-bn'; 588 overlay.id = 'cs-ov'; 589 settings_close_btn_container.id = 's-c-bnc'; 590 settings_close_btn.className = 'c-bn'; 591 592 settings_container.setAttribute('role', 'dialog'); 593 settings_container.setAttribute('aria-modal', 'true'); 594 settings_container.setAttribute('aria-hidden', 'true'); 595 settings_container.setAttribute('aria-labelledby', 's-ttl'); 596 settings_title.setAttribute('role', 'heading'); 597 settings_container.style.visibility = overlay.style.visibility = "hidden"; 598 overlay.style.opacity = 0; 599 600 settings_close_btn_container.appendChild(settings_close_btn); 601 602 // If 'esc' key is pressed inside settings_container div => hide settings 603 _addEvent(settings_container_valign, 'keydown', function(evt){ 604 evt = evt || window.event; 605 if (evt.keyCode === 27) { 606 _cookieconsent.hideSettings(0); 607 } 608 }, true); 609 610 _addEvent(settings_close_btn, 'click', function(){ 611 _cookieconsent.hideSettings(0); 612 }); 613 }else{ 614 new_settings_blocks = _createNode('div'); 615 new_settings_blocks.id = 's-bl'; 616 } 617 618 // Add label to close button 619 settings_close_btn.setAttribute('aria-label', user_config.languages[lang]['settings_modal']['close_btn_label'] || 'Close'); 620 621 all_blocks = user_config.languages[lang]['settings_modal']['blocks']; 622 all_table_headers = user_config.languages[lang]['settings_modal']['cookie_table_headers']; 623 624 var n_blocks = all_blocks.length; 625 626 // Set settings modal title 627 settings_title.innerHTML = user_config.languages[lang]['settings_modal']['title']; 628 629 // Create settings modal content (blocks) 630 for(var i=0; i<n_blocks; ++i){ 631 632 var title_data = all_blocks[i]['title'], 633 description_data = all_blocks[i]['description'], 634 toggle_data = all_blocks[i]['toggle'], 635 cookie_table_data = all_blocks[i]['cookie_table'], 636 remove_cookie_tables = user_config['remove_cookie_tables'] === true, 637 isExpandable = (description_data && 'truthy') || (!remove_cookie_tables && (cookie_table_data && 'truthy')); 638 639 // Create title 640 var block_section = _createNode('div'); 641 var block_table_container = _createNode('div'); 642 643 // Create description 644 if(description_data){ 645 var block_desc = _createNode('div'); 646 block_desc.className = 'p'; 647 block_desc.insertAdjacentHTML('beforeend', description_data); 648 } 649 650 var block_title_container = _createNode('div'); 651 block_title_container.className = 'title'; 652 653 block_section.className = 'c-bl'; 654 block_table_container.className = 'desc'; 655 656 // Create toggle if specified (opt in/out) 657 if(typeof toggle_data !== 'undefined'){ 658 659 var accordion_id = "c-ac-"+i; 660 661 // Create button (to collapse/expand block description) 662 var block_title_btn = isExpandable ? _createNode('button') : _createNode('div'); 663 var block_switch_label = _createNode('label'); 664 var block_switch = _createNode('input'); 665 var block_switch_span = _createNode('span'); 666 var label_text_span = _createNode('span'); 667 668 // These 2 spans will contain each 2 pseudo-elements to generate 'tick' and 'x' icons 669 var block_switch_span_on_icon = _createNode('span'); 670 var block_switch_span_off_icon = _createNode('span'); 671 672 block_title_btn.className = isExpandable ? 'b-tl exp' : 'b-tl';
673 block_switch_label.className = 'b-tg'; 674 block_switch.className = 'c-tgl'; 675 block_switch_span_on_icon.className = 'on-i'; 676 block_switch_span_off_icon.className = 'off-i'; 677 block_switch_span.className = 'c-tg'; 678 label_text_span.className = "t-lb"; 679 680 if(isExpandable){ 681 block_title_btn.setAttribute('aria-expanded', 'false'); 682 block_title_btn.setAttribute('aria-controls', accordion_id); 683 } 684 685 block_switch.type = 'checkbox'; 686 block_switch_span.setAttribute('aria-hidden', 'true'); 687 688 var cookie_category = toggle_data.value; 689 block_switch.value = cookie_category; 690 691 label_text_span.textContent = title_data; 692 block_title_btn.insertAdjacentHTML('beforeend', title_data); 693 694 block_title_container.appendChild(block_title_btn); 695 block_switch_span.appendChild(block_switch_span_on_icon); 696 block_switch_span.appendChild(block_switch_span_off_icon); 697 698 /** 699 * If consent is valid => retrieve category states from cookie 700 * Otherwise use states defined in the user_config. object 701 */ 702 if(!invalid_consent){ 703 if(_inArray(saved_cookie_content['categories'], cookie_category) > -1){ 704 block_switch.checked = true; 705 !new_settings_blocks && toggle_states.push(true); 706 }else{ 707 !new_settings_blocks && toggle_states.push(false); 708 } 709 }else if(toggle_data['enabled']){ 710 block_switch.checked = true; 711 !new_settings_blocks && toggle_states.push(true); 712 713 /** 714 * Keep track of categories enabled by default (useful when mode=='opt-out') 715 */ 716 if(toggle_data['enabled']) 717 !new_settings_blocks && default_enabled_categories.push(cookie_category); 718 719 }else{ 720 !new_settings_blocks && toggle_states.push(false); 721 } 722 723 !new_settings_blocks && all_categories.push(cookie_category); 724 725 /** 726 * Set toggle as readonly if true (disable checkbox) 727 */ 728 if(toggle_data['readonly']){ 729 block_switch.disabled = true; 730 _addClass(block_switch_span, 'c-ro'); 731 !new_settings_blocks && readonly_categories.push(true); 732 }else{ 733 !new_settings_blocks && readonly_categories.push(false); 734 } 735 736 _addClass(block_table_container, 'b-acc'); 737 _addClass(block_title_container, 'b-bn'); 738 _addClass(block_section, 'b-ex'); 739 740 block_table_container.id = accordion_id; 741 block_table_container.setAttribute('aria-hidden', 'true'); 742 743 block_switch_label.appendChild(block_switch); 744 block_switch_label.appendChild(block_switch_span); 745 block_switch_label.appendChild(label_text_span); 746 block_title_container.appendChild(block_switch_label); 747 748 /** 749 * On button click handle the following :=> aria-expanded, aria-hidden and act class for current block 750 */ 751 isExpandable && (function(accordion, block_section, btn){ 752 _addEvent(block_title_btn, 'click', function(){ 753 if(!_hasClass(block_section, 'act')){ 754 _addClass(block_section, 'act'); 755 btn.setAttribute('aria-expanded', 'true'); 756 accordion.setAttribute('aria-hidden', 'false'); 757 }else{ 758 _removeClass(block_section, 'act'); 759 btn.setAttribute('aria-expanded', 'false'); 760 accordion.setAttribute('aria-hidden', 'true'); 761 } 762 }, false); 763 })(block_table_container, block_section, block_title_btn); 764 765 }else{ 766 /** 767 * If block is not a button (no toggle defined), 768 * create a simple div instead 769 */ 770 if(title_data){ 771 var block_title = _createNode('div'); 772 block_title.className = 'b-tl';
773 block_title.setAttribute('role', 'heading'); 774 block_title.setAttribute('aria-level', '3'); 775 block_title.insertAdjacentHTML('beforeend', title_data); 776 block_title_container.appendChild(block_title); 777 } 778 } 779 780 title_data && block_section.appendChild(block_title_container); 781 description_data && block_table_container.appendChild(block_desc); 782 783 // if cookie table found, generate table for this block 784 if(!remove_cookie_tables && typeof cookie_table_data !== 'undefined'){ 785 var tr_tmp_fragment = document.createDocumentFragment(); 786 787 /** 788 * Use custom table headers 789 */ 790 for(var p=0; p<all_table_headers.length; ++p){ 791 // create new header 792 var th1 = _createNode('th'); 793 var obj = all_table_headers[p]; 794 th1.setAttribute('scope', 'col'); 795 796 // get custom header content 797 if(obj){ 798 var new_column_key = obj && _getKeys(obj)[0]; 799 th1.textContent = all_table_headers[p][new_column_key]; 800 tr_tmp_fragment.appendChild(th1); 801 } 802 } 803 804 var tr_tmp = _createNode('tr'); 805 tr_tmp.appendChild(tr_tmp_fragment); 806 807 // create table header & append fragment 808 var thead = _createNode('thead'); 809 thead.appendChild(tr_tmp); 810 811 // append header to table 812 var block_table = _createNode('table'); 813 block_table.appendChild(thead); 814 815 var tbody_fragment = document.createDocumentFragment(); 816 817 // create table content 818 for(var n=0; n<cookie_table_data.length; n++){ 819 var tr = _createNode('tr'); 820 821 for(var g=0; g<all_table_headers.length; ++g){ 822 // get custom header content 823 obj = all_table_headers[g]; 824 if(obj){ 825 new_column_key = _getKeys(obj)[0]; 826 827 var td_tmp = _createNode('td'); 828 829 // Allow html inside table cells 830 td_tmp.insertAdjacentHTML('beforeend', cookie_table_data[n][new_column_key]); 831 td_tmp.setAttribute('data-column', obj[new_column_key]); 832 833 tr.appendChild(td_tmp); 834 } 835 } 836 837 tbody_fragment.appendChild(tr); 838 } 839 840 // append tbody_fragment to tbody & append the latter into the table 841 var tbody = _createNode('tbody'); 842 tbody.appendChild(tbody_fragment); 843 block_table.appendChild(tbody); 844 845 block_table_container.appendChild(block_table); 846 } 847 848 /** 849 * Append only if is either: 850 * - togglable div with title 851 * - a simple div with at least a title or description 852 */ 853 if(toggle_data && title_data || (!toggle_data && (title_data || description_data))){ 854 block_section.appendChild(block_table_container); 855 856 if(new_settings_blocks) 857 new_settings_blocks.appendChild(block_section); 858 else 859 settings_blocks.appendChild(block_section); 860 } 861 } 862 863 // Create settings buttons 864 if(!settings_buttons){ 865 settings_buttons = _createNode('div'); 866 settings_buttons.id = 's-bns'; 867 } 868 869 if(!settings_accept_all_btn){ 870 settings_accept_all_btn = _createNode('button'); 871 settings_accept_all_btn.id = 's-all-bn'; 872 settings_accept_all_btn.className ='c-bn'; 873 settings_buttons.appendChild(settings_accept_all_btn); 874 875 _addEvent(settings_accept_all_btn, 'click', function(){ 876 _cookieconsent.hideSettings(); 877 _cookieconsent.hide(); 878 _cookieconsent.accept('all'); 879 }); 880 } 881 882 settings_accept_all_btn.innerHTML = user_config.languages[lang]['settings_modal']['accept_all_btn']; 883 884 var reject_all_btn_text = user_config.languages[lang]['settings_modal']['reject_all_btn']; 885 886 // Add third [optional] reject all button if provided 887 if(reject_all_btn_text){ 888 889 if(!settings_reject_all_btn){ 890 settings_reject_all_btn = _createNode('button'); 891 settings_reject_all_btn.id = 's-rall-bn'; 892 settings_reject_all_btn.className = 'c-bn'; 893 894 _addEvent(settings_reject_all_btn, 'click', function(){ 895 _cookieconsent.hideSettings(); 896 _cookieconsent.hide(); 897 _cookieconsent.accept([]); 898 }); 899 900 settings_inner.className = "bns-t"; 901 settings_buttons.appendChild(settings_reject_all_btn); 902 } 903 904 settings_reject_all_btn.innerHTML = reject_all_btn_text; 905 } 906 907 908 if(!settings_save_btn){ 909 settings_save_btn = _createNode('button'); 910 settings_save_btn.id = 's-sv-bn'; 911 settings_save_btn.className ='c-bn'; 912 settings_buttons.appendChild(settings_save_btn); 913 914 // Add save preferences button onClick event 915 // Hide both settings modal and consent modal 916 _addEvent(settings_save_btn, 'click', function(){ 917 _cookieconsent.hideSettings(); 918 _cookieconsent.hide(); 919 _cookieconsent.accept(); 920 }); 921 } 922 923 settings_save_btn.innerHTML = user_config.languages[lang]['settings_modal']['s
923ave_settings_btn']; 924 925 926 if(new_settings_blocks) { 927 // replace entire existing cookie category blocks with the new cookie categories new blocks (in a different language) 928 settings_inner.replaceChild(new_settings_blocks, settings_blocks); 929 settings_blocks = new_settings_blocks; 930 return; 931 }; 932 933 settings_header.appendChild(settings_title); 934 settings_header.appendChild(settings_close_btn_container); 935 settings_inner.appendChild(settings_header); 936 settings_inner.appendChild(settings_blocks); 937 settings_inner.appendChild(settings_buttons); 938 settings_container_inner.appendChild(settings_inner); 939 940 settings.appendChild(settings_container_inner); 941 settings_container_valign.appendChild(settings); 942 settings_container.appendChild(settings_container_valign); 943 944 all_modals_container.appendChild(settings_container); 945 all_modals_container.appendChild(overlay); 946 } 947 948 /** 949 * Generate cookie consent html markup 950 */ 951 var _createCookieConsentHTML = function(){ 952 953 // Create main container which holds both consent modal & settings modal 954 main_container = _createNode('div'); 955 main_container.id = 'cc--main'; 956 957 // Fix layout flash 958 main_container.style.position = "fixed"; 959 main_container.style.zIndex = "1000000"; 960 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]-->' 961 all_modals_container = main_container.children[0]; 962 963 // Get current language 964 var lang = _config.current_lang; 965 966 // Create consent modal 967 if(consent_modal_exists) 968 _createConsentModal(lang); 969 970 // Always create settings modal 971 _createSettingsModal(lang); 972 973 // Finally append everything (main_container holds both modals) 974 (root || document.body).appendChild(main_container); 975 } 976 977 /** 978 * Update/change modals language 979 * @param {String} lang new language 980 * @param {Boolean} [force] update language fields forcefully 981 * @returns {Boolean} 982 */ 983 _cookieconsent.updateLanguage = function(lang, force){ 984 985 if(typeof lang !== 'string') return; 986 987 /** 988 * Validate language to avoid errors 989 */ 990 var new_validated_lang = _getValidatedLanguage(lang, user_config.languages); 991 992 /** 993 * Set language only if it differs from current 994 */ 995 if(new_validated_lang !== _config.current_lang || force === true){ 996 _config.current_lang = new_validated_lang; 997 998 if(consent_modal_exists){ 999 _createConsentModal(new_validated_lang); 1000 _addDataButtonListeners(consent_modal_inner); 1001 } 1002 1003 _createSettingsModal(new_validated_lang); 1004 1005 _log("CookieConsent [LANGUAGE]: curr_lang: '" + new_validated_lang + "'"); 1006 1007 return true; 1008 } 1009 1010 return false; 1011 } 1012 1013 /** 1014 * Delete all cookies which are unused (based on selected preferences) 1015 * 1016 * @param {boolean} [clearOnFirstAction] 1017 */ 1018 var _autoclearCookies = function(clearOnFirstAction){ 1019 1020 // Get number of blocks 1021 var len = all_blocks.length; 1022 var count = -1; 1023 1024 // reset reload state 1025 reload_page = false; 1026 1027 // Retrieve all cookies 1028 var all_cookies_array = _getCookie('', 'all'); 1029 1030 // delete cookies on 'www.domain.com' and '.www.domain.com' (can also be without www) 1031 var domains = [_config.cookie_domain, '.'+_config.cookie_domain]; 1032 1033 // if domain has www, delete cookies also for 'domain.com' and '.domain.com' 1034 if(_config.cookie_domain.slice(0, 4) === 'www.'){ 1035 var non_www_domain = _config.cookie_domain.substr(4); // remove first 4 chars (www.) 1036 domains.push(non_www_domain); 1037 domains.push('.' + non_www_domain); 1038 } 1039 1040 // For each block 1041 for(var i=0; i<len; i++){ 1042 1043 // Save current block (local scope & less accesses -> ~faster value retrieval) 1044 var curr_block = all_blocks[i]; 1045 1046 // If current block has a toggle for opt in/out 1047 if(Object.prototype.hasOwnProperty.call(curr_block, "toggle")){ 1048 1049 // if current block has a cookie table, an off toggle, 1050 // and its preferences were just changed => delete cookies 1051 var category_just_disabled = _inArray(changed_settings, curr_block['toggle']['value']) > -1; 1052 if( 1053 !toggle_states[++count] && 1054 Object.prototype.hasOwnProperty.call(curr_block, "cookie_table") && 1055 (clearOnFirstAction || category_just_disabled) 1056 ){ 1057 var curr_cookie_table = curr_block['cookie_table']; 1058 1059 // Get first property name 1060 var ckey = _getKeys(all_table_headers[0])[0]; 1061 1062 // Get number of cookies defined in cookie_table 1063 var clen = curr_cookie_table.length; 1064 1065 // set "reload_page" to true if reload=on_disable 1066 if(curr_block['toggle']['reload'] === 'on_disable') 1067 category_just_disabled && (reload_page = true); 1068 1069 // for each row defined in the cookie table 1070 for(var j=0; j<clen; j++){ 1071 1072 // Get current row of table (corresponds to all cookie params) 1073 var curr_row = curr_cookie_table[j], found_cookies = []; 1074 var curr_cookie_name = curr_row[ckey]; 1075 var is_regex = curr_row['is_regex'] || false; 1076 var curr_cookie_domain = curr_row['domain'] || null; 1077 var curr_cookie_path = curr_row['path'] || false; 1078 1079 // set domain to the specified domain 1080 curr_cookie_domain && ( domains = [curr_cookie_domain, '.'+curr_cookie_domain]); 1081 1082 // If regex provided => filter cookie array 1083 if(is_regex){ 1084 for(var n=0; n<all_cookies_array.length; n++){ 1085 if(all_cookies_array[n].match(curr_cookie_name)){ 1086 found_cookies.push(all_cookies_array[n]); 1087 } 1088 } 1089 }else{ 1090 var found_index = _inArray(all_cookies_array, curr_cookie_name); 1091 if(found_index > -1) found_cookies.push(all_cookies_array[found_index]); 1092 } 1093 1094 _log("CookieConsent [AUTOCLEAR]: search cookie: '" + curr_cookie_name + "', found:", found_cookies); 1095 1096 // If cookie exists -> delete it 1097 if(found_cookies.length > 0){ 1098 _eraseCookies(found_cookies, curr_cookie_path, domains); 1099 curr_block['toggle']['reload'] === 'on_clear' && (reload_page = true); 1100 } 1101 } 1102 } 1103 } 1104 } 1105 } 1106 1107 /** 1108 * Set toggles/checkboxes based on accepted categories and save cookie 1109 * @param {string[]} accepted_categories - Array of categories to accept 1110 */ 1111 var _saveCookiePreferences = function(accepted_categories){ 1112 1113 changed_settings = []; 1114 1115 // Retrieve all toggle/checkbox values 1116 var category_toggles = document.querySelectorAll('.c-tgl') || []; 1117 1118 // If there are opt in/out toggles ... 1119 if(category_toggles.length > 0){ 1120 1121 for(var i=0; i<category_toggles.length; i++){ 1122 if(_inArray(accepted_categories, all_categories[i]) !== -1){ 1123 category_toggles[i].checked = true; 1124 if(!toggle_states[i]){ 1125 changed_settings.push(all_categories[i]); 1126 toggle_states[i] = true; 1127 } 1128 }else{ 1129 category_toggles[i].checked = false;
1130 if(toggle_states[i]){ 1131 changed_settings.push(all_categories[i]); 1132 toggle_states[i] = false; 1133 } 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Clear cookies when settings/preferences change 1140 */ 1141 if(!invalid_consent && _config.autoclear_cookies && changed_settings.length > 0) 1142 _autoclearCookies(); 1143 1144 if(!consent_date) consent_date = new Date(); 1145 if(!consent_uuid) consent_uuid = _uuidv4(); 1146 1147 saved_cookie_content = { 1148 "categories": accepted_categories, 1149 "level": accepted_categories, // Copy of the `categories` property for compatibility purposes with version v2.8.0 and below. 1150 "revision": _config.revision, 1151 "data": cookie_data, 1152 "rfc_cookie": _config.use_rfc_cookie, 1153 "consent_date": consent_date.toISOString(), 1154 "consent_uuid": consent_uuid 1155 } 1156 1157 // save cookie with preferences 'categories' (only if never accepted or settings were updated) 1158 if(invalid_consent || changed_settings.length > 0){ 1159 valid_revision = true; 1160 1161 /** 1162 * Update "last_consent_update" only if it is invalid (after t) 1163 */ 1164 if(!last_consent_update) 1165 last_consent_update = consent_date; 1166 else 1167 last_consent_update = new Date(); 1168 1169 saved_cookie_content['last_consent_update'] = last_consent_update.toISOString(); 1170 1171 /** 1172 * Update accept type 1173 */ 1174 accept_type = _getAcceptType(_getCurrentCategoriesState()); 1175 1176 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1177 _manageExistingScripts(); 1178 } 1179 1180 if(invalid_consent){ 1181 1182 /** 1183 * Delete unused/"zombie" cookies if consent is not valid (not yet expressed or cookie has expired) 1184 */ 1185 if(_config.autoclear_cookies) 1186 _autoclearCookies(true); 1187 1188 if(typeof onFirstAction === 'function') 1189 onFirstAction(_cookieconsent.getUserPreferences(), saved_cookie_content); 1190 1191 if(typeof onAccept === 'function') 1192 onAccept(saved_cookie_content); 1193 1194 /** 1195 * Set consent as valid 1196 */ 1197 invalid_consent = false; 1198 1199 if(_config.mode === 'opt-in') return; 1200 } 1201 1202 // fire onChange only if settings were changed 1203 if(typeof onChange === "function" && changed_settings.length > 0) 1204 onChange(saved_cookie_content, changed_settings); 1205 1206 /** 1207 * reload page if needed 1208 */ 1209 if(reload_page) 1210 window.location.reload(); 1211 } 1212 1213 /** 1214 * Returns index of found element inside array, otherwise -1 1215 * @param {Array} arr 1216 * @param {Object} value 1217 * @returns {number} 1218 */ 1219 var _inArray = function(arr, value){ 1220 return arr.indexOf(value); 1221 } 1222 1223 /** 1224 * Helper function which prints info (console.log()) 1225 * @param {Object} print_msg 1226 * @param {Object} [optional_param] 1227 */ 1228 var _log = function(print_msg, optional_param, error){ 1229 ENABLE_LOGS && (!error ? console.log(print_msg, optional_param !== undefined ? optional_param : ' ') : console.error(print_msg, optional_param || "")); 1230 } 1231 1232 /** 1233 * Helper function which creates an HTMLElement object based on 'type' and returns it. 1234 * @param {string} type 1235 * @returns {HTMLElement} 1236 */ 1237 var _createNode = function(type){ 1238 var el = document.createElement(type); 1239 if(type === 'button'){ 1240 el.setAttribute('type', type); 1241 } 1242 return el; 1243 } 1244 1245 /** 1246 * Generate RFC4122-compliant UUIDs. 1247 * https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid?page=1&tab=votes#tab-top 1248 * @returns {string} 1249 */ 1250 var _uuidv4 = function(){ 1251 return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, function(c){ 1252 try{ 1253 return (c ^ (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) 1254 }catch(e){ 1255 return ''; 1256 } 1257 }); 1258 } 1259 1260 /** 1261 * Resolve which language should be used. 1262 * 1263 * @param {Object} languages Object with language translations 1264 * @param {string} [requested_language] Language specified by given configuration parameters 1265 * @returns {string} 1266 */ 1267 var _resolveCurrentLang = function (languages, requested_language) { 1268 1269 if (_config.auto_language === 'browser') { 1270 return _getValidatedLanguage(_getBrowserLang(), languages); 1271 } else if (_config.auto_language === 'document') { 1272 return _getValidatedLanguage(document.documentElement.lang, languages); 1273 } else { 1274 if (typeof requested_language === 'string') { 1275 return _config.current_lang = _getValidatedLanguage(requested_language, languages); 1276 } 1277 } 1278 1279 _log("CookieConsent [LANG]: setting current_lang = '" + _config.current_lang + "'"); 1280 return _config.current_lang; // otherwise return default 1281 } 1282 1283 /** 1284 * Get current client's browser language 1285 * @returns {string} 1286 */ 1287 var _getBrowserLang = function(){ 1288 var browser_lang = navigator.language || navigator.browserLanguage; 1289 browser_lang.length > 2 && (browser_lang = browser_lang[0]+browser_lang[1]); 1290 _log("CookieConsent [LANG]: detected_browser_lang = '"+ browser_lang + "'"); 1291 return browser_lang.toLowerCase() 1292 } 1293 1294 /** 1295 * Trap focus inside modal and focus the first 1296 * focusable element of current active modal 1297 */ 1298 var _handleFocusTrap = function(){ 1299 var tabbedOutsideDiv = false;
1300 var tabbedInsideModal = false; 1301 1302 _addEvent(document, 'keydown', function(e){ 1303 e = e || window.event; 1304 1305 // If is tab key => ok 1306 if(e.key !== 'Tab') return; 1307 1308 // If there is any modal to focus 1309 if(current_modal_focusable){ 1310 // If reached natural end of the tab sequence => restart 1311 if(e.shiftKey){ 1312 if (document.activeElement === current_modal_focusable[0]) { 1313 current_modal_focusable[1].focus(); 1314 e.preventDefault(); 1315 } 1316 }else{ 1317 if (document.activeElement === current_modal_focusable[1]) { 1318 current_modal_focusable[0].focus(); 1319 e.preventDefault(); 1320 } 1321 } 1322 1323 // If have not yet used tab (or shift+tab) and modal is open ... 1324 // Focus the first focusable element 1325 if(!tabbedInsideModal && !clicked_inside_modal){ 1326 tabbedInsideModal = true; 1327 !tabbedOutsideDiv && e.preventDefault(); 1328 1329 if(e.shiftKey){ 1330 if(current_modal_focusable[3]){ 1331 if(!current_modal_focusable[2]){ 1332 current_modal_focusable[0].focus(); 1333 }else{ 1334 current_modal_focusable[2].focus(); 1335 } 1336 }else{ 1337 current_modal_focusable[1].focus(); 1338 } 1339 }else{ 1340 if(current_modal_focusable[3]){ 1341 current_modal_focusable[3].focus(); 1342 }else{ 1343 current_modal_focusable[0].focus(); 1344 } 1345 } 1346 } 1347 } 1348 1349 !tabbedInsideModal && (tabbedOutsideDiv = true); 1350 }); 1351 1352 if(document.contains){ 1353 _addEvent(main_container, 'click', function(e){ 1354 e = e || window.event; 1355 /** 1356 * If click is on the foreground overlay (and not inside settings_modal), 1357 * hide settings modal 1358 * 1359 * Notice: click on div is not supported in IE 1360 */ 1361 if(settings_modal_visible){ 1362 if(!settings_inner.contains(e.target)){ 1363 _cookieconsent.hideSettings(0); 1364 clicked_inside_modal = false; 1365 }else{ 1366 clicked_inside_modal = true; 1367 } 1368 }else if(consent_modal_visible){ 1369 if(consent_modal.contains(e.target)){ 1370 clicked_inside_modal = true; 1371 } 1372 } 1373 1374 }, true); 1375 } 1376 } 1377 1378 /** 1379 * Manage each modal's layout 1380 * @param {Object} gui_options 1381 */ 1382 var _guiManager = function(gui_options, only_consent_modal){ 1383 1384 // If gui_options is not object => exit 1385 if(typeof gui_options !== 'object') return; 1386 1387 var consent_modal_options = gui_options['consent_modal']; 1388 var settings_modal_options = gui_options['settings_modal']; 1389 1390 /** 1391 * Helper function which adds layout and 1392 * position classes to given modal 1393 * 1394 * @param {HTMLElement} modal 1395 * @param {string[]} allowed_layouts 1396 * @param {string[]} allowed_positions 1397 * @param {string} layout 1398 * @param {string[]} position 1399 */ 1400 function _setLayout(modal, allowed_layouts, allowed_positions, allowed_transitions, layout, position, transition){ 1401 position = (position && position.split(" ")) || []; 1402 1403 // Check if specified layout is valid 1404 if(_inArray(allowed_layouts, layout) > -1){ 1405 1406 // Add layout classes 1407 _addClass(modal, layout); 1408 1409 // Add position class (if specified) 1410 if(!(layout === 'bar' && position[0] === 'middle') && _inArray(allowed_positions, position[0]) > -1){ 1411 for(var i=0; i<position.length; i++){ 1412 _addClass(modal, position[i]); 1413 } 1414 } 1415 } 1416 1417 // Add transition class 1418 (_inArray(allowed_transitions, transition) > -1) && _addClass(modal, transition); 1419 } 1420 1421 if(consent_modal_exists && consent_modal_options){ 1422 _setLayout( 1423 consent_modal, 1424 ['box', 'bar', 'cloud'], 1425 ['top', 'middle', 'bottom'], 1426 ['zoom', 'slide'], 1427 consent_modal_options['layout'], 1428 consent_modal_options['position'], 1429 consent_modal_options['transition'] 1430 ); 1431 } 1432 1433 if(!only_consent_modal && settings_modal_options){ 1434 _setLayout( 1435 settings_container, 1436 ['bar'], 1437 ['left', 'right'], 1438 ['zoom', 'slide'], 1439 settings_modal_options['layout'], 1440 settings_modal_options['position'], 1441 settings_modal_options['transition'] 1442 ); 1443 } 1444 } 1445 1446 /** 1447 * Returns true if cookie category is accepted by the user 1448 * @param {string} cookie_category 1449 * @returns {boolean} 1450 */ 1451 _cookieconsent.allowedCategory = function(cookie_category){ 1452 1453 if(!invalid_consent || _config.mode === 'opt-in') 1454 var allowed_categories = JSON.parse(_getCookie(_config.cookie_name, 'one', true) || '{}')['categories'] || [] 1455 else // mode is 'opt-out' 1456 var allowed_categories = default_enabled_categories; 1457 1458 return _inArray(allowed_categories, cookie_category) > -1; 1459 } 1460 1461 /** 1462 * "Init" method. Will run once and only if modals do not exist 1463 */ 1464 _cookieconsent.run = function(user_config){ 1465 if(!document.getElementById('cc_div')){ 1466 1467 // configure all parameters 1468 _setConfig(user_config); 1469 1470 // if is bot, don't run plugin 1471 if(is_bot) return; 1472 1473 // Retrieve cookie value (if set) 1474 saved_cookie_content = JSON.parse(_getCookie(_config.cookie_name, 'one', true) || "{}"); 1475 1476 // Retrieve "consent_uuid" 1477 consent_uuid = saved_cookie_content['consent_uuid']; 1478 1479 // If "consent_uuid" is present => assume that consent was previously given 1480 var cookie_consent_accepted = consent_uuid !== undefined; 1481 1482 // Retrieve "consent_date" 1483 consent_date = saved_cookie_content['consent_date']; 1484 consent_date && (consent_date = new Date(consent_date)); 1485 1486 // Retrieve "last_consent_update" 1487 last_consent_update = saved_cookie_content['last_consent_update']; 1488 last_consent_update && (last_consent_update = new Date(last_consent_update)); 1489 1490 // Retrieve "data" 1491 cookie_data = saved_cookie_content['data'] !== undefined ? saved_cookie_content['data'] : null; 1492 1493 // If revision is enabled and current value !== saved value inside the cookie =>
1493 revision is not valid 1494 if(revision_enabled && saved_cookie_content['revision'] !== _config.revision){ 1495 valid_revision = false; 1496 } 1497 1498 // If consent is not valid => create consent modal 1499 consent_modal_exists = invalid_consent = (!cookie_consent_accepted || !valid_revision || !consent_date || !last_consent_update || !consent_uuid); 1500 1501 // Generate cookie-settings dom (& consent modal) 1502 _createCookieConsentHTML(); 1503 1504 _getModalFocusableData(); 1505 _guiManager(user_config['gui_options']); 1506 _addDataButtonListeners(); 1507 1508 if(_config.autorun && consent_modal_exists){ 1509 _cookieconsent.show(user_config['delay'] || 0); 1510 } 1511 1512 // Add class to enable animations/transitions 1513 setTimeout(function(){_addClass(main_container, 'c--anim');}, 30); 1514 1515 // Accessibility :=> if tab pressed => trap focus inside modal 1516 setTimeout(function(){_handleFocusTrap();}, 100); 1517 1518 // If consent is valid 1519 if(!invalid_consent){ 1520 var rfc_prop_exists = typeof saved_cookie_content['rfc_cookie'] === "boolean"; 1521 1522 /* 1523 * Convert cookie to rfc format (if `use_rfc_cookie` is enabled) 1524 */ 1525 if(!rfc_prop_exists || (rfc_prop_exists && saved_cookie_content['rfc_cookie'] !== _config.use_rfc_cookie)){ 1526 saved_cookie_content['rfc_cookie'] = _config.use_rfc_cookie; 1527 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1528 } 1529 1530 /** 1531 * Update accept type 1532 */ 1533 accept_type = _getAcceptType(_getCurrentCategoriesState()); 1534 1535 _manageExistingScripts(); 1536 1537 if(typeof onAccept === 'function') 1538 onAccept(saved_cookie_content); 1539 1540 _log("CookieConsent [NOTICE]: consent already given!", saved_cookie_content); 1541 1542 }else{ 1543 if(_config.mode === 'opt-out'){ 1544 _log("CookieConsent [CONFIG] mode='" + _config.mode + "', default enabled categories:", default_enabled_categories); 1545 _manageExistingScripts(default_enabled_categories); 1546 } 1547 _log("CookieConsent [NOTICE]: ask for consent!"); 1548 } 1549 }else{ 1550 _log("CookieConsent [NOTICE]: cookie consent already attached to body!"); 1551 } 1552 } 1553 1554 /** 1555 * Show settings modal (with optional delay) 1556 * @param {number} delay 1557 */ 1558 _cookieconsent.showSettings = function(delay){ 1559 setTimeout(function() { 1560 _addClass(html_dom, "show--settings"); 1561 settings_container.setAttribute('aria-hidden', 'false'); 1562 settings_modal_visible = true; 1563 1564 /** 1565 * Set focus to the first focusable element inside settings modal 1566 */ 1567 setTimeout(function(){ 1568 // If there is no consent-modal, keep track of the last focused elem. 1569 if(!consent_modal_visible){ 1570 last_elem_before_modal = document.activeElement; 1571 }else{ 1572 last_consent_modal_btn_focus = document.activeElement; 1573 } 1574 1575 if (settings_modal_focusable.length === 0) return; 1576 1577 if(settings_modal_focusable[3]){ 1578 settings_modal_focusable[3].focus(); 1579 }else{ 1580 settings_modal_focusable[0].focus(); 1581 } 1582 current_modal_focusable = settings_modal_focusable; 1583 }, 200); 1584 1585 _log("CookieConsent [SETTINGS]: show settings_modal"); 1586 }, delay > 0 ? delay : 0); 1587 } 1588 1589 /** 1590 * This function handles the loading/activation logic of the already 1591 * existing scripts based on the current accepted cookie categories 1592 * 1593 * @param {string[]} [must_enable_categories] 1594 */ 1595 var _manageExistingScripts = function(must_enable_categories){ 1596 1597 if(!_config.page_scripts) return; 1598 1599 // get all the scripts with "cookie-category" attribute 1600 var scripts = document.querySelectorAll('script[' + _config.script_selector + ']'); 1601 var accepted_categories = must_enable_categories || saved_cookie_content['categories'] || []; 1602 1603 /** 1604 * Load scripts (sequentially), using a recursive function 1605 * which loops through the scripts array 1606 * @param {Element[]} scripts scripts to load 1607 * @param {number} index current script to load 1608 */ 1609 var _loadScripts = function(scripts, index){ 1610 if(index < scripts.length){ 1611 1612 var curr_script = scripts[index]; 1613 var curr_script_category = curr_script.getAttribute(_config.script_selector); 1614 1615 /** 1616 * If current script's category is on the array of categories 1617 * accepted by the user => load script 1618 */ 1619 if(_inArray(accepted_categories, curr_script_category) > -1){ 1620 1621 curr_script.type = 'text/javascript'; 1622 curr_script.removeAttribute(_config.script_selector); 1623 1624 // get current script data-src 1625 var src = curr_script.getAttribute('data-src'); 1626 1627 // some scripts (like ga) might throw warning if data-src is present 1628 src && curr_script.removeAttribute('data-src'); 1629 1630 // create fresh script (with the same code) 1631 var fresh_script = _createNode('script'); 1632 fresh_script.textContent = curr_script.innerHTML; 1633 1634 // Copy attributes over to the new "revived" script 1635 (function(destination, source){ 1636 var attributes = source.attributes; 1637 var len = attributes.length; 1638 for(var i=0; i<len; i++){ 1639 var attr_name = attributes[i].nodeName; 1640 destination.setAttribute(attr_name , source[attr_name] || source.getAttribute(attr_name)); 1641 } 1642 })(fresh_script, curr_script); 1643 1644 // set src (if data-src found) 1645 src ? (fresh_script.src = src) : (src = curr_script.src); 1646 1647 // if script has "src" attribute 1648 // try loading it sequentially 1649 if(src){ 1650 // load script sequentially => the next script will not be loaded 1651 // until the current's script onload event triggers 1652 if(fresh_script.readyState) { // only required for IE <9 1653 fresh_script.onreadystatechange = function() { 1654 if (fresh_script.readyState === "loaded" || fresh_script.readyState === "complete" ) { 1655 fresh_script.onreadystatechange = null; 1656 _loadScripts(scripts, ++index); 1657 } 1658 }; 1659 }else{ // others 1660 fresh_script.onload = function(){ 1661 fresh_script.onload = null; 1662 _loadScripts(scripts, ++index); 1663 }; 1664 } 1665 } 1666 1667 // Replace current "sleeping" script with the new "revived" one 1668 curr_script.parentNode.replaceChild(fresh_script, curr_script); 1669 1670 /** 1671 * If we managed to get here and scr is still set, it means that 1672 * the script is loading/loaded sequentially so don't go any further 1673 */ 1674 if(src) return; 1675 } 1676 1677 // Go to next script right away 1678 _loadScripts(scripts, ++index); 1679 } 1680 } 1681 1682 _loadScripts(scripts, 0); 1683 } 1684 1685 /** 1686 * Save custom data inside cookie 1687 * @param {object|string} new_data 1688 * @param {string} [mode] 1689 * @returns {boolean} 1690 */
1691 var _setCookieData = function(new_data, mode){ 1692 1693 var set = false; 1694 /** 1695 * If mode is 'update': 1696 * add/update only the specified props. 1697 */ 1698 if(mode === 'update'){ 1699 cookie_data = _cookieconsent.get('data'); 1700 var same_type = typeof cookie_data === typeof new_data; 1701 1702 if(same_type && typeof cookie_data === "object"){ 1703 !cookie_data && (cookie_data = {}); 1704 1705 for(var prop in new_data){ 1706 if(cookie_data[prop] !== new_data[prop]){ 1707 cookie_data[prop] = new_data[prop] 1708 set = true; 1709 } 1710 } 1711 }else if((same_type || !cookie_data) && cookie_data !== new_data){ 1712 cookie_data = new_data; 1713 set = true; 1714 } 1715 }else{ 1716 cookie_data = new_data; 1717 set = true; 1718 } 1719 1720 if(set){ 1721 saved_cookie_content['data'] = cookie_data; 1722 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1723 } 1724 1725 return set; 1726 } 1727 1728 /** 1729 * Helper method to set a variety of fields 1730 * @param {string} field 1731 * @param {object} data 1732 * @returns {boolean} 1733 */ 1734 _cookieconsent.set = function(field, data){ 1735 switch(field){ 1736 case 'data': return _setCookieData(data['value'], data['mode']); 1737 default: return false; 1738 } 1739 } 1740 1741 /** 1742 * Retrieve data from existing cookie 1743 * @param {string} field 1744 * @param {string} [cookie_name] 1745 * @returns {any} 1746 */ 1747 _cookieconsent.get = function(field, cookie_name){ 1748 var cookie = JSON.parse(_getCookie(cookie_name || _config.cookie_name, 'one', true) || "{}"); 1749 1750 return cookie[field]; 1751 } 1752 1753 /** 1754 * Read current configuration value 1755 * @returns {any} 1756 */ 1757 _cookieconsent.getConfig = function(field){ 1758 return _config[field] || user_config[field]; 1759 } 1760 1761 /** 1762 * Obtain accepted and rejected categories 1763 * @returns {{accepted: string[], rejected: string[]}} 1764 */ 1765 var _getCurrentCategoriesState = function(){ 1766 1767 // get accepted categories 1768 accepted_categories = saved_cookie_content['categories'] || []; 1769 1770 // calculate rejected categories (all_categories - accepted_categories) 1771 rejected_categories = all_categories.filter(function(category){ 1772 return (_inArray(accepted_categories, category) === -1); 1773 }); 1774 1775 return { 1776 accepted: accepted_categories, 1777 rejected: rejected_categories 1778 } 1779 } 1780 1781 /** 1782 * Calculate "accept type" given current categories state 1783 * @param {{accepted: string[], rejected: string[]}} currentCategoriesState 1784 * @returns {string} 1785 */ 1786 var _getAcceptType = function(currentCategoriesState){ 1787 1788 var type = 'custom'; 1789 1790 // number of categories marked as necessary/readonly 1791 var necessary_categories_length = readonly_categories.filter(function(readonly){ 1792 return readonly === true; 1793 }).length; 1794 1795 // calculate accept type based on accepted/rejected categories 1796 if(currentCategoriesState.accepted.length === all_categories.length) 1797 type = 'all'; 1798 else if(currentCategoriesState.accepted.length === necessary_categories_length) 1799 type = 'necessary' 1800 1801 return type; 1802 } 1803 1804 /** 1805 * @typedef {object} userPreferences 1806 * @property {string} accept_type 1807 * @property {string[]} accepted_categories 1808 * @property {string[]} rejected_categories 1809 */ 1810 1811 /** 1812 * Retrieve current user preferences (summary) 1813 * @returns {userPreferences} 1814 */ 1815 _cookieconsent.getUserPreferences = function(){ 1816 var currentCategoriesState = _getCurrentCategoriesState(); 1817 var accept_type = _getAcceptType(currentCategoriesState); 1818 1819 return { 1820 'accept_type': accept_type, 1821 'accepted_categories': currentCategoriesState.accepted, 1822 'rejected_categories': currentCategoriesState.rejected 1823 } 1824 } 1825 1826 /** 1827 * Function which will run after script load 1828 * @callback scriptLoaded 1829 */ 1830 1831 /** 1832 * Dynamically load script (append to head) 1833 * @param {string} src 1834 * @param {scriptLoaded} callback 1835 * @param {object[]} [attrs] Custom attributes 1836 */ 1837 _cookieconsent.loadScript = function(src, callback, attrs){ 1838 1839 var function_defined = typeof callback === 'function'; 1840 1841 // Load script only if not already loaded 1842 if(!document.querySelector('script[src="' + src + '"]')){ 1843 1844 var script = _createNode('script'); 1845 1846 // if an array is provided => add custom attributes 1847 if(attrs && attrs.length > 0){ 1848 for(var i=0; i<attrs.length; ++i){ 1849 attrs[i] && script.setAttribute(attrs[i]['name'], attrs[i]['value']); 1850 } 1851 } 1852 1853 // if callback function defined => run callback onload 1854 if(function_defined){ 1855 script.onload = callback; 1856 } 1857 1858 script.src = src; 1859 1860 /** 1861 * Append script to head 1862 */ 1863 document.head.appendChild(script); 1864 }else{ 1865 function_defined && callback(); 1866 } 1867 } 1868 1869 /** 1870 * Manage dynamically loaded scripts: https://github.com/orestbida/cookieconsent/issues/101 1871 * If plugin has already run, call this method to enable 1872 * the newly added scripts based on currently selected preferences 1873 */ 1874 _cookieconsent.updateScripts = function(){ 1875 _manageExistingScripts(); 1876 } 1877 1878 /** 1879 * Show cookie consent modal (with delay parameter) 1880 * @param {number} [delay] 1881 * @param {boolean} [create_modal] create modal if it doesn't exist 1882 */ 1883 _cookieconsent.show = function(delay, create_modal){ 1884 1885 if(create_modal === true) 1886 _createConsentModal(_config.current_lang); 1887 1888 if(consent_modal_exists){ 1889 setTimeout(function() { 1890 _addClass(html_dom, "show--consent"); 1891 1892 /** 1893 * Update attributes/internal statuses 1894 */ 1895 consent_modal.setAttribute('aria-hidden', 'false'); 1896 consent_modal_visible = true; 1897 1898 setTimeout(function(){ 1899 last_elem_before_modal = document.activeElement; 1900 current_modal_focusable = consent_modal_focusable; 1901 }, 200); 1902 1903 _log("CookieConsent [MODAL]: show consent_modal"); 1904 }, delay > 0 ? delay : (create_modal ? 30 : 0)); 1905 } 1906 } 1907 1908 /** 1909 * Hide consent modal 1910 */ 1911 _cookieconsent.hide = function(){ 1912 if(consent_modal_exists){ 1913 _removeClass(html_dom, "show--consent"); 1914 consent_modal.setAttribute('aria-hidden', 'true'); 1915 consent_modal_visible = false;
1916 1917 setTimeout(function(){ 1918 //restore focus to the last page element which had focus before modal opening 1919 last_elem_before_modal.focus(); 1920 current_modal_focusable = null; 1921 }, 200); 1922 1923 _log("CookieConsent [MODAL]: hide"); 1924 } 1925 } 1926 1927 /** 1928 * Hide settings modal 1929 */ 1930 _cookieconsent.hideSettings = function(){ 1931 _removeClass(html_dom, "show--settings"); 1932 settings_modal_visible = false; 1933 settings_container.setAttribute('aria-hidden', 'true'); 1934 1935 1936 setTimeout(function(){ 1937 /** 1938 * If consent modal is visible, focus him (instead of page document) 1939 */ 1940 if(consent_modal_visible){ 1941 last_consent_modal_btn_focus && last_consent_modal_btn_focus.focus(); 1942 current_modal_focusable = consent_modal_focusable; 1943 }else{ 1944 /** 1945 * Restore focus to last page element which had focus before modal opening 1946 */ 1947 last_elem_before_modal && last_elem_before_modal.focus(); 1948 current_modal_focusable = null; 1949 } 1950 1951 clicked_inside_modal = false; 1952 }, 200); 1953 1954 _log("CookieConsent [SETTINGS]: hide settings_modal"); 1955 } 1956 1957 /** 1958 * Accept cookieconsent function API 1959 * @param {string[]|string} _categories - Categories to accept 1960 * @param {string[]} [_exclusions] - Excluded categories [optional] 1961 */ 1962 _cookieconsent.accept = function(_categories, _exclusions){ 1963 var categories = _categories || undefined; 1964 var exclusions = _exclusions || []; 1965 var to_accept = []; 1966 1967 /** 1968 * Get all accepted categories 1969 * @returns {string[]} 1970 */ 1971 var _getCurrentPreferences = function(){ 1972 var toggles = document.querySelectorAll('.c-tgl') || []; 1973 var states = []; 1974 1975 for(var i=0; i<toggles.length; i++){ 1976 if(toggles[i].checked){ 1977 states.push(toggles[i].value); 1978 } 1979 } 1980 return states; 1981 } 1982 1983 if(!categories){ 1984 to_accept = _getCurrentPreferences(); 1985 }else{ 1986 if( 1987 typeof categories === "object" && 1988 typeof categories.length === "number" 1989 ){ 1990 for(var i=0; i<categories.length; i++){ 1991 if(_inArray(all_categories, categories[i]) !== -1) 1992 to_accept.push(categories[i]); 1993 } 1994 }else if(typeof categories === "string"){ 1995 if(categories === 'all') 1996 to_accept = all_categories.slice(); 1997 else{ 1998 if(_inArray(all_categories, categories) !== -1) 1999 to_accept.push(categories); 2000 } 2001 } 2002 } 2003 2004 // Remove excluded categories 2005 if(exclusions.length >= 1){ 2006 for(i=0; i<exclusions.length; i++){ 2007 to_accept = to_accept.filter(function(item) { 2008 return item !== exclusions[i] 2009 }) 2010 } 2011 } 2012 2013 // Add back all the categories set as "readonly/required" 2014 for(i=0; i<all_categories.length; i++){ 2015 if( 2016 readonly_categories[i] === true && 2017 _inArray(to_accept, all_categories[i]) === -1 2018 ){ 2019 to_accept.push(all_categories[i]); 2020 } 2021 } 2022 2023 _saveCookiePreferences(to_accept); 2024 } 2025 2026 /** 2027 * API function to easily erase cookies 2028 * @param {(string|string[])} _cookies 2029 * @param {string} [_path] - optional 2030 * @param {string} [_domain] - optional 2031 */
2032 _cookieconsent.eraseCookies = function(_cookies, _path, _domain){ 2033 var cookies = []; 2034 var domains = _domain 2035 ? [_domain, "."+_domain] 2036 : [_config.cookie_domain, "."+_config.cookie_domain]; 2037 2038 if(typeof _cookies === "object" && _cookies.length > 0){ 2039 for(var i=0; i<_cookies.length; i++){ 2040 this.validCookie(_cookies[i]) && cookies.push(_cookies[i]); 2041 } 2042 }else{ 2043 this.validCookie(_cookies) && cookies.push(_cookies); 2044 } 2045 2046 _eraseCookies(cookies, _path, domains); 2047 } 2048 2049 /** 2050 * Set cookie, by specifying name and value 2051 * @param {string} name 2052 * @param {string} value 2053 */ 2054 var _setCookie = function(name, value) { 2055 2056 var cookie_expiration = _config.cookie_expiration; 2057 2058 if(typeof _config.cookie_necessary_only_expiration === 'number' && accept_type === 'necessary') 2059 cookie_expiration = _config.cookie_necessary_only_expiration; 2060 2061 value = _config.use_rfc_cookie ? encodeURIComponent(value) : value; 2062 2063 var date = new Date(); 2064 date.setTime(date.getTime() + (1000 * (cookie_expiration * 24 * 60 * 60))); 2065 var expires = "; expires=" + date.toUTCString(); 2066 2067 var cookieStr = name + "=" + (value || "") + expires + "; Path=" + _config.cookie_path + ";"; 2068 cookieStr += " SameSite=" + _config.cookie_same_site + ";"; 2069 2070 // assures cookie works with localhost (=> don't specify domain if on localhost) 2071 if(window.location.hostname.indexOf(".") > -1){ 2072 cookieStr += " Domain=" + _config.cookie_domain + ";"; 2073 } 2074 2075 if(window.location.protocol === "https:") { 2076 cookieStr += " Secure;"; 2077 } 2078 2079 document.cookie = cookieStr; 2080 2081 _log("CookieConsent [SET_COOKIE]: '" + name + "' expires after " + cookie_expiration + " day(s)"); 2082 } 2083 2084 /** 2085 * Get cookie value by name, 2086 * returns the cookie value if found (or an array 2087 * of cookies if filter provided), otherwise empty string: "" 2088 * @param {string} name 2089 * @param {string} filter 'one' or 'all' 2090 * @param {boolean} [get_value] set to true to obtain its value 2091 * @returns {string|string[]} 2092 */ 2093 var _getCookie = function(name, filter, get_value) { 2094 var found; 2095 2096 if(filter === 'one'){ 2097 found = document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)"); 2098 found = found ? (get_value ? found.pop() : name) : ""; 2099 2100 if(found && name === _config.cookie_name){ 2101 try{ 2102 found = JSON.parse(found) 2103 }catch(e){ 2104 try { 2105 found = JSON.parse(decodeURIComponent(found)) 2106 } catch (e) { 2107 // if I got here => cookie value is not a valid json string 2108 found = {}; 2109 } 2110 } 2111 found = JSON.stringify(found); 2112 } 2113 }else if(filter === 'all'){ 2114 // array of names of all existing cookies 2115 var cookies = document.cookie.split(/;\s*/); found = []; 2116 for(var i=0; i<cookies.length; i++){ 2117 found.push(cookies[i].split("=")[0]); 2118 } 2119 } 2120 2121 return found; 2122 } 2123 2124 /** 2125 * Delete cookie by name & path 2126 * @param {string[]} cookies 2127 * @param {string} [custom_path] - optional 2128 * @param {string[]} domains - example: ['domain.com', '.domain.com'] 2129 */ 2130 var _eraseCookies = function(cookies, custom_path, domains) { 2131 var path = custom_path ? custom_path : '/'; 2132 var expires = 'Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 2133 2134 for(var i=0; i<cookies.length; i++){ 2135 for(var j=0; j<domains.length; j++){ 2136 document.cookie = cookies[i] + '=; path=' + path + 2137 (domains[j].indexOf('.') == 0 ? '; domain=' + domains[j] : "") + '; ' + expires; 2138 } 2139 _log("CookieConsent [AUTOCLEAR]: deleting cookie: '" + cookies[i] + "' path: '" + path + "' domain:", domains); 2140 } 2141 } 2142 2143 /** 2144 * Returns true if cookie was found and has valid value (not empty string) 2145 * @param {string} cookie_name 2146 * @returns {boolean} 2147 */ 2148 _cookieconsent.validCookie = function(cookie_name){ 2149 return _getCookie(cookie_name, 'one', true) !== ""; 2150 } 2151 2152 /** 2153 * Function to run when event is fired 2154 * @callback eventFired 2155 */ 2156 2157 /** 2158 * Add event listener to dom object (cross browser function) 2159 * @param {Element} elem 2160 * @param {string} event 2161 * @param {eventFired} fn 2162 * @param {boolean} [isPassive] 2163 */ 2164 var _addEvent = function(elem, event, fn, isPassive) { 2165 elem.addEventListener(event, fn , isPassive === true ? { passive: true } : false); 2166 } 2167 2168 /** 2169 * Get all prop. keys defined inside object 2170 * @param {Object} obj 2171 */ 2172 var _getKeys = function(obj){ 2173 if(typeof obj === "object"){ 2174 return Object.keys(obj); 2175 } 2176 } 2177 2178 /** 2179 * Append class to the specified dom element 2180 * @param {HTMLElement} elem 2181 * @param {string} classname 2182 */ 2183 var _addClass = function (elem, classname){ 2184 elem.classList.add(classname); 2185 } 2186 2187 /** 2188 * Remove specified class from dom element 2189 * @param {HTMLElement} elem 2190 * @param {string} classname 2191 */ 2192 var _removeClass = function (el, className) { 2193 el.classList.remove(className); 2194 } 2195 2196 /** 2197 * Check if html element has class 2198 * @param {HTMLElement} el 2199 * @param {string} className 2200 */ 2201 var _hasClass = function(el, className) { 2202 return el.classList.contains(className); 2203 } 2204 2205 return _cookieconsent; 2206 }; 2207 2208 var init = 'initCookieConsent'; 2209 /** 2210 * Make CookieConsent object accessible globally 2211 */ 2212 if(typeof window !== 'undefined' && typeof window[init] !== 'function'){ 2213 window[init] = CookieConsent 2214 } 2215})();
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.