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 1181 var _loadCSS = function(css_path, callback){ 1182 1183 // Enable if given path is string and non empty 1184 var enable = typeof css_path === 'string' && css_path !== ""; 1185 1186 if(enable && !document.getElementById('cc--style')){ 1187 1188 // Create style tag 1189 var style = _createNode('style'); 1190 1191 // ad an id so that in SPA apps (react-like) the style doesn't get loaded multiple times when plugin is called 1192 style.id = 'cc--style'; 1193 1194 var xhr = new XMLHttpRequest(); 1195 1196 xhr.onreadystatechange = function() { 1197 if(this.readyState === 4 && this.status === 200){ 1198 1199 // Necessary for <IE9 1200 style.setAttribute('type', 'text/css'); 1201 1202 if(style.styleSheet){ // if <IE9 1203 style.styleSheet.cssText = this.responseText; 1204 }else{ // all other browsers 1205 style.appendChild(document.createTextNode(this.responseText)); 1206 } 1207 1208 // Append css text content 1209 document.getElementsByTagName('head')[0].appendChild(style); 1210 _log("CookieConsent [AUTOLOAD_CSS]: loaded style = '"+ css_path + "'"); 1211 1212 callback(); 1213 } 1214 }; 1215 1216 xhr.open("GET", css_path); 1217 xhr.send(); 1218 }else{ 1219 callback(); 1220 } 1221 } 1222 1223 /** 1224 * Returns index of found element inside array, otherwise -1 1225 * @param {Array} arr 1226 * @param {Object} value 1227 * @returns {number} 1228 */ 1229 var _inArray = function(arr, value){ 1230 var len = arr.length; 1231 for(var i=0; i<len; i++){ 1232 if(arr[i] === value) 1233 return i; 1234 } 1235 return -1; 1236 } 1237 1238 /** 1239 * Helper function which prints info (console.log()) 1240 * @param {Object} print_msg 1241 * @param {Object} [optional_param] 1242 */ 1243 var _log = function(print_msg, optional_param, error){ 1244 ENABLE_LOGS && (!error ? console.log(print_msg, optional_param !== undefined ? optional_param : ' ') : console.error(print_msg, optional_param || "")); 1245 } 1246 1247 /** 1248 * Helper function which creates an HTMLElement object based on 'type' and returns it. 1249 * @param {string} type 1250 * @returns {HTMLElement} 1251 */ 1252 var _createNode = function(type){ 1253 var el = document.createElement(type); 1254 if(type === 'button'){ 1255 el.setAttribute('type', type); 1256 } 1257 return el; 1258 } 1259 1260 /** 1261 * Resolve which language should be used. 1262 * 1263 * @param {Object} languages Object with language translations 1264 * @param {string} [requested_language] Language specified by given configuration parameters 1265 * @returns {string} 1266 */ 1267 var _resolveCurrentLang = function (languages, requested_language) { 1268 1269 if (_config.auto_language === 'browser') { 1270 return _getValidatedLanguage(_getBrowserLang(), languages); 1271 } else if (_config.auto_language === 'document') { 1272 return _getValidatedLanguage(document.documentElement.lang, languages); 1273 } else { 1274 if (typeof requested_language === 'string') { 1275 return _config.current_lang = _getValidatedLanguage(requested_language, languages); 1276 } 1277 } 1278 1279 _log("CookieConsent [LANG]: setting current_lang = '" + _config.current_lang + "'"); 1280 return _config.current_lang; // otherwise return default 1281 } 1282 1283 /** 1284 * Get current client's browser language 1285 * @returns {string} 1286 */ 1287 var _getBrowserLang = function(){ 1288 var browser_lang = navigator.language || navigator.browserLanguage; 1289 browser_lang.length > 2 && (browser_lang = browser_lang[0]+browser_lang[1]); 1290 _log("CookieConsent [LANG]: detected_browser_lang = '"+ browser_lang + "'"); 1291 return browser_lang.toLowerCase() 1292 } 1293 1294 /** 1295 * Trap focus inside modal and focus the first 1296 * focusable element of current active modal 1297 */ 1298 var _handleFocusTrap = function(){ 1299 var tabbedOutsideDiv = false;
1300 var tabbedInsideModal = false; 1301 1302 _addEvent(document, 'keydown', function(e){ 1303 e = e || window.event; 1304 1305 // If is tab key => ok 1306 if(e.key !== 'Tab') return; 1307 1308 // If there is any modal to focus 1309 if(current_modal_focusable){ 1310 // If reached natural end of the tab sequence => restart 1311 if(e.shiftKey){ 1312 if (document.activeElement === current_modal_focusable[0]) { 1313 current_modal_focusable[1].focus(); 1314 e.preventDefault(); 1315 } 1316 }else{ 1317 if (document.activeElement === current_modal_focusable[1]) { 1318 current_modal_focusable[0].focus(); 1319 e.preventDefault(); 1320 } 1321 } 1322 1323 // If have not yet used tab (or shift+tab) and modal is open ... 1324 // Focus the first focusable element 1325 if(!tabbedInsideModal && !clicked_inside_modal){ 1326 tabbedInsideModal = true; 1327 !tabbedOutsideDiv && e.preventDefault(); 1328 1329 if(e.shiftKey){ 1330 if(current_modal_focusable[3]){ 1331 if(!current_modal_focusable[2]){ 1332 current_modal_focusable[0].focus(); 1333 }else{ 1334 current_modal_focusable[2].focus(); 1335 } 1336 }else{ 1337 current_modal_focusable[1].focus(); 1338 } 1339 }else{ 1340 if(current_modal_focusable[3]){ 1341 current_modal_focusable[3].focus(); 1342 }else{ 1343 current_modal_focusable[0].focus(); 1344 } 1345 } 1346 } 1347 } 1348 1349 !tabbedInsideModal && (tabbedOutsideDiv = true); 1350 }); 1351 1352 if(document.contains){ 1353 _addEvent(main_container, 'click', function(e){ 1354 e = e || window.event; 1355 /** 1356 * If click is on the foreground overlay (and not inside settings_modal), 1357 * hide settings modal 1358 * 1359 * Notice: click on div is not supported in IE 1360 */ 1361 if(settings_modal_visible){ 1362 if(!settings_inner.contains(e.target)){ 1363 _cookieconsent.hideSettings(0); 1364 clicked_inside_modal = false; 1365 }else{ 1366 clicked_inside_modal = true; 1367 } 1368 }else if(consent_modal_visible){ 1369 if(consent_modal.contains(e.target)){ 1370 clicked_inside_modal = true; 1371 } 1372 } 1373 1374 }, true); 1375 } 1376 } 1377 1378 /** 1379 * Manage each modal's layout 1380 * @param {Object} gui_options 1381 */ 1382 var _guiManager = function(gui_options, only_consent_modal){ 1383 1384 // If gui_options is not object => exit 1385 if(typeof gui_options !== 'object') return; 1386 1387 var consent_modal_options = gui_options['consent_modal']; 1388 var settings_modal_options = gui_options['settings_modal']; 1389 1390 /** 1391 * Helper function which adds layout and 1392 * position classes to given modal 1393 * 1394 * @param {HTMLElement} modal 1395 * @param {string[]} allowed_layouts 1396 * @param {string[]} allowed_positions 1397 * @param {string} layout 1398 * @param {string[]} position 1399 */ 1400 function _setLayout(modal, allowed_layouts, allowed_positions, allowed_transitions, layout, position, transition){ 1401 position = (position && position.split(" ")) || []; 1402 1403 // Check if specified layout is valid 1404 if(_inArray(allowed_layouts, layout) > -1){ 1405 1406 // Add layout classes 1407 _addClass(modal, layout); 1408 1409 // Add position class (if specified) 1410 if(!(layout === 'bar' && position[0] === 'middle') && _inArray(allowed_positions, position[0]) > -1){ 1411 for(var i=0; i<position.length; i++){ 1412 _addClass(modal, position[i]); 1413 } 1414 } 1415 } 1416 1417 // Add transition class 1418 (_inArray(allowed_transitions, transition) > -1) && _addClass(modal, transition); 1419 } 1420 1421 if(consent_modal_exists && consent_modal_options){ 1422 _setLayout( 1423 consent_modal, 1424 ['box', 'bar', 'cloud'], 1425 ['top', 'middle', 'bottom'], 1426 ['zoom', 'slide'], 1427 consent_modal_options['layout'], 1428 consent_modal_options['position'], 1429 consent_modal_options['transition'] 1430 ); 1431 } 1432 1433 if(!only_consent_modal && settings_modal_options){ 1434 _setLayout( 1435 settings_container, 1436 ['bar'], 1437 ['left', 'right'], 1438 ['zoom', 'slide'], 1439 settings_modal_options['layout'], 1440 settings_modal_options['position'], 1441 settings_modal_options['transition'] 1442 ); 1443 } 1444 } 1445 1446 /** 1447 * Returns true if cookie category is accepted by the user 1448 * @param {string} cookie_category 1449 * @returns {boolean} 1450 */ 1451 _cookieconsent.allowedCategory = function(cookie_category){ 1452 1453 if(cookie_consent_accepted || _config.mode === 'opt-in') 1454 var allowed_categories = JSON.parse(_getCookie(_config.cookie_name, 'one', true) || '{}')['level'] || [] 1455 else // mode is 'opt-out' 1456 var allowed_categories = default_enabled_categories; 1457 1458 return _inArray(allowed_categories, cookie_category) > -1; 1459 } 1460 1461 /** 1462 * "Init" method. Will run once and only if modals do not exist 1463 */ 1464 _cookieconsent.run = function(user_config){ 1465 if(!document.getElementById('cc_div')){ 1466 1467 // configure all parameters 1468 _setConfig(user_config); 1469 1470 // if is bot, don't run plugin 1471 if(is_bot) return; 1472 1473 // Retrieve cookie value (if set) 1474 saved_cookie_content = JSON.parse(_getCookie(_config.cookie_name, 'one', true) || "{}"); 1475 cookie_consent_accepted = saved_cookie_content['level'] !== undefined; 1476 1477 /** 1478 * Immediately retrieve the 'data' field from cookie 1479 * (since this value is allowed to be accessed/used before the .run method) 1480 */ 1481 cookie_data = saved_cookie_content['data'] !== undefined ? saved_cookie_content['data'] : null; 1482 1483 // Compare current revision with the one retrieved from cookie 1484 valid_revision = typeof user_config['revision']
1484=== "number" 1485 ? cookie_consent_accepted 1486 ? user_config['revision'] > -1 1487 ? saved_cookie_content['revision'] === _config.revision 1488 : true 1489 : true 1490 : true; 1491 1492 // If invalid revision or cookie is empty => create consent modal 1493 consent_modal_exists = (!cookie_consent_accepted || !valid_revision); 1494 1495 // Generate cookie-settings dom (& consent modal) 1496 _createCookieConsentHTML(); 1497 1498 _loadCSS(user_config['theme_css'], function(){ 1499 _getModalFocusableData(); 1500 _guiManager(user_config['gui_options']); 1501 _addDataButtonListeners(); 1502 1503 if(_config.autorun && consent_modal_exists){ 1504 _cookieconsent.show(user_config['delay'] || 0); 1505 } 1506 1507 // Add class to enable animations/transitions 1508 setTimeout(function(){_addClass(main_container, 'c--anim');}, 30); 1509 1510 // Accessibility :=> if tab pressed => trap focus inside modal 1511 setTimeout(function(){_handleFocusTrap();}, 100); 1512 }); 1513 1514 if(cookie_consent_accepted && valid_revision){ 1515 var rfc_prop_exists = typeof saved_cookie_content['rfc_cookie'] === "boolean"; 1516 1517 /* 1518 * Convert cookie to rfc format (if `use_rfc_cookie` is enabled) 1519 */ 1520 if(!rfc_prop_exists || (rfc_prop_exists && saved_cookie_content['rfc_cookie'] !== _config.use_rfc_cookie)){ 1521 saved_cookie_content['rfc_cookie'] = _config.use_rfc_cookie; 1522 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1523 } 1524 1525 /** 1526 * Update accept type 1527 */ 1528 accept_type = _getAcceptType(_getCurrentCategoriesState()); 1529 1530 _manageExistingScripts(); 1531 1532 if(typeof onAccept === 'function') 1533 onAccept(saved_cookie_content); 1534 1535 1536 }else if(_config.mode === 'opt-out'){ 1537 _log("CookieConsent [CONFIG] mode='" + _config.mode + "', default enabled categories:", default_enabled_categories); 1538 _manageExistingScripts(default_enabled_categories); 1539 } 1540 }else{ 1541 _log("CookieConsent [NOTICE]: cookie consent already attached to body!"); 1542 } 1543 } 1544 1545 /** 1546 * Show settings modal (with optional delay) 1547 * @param {number} delay 1548 */ 1549 _cookieconsent.showSettings = function(delay){ 1550 setTimeout(function() { 1551 _addClass(html_dom, "show--settings"); 1552 settings_container.setAttribute('aria-hidden', 'false'); 1553 settings_modal_visible = true; 1554 1555 /** 1556 * Set focus to the first focusable element inside settings modal 1557 */ 1558 setTimeout(function(){ 1559 // If there is no consent-modal, keep track of the last focused elem. 1560 if(!consent_modal_visible){ 1561 last_elem_before_modal = document.activeElement; 1562 }else{ 1563 last_consent_modal_btn_focus = document.activeElement; 1564 } 1565 1566 if (settings_modal_focusable.length === 0) return; 1567 1568 if(settings_modal_focusable[3]){ 1569 settings_modal_focusable[3].focus(); 1570 }else{ 1571 settings_modal_focusable[0].focus(); 1572 } 1573 current_modal_focusable = settings_modal_focusable; 1574 }, 200); 1575 1576 _log("CookieConsent [SETTINGS]: show settings_modal"); 1577 }, delay > 0 ? delay : 0); 1578 } 1579 1580 /** 1581 * This function handles the loading/activation logic of the already 1582 * existing scripts based on the current accepted cookie categories 1583 * 1584 * @param {string[]} [must_enable_categories] 1585 */ 1586 var _manageExistingScripts = function(must_enable_categories){ 1587 1588 if(!_config.page_scripts) return; 1589 1590 // get all the scripts with "cookie-category" attribute 1591 var scripts = document.querySelectorAll('script[' + _config.script_selector + ']'); 1592 var sequential_enabled = _config.page_scripts_order; 1593 var accepted_categories = must_enable_categories || saved_cookie_content['level'] || []; 1594 _log("CookieConsent [SCRIPT_MANAGER]: sequential loading:", sequential_enabled); 1595 1596 /** 1597 * Load scripts (sequentially), using a recursive function 1598 * which loops through the scripts array 1599 * @param {Element[]} scripts scripts to load 1600 * @param {number} index current script to load 1601 */ 1602 var _loadScripts = function(scripts, index){ 1603 if(index < scripts.length){ 1604 1605 var curr_script = scripts[index]; 1606 var curr_script_category = curr_script.getAttribute(_config.script_selector); 1607 1608 /** 1609 * If current script's category is on the array of categories 1610 * accepted by the user => load script 1611 */ 1612 if(_inArray(accepted_categories, curr_script_category) > -1){ 1613 1614 curr_script.type = 'text/javascript'; 1615 curr_script.removeAttribute(_config.script_selector); 1616 1617 // get current script data-src 1618 var src = curr_script.getAttribute('data-src'); 1619 1620 // some scripts (like ga) might throw warning if data-src is present 1621 src && curr_script.removeAttribute('data-src'); 1622 1623 // create fresh script (with the same code) 1624 var fresh_script = _createNode('script'); 1625 fresh_script.textContent = curr_script.innerHTML; 1626 1627 // Copy attributes over to the new "revived" script 1628 (function(destination, source){ 1629 var attributes = source.attributes; 1630 var len = attributes.length; 1631 for(var i=0; i<len; i++){ 1632 var attr_name = attributes[i].nodeName; 1633 destination.setAttribute(attr_name , source[attr_name] || source.getAttribute(attr_name)); 1634 } 1635 })(fresh_script, curr_script); 1636 1637 // set src (if data-src found) 1638 src ? (fresh_script.src = src) : (src = curr_script.src); 1639 1640 // if script has "src" attribute 1641 // try loading it sequentially 1642 if(src){ 1643 if(sequential_enabled){ 1644 // load script sequentially => the next script will not be loaded 1645 // until the current's script onload event triggers 1646 if(fresh_script.readyState) { // only required for IE <9 1647 fresh_script.onreadystatechange = function() { 1648 if (fresh_script.readyState === "loaded" || fresh_script.readyState === "complete" ) { 1649 fresh_script.onreadystatechange = null; 1650 _loadScripts(scripts, ++index); 1651 } 1652 }; 1653 }else{ // others 1654 fresh_script.onload = function(){ 1655 fresh_script.onload = null; 1656 _loadScripts(scripts, ++index); 1657 }; 1658 } 1659 }else{ 1660 // if sequential option is disabled 1661 // treat current script as inline (without onload event) 1662 src = false;
1663 } 1664 } 1665 1666 // Replace current "sleeping" script with the new "revived" one 1667 curr_script.parentNode.replaceChild(fresh_script, curr_script); 1668 1669 /** 1670 * If we managed to get here and scr is still set, it means that 1671 * the script is loading/loaded sequentially so don't go any further 1672 */ 1673 if(src) return; 1674 } 1675 1676 // Go to next script right away 1677 _loadScripts(scripts, ++index); 1678 } 1679 } 1680 1681 _loadScripts(scripts, 0); 1682 } 1683 1684 /** 1685 * Save custom data inside cookie 1686 * @param {object|string} new_data 1687 * @param {string} [mode] 1688 * @returns {boolean} 1689 */ 1690 var _setCookieData = function(new_data, mode){ 1691 1692 var set = false; 1693 /** 1694 * If mode is 'update': 1695 * add/update only the specified props. 1696 */ 1697 if(mode === 'update'){ 1698 cookie_data = _cookieconsent.get('data'); 1699 var same_type = typeof cookie_data === typeof new_data; 1700 1701 if(same_type && typeof cookie_data === "object"){ 1702 !cookie_data && (cookie_data = {}); 1703 1704 for(var prop in new_data){ 1705 if(cookie_data[prop] !== new_data[prop]){ 1706 cookie_data[prop] = new_data[prop] 1707 set = true; 1708 } 1709 } 1710 }else if((same_type || !cookie_data) && cookie_data !== new_data){ 1711 cookie_data = new_data; 1712 set = true; 1713 } 1714 }else{ 1715 cookie_data = new_data; 1716 set = true; 1717 } 1718 1719 if(set){ 1720 saved_cookie_content['data'] = cookie_data; 1721 _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); 1722 } 1723 1724 return set; 1725 } 1726 1727 /** 1728 * Forcefully set a specific revision and show consent modal 1729 * @param {number} new_revision 1730 * @param {boolean} [prompt_consent] 1731 * @returns {boolean} 1732 */ 1733 var _setRevision = function(new_revision, prompt_consent, message){ 1734 1735 // If plugin has been initialized and new revision is valid 1736 if( 1737 main_container 1738 && typeof new_revision === "number" 1739 && saved_cookie_content['revision'] !== new_revision 1740 ){ 1741 1742 revision_enabled = true; 1743 revision_message = message; 1744 valid_revision = false; 1745 _config.revision = new_revision; 1746 1747 // Show consent modal ? 1748 if(prompt_consent === true){ 1749 _createConsentModal(user_config); 1750 _guiManager(user_config['gui_options'], true); 1751 _getModalFocusableData(); 1752 _cookieconsent.show(); 1753 }else { 1754 // If revision was modified, save cookie with the new revision 1755 _cookieconsent.accept(); 1756 } 1757 1758 return true; 1759 } 1760 1761 return false; 1762 } 1763 1764 /** 1765 * Helper method to set a variety of fields 1766 * @param {string} field 1767 * @param {object} data 1768 * @returns {boolean} 1769 */ 1770 _cookieconsent.set = function(field, data){ 1771 switch(field){ 1772 case 'data': return _setCookieData(data['value'], data['mode']); 1773 case 'revision': return _setRevision(data['value'], data['prompt_consent'], data['message']); //[WARNING] Will be removed in v3 1774 default: return false; 1775 } 1776 } 1777 1778 /** 1779 * Retrieve data from existing cookie 1780 * @param {string} field 1781 * @param {string} [cookie_name] 1782 * @returns {any} 1783 */ 1784 _cookieconsent.get = function(field, cookie_name){ 1785 var cookie = JSON.parse(_getCookie(cookie_name || _config.cookie_name, 'one', true) || "{}"); 1786 1787 return cookie[field]; 1788 } 1789 1790 /** 1791 * Read current configuration value 1792 * @returns {any} 1793 */ 1794 _cookieconsent.getConfig = function(field){ 1795 return _config[field] || user_config[field]; 1796 } 1797 1798 /** 1799 * Obtain accepted and rejected categories 1800 * @returns {{accepted: string[], rejected: string[]}} 1801 */ 1802 var _getCurrentCategoriesState = function(){ 1803 1804 // get accepted categories 1805 accepted_categories = saved_cookie_content['level'] || []; 1806 1807 // calculate rejected categories (all_categories - accepted_categories) 1808 rejected_categories = all_categories.filter(function(category){ 1809 return (_inArray(accepted_categories, category) === -1); 1810 }); 1811 1812 return { 1813 accepted: accepted_categories, 1814 rejected: rejected_categories 1815 } 1816 } 1817 1818 /** 1819 * Calculate "accept type" given current categories state 1820 * @param {{accepted: string[], rejected: string[]}} currentCategoriesState 1821 * @returns {string} 1822 */ 1823 var _getAcceptType = function(currentCategoriesState){ 1824 1825 var type = 'custom'; 1826
1827 // number of categories marked as necessary/readonly 1828 var necessary_categories_length = readonly_categories.filter(function(readonly){ 1829 return readonly === true; 1830 }).length; 1831 1832 // calculate accept type based on accepted/rejected categories 1833 if(currentCategoriesState.accepted.length === all_categories.length) 1834 type = 'all'; 1835 else if(currentCategoriesState.accepted.length === necessary_categories_length) 1836 type = 'necessary' 1837 1838 return type; 1839 } 1840 1841 /** 1842 * @typedef {object} userPreferences 1843 * @property {string} accept_type 1844 * @property {string[]} accepted_categories 1845 * @property {string[]} rejected_categories 1846 */ 1847 1848 /** 1849 * Retrieve current user preferences (summary) 1850 * @returns {userPreferences} 1851 */ 1852 _cookieconsent.getUserPreferences = function(){ 1853 var currentCategoriesState = _getCurrentCategoriesState(); 1854 var accept_type = _getAcceptType(currentCategoriesState); 1855 1856 return { 1857 'accept_type': accept_type, 1858 'accepted_categories': currentCategoriesState.accepted, 1859 'rejected_categories': currentCategoriesState.rejected 1860 } 1861 } 1862 1863 /** 1864 * Function which will run after script load 1865 * @callback scriptLoaded 1866 */ 1867 1868 /** 1869 * Dynamically load script (append to head) 1870 * @param {string} src 1871 * @param {scriptLoaded} callback 1872 * @param {string[]} attrs 1873 */ 1874 _cookieconsent.loadScript = function(src, callback, attrs){ 1875 1876 var function_defined = typeof callback === 'function'; 1877 1878 // Load script only if not already loaded 1879 if(!document.querySelector('script[src="' + src + '"]')){ 1880 1881 var script = _createNode('script'); 1882 1883 // if an array is provided => add custom attributes 1884 if(attrs && attrs.length > 0){ 1885 for(var i=0; i<attrs.length; ++i){ 1886 attrs[i] && script.setAttribute(attrs[i]['name'], attrs[i]['value']); 1887 } 1888 } 1889 1890 // if callback function defined => run callback onload 1891 if(function_defined){ 1892 if(script.readyState) { // only required for IE <9 1893 script.onreadystatechange = function() { 1894 if ( script.readyState === "loaded" || script.readyState === "complete" ) { 1895 script.onreadystatechange = null; 1896 callback(); 1897 } 1898 }; 1899 }else{ //Others 1900 script.onload = callback; 1901 } 1902 } 1903 1904 script.src = src; 1905 1906 /** 1907 * Append script to head 1908 */ 1909 (document.head ? document.head : document.getElementsByTagName('head')[0]).appendChild(script); 1910 }else{ 1911 function_defined && callback(); 1912 } 1913 } 1914 1915 /** 1916 * Manage dynamically loaded scripts: https://github.com/orestbida/cookieconsent/issues/101 1917 * If plugin has already run, call this method to enable 1918 * the newly added scripts based on currently selected preferences 1919 */ 1920 _cookieconsent.updateScripts = function(){ 1921 _manageExistingScripts(); 1922 } 1923 1924 /** 1925 * Show cookie consent modal (with delay parameter) 1926 * @param {number} delay 1927 */ 1928 _cookieconsent.show = function(delay){ 1929 if(consent_modal_exists){ 1930 setTimeout(function() { 1931 _addClass(html_dom, "show--consent"); 1932 1933 /** 1934 * Update attributes/internal statuses 1935 */ 1936 consent_modal.setAttribute('aria-hidden', 'false'); 1937 consent_modal_visible = true; 1938 1939 setTimeout(function(){ 1940 last_elem_before_modal = document.activeElement; 1941 current_modal_focusable = consent_modal_focusable; 1942 }, 200); 1943 1944 _log("CookieConsent [MODAL]: show consent_modal"); 1945 }, delay > 0 ? delay : 0); 1946 } 1947 } 1948 1949 /** 1950 * Hide consent modal 1951 */ 1952 _cookieconsent.hide = function(){ 1953 if(consent_modal_exists){ 1954 _removeClass(html_dom, "show--consent"); 1955 consent_modal.setAttribute('aria-hidden', 'true'); 1956 consent_modal_visible = false;
1957 1958 setTimeout(function(){ 1959 //restore focus to the last page element which had focus before modal opening 1960 last_elem_before_modal.focus(); 1961 current_modal_focusable = null; 1962 }, 200); 1963 1964 _log("CookieConsent [MODAL]: hide"); 1965 } 1966 } 1967 1968 /** 1969 * Hide settings modal 1970 */ 1971 _cookieconsent.hideSettings = function(){ 1972 _removeClass(html_dom, "show--settings"); 1973 settings_modal_visible = false; 1974 settings_container.setAttribute('aria-hidden', 'true'); 1975 1976 1977 setTimeout(function(){ 1978 /** 1979 * If consent modal is visible, focus him (instead of page document) 1980 */ 1981 if(consent_modal_visible){ 1982 last_consent_modal_btn_focus && last_consent_modal_btn_focus.focus(); 1983 current_modal_focusable = consent_modal_focusable; 1984 }else{ 1985 /** 1986 * Restore focus to last page element which had focus before modal opening 1987 */ 1988 last_elem_before_modal && last_elem_before_modal.focus(); 1989 current_modal_focusable = null; 1990 } 1991 1992 clicked_inside_modal = false; 1993 }, 200); 1994 1995 _log("CookieConsent [SETTINGS]: hide settings_modal"); 1996 } 1997 1998 /** 1999 * Accept cookieconsent function API 2000 * @param {string[]|string} _categories - Categories to accept 2001 * @param {string[]} [_exclusions] - Excluded categories [optional] 2002 */ 2003 _cookieconsent.accept = function(_categories, _exclusions){ 2004 var categories = _categories || undefined; 2005 var exclusions = _exclusions || []; 2006 var to_accept = []; 2007 2008 /** 2009 * Get all accepted categories 2010 * @returns {string[]} 2011 */ 2012 var _getCurrentPreferences = function(){ 2013 var toggles = document.querySelectorAll('.c-tgl') || []; 2014 var states = []; 2015 2016 for(var i=0; i<toggles.length; i++){ 2017 if(toggles[i].checked){ 2018 states.push(toggles[i].value); 2019 } 2020 } 2021 return states; 2022 } 2023 2024 if(!categories){ 2025 to_accept = _getCurrentPreferences(); 2026 }else{ 2027 if( 2028 typeof categories === "object" && 2029 typeof categories.length === "number" 2030 ){ 2031 for(var i=0; i<categories.length; i++){ 2032 if(_inArray(all_categories, categories[i]) !== -1) 2033 to_accept.push(categories[i]); 2034 } 2035 }else if(typeof categories === "string"){ 2036 if(categories === 'all') 2037 to_accept = all_categories.slice(); 2038 else{ 2039 if(_inArray(all_categories, categories) !== -1) 2040 to_accept.push(categories); 2041 } 2042 } 2043 } 2044 2045 // Remove excluded categories 2046 if(exclusions.length >= 1){ 2047 for(i=0; i<exclusions.length; i++){ 2048 to_accept = to_accept.filter(function(item) { 2049 return item !== exclusions[i] 2050 }) 2051 } 2052 } 2053 2054 // Add back all the categories set as "readonly/required" 2055 for(i=0; i<all_categories.length; i++){ 2056 if( 2057 readonly_categories[i] === true && 2058 _inArray(to_accept, all_categories[i]) === -1 2059 ){ 2060 to_accept.push(all_categories[i]); 2061 } 2062 } 2063 2064 _saveCookiePreferences(to_accept); 2065 } 2066 2067 /** 2068 * API function to easily erase cookies 2069 * @param {(string|string[])} _cookies 2070 * @param {string} [_path] - optional 2071 * @param {string} [_domain] - optional 2072 */
2073 _cookieconsent.eraseCookies = function(_cookies, _path, _domain){ 2074 var cookies = []; 2075 var domains = _domain 2076 ? [_domain, "."+_domain] 2077 : [_config.cookie_domain, "."+_config.cookie_domain]; 2078 2079 if(typeof _cookies === "object" && _cookies.length > 0){ 2080 for(var i=0; i<_cookies.length; i++){ 2081 this.validCookie(_cookies[i]) && cookies.push(_cookies[i]); 2082 } 2083 }else{ 2084 this.validCookie(_cookies) && cookies.push(_cookies); 2085 } 2086 2087 _eraseCookies(cookies, _path, domains); 2088 } 2089 2090 /** 2091 * Set cookie, by specifying name and value 2092 * @param {string} name 2093 * @param {string} value 2094 */ 2095 var _setCookie = function(name, value) { 2096 2097 var cookie_expiration = _config.cookie_expiration; 2098 2099 if(typeof _config.cookie_necessary_only_expiration === 'number' && accept_type === 'necessary') 2100 cookie_expiration = _config.cookie_necessary_only_expiration; 2101 2102 value = _config.use_rfc_cookie ? encodeURIComponent(value) : value; 2103 2104 var date = new Date(); 2105 date.setTime(date.getTime() + (1000 * (cookie_expiration * 24 * 60 * 60))); 2106 var expires = "; expires=" + date.toUTCString(); 2107 2108 var cookieStr = name + "=" + (value || "") + expires + "; Path=" + _config.cookie_path + ";"; 2109 cookieStr += " SameSite=" + _config.cookie_same_site + ";"; 2110 2111 // assures cookie works with localhost (=> don't specify domain if on localhost) 2112 if(window.location.hostname.indexOf(".") > -1){ 2113 cookieStr += " Domain=" + _config.cookie_domain + ";"; 2114 } 2115 2116 if(window.location.protocol === "https:") { 2117 cookieStr += " Secure;"; 2118 } 2119 2120 document.cookie = cookieStr; 2121 2122 _log("CookieConsent [SET_COOKIE]: cookie "+ name + "='" + value + "' was set! Expires after " + cookie_expiration + " days"); 2123 } 2124 2125 /** 2126 * Get cookie value by name, 2127 * returns the cookie value if found (or an array 2128 * of cookies if filter provided), otherwise empty string: "" 2129 * @param {string} name 2130 * @param {string} filter - 'one' or 'all' 2131 * @param {boolean} get_value - set to true to obtain its value 2132 * @returns {string|string[]} 2133 */ 2134 var _getCookie = function(name, filter, get_value) { 2135 var found; 2136 2137 if(filter === 'one'){ 2138 found = document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)"); 2139 found = found ? (get_value ? found.pop() : name) : ""; 2140 2141 if(found && name === _config.cookie_name){ 2142 try{ 2143 found = JSON.parse(found) 2144 }catch(e){ 2145 try { 2146 found = JSON.parse(decodeURIComponent(found)) 2147 } catch (e) { 2148 // if I got here => cookie value is not a valid json string 2149 found = {}; 2150 } 2151 } 2152 found = JSON.stringify(found); 2153 } 2154 }else if(filter === 'all'){ 2155 // array of names of all existing cookies 2156 var cookies = document.cookie.split(/;\s*/); found = []; 2157 for(var i=0; i<cookies.length; i++){ 2158 found.push(cookies[i].split("=")[0]); 2159 } 2160 } 2161 2162 return found; 2163 } 2164 2165 /** 2166 * Delete cookie by name & path 2167 * @param {string[]} cookies 2168 * @param {string} [custom_path] - optional 2169 * @param {string[]} domains - example: ['domain.com', '.domain.com'] 2170 */ 2171 var _eraseCookies = function(cookies, custom_path, domains) { 2172 var path = custom_path ? custom_path : '/'; 2173 var expires = 'Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 2174 2175 for(var i=0; i<cookies.length; i++){ 2176 for(var j=0; j<domains.length; j++){ 2177 document.cookie = cookies[i] + '=; path=' + path + 2178 (domains[j].indexOf('.') > -1 ? '; domain=' + domains[j] : "") + '; ' + expires; 2179 } 2180 _log("CookieConsent [AUTOCLEAR]: deleting cookie: '" + cookies[i] + "' path: '" + path + "' domain:", domains); 2181 } 2182 } 2183 2184 /** 2185 * Returns true if cookie was found and has valid value (not empty string) 2186 * @param {string} cookie_name 2187 * @returns {boolean} 2188 */ 2189 _cookieconsent.validCookie = function(cookie_name){ 2190 return _getCookie(cookie_name, 'one', true) !== ""; 2191 } 2192 2193 /** 2194 * Function to run when event is fired 2195 * @callback eventFired 2196 */ 2197 2198 /** 2199 * Add event listener to dom object (cross browser function) 2200 * @param {Element} elem 2201 * @param {string} event 2202 * @param {eventFired} fn 2203 * @param {boolean} passive 2204 */ 2205 var _addEvent = function(elem, event, fn, _passive) { 2206 var passive = _passive === true; 2207 2208 if (elem.addEventListener) { 2209 passive ? elem.addEventListener(event, fn , { passive: true }) : elem.addEventListener(event, fn, false); 2210 } else { 2211 /** 2212 * For old browser, add 'on' before event: 2213 * 'click':=> 'onclick' 2214 */ 2215 elem.attachEvent("on" + event, fn); 2216 } 2217 } 2218 2219 /** 2220 * Get all prop. keys defined inside object 2221 * @param {Object} obj 2222 */ 2223 var _getKeys = function(obj){ 2224 if(typeof obj === "object"){ 2225 var keys = [], i = 0; 2226 for (var key in obj) 2227 keys[i++] = key; 2228 return keys; 2229 } 2230 } 2231 2232 /** 2233 * Append class to the specified dom element 2234 * @param {HTMLElement} elem 2235 * @param {string} classname 2236 */ 2237 var _addClass = function (elem, classname){ 2238 if(elem.classList) 2239 elem.classList.add(classname) 2240 else{ 2241 if(!_hasClass(elem, classname)) 2242 elem.className += ' '+classname; 2243 } 2244 } 2245 2246 /** 2247 * Remove specified class from dom element 2248 * @param {HTMLElement} elem 2249 * @param {string} classname 2250 */ 2251 var _removeClass = function (el, className) { 2252 el.classList ? el.classList.remove(className) : el.className = el.className.replace(new RegExp('(\\s|^)' + className + '(\\s|$)'), ' '); 2253 } 2254 2255 /** 2256 * Check if html element has class 2257 * @param {HTMLElement} el 2258 * @param {string} className 2259 */ 2260 var _hasClass = function(el, className) { 2261 if (el.classList) { 2262 return el.classList.contains(className); 2263 } 2264 return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)')); 2265 } 2266 2267 return _cookieconsent; 2268 }; 2269 2270 var init = 'initCookieConsent'; 2271 /** 2272 * Make CookieConsent object accessible globally 2273 */ 2274 if(typeof window[init] !== 'function'){ 2275 window[init] = CookieConsent 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.