PageSourceSearch

https://c.mestskadivadlaprazska.cz/files/cookies/cookieconsent.js

js mestskadivadlaprazska.cz collected 2026-09-24 17:46:41 UTC 90,737 bytes, 2,265 lines download raw bytes

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

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.