PageSourceSearch

https://www.nadanyprvnacek.cz/common7/cookieconsent/src/cookieconsent.js

js nadanyprvnacek.cz collected 2026-10-08 17:33:52 UTC 91,202 bytes, 2,277 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.