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