PageSourceSearch

https://openorthopaedicsjournal.com/cookie/cookieconsent.js

js openorthopaedicsjournal.com collected 2026-09-25 21:01:39 UTC 88,416 bytes, 2,217 lines download raw bytes

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

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.