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