PageSourceSearch

https://edwardsenglish.com/wp-content/themes/salient-child/js/cookieconsent/cookieconsent.js?ver=7.0.5

js edwardsenglish.com collected 2026-09-25 11:09:36 UTC 88,838 bytes, 2,227 lines download raw bytes

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

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.