PageSourceSearch

https://www.consigliochiesemilano.it/wp-content/cdm-plugins/iandu-…/public/js/cookieconsent.js?ver=1.4.1

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

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.