PageSourceSearch

https://cc.bibbiaedu.it/js/cookieconsent.js?pipo

js bibbiaedu.it collected 2026-10-01 11:24:47 UTC 91,199 bytes, 2,278 lines download raw bytes

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

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.