PageSourceSearch

https://istitutopilati.it/static/js/vendor/cookieconsent.fdc2fb809c59.js

js istitutopilati.it collected 2026-10-08 09:38:38 UTC 87,259 bytes, 2,248 lines download raw bytes

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

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.