PageSourceSearch

https://www.pirum.se/assets/shopassets/cookieconsent/cookieconsent.js?v=230

js pirum.se collected 2026-09-26 16:50:15 UTC 46,675 bytes, 1,412 lines download raw bytes

1/*!
2 * CookieConsent v2.3
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 element where the cookieconsent will be appended (default :=> document.body)
11   * @returns {Object}
12   */
13  var CookieConsent = function (root) {
14    // CHANGE THIS FLAG FALSE TO DISABLE console.log()
15    var ENABLE_LOGS = false;
16
17    var _config = {
18      current_lang: "en",
19      autorun: true, // run as soon as loaded
20      cookie_expiration: 182, // default: 6 months (in days)
21    };
22
23    /**
24     * Object which holds the main methods (.show, .run, ...)
25     */
26    var _cookieconsent = {};
27
28    /**
29     * Internal state variables
30     */
31    var _saved_cookie_content;
32    var consent_modal_exists = false;
33    var cookie_consent_accepted = false;
34    var consent_modal_visible = false;
35    var settings_modal_visible = false;
36    var clicked_inside_modal = false;
37    var current_modal_focusable;
38
39    /**
40     * Save reference to the last focused element on the page
41     * (used later on to restore focus when both modals are closed)
42     */
43    var last_elem_before_modal;
44    var last_consent_modal_btn_focus;
45
46    /**
47     * Both of the array below have the same structure:
48     * [0] => holds reference to FIRST focusable element inside modal
49     * [1] => holds reference to LAST focusable element inside modal
50     */
51    var consent_modal_focusable = [];
52    var settings_modal_focusable = [];
53
54    /**
55     * Array of booleans used to keep track of enabled/disabled preferences
56     */
57    var toggle_states = [];
58
59    /**
60     * Pointers to main dom elements (to avoid retrieving them later using document.getElementById)
61     */
62    var html_dom = document.documentElement;
63    var main_container;
64    var consent_modal;
65    var settings_container, settings_inner;
66
67    /**
68     * Update config settings
69     * @param {Object} conf_params
70     */
71    var _setConfig = function (conf_params) {
72      _log("CookieConsent [CONFIG]: recieved_config_settings ", conf_params);
73
74      if (typeof conf_params["cookie_expiration"] === "number") {
75        _config.cookie_expiration = conf_params["cookie_expiration"];
76      }
77
78      if (typeof conf_params["autorun"] === "boolean") {
79        _config.autorun = conf_params["autorun"];
80      }
81
82      if (conf_params["auto_language"]) {
83        _config.current_lang = _getValidatedLanguage(
84          _getBrowserLang(),
85          conf_params.languages
86        );
87      } else {
88        if (typeof conf_params["current_lang"] === "string") {
89          _config.current_lang = _getValidatedLanguage(
90            conf_params["current_lang"],
91            conf_params.languages
92          );
93        }
94      }
95
96      if (conf_params["force_consent"] === true) {
97        _addClass(html_dom, "force--consent");
98      }
99
100      _log(
101        "CookieConsent [LANG]: setting current_lang = '" +
102          _config.current_lang +
103          "'"
104      );
105    };
106
107    /**
108     * Search for all occurrences in webpage and add an onClick listener :
109     * when clicked => open settings modal
110     */
111    var _addCookieSettingsButtonListener = function () {
112      var all_links = document.querySelectorAll(
113        'a[data-cc="c-settings"], button[data-cc="c-settings"]'
114      );
115      for (var x = 0; x < all_links.length; x++) {
116        all_links[x].setAttribute("aria-haspopup", "dialog");
117        _addEvent(all_links[x], "click", function (event) {
118          _cookieconsent.showSettings(0);
119          event.preventDefault
120            ? event.preventDefault()
121            : (event.returnValue = false);
122        });
123      }
124    };
125
126    /**
127     * Check if given lang index exists as a defined property inside _config object
128     * If it exists -> desired language is implemented for cookieconsent
129     * Otherwise switch back to default current_lang
130     * @param {String} lang
131     * @param {Object} all_languages
132     */
133    var _getValidatedLanguage = function (lang, all_languages) {
134      if (all_languages.hasOwnProperty(lang)) {
135        return lang;
136      } else if (_getKeys(all_languages).length > 0) {
137        if (all_languages.hasOwnProperty(_config.current_lang)) {
138          return _config.current_lang;
139        } else {
140          return _getKeys(all_languages)[0];
141        }
142      }
143    };
144
145    /**
146     * Save reference to first and last focusable elements inside each modal
147     * to prevent losing focus while navigating with TAB
148     * @param {HTMLElement} modal_dom
149     */
150    var _getModalFocusableData = function () {
151      /**
152       * Note: any of the below focusable elements, which has the attribute tabindex="-1" AND is either
153       * the first or last element of the modal, won't recieve focus during "open/close" modal
154       */
155      var allowed_focusable_types = [
156        "[href]",
157        "button",
158        "input",
159        "details",
160        '[tabindex="0"]',
161      ];
162      var focus_later, focus_first;
163
164      function _getAllFocusableElements(modal) {
165        focus_later = focus_first = false;
166
167        // ie might throw exception due to complex unsupported selector => a:not([tabindex="-1"])
168        try {
169          var elems = modal.querySelectorAll(
170            allowed_focusable_types.join(':not([tabindex="-1"]), ')
171          );
172          var attr,
173            len = elems.length,
174            i = 0;
175
176          while (i < len) {
177            attr = elems[i].getAttribute("data-focus");
178
179            if (!focus_first && attr === "1") {
180              focus_first = elems[i];
181            } else if (attr === "0") {
182              focus_later = elems[i];
183              if (
184                !focus_first &&
185                elems[i + 1].getAttribute("data-focus") !== "0"
186              ) {
187                focus_first = elems[i + 1];
188              }
189            }
190
191            i++;
192          }
193
194          return elems;
195        } catch (e) {
196          return modal.querySelectorAll(allowed_focusable_types.join(", "));
197        }
198      }
199
200      /**
201       * Get settings modal'S all focusable elements
202       */
203      var focusableContent = _getAllFocusableElements(settings_inner);
204
205      /**
206       * Save first and last elements (used to lock/trap focus inside modal)
207       */
208      settings_modal_focusable.push(focusableContent[0]);
209      settings_modal_focusable.push(
210        focusableContent[focusableContent.length - 1]
211      );
212      settings_modal_focusable[2] = focus_later || false;
213      settings_modal_focusable[3] = focus_first || false;
214
215      /**
216       * If consent modal exists, do the same
217       */
218      if (consent_modal_exists) {
219        focusableContent = _getAllFocusableElements(consent_modal);
220        consent_modal_focusable.push(focusableContent[0]);
221        consent_modal_focusable.push(
222          focusableContent[focusableContent.length - 1]
223        );
224        consent_modal_focusable[2] = focus_later || false;
225        consent_modal_focusable[3] = focus_first || false;
226      }
227
228      focus_later = focus_first = focusableContent = null;
229    };
230
231    /**
232     * Generate cookie consent html based on config settings
233     */
234    var _createCookieConsentHTML = function (never_accepted, conf_params) {
235      // Create main container which holds both consent modal & settings modal
236      main_container = _createNode("div");
237      main_container.id = "cc--main";
238
239      // Fix layout flash
240      main_container.style.position = "fixed";
241      main_container.style.zIndex = "1000000";
242      main_container.innerHTML =
243        '<!--[if lt IE 9 ]><div id="cc_div" class="ie"></div><![endif]--><!--[if (gt IE 8)|!(IE)]><!--><div id="cc_div"></div><!--<![endif]-->';
244      var all_modals_container = main_container.children[0];
245
246      // Get current language
247      var lang = _config.current_lang;
248
249      // Feature detection :=> avoid IE exception since .textContent is not always supported
250      var innerText =
251        typeof html_dom.textContent === "string" ? "textContent" : "innerText";
252
253      // If never_accepted => create consent-modal
254      if (!never_accepted) {
255        consent_modal = _createNode("div");
256        var consent_modal_inner = _createNode("div");
257        var consent_title = _createNode("div");
258        var consent_text = _createNode("div");
259        var consent_buttons = _createNode("div");
260        var consent_primary_btn = _createNode("button");
261        var consent_secondary_btn = _createNode("button");
262
263        consent_modal.id = "cm";
264        consent_modal_inner.id = "cm_inner";
265        consent_title.id = "cm_title";
266        consent_text.id = "cm_text";
267        consent_buttons.id = "cm_btns";
268        consent_primary_btn.id = "cm_primary_btn";
269        consent_secondary_btn.id = "cm_secondary_btn";
270        consent_primary_btn.className = "c_button";
271        consent_secondary_btn.className = "c_button c_link";
272
273        consent_title.setAttribute("role", "heading");
274        consent_title.setAttribute("aria-level", "2");
275        consent_modal.setAttribute("role", "dialog");
276        consent_modal.setAttribute("aria-modal", "true");
277        consent_modal.setAttribute("aria-hidden", "false");
278        consent_modal.setAttribute("aria-labelledby", "cm_title");
279        consent_modal.setAttribute("aria-describedby", "cm_text");
280        consent_primary_btn.setAttribute("type", "button");
281        consent_secondary_btn.setAttribute("type", "button");
282
283        /**
284         * Make modal by default hidden to prevent weird page jumps/flashes (shown only once css is loaded)
285         */
286        consent_modal.style.visibility = "hidden";
287
288        // Use insertAdjacentHTML instead of innerHTML
289        consent_title.insertAdjacentHTML(
290          "beforeend",
291          conf_params.languages[lang]["consent_modal"]["title"]
292        );
293        consent_text.insertAdjacentHTML(
294          "beforeend",
295          conf_params.languages[lang]["consent_modal"]["description"]
296        );
297
298        consent_primary_btn[innerText] =
299          conf_params.languages[lang]["consent_modal"]["primary_btn"]["text"];
300        consent_secondary_btn[innerText] =
301          conf_params.languages[lang]["consent_modal"]["secondary_btn"]["text"];
302
303        /**
304         * Add click event listeners
305         */
306        if (
307          conf_params.languages[lang]["consent_modal"]["primary_btn"]["role"] ==
308          "accept_all"
309        ) {
310          _addEvent(consent_primary_btn, "click", function () {
311            _cookieconsent.hide();
312            _log("CookieConsent [ACCEPT]: cookie_consent was accepted!");
313            _saveCookiePreferences(conf_params, 1); // 1 => accept all
314          });
315        } else {
316          _addEvent(consent_primary_btn, "click", function () {
317            _cookieconsent.hide();
318            _log(
319              "CookieConsent [ACCEPT]: cookie_consent was accepted (necessary only)!"
320            );
321            _saveCookiePreferences(conf_params, -1); // -1 => accept current selection
322          });
323        }
324
325        if (
326          conf_params.languages[lang]["consent_modal"]["secondary_btn"][
327            "role"
328          ] == "accept_necessary"
329        ) {
330          _addEvent(consent_secondary_btn, "click", function () {
331            _cookieconsent.hide();
332            _saveCookiePreferences(conf_params, 0); // 0 => accept necessary only
333          });
334        } else {
335          _addEvent(consent_secondary_btn, "click", function () {
336            _cookieconsent.showSettings(0);
337          });
338        }
339
340        consent_modal_inner.appendChild(consent_title);
341        consent_modal_inner.appendChild(consent_text);
342        consent_buttons.appendChild(consent_primary_btn);
343        consent_buttons.appendChild(consent_secondary_btn);
344        consent_modal_inner.appendChild(consent_buttons);
345        consent_modal.appendChild(consent_modal_inner);
346
347        // Append consent modal to main container
348        all_modals_container.appendChild(consent_modal);
349      }
350
351      /**
352       * Create all consent_modal elements
353       */
354      settings_container = _createNode("div");
355      var settings_container_valign = _createNode("div");
356      var settings = _createNode("div");
357      var settings_container_inner = _createNode("div");
358      settings_inner = _createNode("div");
359      var settings_title = _createNode("div");
360      var settings_header = _createNode("div");
361      var settings_close_btn = _createNode("button");
362      var settings_close_btn_container = _createNode("div");
363      var settings_blocks = _createNode("div");
364      settings_close_btn.setAttribute("type", "button");
365
366      /**
367       * Set ids
368       */
369      settings_container.id = "cs_cont";
370      settings_container_valign.id = "cs_valign";
371      settings_container_inner.id = "cs_cont_inner";
372      settings.id = "cs";
373      settings_title.id = "cs_title";
374      settings_inner.id = "cs_inner";
375      settings_header.id = "cs_header";
376      settings_blocks.id = "cs_blocks";
377      settings_close_btn.id = "cs_close_btn";
378      settings_close_btn_container.id = "cs_close_btn_cont";
379      settings_close_btn.className = "c_button";
380
381      settings_close_btn.setAttribute(
382        "aria-label",
383        conf_params.languages[lang]["settings_modal"]["close_btn_label"] ||
384          "Close"
385      );
386      settings_container.setAttribute("role", "dialog");
387      settings_container.setAttribute("aria-modal", "true");
388      settings_container.setAttribute("aria-hidden", "true");
389      settings_container.setAttribute("aria-labelledby", "cs_title");
390      settings_blocks.setAttribute("tabindex", "-1");
391      settings_title.setAttribute("role", "heading");
392      settings_container.style.visibility = "hidden";
393
394      settings_close_btn_container.appendChild(settings_close_btn);
395
396      // If 'esc' key is pressed inside settings_container div => hide settings
397      _addEvent(
398        settings_container_valign,
399        "keydown",
400        function (evt) {
401          evt = evt || window.event;
402          if (evt.keyCode == 27) {
403            _cookieconsent.hideSettings(0);
404          }
405        },
406        true
407      );
408
409      _addEvent(settings_close_btn, "click", function () {
410        _cookieconsent.hideSettings(0);
411      });
412
413      var all_blocks =
414        conf_params.languages[_config.current_lang]["settings_modal"]["blocks"];
415      var n_blocks = all_blocks.length;
416
417      // Set settings modal title
418      settings_title.insertAdjacentHTML(
419        "beforeend",
420        conf_params.languages[_config.current_lang]["settings_modal"]["title"]
421      );
422
423      // Create settings modal content (blocks)
424      for (var i = 0; i < n_blocks; ++i) {
425        // Create title
426        var block_section = _createNode("div");
427        var block_table_container = _createNode("div");
428        var block_desc = _createNode("div");
429        var block_title_container = _createNode("div");
430
431        block_section.className = "cs_block";
432        block_table_container.className = "desc";
433        block_desc.className = "p";
434        block_title_container.className = "title";
435
436        // Sset title and description for each block
437        block_desc.insertAdjacentHTML(
438          "beforeend",
439          all_blocks[i]["description"]
440        );
441
442        // Create toggle if specified (opt in/out)
443        if (typeof all_blocks[i]["toggle"] !== "undefined") {
444          var accordion_id = "acc__" + i;
445
446          // Create button (to collapse/expand block description)
447          var block_title_btn = _createNode("button");
448          var block_switch_label = _createNode("label");
449          var block_switch = _createNode("input");
450          var block_switch_span = _createNode("span");
451          var label_text_span = _createNode("span");
452
453          // These 2 spans will contain each 2 pseudo-elements to generate 'tick' and 'x' icons
454          var block_switch_span_on_icon = _createNode("span");
455          var block_switch_span_off_icon = _createNode("span");
456
457          block_title_btn.className = "b_title";
458          block_switch_label.className = "c_b_toggle";
459          block_switch.className = "c_toggle";
460          block_switch_span_on_icon.className = "c_on_icon";
461          block_switch_span_off_icon.className = "c_off_icon";
462          block_switch_span.className = "sc_toggle";
463          label_text_span.className = "toggle_label";
464
465          block_title_btn.setAttribute("type", "button");
466          block_title_btn.setAttribute("aria-expanded", "false");
467          block_title_btn.setAttribute("aria-controls", accordion_id);
468
469          block_switch.setAttribute("type", "checkbox");
470          block_switch_span.setAttribute("aria-hidden", "true");
471
472          var cookie_category = all_blocks[i]["toggle"].value;
473          block_switch.value = cookie_category;
474
475          label_text_span[innerText] = all_blocks[i]["title"];
476          block_title_btn.insertAdjacentHTML(
477            "beforeend",
478            all_blocks[i]["title"]
479          );
480
481          block_title_container.appendChild(block_title_btn);
482          block_switch_span.appendChild(block_switch_span_on_icon);
483          block_switch_span.appendChild(block_switch_span_off_icon);
484
485          /**
486           * If never accepted => generate toggles with the states defined in the config. object
487           * Otherwise, retrieve values from saved cookie
488           */
489          if (never_accepted) {
490            if (
491              _arrayContains(
492                JSON.parse(_saved_cookie_content).level,
493                cookie_category
494              )
495            ) {
496              block_switch.checked = true;
497              toggle_states.push(true);
498            } else {
499              toggle_states.push(false);
500            }
501          } else if (all_blocks[i]["toggle"]["enabled"]) {
502            block_switch.checked = true;
503          }
504
505          /**
506           * Set toggle as readonly if true (disable checkbox)
507           */
508          if (all_blocks[i]["toggle"]["readonly"]) {
509            block_switch.disabled = true;
510            block_switch.setAttribute("aria-readonly", "true");
511            _addClass(block_switch_span, "sc_readonly");
512          }
513
514          _addClass(block_table_container, "accordion");
515          _addClass(block_title_container, "block_button");
516
517          block_table_container.id = accordion_id;
518          block_table_container.setAttribute("aria-hidden", "true");
519
520          block_switch_label.appendChild(block_switch);
521          block_switch_label.appendChild(block_switch_span);
522          block_switch_label.appendChild(label_text_span);
523          block_title_container.appendChild(block_switch_label);
524
525          _addClass(block_section, "block__expand");
526
527          /**
528           * On button click handle the following :=> aria-expanded, aria-hidden and _active class for current block
529           */
530          (function (accordion, block_section, btn) {
531            _addEvent(
532              block_title_btn,
533              "click",
534              function () {
535                if (!_hasClass(block_section, "_active")) {
536                  _addClass(block_section, "_active");
537                  btn.setAttribute("aria-expanded", "true");
538                  accordion.setAttribute("aria-hidden", "false");
539                } else {
540                  _removeClass(block_section, "_active");
541                  btn.setAttribute("aria-expanded", "false");
542                  accordion.setAttribute("aria-hidden", "true");
543                }
544              },
545              false
546            );
547          })(block_table_container, block_section, block_title_btn);
548        } else {
549          /**
550           * If block is not a button (no toggle defined),
551           * create a simple div instead
552           */
553          var block_title = _createNode("div");
554          block_title.className = "b_title";
555          block_title.setAttribute("role", "heading");
556          block_title.setAttribute("aria-level", "3");
557          block_title.insertAdjacentHTML("beforeend", all_blocks[i]["title"]);
558          block_title_container.appendChild(block_title);
559        }
560
561        block_section.appendChild(block_title_container);
562        block_table_container.appendChild(block_desc);
563
564        // if cookie table found, generate table for this block
565        if (typeof all_blocks[i]["cookie_table"] !== "undefined") {
566          var tr_tmp_fragment = document.createDocumentFragment();
567          var all_table_headers =
568            conf_params.languages[_config.current_lang]["settings_modal"][
569              "cookie_table_headers"
570            ];
571
572          /**
573           * Use custom table headers
574           */
575          for (var p = 0; p < all_table_headers.length; ++p) {
576            // create new header
577            var th1 = _createNode("th");
578            th1.setAttribute("scope", "col");
579
580            // get custom header content
581            var new_column_key = _getKeys(all_table_headers[p])[0];
582
583            th1[innerText] = all_table_headers[p][new_column_key];
584            tr_tmp_fragment.appendChild(th1);
585          }
586
587          var tr_tmp = _createNode("tr");
588          tr_tmp.appendChild(tr_tmp_fragment);
589
590          // create table header & append fragment
591          var thead = _createNode("thead");
592          thead.appendChild(tr_tmp);
593
594          // append header to table
595          var block_table = _createNode("table");
596          block_table.appendChild(thead);
597
598          var tbody_fragment = document.createDocumentFragment();
599
600          // create table content
601          for (var n = 0; n < all_blocks[i]["cookie_table"].length; n++) {
602            var tr = _createNode("tr");
603
604            for (var g = 0; g < all_table_headers.length; ++g) {
605              // get custom header content
606              var new_column_key = _getKeys(all_table_headers[g])[0];
607
608              var td_tmp = _createNode("td");
609              td_tmp[innerText] =
610                all_blocks[i]["cookie_table"][n][new_column_key];
611              td_tmp.setAttribute(
612                "data-column",
613                all_table_headers[g][new_column_key]
614              );
615
616              tr.appendChild(td_tmp);
617            }
618
619            tbody_fragment.appendChild(tr);
620          }
621
622          // append tbody_fragment to tbody & append the latter into the table
623          var tbody = _createNode("tbody");
624          tbody.appendChild(tbody_fragment);
625          block_table.appendChild(tbody);
626
627          //block_section.appendChild(block_table);
628          block_table_container.appendChild(block_table);
629        }
630
631        block_section.appendChild(block_table_container);
632
633        // append block inside settings dom
634        settings_blocks.appendChild(block_section);
635      }
636
637      // Create settings buttons
638      var settings_buttons = _createNode("div");
639      var settings_save_btn = _createNode("button");
640      var settings_accept_all_btn = _createNode("button");
641
642      settings_buttons.id = "cs_buttons";
643      settings_save_btn.id = "cs_save__btn";
644      settings_accept_all_btn.id = "cs_acceptall_btn";
645      settings_save_btn.setAttribute("type", "button");
646      settings_accept_all_btn.setAttribute("type", "button");
647      settings_save_btn.className = "c_button";
648      settings_accept_all_btn.className = "c_button";
649      settings_save_btn.insertAdjacentHTML(
650        "beforeend",
651        conf_params.languages[_config.current_lang]["settings_modal"][
652          "save_settings_btn"
653        ]
654      );
655      settings_accept_all_btn.insertAdjacentHTML(
656        "beforeend",
657        conf_params.languages[_config.current_lang]["settings_modal"][
658          "accept_all_btn"
659        ]
660      );
661
662      settings_buttons.appendChild(settings_accept_all_btn);
663      settings_buttons.appendChild(settings_save_btn);
664
665      // Add save preferences button onClick event
666      // Hide both settings modal and consent modal
667      _addEvent(settings_save_btn, "click", function () {
668        _cookieconsent.hideSettings();
669        _cookieconsent.hide();
670        _saveCookiePreferences(conf_params, -1);
671      });
672
673      _addEvent(settings_accept_all_btn, "click", function () {
674        _cookieconsent.hideSettings();
675        _cookieconsent.hide();
676        _saveCookiePreferences(conf_params, 1);
677      });
678
679      settings_header.appendChild(settings_title);
680      settings_header.appendChild(settings_close_btn_container);
681
682      settings_inner.appendChild(settings_header);
683      settings_inner.appendChild(settings_blocks);
684      settings_inner.appendChild(settings_buttons);
685      settings_container_inner.appendChild(settings_inner);
686
687      settings.appendChild(settings_container_inner);
688      settings_container_valign.appendChild(settings);
689      settings_container.appendChild(settings_container_valign);
690
691      all_modals_container.appendChild(settings_container);
692
693      // Finally append everything to body (main_container holds both modals)
694      (root || document.body).appendChild(main_container);
695    };
696
697    /**
698     * Save cookie preferences
699     * accept_type = 0: accept necessary only
700     * accept_type = 1: accept all
701     * accept_type = -1: accept selection
702     */
703    var _saveCookiePreferences = function (conf_params, accept_type) {
704      // Get all cookiepreferences values saved in cookieconsent settings modal
705      var category_toggles = document.querySelectorAll(".c_toggle");
706      var c_cookie_level = "",
707        changedSettings = false;
708
709      // If there are opt in/out toggles ...
710      if (typeof category_toggles.length === "number") {
711        switch (accept_type) {
712          case -1:
713            //accept current selection
714            for (var i = 0; i < category_toggles.length; i++) {
715              if (category_toggles[i].checked) {
716                c_cookie_level += '"' + category_toggles[i].value + '",';
717                if (!toggle_states[i]) {
718                  changedSettings = true;
719                  toggle_states[i] = true;
720                }
721              } else {
722                if (toggle_states[i]) {
723                  changedSettings = true;
724                  toggle_states[i] = false;
725                }
726              }
727            }
728            break;
729          case 0:
730            // disable all except necessary
731            for (var i = 0; i < category_toggles.length; i++) {
732              if (category_toggles[i].disabled) {
733                c_cookie_level += '"' + category_toggles[i].value + '",';
734                toggle_states[i] = true;
735              } else {
736                category_toggles[i].checked = false;
737                if (toggle_states[i]) {
738                  changedSettings = true;
739                  toggle_states[i] = false;
740                }
741              }
742            }
743            break;
744          case 1:
745            // enable all
746            for (var i = 0; i < category_toggles.length; i++) {
747              category_toggles[i].checked = true;
748              c_cookie_level += '"' + category_toggles[i].value + '",';
749              if (!toggle_states[i]) {
750                changedSettings = true;
751              }
752
753              toggle_states[i] = true;
754            }
755            break;
756        }
757
758        // remove last ',' character
759        c_cookie_level = c_cookie_level.slice(0, -1);
760
761        /**
762         * If autoclear_cookies==true -> delete all cookies which are unused (based on selected preferences)
763         */
764        if (conf_params["autoclear_cookies"] && cookie_consent_accepted) {
765          // Get array of all blocks defined inside settings
766          var all_blocks =
767            conf_params.languages[_config.current_lang]["settings_modal"][
768              "blocks"
769            ];
770
771          // Get number of blocks
772          var len = all_blocks.length;
773
774          // For each block
775          var count = -1;
776          for (var jk = 0; jk < len; jk++) {
777            // Save current block (local scope & less accesses -> ~faster value retrieval)
778            var curr_block = all_blocks[jk];
779
780            // If current block has a toggle for opt in/out
781            if (curr_block.hasOwnProperty("toggle")) {
782              // if current block has a cookie table with toggle off => delete cookies
783              if (
784                !toggle_states[++count] &&
785                curr_block.hasOwnProperty("cookie_table")
786              ) {
787                var ckey = _getKeys(
788                  conf_params.languages[_config.current_lang]["settings_modal"][
789                    "cookie_table_headers"
790                  ][0]
791                )[0];
792
793                // Get number of cookies defined in cookie_table
794                var clen = curr_block["cookie_table"].length;
795
796                // Delete each cookie defined in ccb_cookie_table of current block
797                for (var hk = 0; hk < clen; hk++) {
798                  // Get current row of table (corresponds to all cookie params)
799                  var curr_row = curr_block["cookie_table"][hk];
800
801                  // If cookie exists -> delete it
802                  if (_getCookie(curr_row[ckey]) != "") {
803                    _eraseCookie(curr_row[ckey]);
804                    _log(
805                      "CookieConsent [AUTOCLEAR]: deleting cookie: '" +
806                        curr_row[ckey] +
807                        "'"
808                    );
809                  }
810                }
811              }
812            }
813          }
814        }
815      }
816
817      _saved_cookie_content = '{"level": [' + c_cookie_level + "]}";
818
819      // save cookie with preferences 'level' (only if never accepted or settings were updated)
820      if (!cookie_consent_accepted || changedSettings)
821        _setCookie(
822          "cc_cookie",
823          _saved_cookie_content,
824          _config.cookie_expiration,
825          0,
826          0
827        );
828
829      if (
830        typeof conf_params["onAccept"] === "function" &&
831        !cookie_consent_accepted
832      ) {
833        cookie_consent_accepted = true;
834        return conf_params["onAccept"](JSON.parse(_saved_cookie_content));
835      }
836
837      // fire onChange only if settings were changed
838      if (typeof conf_params["onChange"] === "function" && changedSettings) {
839        conf_params["onChange"](JSON.parse(_saved_cookie_content));
840      }
841    };
842
843    /**
844     * Load style via ajax in background (and then show modal)
845     * @param {Object} conf_params
846     * @param {Function} callback
847     */
848    var _loadCSS = function (conf_params, callback) {
849      if (
850        conf_params["autoload_css"] &&
851        !document.getElementById("cc--style")
852      ) {
853        // Create style tag
854        var style = _createNode("style");
855
856        // ad an id so that in SPA apps (react-like) the style doesn't get loaded multiple times when plugin is called
857        style.id = "cc--style";
858
859        var xhr = new XMLHttpRequest();
860
861        xhr.onreadystatechange = function () {
862          if (this.readyState == 4 && this.status == 200) {
863            // Necessary for <IE9
864            style.setAttribute("type", "text/css");
865
866            if (style.styleSheet) {
867              // if <IE9
868              style.styleSheet.cssText = this.responseText;
869            } else {
870              // all other browsers
871              style.appendChild(document.createTextNode(this.responseText));
872            }
873
874            // Append css text content
875            document.getElementsByTagName("head")[0].appendChild(style);
876            _log(
877              "CookieConsent [AUTOLOAD_CSS]: loaded style = '" +
878                conf_params["theme_css"] +
879                "'"
880            );
881
882            // Call function with minimal delay (to make sure that initial fade-zoom-in animations dont get skipped)
883            setTimeout(function () {
884              callback();
885            }, 10);
886          }
887        };
888
889        xhr.open("GET", conf_params["theme_css"]);
890        xhr.send();
891      } else {
892        callback();
893      }
894    };
895
896    /**
897     * Returns true if value is found inside array
898     * @param {Array} arr
899     * @param {Object} value
900     * @returns {Boolean}
901     */
902    var _arrayContains = function (arr, value) {
903      var len = arr.length;
904      for (var i = 0; i < len; i++) {
905        if (arr[i] == value) return true;
906      }
907      return false;
908    };
909
910    /**
911     * Helper function which prints info (console.log())
912     * @param {Object} print_msg
913     * @param {Object} optional_param
914     */
915    var _log = function (print_msg, optional_param, error) {
916      ENABLE_LOGS &&
917        (!error
918          ? console.log(print_msg, optional_param || " ")
919          : console.error(print_msg, optional_param || ""));
920    };
921
922    /**
923     * Helper function which creates an HTMLElement object based on 'type' and returns it.
924     * @param {String} type
925     * @returns {HTMLElement}
926     */
927    var _createNode = function (type) {
928      return document.createElement(type);
929    };
930
931    /**
932     * Get current client - browser language
933     * Used when 'auto_language' config property is set to 'true' (boolean)
934     * @returns {String}
935     */
936    var _getBrowserLang = function () {
937      var browser_lang = navigator.language || navigator.browserLanguage;
938      browser_lang.length > 2 &&
939        (browser_lang = browser_lang[0] + browser_lang[1]);
940      _log(
941        "CookieConsent [LANG]: detected_browser_lang = '" + browser_lang + "'"
942      );
943      return browser_lang.toLowerCase();
944    };
945
946    /**
947     * Traps focus inside modal && focuses the first focusable element of current active modal
948     */
949    var _handleFocusOutline = function () {
950      var tabbedOutsideDiv = false;
951      var tabbedInsideModal = false;
952
953      _addEvent(document, "keydown", function (e) {
954        e = e || window.event;
955
956        // If is tab key => ok
957        if (e.key !== "Tab") return;
958
959        // If there is any modal to focus
960        if (current_modal_focusable) {
961          // If reached natural end of the tab sequence => restart
962          if (e.shiftKey) {
963            if (document.activeElement === current_modal_focusable[0]) {
964              current_modal_focusable[1].focus();
965              e.preventDefault();
966            }
967          } else {
968            if (document.activeElement === current_modal_focusable[1]) {
969              current_modal_focusable[0].focus();
970              e.preventDefault();
971            }
972          }
973
974          // If have not yet used tab (or shift+tab) and modal is open ...
975          // Focus the first focusable element
976          if (!tabbedInsideModal && !clicked_inside_modal) {
977            tabbedInsideModal = true;
978            !tabbedOutsideDiv && e.preventDefault();
979
980            if (e.shiftKey) {
981              if (current_modal_focusable[3]) {
982                if (!current_modal_focusable[2]) {
983                  current_modal_focusable[0].focus();
984                } else {
985                  current_modal_focusable[2].focus();
986                }
987              } else {
988                current_modal_focusable[1].focus();
989              }
990            } else {
991              if (current_modal_focusable[3]) {
992                current_modal_focusable[3].focus();
993              } else {
994                current_modal_focusable[0].focus();
995              }
996            }
997          }
998        }
999
1000        !tabbedInsideModal && (tabbedOutsideDiv = true);
1001      });
1002
1003      if (document.contains) {
1004        _addEvent(
1005          main_container,
1006          "click",
1007          function (e) {
1008            e = e || window.event;
1009            /**
1010             * If click is on the foreground overlay (and not inside settings_modal),
1011             * hide settings modal
1012             *
1013             * Notice: click on div is not supported in IE
1014             */
1015            if (settings_modal_visible) {
1016              if (!settings_inner.contains(e.target)) {
1017                _cookieconsent.hideSettings(0);
1018                clicked_inside_modal = false;
1019              } else {
1020                clicked_inside_modal = true;
1021              }
1022            } else if (consent_modal_visible) {
1023              if (consent_modal.contains(e.target)) {
1024                clicked_inside_modal = true;
1025              }
1026            }
1027          },
1028          true
1029        );
1030      }
1031    };
1032
1033    /**
1034     * Returns true cookie category is saved into cc_cookie
1035     * @param {String} cookie_name
1036     * @returns {Boolean}
1037     */
1038    _cookieconsent.allowedCategory = function (cookie_name) {
1039      return _arrayContains(
1040        JSON.parse(_getCookie("cc_cookie") || "{}")["level"] || [],
1041        cookie_name
1042      );
1043    };
1044
1045    /**
1046     * Check if cookieconsent is alredy attached to dom
1047     * If not, create one, configure it and attach it to body
1048     */
1049    _cookieconsent.run = function (conf_params) {
1050      if (!main_container) {
1051        // configure all parameters
1052        _setConfig(conf_params);
1053
1054        // Retrieve cookie value (if set)
1055        _saved_cookie_content = _getCookie("cc_cookie");
1056
1057        // If cookie is empty => create consent modal
1058        consent_modal_exists = _saved_cookie_content == "";
1059
1060        _loadCSS(conf_params, function () {
1061          // Generate cookie-settings dom (& consent modal)
1062          _createCookieConsentHTML(!consent_modal_exists, conf_params);
1063          _addCookieSettingsButtonListener();
1064          _getModalFocusableData();
1065
1066          if (!_saved_cookie_content && _config.autorun) {
1067            _cookieconsent.show(conf_params["delay"] || 0);
1068          }
1069
1070          // Add class to enable animations
1071          setTimeout(function () {
1072            _addClass(main_container, "c--anim");
1073          }, 15);
1074
1075          // Accessibility :=> if tab pressed => show outline focus
1076          setTimeout(function () {
1077            _handleFocusOutline();
1078          }, 100);
1079        });
1080
1081        // if cookie accepted => fire once onAccept method (if defined)
1082        if (
1083          _saved_cookie_content &&
1084          typeof conf_params["onAccept"] === "function" &&
1085          !cookie_consent_accepted
1086        ) {
1087          cookie_consent_accepted = true;
1088          conf_params["onAccept"](JSON.parse(_saved_cookie_content || "{}"));
1089        }
1090      } else {
1091        _log("CookieConsent [NOTICE]: cookie consent alredy attached to body!");
1092      }
1093    };
1094
1095    /**
1096     * Show settings modal
1097     * @param {Number} delay
1098     */
1099    _cookieconsent.showSettings = function (delay) {
1100      setTimeout(
1101        function () {
1102          _addClass(html_dom, "show--settings");
1103          settings_container.setAttribute("aria-hidden", "false");
1104          settings_modal_visible = true;
1105
1106          // If there is no consent-modal, keep track of last focused elem
1107          if (!consent_modal_visible) {
1108            last_elem_before_modal = document.activeElement;
1109          } else {
1110            last_consent_modal_btn_focus = document.activeElement;
1111          }
1112
1113          /**
1114           * Set focus to first focusable element inside settings modal
1115           */
1116          setTimeout(function () {
1117            if (settings_modal_focusable[3]) {
1118              settings_modal_focusable[3].focus();
1119            } else {
1120              settings_modal_focusable[0].focus();
1121            }
1122            current_modal_focusable = settings_modal_focusable;
1123          }, 100);
1124
1125          _log("CookieConsent [SETTINGS]: show settings_modal");
1126        },
1127        delay > 0 ? delay : 0
1128      );
1129    };
1130
1131    /**
1132     * Dynamically load script
1133     * @param {String} src
1134     * @param {Function} callback
1135     * @param {Array} attrs
1136     */
1137    _cookieconsent.loadScript = function (src, callback, attrs) {
1138      // Load script only if not alredy loaded
1139      if (!document.querySelector('script[src="' + src + '"]')) {
1140        var script = _createNode("script");
1141
1142        // if an array is provided => add custom attributes
1143        if (attrs && attrs.length > 0) {
1144          for (var i = 0; i < attrs.length; ++i) {
1145            attrs[i] &&
1146              script.setAttribute(attrs[i]["name"], attrs[i]["value"]);
1147          }
1148        }
1149
1150        if (script.readyState) {
1151          // only required for IE <9
1152          script.onreadystatechange = function () {
1153            if (
1154              script.readyState === "loaded" ||
1155              script.readyState === "complete"
1156            ) {
1157              script.onreadystatechange = null;
1158              callback();
1159            }
1160          };
1161        } else {
1162          //Others
1163          script.onload = callback;
1164        }
1165
1166        script.src = src;
1167
1168        /**
1169         * Append script to head
1170         */
1171        (document.head
1172          ? document.head
1173          : document.getElementsByTagName("head")[0]
1174        ).appendChild(script);
1175      } else {
1176        callback();
1177      }
1178    };
1179
1180    /**
1181     * Show cookie consent modal (with delay parameter)
1182     * @param {Number} delay
1183     */
1184    _cookieconsent.show = function (delay) {
1185      if (consent_modal_exists) {
1186        setTimeout(
1187          function () {
1188            _addClass(html_dom, "show--consent");
1189
1190            /**
1191             * Update attributes/internal statuses
1192             */
1193            consent_modal.setAttribute("aria-hidden", "false");
1194            consent_modal_visible = true;
1195            last_elem_before_modal = document.activeElement;
1196            current_modal_focusable = consent_modal_focusable;
1197
1198            _log("CookieConsent [MODAL]: show consent_modal");
1199          },
1200          delay > 0 ? delay : 0
1201        );
1202      }
1203    };
1204
1205    /**
1206     * Hide consent modal
1207     */
1208    _cookieconsent.hide = function () {
1209      if (consent_modal_exists) {
1210        _removeClass(html_dom, "show--consent");
1211        consent_modal.setAttribute("aria-hidden", "true");
1212        consent_modal_visible = false;
1213
1214        //restore focus to last page element which had focus before modal opening
1215        last_elem_before_modal.focus();
1216        current_modal_focusable = null;
1217        _log("CookieConsent [MODAL]: hide");
1218      }
1219    };
1220
1221    /**
1222     * Hide settings modal
1223     */
1224    _cookieconsent.hideSettings = function () {
1225      _removeClass(html_dom, "show--settings");
1226      settings_modal_visible = false;
1227      settings_container.setAttribute("aria-hidden", "true");
1228
1229      /**
1230       * If consent modal is visible, focus him (instead of page document)
1231       */
1232      if (consent_modal_visible) {
1233        last_consent_modal_btn_focus.focus();
1234        current_modal_focusable = consent_modal_focusable;
1235      } else {
1236        /**
1237         * Restore focus to last page element which had focus before modal opening
1238         */
1239        last_elem_before_modal.focus();
1240        current_modal_focusable = null;
1241      }
1242
1243      clicked_inside_modal = false;
1244      _log("CookieConsent [SETTINGS]: hide settings_modal");
1245    };
1246
1247    /**
1248     * Set cookie, specifying name, value and expiration time
1249     * @param {String} name
1250     * @param {String} value
1251     * @param {Number} days
1252     */
1253    var _setCookie = function (name, value, days) {
1254      var expires = "";
1255
1256      var date = new Date();
1257      date.setTime(date.getTime() + 1000 * (days * 24 * 60 * 60));
1258      expires = "; expires=" + date.toUTCString();
1259
1260      /**
1261       * Set secure cookie if https found
1262       */
1263      if (location.protocol === "https:") {
1264        document.cookie =
1265          name +
1266          "=" +
1267          (value || "") +
1268          expires +
1269          "; path=/; Domain=" +
1270          window.location.hostname +
1271          "; SameSite=Lax; Secure";
1272      } else {
1273        document.cookie =
1274          name + "=" + (value || "") + expires + "; path=/; SameSite=Lax;";
1275      }
1276
1277      _log(
1278        "CookieConsent [SET_COOKIE]: cookie " +
1279          name +
1280          "='" +
1281          value +
1282          "' was set!"
1283      );
1284    };
1285
1286    /**
1287     * Get cookie value by name,
1288     * returns cookie value if found, otherwise empty string: ""
1289     * @param {String} name
1290     * @returns {String}
1291     */
1292    var _getCookie = function (name) {
1293      return (name = document.cookie.match(
1294        "(^|;)\\s*" + name + "\\s*=\\s*([^;]+)"
1295      ))
1296        ? name.pop()
1297        : "";
1298    };
1299
1300    /**
1301     * Delete cookie by name
1302     * @param {String} name
1303     */
1304    var _eraseCookie = function (name) {
1305      document.cookie =
1306        name +
1307        "=; Path=/; Domain=" +
1308        window.location.hostname +
1309        "; Expires=Thu, 01 Jan 1970 00:00:01 GMT;";
1310      document.cookie =
1311        name +
1312        "=; Path=/; Domain=." +
1313        window.location.hostname +
1314        "; Expires=Thu, 01 Jan 1970 00:00:01 GMT;";
1315    };
1316
1317    /**
1318     * Return true if cookie was found and has valid value (not empty string)
1319     * @param {String} cookie_name
1320     * @returns {Boolean}
1321     */
1322    _cookieconsent.validCookie = function (cookie_name) {
1323      return _getCookie(cookie_name) != "";
1324    };
1325
1326    /**
1327     * Add event listener to dom object (cross browser function)
1328     * @param {Object} elem
1329     * @param {String} event //event type
1330     * @param {Object } fn
1331     * @param {Boolean} passive
1332     */
1333    var _addEvent = function (elem, event, fn, passive) {
1334      var passive = passive || false;
1335
1336      if (elem.addEventListener) {
1337        passive
1338          ? elem.addEventListener(event, fn, { passive: true })
1339          : elem.addEventListener(event, fn, false);
1340      } else {
1341        /**
1342         * For old browser, add 'on' before event:
1343         * 'click':=> 'onclick'
1344         */
1345        elem.attachEvent("on" + event, fn);
1346      }
1347    };
1348
1349    /**
1350     * Get all prop. keys defined inside object
1351     * @param {Object} obj
1352     */
1353    var _getKeys = function (obj) {
1354      if (typeof obj === "object") {
1355        var keys = [],
1356          i = 0;
1357        for (keys[i++] in obj) {
1358        }
1359        return keys;
1360      }
1361    };
1362
1363    /**
1364     * Append class to specified dom element
1365     * @param {Object} elem
1366     * @param {String} classname
1367     */
1368    var _addClass = function (elem, classname) {
1369      if (elem.classList) elem.classList.add(classname);
1370      else {
1371        if (!_hasClass(elem, classname)) elem.className += " " + classname;
1372      }
1373    };
1374
1375    /**
1376     * Remove specified class from dom element
1377     * @param {HTMLElement} elem
1378     * @param {String} classname
1379     */
1380    var _removeClass = function (el, className) {
1381      el.classList
1382        ? el.classList.remove(className)
1383        : (el.className = el.className.replace(
1384            new RegExp("(\\s|^)" + className + "(\\s|$)"),
1385            " "
1386          ));
1387    };
1388
1389    /**
1390     * Check if html element has classname
1391     * @param {HTMLElement} el
1392     * @param {String} className
1393     */
1394    var _hasClass = function (el, className) {
1395      if (el.classList) {
1396        return el.classList.contains(className);
1397      }
1398      return !!el.className.match(
1399        new RegExp("(\\s|^)" + className + "(\\s|$)")
1400      );
1401    };
1402
1403    return _cookieconsent;
1404  };
1405
1406  /**
1407   * Make CookieConsent object accessible globally
1408   */
1409  if (typeof window["initCookieConsent"] !== "function") {
1410    window["initCookieConsent"] = CookieConsent;
1411  }
1412})();

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.