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