source: gs3-installations/thewillow/trunk/sites/thewillow/dlcol-chatgpt/iframe-page/dl-chatgpt_files/cookieconsent.js.download@ 38779

Last change on this file since 38779 was 38779, checked in by davidb, 4 months ago

Files that need to be moved to <gsdl3srchome>/web to provide a static web page example using an iframe to embedd the React/NextJS based chatbot

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