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