PageSourceSearch

https://abitareoggi.it/ui/common_plugin/cookieconsent.js?v=%7B%7B@no_cache%7D%7D

js abitareoggi.it collected 2026-10-08 15:07:41 UTC 88,662 bytes, 2,214 lines download raw bytes

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

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.