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