PageSourceSearch

https://regione.puglia.it/o/portale-istituzionale-theme/js/vendor/cookieconsent.js

js regione.puglia.it collected 2026-09-25 08:48:43 UTC 91,421 bytes, 2,231 lines download raw bytes

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

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.