PageSourceSearch

https://www.p-portal.go.jp/pps-web-biz/resources/app/javascript/techtouch/cookieconsent.js?v=001

js p-portal.go.jp collected 2026-10-03 21:38:48 UTC 76,375 bytes, 2,341 lines download raw bytes

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.