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