PageSourceSearch

https://vif-selbstbestimmt-leben.de/_scripts/cookieconsent.js

js vif-selbstbestimmt-leben.de collected 2026-10-05 02:23:17 UTC 88,354 bytes, 2,215 lines download raw bytes

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

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.