PageSourceSearch

https://wolli.ai/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.js?ver=1741638161

js wolli.ai collected 2026-10-05 15:59:47 UTC 74,300 bytes, 2,317 lines download raw bytes

1'use strict';
2/*
3 * Opt in (e.g. EU):
4 * default all scripts disabled.
5 * cookie banner
6 *
7 * Opt out (e.g. US):
8 * default all scripts enabled
9 * opt out cookie banner
10 *
11 * Other regions:
12 * default all scripts enabled
13 * no banner
14 *
15 *
16 * For examples to edit the behaviour of the banner, please see https://github.com/really-Simple-Plugins/complianz-integrations
17 * */
18
19/*
20 * Create an element
21 * @param el
22 * @param content
23 * @returns {*}
24 */
25function cmplz_create_element(el, content) {
26	let obj = document.createElement(el);
27	obj.innerHtml = content;
28	return obj;
29}
30
31/*
32 * Add an event
33 * @param event
34 * @param selector
35 * @param callback
36 * @param context
37 */
38function cmplz_add_event(event, selector, callback ) {
39	document.addEventListener(event, e => {
40		if ( e.target.closest(selector) ) {
41			callback(e);
42		}
43	});
44}
45
46/*
47 * Check if the element is hidden
48 * @param el
49 * @returns {boolean}
50 */
51function cmplz_is_hidden(el) {
52	return (el.offsetParent === null)
53}
54
55function cmplz_html_decode(input) {
56	let doc = new DOMParser().parseFromString(input, "text/html");
57	return doc.documentElement.textContent;
58}
59/**
60 * Consent Area management for shortcode and block editor
61 */
62document.querySelectorAll('.cmplz-consent-area.cmplz-placeholder').forEach(cmplzConsentArea => {
63	cmplzConsentArea.addEventListener('click', e => {
64		let container = e.target;
65		if ( !container.classList.contains('cmplz-consent-area') ) {
66			container = e.target.closest('.cmplz-consent-area.cmplz-placeholder');
67		}
68		if (container) {
69			let consentedService = container.getAttribute('data-service');
70			cmplz_set_service_consent(consentedService, true);
71			cmplzLoadConsentAreaContent(false, consentedService);
72			cmplz_enable_category(null, consentedService);
73			cmplz_set_banner_status('dismissed');
74		}
75
76	});
77
78	document.addEventListener("cmplz_enable_category", function(consentData) {
79		let consentedCategory = consentData.detail.category;
80		let consentedService = consentData.detail.service;
81		cmplzLoadConsentAreaContent(consentedCategory, consentedService)
82	});
83});
84
85function cmplzLoadConsentAreaContent(consentedCategory, consentedService){
86	document.querySelectorAll('.cmplz-consent-area.cmplz-placeholder').forEach(obj => {
87		let category = obj.getAttribute('data-category');
88		let service = obj.getAttribute('data-service');
89		let postId = obj.getAttribute('data-post_id');
90		let blockId = obj.getAttribute('data-block_id');
91		if ( consentedCategory === category || consentedService === service ) {
92			//if not stored yet, load. As features in the user object can be changed on updates, we also check for the version
93			let request = new XMLHttpRequest();
94			request.open('GET', complianz.url+'consent-area/'+postId+'/'+blockId, true);
95			request.setRequestHeader('Content-type', 'application/json');
96			request.send();
97			obj.classList.remove('cmplz-placeholder');
98			request.onload = function() {
99				obj.innerHTML = JSON.parse(request.response);
100				//search for script elements and execute them
101				obj.querySelectorAll('script').forEach(scriptEl => {
102					cmplz_run_script(scriptEl.innerHTML, category, service, 'inline', scriptEl);
103				})
104			};
105		}
106	});
107}
108
109/*
110 * If an anchor is passed for an element which may load only after an ajax call, make sure it will scroll into view.
111 */
112document.addEventListener('cmplz_manage_consent_container_loaded', function(e){
113	let url = window.location.href;
114	if ( url.indexOf('#') != -1 ) {
115		let end_pos = url.lastIndexOf("?") != -1 ? url.lastIndexOf("?") : undefined;
116		let anchor = url.substring(url.indexOf("#") + 1, end_pos);
117		const element = document.getElementById(anchor);
118		if (element) {
119			const y = element.getBoundingClientRect().top + window.pageYOffset - 200;
120			window.scrollTo({top: y, behavior: 'smooth'});
121		}
122	}
123});
124
125/*
126 * prevent caching of the WP Rest API by varnish or other caching tools
127 */
128complianz.locale = complianz.locale + '&token='+Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
129
130/*
131 * CustomEvent() polyfill
132 * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
133 */
134(function () {
135	if (typeof window.CustomEvent === 'function') return false;
136	function CustomEvent(event, params) {
137		params = params || { bubbles: false, cancelable: false, detail: undefined };
138		let evt = document.createEvent('CustomEvent');
139		evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
140		return evt;
141	}
142	CustomEvent.prototype = window.Event.prototype;
143	window.CustomEvent = CustomEvent;
144})();
145
146var cmplz_banner;//look this one up when the cookiebanner loads.
147var cmplz_banner_container = document.getElementById('cmplz-cookiebanner-container');
148var cmplz_manage_consent_button;
149var cmplz_waiting_inline_scripts = [];
150var cmplz_waiting_scripts = [];
151var cmplz_fired_scripts = [];
152var cmplz_placeholder_class_index = 0;
153var cmplz_all_scripts_hook_fired = false;
154var cmplz_consent_stored_once = false;
155var cmplz_fired_category_events = ['functional'];
156var cmplz_fired_service_events = [];
157var cmplz_categories = [
158	'functional',
159	'preferences',
160	'statistics',
161	'marketing',
162];
163
164/*
165 * Get a cookie by name
166 * @param name
167 * @returns {string}
168 */
169
170window.cmplz_get_cookie = function (name) {
171	if (typeof document === "undefined") {
172		return "";
173	}
174	name = complianz.prefix + name;
175	const cookies = document.cookie.split(';');
176	for ( let i = 0; i < cookies.length; i++ ) {
177		const cookie = cookies[i].trim();
178		if ( cookie.startsWith(name + '=') ) {
179			return cookie.substring(name.length + 1);
180		}
181	}
182	return "";
183};
184
185/*
186 * set a cookie
187 * @param name
188 * @param value
189 * @param use_prefix
190 */
191
192window.cmplz_set_cookie = function(name, value, use_prefix) {
193	if (typeof document === 'undefined') {
194		return;
195	}
196
197	use_prefix = typeof use_prefix !== 'undefined' ? use_prefix : true;
198	const secure = window.location.protocol === "https:" ? ";secure" : '';
199	const date = new Date();
200	date.setTime(date.getTime() + (complianz.cookie_expiry * 24 * 60 * 60 * 1000));
201	const expires = ";expires=" + date.toGMTString();
202
203	const domain = cmplz_get_cookie_domain();
204	const domainString = domain.length > 0 ? `;domain=${domain}` : '';
205
206	const prefix = use_prefix ? complianz.prefix : '';
207	const cookiePath = cmplz_get_cookie_path();
208
209	document.cookie = `${prefix}${name}=${value};SameSite=Lax${secure}${expires}${domainString};path=${cookiePath}`;
210}
211
212/*
213 * Check if needle occurs in the haystack
214 * @param needle
215 * @param haystack
216 * @returns {boolean}
217 */
218window.cmplz_in_array = function(needle, haystack) {
219	return haystack.includes(needle);
220}
221
222/*
223 * Retrieve the highest level of consent that has been given
224 *
225 * */
226
227window.cmplz_highest_accepted_category = function() {
228	const consentedCategories = cmplz_accepted_categories();
229	const priorityCategories = ['marketing', 'statistics', 'preferences'];
230
231	for (let i = 0; i < priorityCategories.length; i++) {
232		if (cmplz_in_array(priorityCategories[i], consentedCategories)) {
233			return priorityCategories[i];
234		}
235	}
236	return 'functional';
237}
238
239/*
240 * Sets all accepted categories as class in body
241 */
242
243const cmplz_set_category_as_body_class = () => {
244	const classList = document.body.classList;
245	for (let i = classList.length - 1; i >= 0; i--) {
246		if (classList[i].startsWith('cmplz-') && classList[i] !== 'cmplz-document') {
247			classList.remove(classList[i]);
248		}
249	}
250	const cats = cmplz_accepted_categories();
251	Object.values(cats).forEach(category => {
252		if (typeof category === 'string') {
253			classList.add('cmplz-' + category);
254		}
255	});
256	const services = cmplz_get_all_service_consents();
257	Object.entries(services).forEach(([service, consent]) => {
258		if (consent) {
259			classList.add('cmplz-' + service);
260		}
261	});
262
263	classList.add('cmplz-' + complianz.region, 'cmplz-' + complianz.consenttype);
264	const event = new CustomEvent('cmplz_set_category_as_bodyclass');
265	document.dispatchEvent(event);
266}
267
268const cmplz_append_css = (css) => {
269	const head = document.head || document.getElementsByTagName('head')[0];
270	const style = document.createElement('style');
271	style.setAttribute('type', 'text/css');
272	style.appendChild(document.createTextNode(css));
273	head.appendChild(style);
274}
275
276const cmplz_load_css = (path) => {
277	const head = document.head || document.getElementsByTagName("head")[0];
278	const targetObj = document.createElement("link");
279	targetObj.rel = "stylesheet";
280	targetObj.type = "text/css";
281	targetObj.href = path;
282	head.appendChild(targetObj);
283}
284
285/*
286 * Run script, src or inline
287 * @param script //src or inline script
288 * @param category
289 * @param type
290 */
291
292function cmplz_run_script( script, category, service, type, sourceObj ) {
293	let targetObj = document.createElement("script");
294	if ( type !== 'inline' ) {
295		targetObj.src = script;
296	} else {
297		if (typeof script !== 'string') {
298			script = script.innerHTML;
299		}
300		targetObj.innerHTML = [script, 'cmplzScriptLoaded();'].join('\n');
301	}
302
303	//check if already fired
304	if ( cmplz_in_array( script, cmplz_fired_scripts) ) {
305		return;
306	}
307
308	cmplzCopyAttributes(sourceObj, targetObj);
309	try {
310		if (type!=='inline') {
311			targetObj.onload = function () {
312				cmplz_run_after_all_scripts(category, service);
313				cmplz_maybe_run_waiting_scripts(script, category, service, sourceObj);
314			}
315		} else {
316			window.cmplzScriptLoaded = function() {
317				cmplz_run_after_all_scripts(category, service);
318				cmplz_maybe_run_waiting_scripts(script, category, service, sourceObj);
319			}
320		}
321		document.head.appendChild(targetObj);
322
323	} catch(exception) {
324		//only runs in case of error
325		cmplz_run_after_all_scripts(category, service);
326		throw "Something went wrong " + exception + " while loading "+script;
327	}
328}
329
330/*
331 * Check if there are waiting scripts, and if so, run them.
332 * @param script //src or inline script
333 * @param category
334 */
335
336function cmplz_maybe_run_waiting_scripts( script, category, service, sourceObj ){
337	let waitingScript = cmplz_get_waiting_script(cmplz_waiting_scripts, script);
338	if ( waitingScript ) {
339		cmplz_run_script( waitingScript, category, service, 'src', sourceObj );
340	}
341
342	let waiting_inline_script = cmplz_get_waiting_script(cmplz_waiting_inline_scripts, script);
343	if (waiting_inline_script) {
344		cmplz_run_script(waiting_inline_script, category, service, 'inline', sourceObj);
345	}
346}
347
348const cmplzLazyLoader = () => {
349	// Get all elements with the "lazy-load" class
350	const cmplzLazyLoadElements = document.querySelectorAll('.cmplz-blocked-content-container');
351
352	// Create an Intersection Observer instance
353	const cmplzObserver = new IntersectionObserver((entries, observer) => {
354		entries.forEach((entry) => {
355			if (entry.isIntersecting) {
356				// When the element is in view, load the background image
357				const element = entry.target;
358				let src = element.getAttribute('data-placeholder-image');
359				if ( src ) {
360					const index = element.getAttribute('data-placeholder_class_index');
361					cmplz_append_css('.cmplz-placeholder-' + index + ' {background-image: url(' + src + ') !important;}');
362					cmplz_set_blocked_content_container_aspect_ratio(element, src, index);
363				}
364
365				// Stop observing the element
366				observer.unobserve(element);
367			}
368
369		});
370	});
371
372	// Start observing each lazy-load element
373	cmplzLazyLoadElements.forEach((element) => {
374		cmplzObserver.observe(element);
375	});
376}
377
378/*
379 * Set placeholder image as background on the parent div, set notice, and handle height.
380 *
381 * */
382
383function cmplz_set_blocked_content_container() {
384	//to prevent this function to twice run on an element, we add an attribute to each element that has been processed.
385	//then skip elements with that element.
386	document.querySelectorAll('.cmplz-image').forEach(obj => {
387		if ( obj.classList.contains('cmplz-processed') ) {
388			return;
389		}
390		obj.classList.add('cmplz-processed' );
391		let service = obj.getAttribute('data-service');
392		let category = obj.getAttribute('data-category');
393		let blocked_image_container = obj.parentElement;
394		blocked_image_container.classList.add('cmplz-blocked-content-container');
395		let curIndex = blocked_image_container.getAttribute('data-placeholder_class_index');
396		//handle browser native lazy load feature
397		if (obj.getAttribute('loading') === 'lazy' ) {
398			obj.removeAttribute('loading');
399			obj.setAttribute('data-deferlazy', 1);
400		}
401
402		if ( curIndex == null ) {
403			cmplz_placeholder_class_index++;
404			blocked_image_container.classList.add('cmplz-placeholder-' + cmplz_placeholder_class_index, 'cmplz-blocked-content-container');
405			blocked_image_container.setAttribute('data-placeholder_class_index', cmplz_placeholder_class_index);
406			cmplz_insert_placeholder_text(blocked_image_container, category, service);
407		}
408	});
409
410	document.querySelectorAll('.cmplz-placeholder-element').forEach(obj => {
411		if ( obj.classList.contains('cmplz-processed') ) {
412			return;
413		}
414		obj.classList.add('cmplz-processed' );
415		let service = obj.getAttribute('data-service');
416		let category = obj.getAttribute('data-category');
417
418		//we set this element as container with placeholder image
419		let blocked_content_container;
420		if ( obj.classList.contains('cmplz-iframe')) {
421			//handle browser native lazy load feature
422			if ( obj.getAttribute('loading') === 'lazy' ) {
423				obj.removeAttribute('loading');
424				obj.setAttribute('data-deferlazy', 1);
425			}
426			blocked_content_container = obj.parentElement;
427		} else {
428			blocked_content_container = obj;
429		}
430		let curIndex = blocked_content_container.getAttribute('data-placeholder_class_index');
431		//if the blocked content container class is already added, don't add it again
432		if ( curIndex === null ) {
433			cmplz_placeholder_class_index++;
434			blocked_content_container.classList.add('cmplz-placeholder-' + cmplz_placeholder_class_index, 'cmplz-blocked-content-container');
435			blocked_content_container.setAttribute('data-placeholder_class_index', cmplz_placeholder_class_index);
436			cmplz_insert_placeholder_text(blocked_content_container, category, service);
437			//handle image size for video
438			let src = obj.getAttribute('data-placeholder-image');
439			if (src && typeof src !== 'undefined' && src.length ) {
440				//move src to parent, if needed.
441				blocked_content_container.setAttribute('data-placeholder-image', src);
442			}
443		}
444	});
445
446	cmplzLazyLoader();
447
448	/*
449	 * In some cases, like ajax loaded content, the placeholders are initialized again. In that case, the scripts may need to be fired again as well.
450	 * In case of an opt-out region and Do Not Track, a consent check will return 'allow', because it doesn't take DNT into account
451	 */
452
453	if ( cmplz_has_consent('statistics') ) {
454		cmplz_enable_category('statistics');
455	}
456
457	if ( cmplz_has_consent('marketing') ) {
458		cmplz_enable_category('marketing');
459	}
460}
461
462function cmplz_insert_placeholder_text(container, category, service ){
463	if ( !container.querySelector( ".cmplz-blocked-content-notice" ) ) {
464		let placeholder_text = complianz.placeholdertext;
465		category = category || 'marketing';
466		let body;
467		if ( typeof placeholder_text !== 'undefined' ) {
468			if ( complianz.clean_cookies == 1 ) {
469				//make service human readable
470				let service_nicename = service ? service.replace('-', ' ') : '';
471				service_nicename = service_nicename.charAt(0).toUpperCase() + service_nicename.slice(1);
472				placeholder_text = placeholder_text.replace('{service}', service_nicename);
473				body = cmplz_create_element('div', placeholder_text);
474				body.innerHTML = placeholder_text;
475				body.classList.add('cmplz-blocked-content-notice');
476				let btn = body.querySelector('button');
477				btn.setAttribute('data-service', service);
478				btn.setAttribute('data-category', category);
479				btn.setAttribute( 'aria-label', complianz.aria_label.replace('{service}', service_nicename) );
480				let pageLinks = complianz.page_links[complianz.region];
481				let link = body.querySelector('.cmplz-links a');
482				if ( pageLinks && pageLinks.hasOwnProperty('cookie-statement') ) {
483					link.setAttribute('href', pageLinks['cookie-statement']['url']);
484					if (link.innerText === '{title}') {
485						link.innerText = pageLinks['cookie-statement']['title'];
486					}
487				}
488			} else {
489				let btn = cmplz_create_element('button', '');
490				let category_nicename = complianz.categories.hasOwnProperty(category) ? complianz.categories[category] : 'marketing';
491				btn.innerText = placeholder_text.replace('{category}', category_nicename);
492				btn.classList.add('cmplz-blocked-content-notice', 'cmplz-accept-category', 'cmplz-accept-'+category); //'cmplz-accept-'+category is deprecated
493
494				btn.setAttribute('data-service', service );
495				btn.setAttribute('data-category', category );
496				btn.setAttribute( 'aria-label', complianz.aria_label.replace('{category}', category) );
497				body=btn;
498			}
499
500			if ( container.tagName!=='VIDEO' ) {
501				container.appendChild(body);
502			} else{
503				container.parentElement.appendChild(body);
504			}
505
506		}
507	}
508}
509
510/*
511 * Set the height of an image relative to the width, depending on the image widht/height aspect ratio.
512 *
513 *
514 * */
515
516function cmplz_set_blocked_content_container_aspect_ratio(container, src, placeholder_class_index) {
517	if (container == null) return;
518
519	// Handle image size for video
520	let img = new Image();
521	img.addEventListener("load", function() {
522		let imgWidth = this.naturalWidth || 1;
523		let imgHeight = this.naturalHeight;
524
525		let w = container.clientWidth;
526		let h = imgHeight * (w / imgWidth);
527
528		let heightCSS = src.indexOf('placeholder.jpg') === -1 ? 'height:' + h + 'px;' : '';
529		cmplz_append_css('.cmplz-placeholder-' + placeholder_class_index + ' {' + heightCSS + '}');
530	});
531	img.src = src;
532}
533
534/*
535 * Keep window aspect ratio in sync when window resizes
536 * To lower the number of times this code is executed, it is done with a timeout.
537 *
538 * */
539
540var cmplzResizeTimer;
541window.addEventListener('resize', function(event) {
542	clearTimeout(cmplzResizeTimer);
543	cmplzResizeTimer = setTimeout( cmplz_set_blocked_content_container, 500);
544}, true);
545
546/*
547 * 	we run this function also on an interval, because with ajax loaded content, the placeholders would otherwise not be handled.
548 */
549if ( complianz.block_ajax_content == 1 ) {
550	setInterval(function () {
551		cmplz_set_blocked_content_container();
552	}, 2000);
553}
554
555/*
556 * Check if there are any blocked scripts on the page
557 * @returns {boolean}
558 */
559function cmplz_has_blocked_scripts(){
560	let scriptElements = document.querySelectorAll('script[data-category], script[data-service]');
561	return scriptElements.length>0;
562}
563/*
564 * Enable scripts that were blocked
565 *
566 * */
567
568function cmplz_enable_category(category, service) {
569	if ( complianz.tm_categories == 1 && category !== '') {
570		cmplz_run_tm_event(category);
571	}
572
573	let details = {};
574	details.category = category;
575	details.categories = cmplz_accepted_categories();
576	details.region = complianz.region;
577	let event = new CustomEvent('cmplz_before_category', { detail: details });
578	document.dispatchEvent(event);
579
580	service = typeof service !== 'undefined' ? service : 'do_not_match';
581	if ( category === '' ) category = 'do_not_match';
582
583	if ( category === 'functional' ) {
584		return;
585	}
586	//enable cookies for integrations
587	if ( category === 'marketing' ) {
588		cmplz_set_integrations_cookies();
589	}
590
591	//remove accept cookie notice overlay
592	let selector;
593	if (service !== 'do_not_match') {
594		selector = '.cmplz-blocked-content-notice [data-service="'+service+'"]';
595	} else {
596		selector = complianz.clean_cookies!=1 ? '.cmplz-blocked-content-notice.cmplz-accept-'+category : '.cmplz-blocked-content-notice [data-category="'+category+'"]';
597	}
598	document.querySelectorAll(selector).forEach(obj => {
599		let blockedElementService = obj.getAttribute('data-service');
600		if (obj.parentNode.classList.contains('cmplz-blocked-content-notice')) {
601			obj = obj.parentNode;
602		}
603		//only remove if the service is not explicitly denied
604		if ( !cmplz_is_service_denied(blockedElementService) ) {
605			obj.parentNode.removeChild(obj);
606		}
607	});
608
609	document.querySelectorAll('[data-category="'+category+'"], [data-service="'+service+'"]').forEach(obj => {
610		//if a category is activated, but this specific service is denied, skip.
611		let elementService = obj.getAttribute('data-service');
612		if ( cmplz_is_service_denied(elementService) ) {
613			return;
614		}
615
616		//if native class is included, it isn't blocked, so will have run already
617		if ( obj.getAttribute('data-category') === 'functional' ) {
618			return;
619		}
620
621		if ( obj.classList.contains('cmplz-activated') ) {
622			return;
623		}
624
625		let tagName = obj.tagName;
626		if (tagName==='LINK'){
627			obj.classList.add('cmplz-activated' );
628			let src = obj.getAttribute('data-href');
629			cmplz_load_css( src, category);
630		} else if (tagName ==='IMG'){
631			obj.classList.add('cmplz-activated' );
632			let src = obj.getAttribute('data-src-cmplz');
633			obj.setAttribute('src', src);
634			//handle browser native lazy load feature
635			if ( obj.getAttribute('data-deferlazy') ) {
636				obj.setAttribute('loading', 'lazy');
637			}
638			cmplz_remove_placeholder(obj);
639		}
640		else if (tagName==='IFRAME'){
641			obj.classList.add('cmplz-activated' );
642			let src = obj.getAttribute('data-src-cmplz');
643			let srcAttribute = obj.getAttribute('data-cmplz-target') ? obj.getAttribute('data-cmplz-target') : 'src';
644
645			//check if there's an autoplay value we need to pass on, if it's added later on by javascript
646			let autoplay = cmplz_get_url_parameter(obj.getAttribute(srcAttribute), 'autoplay');
647			if ( autoplay === '1' ) src = src + '&autoplay=1';
648			//handle browser native lazy load feature
649			if ( obj.getAttribute('data-deferlazy') ) {
650				obj.setAttribute('loading', 'lazy');
651			}
652
653			obj.addEventListener('load', () => {
654				cmplz_remove_placeholder(obj);
655			});
656			obj.setAttribute(srcAttribute, src);
657		} else if (obj.classList.contains('cmplz-placeholder-element')) {
658			obj.classList.add('cmplz-activated' );
659			//other services, no iframe, with placeholders
660			//remove the added classes
661			let cssIndex = obj.getAttribute('data-placeholder_class_index');
662			obj.classList.remove('cmplz-blocked-content-container', 'cmplz-placeholder-' + cssIndex);
663		}
664	});
665
666	/*
667	 * Let's activate the scripts
668	 */
669
670	//create list of waiting scripts
671	let scriptElements = document.querySelectorAll('script[data-category="'+category+'"], script[data-service="'+service+'"]');
672	scriptElements.forEach(obj => {
673		let waitfor = obj.getAttribute('data-waitfor');
674		let src = obj.getAttribute('data-cmplz-src');
675		if ( waitfor ) {
676			if ( src ) {
677				cmplz_waiting_scripts[waitfor] = src;
678			} else if ( obj.innerText.length > 0 ) {
679				cmplz_waiting_inline_scripts[waitfor] = obj;
680			}
681		}
682
683		//cleanup after adding it to our waiting or scriptElements list.
684		if (obj.parentElement) {
685			obj.parentElement.removeChild(obj);
686		}
687	});
688
689	//scripts: remove text/plain, and move data-src attribute to src attribute
690	scriptElements.forEach(obj => {
691		//we don't want already activate scripts to fire, but also we don't want scripts that weren't blocked to fire. Hence the check for type
692		let script_mime_type = obj.getAttribute('type');
693		if ( obj.classList.contains('cmplz-activated') || ( !script_mime_type || script_mime_type==='text/javascript' ) ) {
694			return;
695		}
696		obj.classList.add('cmplz-activated' );
697		let src = obj.getAttribute('data-cmplz-src');
698		if ( src ) {
699			obj.removeAttribute('type');
700			//check if this src or txt is in a waiting script. If so, skip.
701			if ( cmplz_is_waiting_script(cmplz_waiting_scripts, src) ) {
702				return;
703			}
704
705			if ( obj.getAttribute('data-post_scribe_id') ) {
706				let psID = '#' + obj.getAttribute('data-post_scribe_id');
707				let postScribeObj = document.querySelector(psID);
708				if ( postScribeObj ) {
709					postScribeObj.innerHtml('');
710					postscribe(psID, '<script src=' + src + '></script>');
711				}
712			} else {
713				cmplz_run_script(src, category, service, 'src', obj );
714			}
715
716		} else if (obj.innerText.length > 0 ) {
717			//check if this src or txt is in a waiting script. If so, skip.
718			if (cmplz_is_waiting_script(cmplz_waiting_inline_scripts, obj.innerText )) {
719				return;
720			}
721
722			cmplz_run_script( obj.innerText, category, service, 'inline', obj );
723		}
724	});
725
726	// if there are no blockable scripts at all, we still want to provide a hook
727	// in most cases, there are blocked scripts, and run_after_all_scripts hook won't be fired yet. In that
728	// case only the category event is fired (if activated), and this function will run
729	// again from the script activation callbacks.
730	cmplz_run_after_all_scripts(category, service);
731}
732
733/*
734 * remove added classes from the blocked content container
735 *
736 * @param obj
737 */
738function cmplz_remove_placeholder(obj) {
739	let blocked_content_container = obj.closest('.cmplz-blocked-content-container');
740	if (blocked_content_container) {
741		let cssIndex = blocked_content_container.getAttribute('data-placeholder_class_index');
742		blocked_content_container.classList.remove('cmplz-blocked-content-container', 'cmplz-placeholder-' + cssIndex);
743	}
744	obj.classList.remove('cmplz-iframe-styles', 'cmplz-iframe', 'video-wrap');
745}
746
747/*
748 * check if the passed source has a waiting script that should be executed, and return it if so.
749 * @param waiting_scripts
750 * @param src
751 * @returns {*}
752 */
753
754function cmplz_get_waiting_script( waiting_scripts, src ) {
755	for ( let waitfor in waiting_scripts ) {
756		if ( waiting_scripts.hasOwnProperty(waitfor)) {
757			if ( src.indexOf(waitfor) !== -1 ) {
758				let output = waiting_scripts[waitfor];
759				delete waiting_scripts[waitfor];
760				return output;
761			}
762		}
763	}
764	return false;
765}
766
767/*
768 * Because we need a key=>value array in javascript, the .length check for an empty array doesn't work.
769 * @param arr
770 * @returns {boolean}
771 */
772
773function cmplz_array_is_empty(arr) {
774	for (let key in arr) {
775		if (arr.hasOwnProperty(key)) {
776			return false;
777		}
778	}
779	return true;
780}
781
782/*
783 * Check if the passed src or script is waiting for another script and should not execute
784 * @param waiting_scripts
785 * @param srcOrScript
786 */
787
788function cmplz_is_waiting_script(waiting_scripts, srcOrScript) {
789	for (let waitfor in waiting_scripts) {
790		if ( waiting_scripts.hasOwnProperty(waitfor) ) {
791			let waitingScript = waiting_scripts[waitfor];
792			if (typeof waitingScript !== 'string') waitingScript = waitingScript.innerText;
793
794			if (srcOrScript.indexOf(waitingScript) !== -1 || waitingScript.indexOf(srcOrScript) !== -1) {
795				return true;
796			}
797		}
798	}
799	return false;
800}
801
802/*
803 * if all scripts have been executed, fire a hook.
804 */
805
806function cmplz_run_after_all_scripts(category, service) {
807	//fire an event so custom scripts can hook into this.
808	let enableService = service!=='do_not_match' && !cmplz_in_array(service, cmplz_fired_service_events );
809	let enableCategory = category!=='do_not_match' && !cmplz_in_array( category, cmplz_fired_category_events );
810	if ( enableCategory ||  enableService ) {
811		if (enableCategory) {
812			cmplz_fired_category_events.push(category);
813		}
814		if (enableService) {
815			cmplz_fired_service_events.push(service);
816		}
817		let details = {};
818		details.category = category;
819		details.service = service;
820		details.categories = cmplz_accepted_categories();
821		details.services = cmplz_get_all_service_consents();
822		details.region = complianz.region;
823
824		let event = new CustomEvent('cmplz_enable_category', { detail: details });
825		document.dispatchEvent(event);
826	}
827
828	if ( !cmplz_all_scripts_hook_fired && cmplz_array_is_empty(cmplz_waiting_inline_scripts) && cmplz_array_is_empty(cmplz_waiting_scripts) ) {
829		let event = new CustomEvent('cmplz_run_after_all_scripts', { detail: category, service:service});
830		document.dispatchEvent(event);
831		cmplz_all_scripts_hook_fired = true;
832	}
833}
834
835/*
836 * Fire an event in Tag Manager
837 *
838 *
839 * */
840
841var cmplz_fired_events = [];
842function cmplz_run_tm_event(category) {
843	if (cmplz_fired_events.indexOf(category) === -1) {
844		cmplz_fired_events.push(category);
845		window.dataLayer = window.dataLayer || [];
846		window.dataLayer.push({
847			'event': 'cmplz_event_'+category
848		});
849		let event = new CustomEvent('cmplz_tag_manager_event', { detail: category });
850		document.dispatchEvent(event);
851	}
852}
853
854/*
855 * Accept all categories
856 */
857window.cmplz_accept_all = function(){
858	cmplz_clear_all_service_consents();
859	cmplz_fire_before_categories_consent(cmplz_categories);
860
861	for (let key in cmplz_categories) {
862		if ( cmplz_categories.hasOwnProperty(key) ) {
863			cmplz_set_consent(cmplz_categories[key], 'allow');
864		}
865	}
866	cmplz_sync_category_checkboxes();
867}
868
869function cmplz_fire_before_categories_consent(categories){
870	let details = {};
871	details.categories = categories;
872	details.region = complianz.region;
873	let event = new CustomEvent('cmplz_before_categories_consent', { detail: details });
874	document.dispatchEvent(event);
875}
876
877/*
878 * Deny all categories, and reload if needed.
879 */
880window.cmplz_deny_all = function(){
881	for (let key in cmplz_categories) {
882		if ( cmplz_categories.hasOwnProperty(key) ) {
883			cmplz_set_consent(cmplz_categories[key], 'deny');
884		}
885	}
886	let consentLevel = cmplz_highest_accepted_category();
887	let reload = false;
888
889	if (consentLevel !== 'functional' || cmplz_exists_service_consent() ) {
890		reload = true;
891	}
892	if ( cmplz_clear_cookies('cmplz_service') ) {
893		reload = true;
894	}
895
896	//has to be after the check if should be reloaded, otherwise that check won't work.
897	cmplz_clear_all_service_consents();
898	cmplz_integrations_revoke();
899	cmplz_fire_categories_event();
900	cmplz_track_status();
901
902	let event = new CustomEvent('cmplz_revoke', { detail: reload });
903	document.dispatchEvent(event);
904
905	//we need to let the iab extension handle the reload, otherwise the consent revoke might not be ready yet.
906	if ( !complianz.tcf_active && reload ) {
907		cmplz_reload_browser_compatible();
908	}
909}
910
911/*
912 * If current cookie policy has changed, reset cookie consent
913 *
914 * */
915
916function cmplz_check_cookie_policy_id() {
917	let user_policy_id = cmplz_get_cookie('policy_id');
918	if (user_policy_id && (parseInt(complianz.current_policy_id) !== parseInt(user_policy_id) ) ) {
919		cmplz_deny_all();
920		cmplz_set_banner_status('show');
921		cmplz_clear_cookies('cmplz');
922	}
923}
924
925window.conditionally_show_banner = function() {
926	//merge userdata with complianz data, in case a b testing is used with user specific cookie banner data
927	//objects are merged so user_data will override data in complianz object
928	complianz = cmplz_merge_object( complianz, cmplz_user_data );
929	//check if we need to redirect to another legal document, for a specific region
930	cmplz_maybe_auto_redirect();
931	cmplz_set_blocked_content_container();
932
933	/*
934	 * Integration with WordPress, tell what kind of consent type we're using, then fire an event
935	 */
936
937	window.wp_consent_type = complianz.consenttype;
938	let event = new CustomEvent('wp_consent_type_defined');
939	document.dispatchEvent( event );
940	event = new CustomEvent('cmplz_before_cookiebanner' );
941	document.dispatchEvent(event);
942	if ( complianz.forceEnableStats == 1 && complianz.consenttype === 'optin' ) {
943		cmplz_set_consent('statistics', 'allow');
944	}
945	let rev_cats = cmplz_categories.reverse();
946	let consented_categories = [];
947	for (let key in rev_cats) {
948		if ( rev_cats.hasOwnProperty(key) ) {
949			let category = cmplz_categories[key];
950			if ( cmplz_has_consent(category) ) {
951				consented_categories.push(category);
952			}
953		}
954	}
955	cmplz_fire_before_categories_consent(consented_categories);
956	for (let key in consented_categories) {
957		if ( rev_cats.hasOwnProperty(key) ) {
958			let category = consented_categories[key];
959			cmplz_enable_category(category);
960		}
961	}
962
963	if ( cmplz_exists_service_consent() ) {
964		//if any service is enabled, allow the general services also, because some services are partially 'general'
965		cmplz_enable_category('', 'general');
966		let services = cmplz_get_services_on_page();
967		for (let key in services) {
968			if ( services.hasOwnProperty(key) ) {
969				let service = services[key].service;
970				let category = services[key].category;
971				if ( cmplz_has_service_consent( service, category )) {
972					document.querySelectorAll('.cmplz-accept-service[data-service="' + service + '"]').forEach(obj => {
973						obj.checked = true;
974					});
975					cmplz_enable_category('', service);
976				}
977			}
978		}
979	}
980
981	cmplz_sync_category_checkboxes();
982	cmplz_integrations_init();
983	cmplz_check_cookie_policy_id();
984	cmplz_set_up_auto_dismiss();
985
986	//if we're on the cookie policy page, we dynamically load the applicable revoke checkbox
987	cmplz_load_manage_consent_container();
988
989	event = new CustomEvent('cmplz_cookie_banner_data', { detail: complianz });
990	document.dispatchEvent(event);
991
992	//if no categories were saved before, we do it now
993	if ( cmplz_get_cookie('saved_categories') === '' ) {
994		//for Non optin/optout visitors, and DNT users, we just track the no-warning option
995		if ( complianz.consenttype !== 'optin' && complianz.consenttype !== 'optout' ) {
996			cmplz_track_status( 'no_warning' );
997		} else if ( cmplz_do_not_track() ) {
998			cmplz_track_status('do_not_track' );
999		}
1000	}
1001
1002	cmplz_set_category_as_body_class();
1003	//fire cats event, but do not fire a track, as we do this on exit.
1004	cmplz_fire_categories_event();
1005	if ( !cmplz_do_not_track() ) {
1006		if (complianz.consenttype === 'optin') {
1007			if (complianz.forceEnableStats) {
1008				cmplz_enable_category('statistics');
1009			}
1010			console.log('opt-in');
1011			show_cookie_banner();
1012		} else if (complianz.consenttype === 'optout') {
1013			console.log('opt-out');
1014			show_cookie_banner();
1015		} else {
1016			console.log('other consent type, no cookie warning');
1017			//on other consent types, all scripts are enabled by default.
1018			cmplz_accept_all();
1019		}
1020	} else {
1021		console.log('global privacy control or do not track detected: no banner.');
1022		cmplz_track_status( 'do_not_track' );
1023	}
1024}
1025
1026/**
1027 * Check if User has set either GPC or DNT in the browser.
1028 * @returns {boolean}
1029 */
1030function cmplz_do_not_track(){
1031	let dnt = 'doNotTrack' in navigator && navigator.doNotTrack === '1';
1032	let gpc = 'globalPrivacyControl' in navigator && navigator.globalPrivacyControl;
1033	return !!(complianz.do_not_track_enabled && (gpc || dnt));
1034}
1035
1036/*
1037 * Get list of services active on the page
1038 * @returns {*[]}
1039 */
1040function cmplz_get_services_on_page(){
1041	let services=[];
1042	document.querySelectorAll('[data-service]').forEach(obj => {
1043		let service = obj.getAttribute('data-service');
1044		let category = obj.getAttribute('data-category');
1045		if ( services.indexOf(service)==-1 ) {
1046			services.push({
1047				'category': category,
1048				'service':service,
1049			});
1050		}
1051	});
1052	return services;
1053}
1054
1055/*
1056 * Run the actual cookie warning
1057 *
1058 * */
1059
1060window.show_cookie_banner = function () {
1061	let disableCookiebanner = complianz.disable_cookiebanner || cmplz_is_speedbot();
1062	//do not show banner when manage consent area on cookie policy is visible
1063	//when users use only the shortcode, the manage consent container is not active, but the dropdown cookie policy class is.
1064	//when the complianz shortcode is used, the dropdown cookie policy class is loaded late because it's loaded with javascript.
1065	let tmpDismissCookiebanner = false;
1066	if ( document.querySelector('#cmplz-manage-consent-container') || document.querySelector('.cmplz-dropdown-cookiepolicy') ) {
1067		tmpDismissCookiebanner = true;
1068	}
1069
1070	const container = document.getElementById('cmplz-cookiebanner-container');
1071	if (container) {
1072		document.body.prepend(container);
1073	}
1074
1075	let link = document.createElement("link");
1076	let pageLinks = complianz.page_links[complianz.region];
1077	//get correct banner, based on banner_id
1078	cmplz_banner = document.querySelector('.cmplz-cookiebanner.banner-'+complianz.user_banner_id+'.'+complianz.consenttype);
1079	if ( !cmplz_banner ) {
1080		disableCookiebanner = true;
1081	}
1082	cmplz_manage_consent_button = document.querySelector('#cmplz-manage-consent .cmplz-manage-consent.manage-consent-'+complianz.user_banner_id);
1083	let css_file_url = complianz.css_file.replace('{type}', complianz.consenttype ).replace('{banner_id}', complianz.user_banner_id);
1084	if ( complianz.css_file.indexOf('cookiebanner/css/defaults/banner') !== -1 ) {
1085		console.log('Fallback default css file used. Please re-save banner settings, or check file writing permissions in uploads directory');
1086	}
1087
1088	link.href = css_file_url;
1089	link.type = "text/css";
1090	link.rel = "stylesheet";
1091	link.onload = function () {
1092		if ( !disableCookiebanner ) {
1093			cmplz_banner.classList.remove('cmplz-hidden');
1094			cmplz_manage_consent_button.classList.remove('cmplz-hidden');
1095		}
1096	}
1097	document.getElementsByTagName("head")[0].appendChild(link);
1098	if ( cmplz_banner && !disableCookiebanner ) {
1099		cmplz_banner.querySelectorAll('.cmplz-links a:not(.cmplz-external), .cmplz-buttons a:not(.cmplz-external)').forEach(obj => {
1100			let docElement = obj;
1101			docElement.classList.add('cmplz-hidden');
1102			for (let pageType in pageLinks ) {
1103				if (pageLinks.hasOwnProperty(pageType) && docElement.classList.contains(pageType)) {
1104					docElement.setAttribute('href', pageLinks[pageType]['url'] + docElement.getAttribute('data-relative_url'));
1105					if ( docElement.innerText === '{title}') {
1106						docElement.innerText = cmplz_html_decode(pageLinks[pageType]['title']);
1107					}
1108					docElement.classList.remove('cmplz-hidden');
1109				}
1110			}
1111		});
1112
1113		cmplz_set_banner_status();
1114		//we don't use the setBannerStatus function here, as we don't want to save it in a cookie now.
1115		if ( tmpDismissCookiebanner ) {
1116			cmplz_banner.classList.remove('cmplz-show');
1117			cmplz_banner.classList.add('cmplz-dismissed');
1118			cmplz_manage_consent_button.classList.remove('cmplz-dismissed');
1119			cmplz_manage_consent_button.classList.add('cmplz-show');
1120		}
1121	}
1122
1123	let event = new CustomEvent('cmplz_cookie_warning_loaded', {detail: complianz.region});
1124	document.dispatchEvent(event);
1125}
1126/*
1127 * Get the status of the banner: dismissed | show
1128 * @returns {string}
1129 */
1130window.cmplz_get_banner_status = function (){
1131	return cmplz_get_cookie('banner-status');
1132}
1133
1134/*
1135 * Set the banner status so it will be either shown or dismissed, and store it in a cookie.
1136 * @param status (optional)
1137 */
1138
1139window.cmplz_set_banner_status = function ( status ){
1140	let prevStatus = cmplz_get_cookie('banner-status');
1141	status = typeof status !== 'undefined' ? status : prevStatus;
1142	if ( status !== prevStatus ) {
1143		cmplz_set_cookie( 'banner-status', status );
1144	}
1145	if (status.length===0){
1146		status = 'show';
1147	}
1148
1149	if (status==='show') {
1150		prevStatus = 'dismissed';
1151	} else {
1152		prevStatus = 'show';
1153	}
1154
1155	if ( cmplz_banner && status.length>0 ) {
1156		cmplz_banner.classList.remove('cmplz-'+prevStatus);
1157		cmplz_banner.classList.add('cmplz-'+status );
1158		if ( cmplz_manage_consent_button ) {
1159			cmplz_manage_consent_button.classList.add('cmplz-'+prevStatus);
1160			cmplz_manage_consent_button.classList.remove('cmplz-'+status);
1161		}
1162	}
1163
1164	if ( cmplz_banner_container && complianz.soft_cookiewall ) {
1165		cmplz_banner_container.classList.remove('cmplz-'+prevStatus);
1166		cmplz_banner_container.classList.add('cmplz-'+status, 'cmplz-soft-cookiewall' );
1167	}
1168	let event = new CustomEvent('cmplz_banner_status', { detail: status });
1169	document.dispatchEvent(event);
1170	cmplz_start_clean();
1171}
1172
1173/*
1174 * Check if current visitor is a bot
1175 *
1176 * @returns {boolean}
1177 */
1178function cmplz_is_bot(){
1179	let botPattern = "(googlebot\/|Googlebot-Mobile|Google-InspectionTool|Googlebot-Image|Google favicon|Mediapartners-Google|bingbot|slurp|java|wget|curl|Commons-HttpClient|Python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|jyxobot|FAST-WebCrawler|FAST Enterprise Crawler|biglotron|teoma|convera|seekbot|gigablast|exabot|ngbot|ia_archiver|GingerCrawler|webmon |httrack|webcrawler|grub.org|UsineNouvelleCrawler|antibot|netresearchserver|speedy|fluffy|bibnum.bnf|findlink|msrbot|panscient|yacybot|AISearchBot|IOI|ips-agent|tagoobot|MJ12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|Linguee Bot|Voyager|CyberPatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|Adidxbot|blekkobot|ezooms|dotbot|Mail.RU_Bot|discobot|heritrix|findthatfile|europarchive.org|NerdByNature.Bot|sistrix crawler|ahrefsbot|Aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|RetrevoPageAnalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|DuckDuckBot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|changedetection|blexbot|arabot|WeSEE:Search|niki-bot|CrystalSemanticsBot|rogerbot|360Spider|psbot|InterfaxScanBot|Lipperhey SEO Service|CC Metadata Scaper|g00g1e.net|GrapeshotCrawler|urlappendbot|brainobot|fr-crawler|binlar|SimpleCrawler|Livelapbot|Twitterbot|cXensebot|smtbot|bnf.fr_bot|A6-Indexer|ADmantX|Facebot|Twitterbot|OrangeBot|memorybot|AdvBot|MegaIndex|SemanticScholarBot|ltx71|nerdybot|xovibot|BUbiNG|Qwantify|archive.org_bot|Applebot|TweetmemeBot|crawler4j|findxbot|SemrushBot|yoozBot|lipperhey|y!j-asr|Domain Re-Animator Bot|AddThis)";
1180	let reBot = new RegExp(botPattern, 'i');
1181	let userAgent = navigator.userAgent;
1182	return reBot.test(userAgent);
1183}
1184/*
1185 * Check if current visitor is a speedbot
1186 *
1187 * @returns {boolean}
1188 */
1189function cmplz_is_speedbot(){
1190	let userAgent = navigator.userAgent;
1191	let speedBotPattern = "(GTmetrix|pingdom|pingbot|Lighthouse)";
1192	let speedBot = new RegExp(speedBotPattern, 'i');
1193	return speedBot.test(userAgent);
1194}
1195
1196/*
1197 * Check if there is consent for a category or service
1198 * @param category
1199 * @returns {boolean}
1200 */
1201
1202window.cmplz_has_consent = function ( category ){
1203	if ( cmplz_is_bot() ) {
1204		return true;
1205	}
1206
1207	if ( category === 'functional' ) {
1208		return true;
1209	}
1210	let has_consent, value;
1211
1212	//if DNT is detected, we should only return the actual cookie value, and not look at the consenttype
1213	if ( cmplz_do_not_track() ){
1214		value = cmplz_get_cookie(category);
1215		has_consent = (value === 'allow');
1216		return has_consent;
1217	}
1218
1219	/*
1220	 * categories
1221	 */
1222	value = cmplz_get_cookie(category);
1223	if ( (complianz.consenttype === 'optout' || complianz.consenttype === 'other') && value === '') {
1224		//if it's opt out and no cookie is set we should return true
1225		has_consent = true;
1226	} else {
1227		//all other situations, return only true if value is allow
1228		has_consent = (value === 'allow');
1229	}
1230
1231	return has_consent;
1232}
1233
1234/*
1235 * Check if a service has consent
1236 * @param service
1237 * @returns {boolean|*}
1238 */
1239window.cmplz_is_service_denied = function ( service ) {
1240	//Check if it's in the consented services cookie
1241	let consented_services_json = cmplz_get_cookie('consented_services');
1242	let consented_services;
1243	try {
1244		consented_services = JSON.parse(consented_services_json);
1245	} catch (e) {
1246		consented_services = {};
1247	}
1248
1249	if ( !consented_services.hasOwnProperty( service ) ){
1250		return false;
1251	} else {
1252		return !consented_services[service];
1253	}
1254}
1255
1256/*
1257 * Check if a service has consent
1258 * @param service
1259 * @param category
1260 * @returns {boolean|*}
1261 */
1262window.cmplz_has_service_consent = function ( service, category ) {
1263	//Check if it's in the consented services cookie
1264	let consented_services_json = cmplz_get_cookie('consented_services');
1265	let consented_services;
1266	try {
1267		consented_services = JSON.parse(consented_services_json);
1268	} catch (e) {
1269		consented_services = {};
1270	}
1271
1272	if ( !consented_services.hasOwnProperty( service ) ){
1273		//default to the category
1274		return cmplz_has_consent( category );
1275	} else {
1276		return consented_services[service];
1277	}
1278}
1279
1280/*
1281 * check if there's at least one service with consent
1282 * @returns {boolean}
1283 */
1284function cmplz_exists_service_consent(){
1285	let consented_services_json = cmplz_get_cookie('consented_services');
1286	let consented_services;
1287	try {
1288		consented_services = JSON.parse(consented_services_json);
1289		for (const key in consented_services) {
1290			if ( consented_services.hasOwnProperty(key) ) {
1291				if (consented_services[key] == true) {
1292					return true;
1293				}
1294			}
1295		}
1296	} catch (e) {
1297		return false;
1298	}
1299	return false;
1300}
1301
1302/*
1303 * Set consent for a service
1304 * @param service
1305 * @param consented
1306 */
1307function cmplz_set_service_consent( service, consented ){
1308	let consented_services_json = cmplz_get_cookie('consented_services');
1309	let consented_services;
1310	try {
1311		consented_services = JSON.parse(consented_services_json);
1312	} catch (e) {
1313		consented_services = {};
1314	}
1315	consented_services[service] = consented;
1316	cmplz_set_cookie('consented_services', JSON.stringify(consented_services) );
1317	let details = {};
1318	details.service = service;
1319	details.value = consented;
1320	details.region = complianz.region;
1321	//when the status is changed, we may need to fire scripts again, so we reset it.
1322	cmplz_all_scripts_hook_fired = false;
1323	let event = new CustomEvent('cmplz_status_change_service', { detail: details });
1324	document.dispatchEvent(event);
1325}
1326
1327/*
1328 * Remove all service consents
1329 */
1330function cmplz_clear_all_service_consents(){
1331	cmplz_set_cookie('consented_services', '');
1332}
1333
1334/*
1335 * Get all consented or denied services
1336 */
1337
1338function cmplz_get_all_service_consents(){
1339	let consented_services_json = cmplz_get_cookie('consented_services');
1340	let consented_services;
1341	try {
1342		consented_services = JSON.parse(consented_services_json);
1343	} catch (e) {
1344		consented_services = {};
1345	}
1346	return consented_services;
1347}
1348/*
1349 * Get cookie path
1350 * @returns {*}
1351 */
1352function cmplz_get_cookie_path(){
1353	return typeof complianz.cookie_path !== 'undefined' && complianz.cookie_path !== '' ? complianz.cookie_path : '/';
1354}
1355
1356/*
1357 * retrieve domain to set the cookies on
1358 * @returns {string}
1359 */
1360function cmplz_get_cookie_domain() {
1361	if (complianz.set_cookies_on_root == 1 && complianz.cookie_domain.length > 3 && !complianz.cookie_domain.includes('localhost')) {
1362		return complianz.cookie_domain;
1363	}
1364	return '';
1365}
1366
1367/*
1368 * Set consent for a category
1369 * @param category
1370 * @param value
1371 */
1372window.cmplz_set_consent = function (category, value){
1373	cmplz_set_accepted_cookie_policy_id();
1374
1375	//functional is always allow
1376	value = category==='functional' ? 'allow' : value;
1377	let previous_value = cmplz_get_cookie(category);
1378
1379	//keep checkboxes in banner and on cookie policy in sync
1380	//do this before the change check to ensure sync: https://github.com/Really-Simple-Plugins/complianz-gdpr/issues/324
1381	let checked = value === 'allow';
1382	document.querySelectorAll('input.cmplz-'+category).forEach(obj => {
1383		obj.checked = checked;
1384	});
1385
1386	//do not trigger a change event if nothing has changed.
1387	if ( previous_value === value ) {
1388		return;
1389	}
1390
1391	cmplz_set_cookie(category, value);
1392	if ( value === 'allow' ) {
1393		cmplz_enable_category(category);
1394	}
1395
1396	cmplz_wp_set_consent(category, value);
1397	if ( category === 'statistics' ) {
1398		cmplz_wp_set_consent('statistics-anonymous', 'allow');
1399	}
1400
1401	let details = new Object();
1402	details.category = category;
1403	details.value = value;
1404	details.region = complianz.region;
1405	details.categories = cmplz_accepted_categories();
1406	//when the status is changed, we may need to fire scripts again, so we reset it.
1407	cmplz_all_scripts_hook_fired = false;
1408	let event = new CustomEvent('cmplz_status_change', { detail: details });
1409	document.dispatchEvent(event);
1410	if ( category === 'marketing' && value === 'deny' && previous_value === 'allow' ) {
1411		cmplz_integrations_revoke();
1412		//give the code some time to finish, so our track status code can send a signal to the backend.
1413		setTimeout(function(){
1414			cmplz_reload_browser_compatible()
1415		}, 500);
1416	}
1417}
1418
1419/*
1420* In some browsers, like firefox, the reload does not force reload, but keeps cached data, causing e.g. Google Maps to load anyway.
1421*/
1422function cmplz_reload_browser_compatible(){
1423	if( navigator.userAgent.toLowerCase().indexOf('firefox') > -1 ){
1424		const url = new URL(window.location.href);
1425		url.searchParams.set('cmplz-force-reload', Date.now().toString());
1426		window.location.href = url.toString();
1427	} else {
1428		window.location.reload();
1429	}
1430}
1431
1432/*
1433 * We use ajax to check the consenttype based on region, otherwise caching could prevent the user specific warning
1434 *
1435 * */
1436
1437var cmplz_user_data = [];
1438//check if it's already stored
1439if (typeof (Storage) !== "undefined" && sessionStorage.cmplz_user_data) {
1440	cmplz_user_data = JSON.parse(sessionStorage.cmplz_user_data);
1441}
1442
1443//if not stored yet, load. As features in the user object can be changed on updates, we also check for the version
1444if ( complianz.geoip == 1 && (cmplz_user_data.length == 0 || (cmplz_user_data.version !== complianz.version) || (cmplz_user_data.banner_version !== complianz.banner_version)) ) {
1445
1446	let request = new XMLHttpRequest();
1447	let cmplzUserRegion = cmplz_get_url_parameter(window.location.href, 'cmplz_user_region');
1448	cmplzUserRegion = cmplzUserRegion ? '&cmplz_user_region=' + cmplzUserRegion : '';
1449	request.open('GET', complianz.url+'banner?'+complianz.locale+cmplzUserRegion, true);
1450	request.setRequestHeader('Content-type', 'application/json');
1451	request.send();
1452	request.onload = function() {
1453		cmplz_user_data = JSON.parse(request.response);
1454		sessionStorage.cmplz_user_data = JSON.stringify(cmplz_user_data);
1455		conditionally_show_banner();
1456	};
1457} else {
1458	conditionally_show_banner();
1459}
1460
1461/*
1462 *  when ab testing, or using records of consent, we want to keep track of the unique user id
1463 */
1464
1465if ( complianz.store_consent == 1 ) {
1466	var cmplz_id_cookie = cmplz_get_cookie('id');
1467	var cmplz_id_session = '';
1468	var cmplz_id = '';
1469	if (typeof (Storage) !== "undefined" && sessionStorage.cmplz_id) {
1470		cmplz_id_session = JSON.parse(sessionStorage.cmplz_id);
1471	}
1472
1473	if ( cmplz_id_cookie.length == 0 && cmplz_id_session.length > 0 ) {
1474		cmplz_id = cmplz_id_session;
1475		cmplz_set_cookie('id', cmplz_id );
1476	}
1477
1478	if ( cmplz_id_cookie.length > 0 && cmplz_id_session.length == 0 ) {
1479		cmplz_id = cmplz_id_cookie;
1480	}
1481
1482	if ( typeof (Storage) !== "undefined" ) {
1483		sessionStorage.cmplz_id = JSON.stringify(cmplz_id);
1484	}
1485}
1486
1487// visibilitychange and pagehide work in more browsers hence we check if they are supported and try to use them
1488document.addEventListener('visibilitychange', function () {
1489	if ( document.visibilityState === 'hidden' ) {
1490		cmplz_track_status_end();
1491	}
1492});
1493window.addEventListener("pagehide", cmplz_track_status_end, false );
1494window.addEventListener("beforeunload", cmplz_track_status_end, false );
1495
1496function cmplz_track_status_end(){
1497	if ( !cmplz_consent_stored_once ) {
1498		cmplz_track_status();
1499	}
1500}
1501
1502/*
1503 * This creates an API which devs can use to trigger actions in complianz.
1504 */
1505document.addEventListener('cmplz_consent_action', function (e) {
1506	cmplz_set_consent( e.detail.category , 'allow' );
1507	cmplz_fire_categories_event();
1508	cmplz_track_status();
1509});
1510
1511
1512
1513/*
1514 * For both opt-in and opt-out, clicking cmplz-accept should result in accepting all categories
1515 */
1516cmplz_add_event('click', '.cmplz-accept', function(e){
1517	e.preventDefault();
1518	setTimeout( () => {
1519		cmplz_accept_all();
1520		cmplz_set_banner_status('dismissed');
1521		cmplz_fire_categories_event();
1522		cmplz_track_status();
1523	},0);
1524});
1525
1526/*
1527 *  Accept marketing cookies by clicking any other link cookie acceptance from a custom link
1528 */
1529
1530//cmplz-accept-marketing is deprecated
1531cmplz_add_event('click', '.cmplz-accept-category, .cmplz-accept-marketing', function(e){
1532	e.preventDefault();
1533	let obj = e.target;
1534	let service = obj.getAttribute('data-service');
1535	let category = obj.getAttribute('data-category');
1536	category = category ? category : 'marketing';
1537	if ( complianz.clean_cookies == 1 && typeof service !== 'undefined' && service ){
1538		cmplz_set_service_consent(service, true);
1539		cmplz_enable_category('', 'general');
1540		cmplz_enable_category('', service);
1541	} else {
1542		//we're activating a category, so do not need service activation here.
1543		// cmplz_enable_category('', 'general');
1544		cmplz_set_consent( category, 'allow' );
1545	}
1546	cmplz_set_banner_status('dismissed');
1547	cmplz_fire_categories_event();
1548	cmplz_track_status();
1549});
1550
1551/*
1552 * Accept a specific service
1553 */
1554cmplz_add_event('click', '.cmplz-accept-service', function(e){
1555	let obj = e.target;
1556	//INPUT should be handled by change event, these are checkboxes on the cookie policy
1557	let tagName = obj.tagName;
1558	if ( tagName === 'INPUT' ) {
1559		return;
1560	}
1561	let service = obj.getAttribute('data-service');
1562	if ( typeof service !== 'undefined' ){
1563		cmplz_set_service_consent(service, true);
1564		cmplz_enable_category('', 'general');
1565		cmplz_enable_category('', service);
1566	}
1567	cmplz_fire_categories_event();
1568	cmplz_track_status();
1569});
1570/*
1571 * Accept a specific service
1572 */
1573cmplz_add_event('change', '.cmplz-accept-service', function(e){
1574	let obj = e.target;
1575	let tagName = obj.tagName;
1576	let service = obj.getAttribute('data-service');
1577	if ( typeof service !== 'undefined' ) {
1578		//inputs are from the consent per service option on the cookie policy.
1579		if ( tagName === 'INPUT' ) {
1580			cmplz_set_banner_status('dismissed');
1581			if ( obj.checked ){
1582				cmplz_set_service_consent(service, true);
1583				cmplz_enable_category('', service);
1584			} else {
1585				cmplz_set_service_consent(service, false);
1586				//give our track status time to finish
1587				setTimeout(function(){
1588					cmplz_reload_browser_compatible()
1589				}, 500);
1590			}
1591			//if not input, it's a placeholder
1592		} else {
1593			e.preventDefault();
1594			cmplz_set_service_consent(service, true);
1595			cmplz_enable_category('', 'general');
1596			cmplz_enable_category('', service);
1597
1598			//give our track status time to finish
1599			setTimeout(function(){
1600				cmplz_reload_browser_compatible()
1601			}, 500);
1602		}
1603	}
1604	cmplz_fire_categories_event();
1605	cmplz_track_status();
1606});
1607
1608
1609
1610/*
1611 * On the banner, clicking a category should fire the category only after the save button is clicked.
1612 *
1613 */
1614cmplz_add_event('click', '.cmplz-save-preferences', function(e){
1615	let obj = e.target;
1616	cmplz_banner = obj.closest('.cmplz-cookiebanner');
1617	let consented_categories = [];
1618
1619	for (let key in cmplz_categories) {
1620		if ( cmplz_categories.hasOwnProperty(key) ) {
1621			let category = cmplz_categories[key];
1622			let categoryElement = cmplz_banner.querySelector('input.cmplz-' + category);
1623			if (categoryElement && categoryElement.checked) {
1624				consented_categories.push(category);
1625			}
1626		}
1627	}
1628	cmplz_fire_before_categories_consent(consented_categories);
1629	for (let key in cmplz_categories) {
1630		if ( cmplz_categories.hasOwnProperty(key) ) {
1631			let category = cmplz_categories[key];
1632			if (consented_categories.includes(category)) {
1633				cmplz_set_consent(category, 'allow');
1634			} else {
1635				cmplz_set_consent(category, 'deny');
1636			}
1637		}
1638	}
1639	cmplz_set_banner_status('dismissed');
1640	cmplz_fire_categories_event();
1641	cmplz_track_status();
1642});
1643
1644cmplz_add_event('click', '.cmplz-close', function(e){
1645	cmplz_set_banner_status('dismissed');
1646});
1647
1648cmplz_add_event('click', '.cmplz-view-preferences', function(e){
1649	let obj = e.target;
1650	cmplz_banner = obj.closest('.cmplz-cookiebanner');
1651	if ( cmplz_banner.querySelector('.cmplz-categories').classList.contains('cmplz-fade-in')) {
1652		cmplz_banner.classList.remove('cmplz-categories-visible');
1653		cmplz_banner.querySelector('.cmplz-categories' ).classList.remove('cmplz-fade-in');
1654		cmplz_banner.querySelector('.cmplz-view-preferences' ).style.display = 'block';
1655		cmplz_banner.querySelector('.cmplz-save-preferences' ).style.display = 'none';
1656	} else {
1657		cmplz_banner.classList.add('cmplz-categories-visible');
1658		cmplz_banner.querySelector('.cmplz-categories' ).classList.add('cmplz-fade-in');
1659		cmplz_banner.querySelector('.cmplz-view-preferences' ).style.display = 'none';
1660		cmplz_banner.querySelector('.cmplz-save-preferences' ).style.display = 'block';
1661	}
1662});
1663/*
1664 * On the cookie policy, clicking a category should fire the category immediately
1665 *
1666 */
1667cmplz_add_event('change', '.cmplz-manage-consent-container .cmplz-category', function(e){
1668	for (let key in cmplz_categories) {
1669		if ( cmplz_categories.hasOwnProperty(key) ) {
1670			let category = cmplz_categories[key];
1671			let categoryElement = document.querySelector('.cmplz-manage-consent-container input.cmplz-' + category);
1672			if (categoryElement) {
1673				if (categoryElement.checked) {
1674					cmplz_set_consent(category, 'allow');
1675				} else {
1676					cmplz_set_consent(category, 'deny');
1677				}
1678				cmplz_set_banner_status('dismissed');
1679				cmplz_fire_categories_event();
1680				cmplz_track_status();
1681			}
1682		}
1683	}
1684
1685});
1686
1687cmplz_add_event('click', '.cmplz-deny', function(e){
1688	e.preventDefault();
1689	cmplz_set_banner_status('dismissed');
1690	cmplz_deny_all();
1691
1692});
1693
1694cmplz_add_event('click', 'button.cmplz-manage-settings', function(e){
1695	e.preventDefault();
1696	let catsContainer = document.querySelector('.cmplz-cookiebanner .cmplz-categories');
1697	let saveSettings = document.querySelector('.cmplz-save-settings');
1698	let manageSettings = document.querySelector('button.cmplz-manage-settings');
1699	if ( !cmplz_is_hidden(catsContainer) ){
1700		saveSettings.style.display='none';
1701		manageSettings.style.display = 'block';
1702		catsContainer.style.display='none';
1703	} else {
1704		saveSettings.style.display = 'block';
1705		manageSettings.style.display='none';
1706		catsContainer.style.display = 'block';
1707	}
1708});
1709
1710cmplz_add_event('click', 'button.cmplz-manage-consent', function(e){
1711	e.preventDefault();
1712	cmplz_set_banner_status('show');
1713});
1714
1715/*
1716 * Handle dismiss on scroll and dismiss on timeout
1717 */
1718function cmplz_set_up_auto_dismiss() {
1719	if ( complianz.consenttype === 'optout' ) {
1720		if ( complianz.dismiss_on_scroll==1 ) {
1721			let onWindowScroll = function(evt) {
1722				if (window.pageYOffset > Math.floor(400)) {
1723					cmplz_set_banner_status('dismissed');
1724					cmplz_fire_categories_event();
1725					cmplz_track_status();
1726					window.removeEventListener('scroll', onWindowScroll);
1727					this.onWindowScroll = null;
1728				}
1729			};
1730			window.addEventListener('scroll', onWindowScroll);
1731		}
1732
1733		let delay = parseInt(complianz.dismiss_timeout);
1734		if ( delay > 0 ) {
1735			window.setTimeout(function () {
1736				cmplz_set_banner_status('dismissed');
1737				cmplz_fire_categories_event();
1738				cmplz_track_status();
1739			}, Math.floor(delay));
1740		}
1741	}
1742}
1743
1744/*
1745 * Fire a event wich passes all consented categories
1746 * Separated from the actual category consent because we want to bundle it in one event
1747 */
1748function cmplz_fire_categories_event(){
1749	let details = new Object();
1750	details.category = cmplz_highest_accepted_category();
1751	details.categories = cmplz_accepted_categories();
1752	details.region = complianz.region;
1753	let event = new CustomEvent('cmplz_fire_categories', { detail: details });
1754	document.dispatchEvent(event);
1755}
1756/*
1757 * Track the status of current consent
1758 * @param status
1759 */
1760
1761function cmplz_track_status( status ) {
1762	let cats = [];
1763	status = typeof status !== 'undefined' ? status : false;
1764
1765	let event = new CustomEvent('cmplz_track_status', { detail: status });
1766	document.dispatchEvent(event);
1767
1768	if ( !status ) {
1769		cats = cmplz_accepted_categories();
1770	} else {
1771		cats = [status];
1772	}
1773	cmplz_set_category_as_body_class();
1774	let saved_cats, saved_services;
1775	try {saved_cats = JSON.parse(cmplz_get_cookie('saved_categories'));} catch (e) {saved_cats = {};}
1776	try {saved_services = JSON.parse(cmplz_get_cookie('saved_services'));} catch (e) {saved_services = {};}
1777	let consented_services = cmplz_get_all_service_consents();
1778
1779	//compare current cats with saved cats. When there are no changes, do nothing.
1780	if (cmplz_equals(saved_cats, cats) && cmplz_equals(saved_services, consented_services)) {
1781		return;
1782	}
1783
1784	if ( complianz.store_consent != 1 || cmplz_is_bot() || cmplz_is_speedbot() ) {
1785		return;
1786	}
1787
1788	//keep track of the fact that the status was saved at least once
1789	cmplz_set_cookie('saved_categories', JSON.stringify(cats));
1790	cmplz_set_cookie('saved_services', JSON.stringify(consented_services));
1791	cmplz_consent_stored_once = true;
1792
1793	let data;
1794	let request = new XMLHttpRequest();
1795	request.open('POST', complianz.url+'track', true);
1796	data = {
1797		'consented_categories': cats,
1798		'consented_services':consented_services,
1799		'consenttype': window.wp_consent_type,//store the source consenttype, as our complianz.consenttype will not include optinstats.
1800	};
1801
1802	request.setRequestHeader('Content-type', 'application/json');
1803	request.send(JSON.stringify(data));
1804
1805}
1806
1807/*
1808 * Get accepted categories
1809 *
1810 * @returns {string}
1811 */
1812function cmplz_accepted_categories() {
1813	let consentedCategories = cmplz_categories;
1814	let consentedTemp = [];
1815
1816	//because array filter changes keys, we make a temp arr
1817	for (let key in consentedCategories) {
1818		if ( consentedCategories.hasOwnProperty(key) ) {
1819			let category = consentedCategories[key];
1820			if (cmplz_has_consent(category)) {
1821				consentedTemp.push(category);
1822			}
1823		}
1824	}
1825
1826	consentedCategories = consentedCategories.filter(function(value, index, consentedCategories){
1827		return cmplz_in_array(value, consentedTemp);
1828	});
1829	return consentedCategories;
1830}
1831
1832/*
1833 * Enable the checkbox for each category which was enabled
1834 *
1835 * */
1836
1837function cmplz_sync_category_checkboxes() {
1838	for ( let key in cmplz_categories ) {
1839		if ( cmplz_categories.hasOwnProperty(key) ) {
1840			let category = cmplz_categories[key];
1841			if ( cmplz_has_consent(category) || category === 'functional' ) {
1842				document.querySelectorAll('input.cmplz-' + category).forEach(obj => {
1843					obj.checked = true;
1844				});
1845			} else {
1846				document.querySelectorAll('input.cmplz-' + category).forEach(obj => {
1847					obj.checked = false;
1848				});
1849			}
1850		}
1851
1852		document.querySelectorAll('.cmplz-accept-service').forEach(obj => {
1853			let service = obj.getAttribute('data-service');
1854			let category = obj.getAttribute('data-category');
1855			if ( cmplz_has_service_consent( service, category ) ) {
1856				obj.checked = true;
1857			} else if ( cmplz_is_service_denied( service ) ) {
1858				obj.checked = false;
1859			} else {
1860				//no consent on service level, check if it's category has consent.
1861				let category = obj.getAttribute('data-category' );
1862				obj.checked = !!cmplz_has_consent(category);
1863			}
1864		});
1865	}
1866}
1867
1868/*
1869 * Merge two objects
1870 *
1871 * */
1872
1873function cmplz_merge_object(userdata, ajax_data) {
1874	let output = {};
1875	//first, we fill the important data.
1876	for (let key in ajax_data) {
1877		if (ajax_data.hasOwnProperty(key)) output[key] = ajax_data[key];
1878	}
1879
1880	//conditionally add static data
1881	for (let key in userdata) {
1882		//only add if not in ajax_data
1883		if (!ajax_data.hasOwnProperty(key) || typeof ajax_data[key] === 'undefined') {
1884			if (userdata.hasOwnProperty(key)) output[key] = userdata[key];
1885		}
1886	}
1887
1888	return output;
1889}
1890
1891/*
1892 * Clear all our own cookies, to make sure path issues are resolved.
1893 *
1894 *
1895 */
1896function cmplz_clear_cookies (cookie_part) {
1897	if (typeof document === 'undefined') {
1898		return false;
1899	}
1900
1901	let foundCookie = false;
1902	let secure = window.location.protocol === 'https:' ? ';secure' : '';
1903	let expires = 'expires=' + new Date().toGMTString();
1904	let pathParts = location.pathname.replace(/^\/|\/$/g, '').split('/');
1905
1906	// Loop through all cookies
1907	document.cookie.split('; ').forEach(function(cookie) {
1908		let cookieName = cookie.split(';')[0].split('=')[0];
1909
1910		// If the cookie contains cookie_part, try to delete it
1911		if (cookieName.indexOf(cookie_part) !== -1) {
1912			foundCookie = true;
1913			let domainParts = window.location.hostname.split('.');
1914			let skipLast = domainParts.length > 1;
1915
1916			// Clear cookies on root path and subpaths
1917			pathParts.forEach(function(pathPart) {
1918				let path = '/' + pathPart;
1919				document.cookie = encodeURIComponent(cookieName) + '=;SameSite=Lax' + secure + ';' + expires + ';domain=.' + domainParts.join('.') + ';path=' + path;
1920				document.cookie = encodeURIComponent(cookieName) + '=;SameSite=Lax' + secure + ';' + expires + ';domain=.' + domainParts.join('.') + ';path=' + path + '/';
1921			});
1922
1923			// Clear cookies on parent domains
1924			while (domainParts.length > 0) {
1925				let domain = '.' + domainParts.join('.');
1926				domainParts.shift();
1927				if (skipLast && domainParts.length === 1) domainParts.shift();
1928
1929				pathParts.forEach(function(pathPart) {
1930					let path = '/' + pathPart;
1931					document.cookie = encodeURIComponent(cookieName) + '=;SameSite=Lax' + secure + ';' + expires + ';domain=' + domain + ';path=' + path;
1932					document.cookie = encodeURIComponent(cookieName) + '=;SameSite=Lax' + secure + ';' + expires + ';domain=' + domain + ';path=' + path + '/';
1933				});
1934			}
1935		}
1936	});
1937
1938	// To prevent a double reload, we preserve the cookie policy id
1939	cmplz_set_accepted_cookie_policy_id();
1940
1941	return foundCookie;
1942}
1943
1944/*
1945 *
1946 * If a policy is accepted, save this in the user policy id
1947 *
1948 * */
1949
1950function cmplz_set_accepted_cookie_policy_id() {
1951	cmplz_set_cookie('policy_id', complianz.current_policy_id);
1952}
1953
1954/*
1955 * For supported integrations, initialize the not consented state
1956 *
1957 * */
1958
1959function cmplz_integrations_init() {
1960	let cookiesToSet = complianz.set_cookies;
1961	//check if we have scripts that need to be set to true on init.
1962	for (let key in cookiesToSet) {
1963		if (cookiesToSet.hasOwnProperty(key) && cookiesToSet[key][1] === '1') {
1964			cmplz_set_cookie(key, cookiesToSet[key][1], false);
1965		}
1966	}
1967}
1968
1969/*
1970 * For supported integrations, revoke consent
1971 *
1972 * */
1973function cmplz_integrations_revoke() {
1974	let cookiesToSet = complianz.set_cookies;
1975	for (let key in cookiesToSet) {
1976		if (cookiesToSet.hasOwnProperty(key)) {
1977			cmplz_set_cookie(key, cookiesToSet[key][1], false);
1978			if ( cookiesToSet[key][1] == false ){
1979				cmplz_clear_cookies(key);
1980			}
1981		}
1982	}
1983}
1984
1985/*
1986 * For supported integrations, set consent
1987 *
1988 * */
1989
1990function cmplz_set_integrations_cookies() {
1991	let cookiesToSet = complianz.set_cookies;
1992	for (let key in cookiesToSet) {
1993		if (cookiesToSet.hasOwnProperty(key)) {
1994			cmplz_set_cookie(key, cookiesToSet[key][0], false);
1995		}
1996	}
1997}
1998
1999function cmplz_get_url_parameter(sPageURL, sParam) {
2000	if (!sPageURL || typeof sPageURL === 'undefined' || sPageURL.indexOf('?') === -1) {
2001		return false;
2002	}
2003
2004	const queryString = sPageURL.split('?')[1];
2005	if (!queryString) return false;
2006
2007	const sURLVariables = queryString.split('&');
2008	for (let i = 0; i < sURLVariables.length; i++) {
2009		const sParameterName = sURLVariables[i].split('=');
2010		if (sParameterName[0] === sParam) {
2011			return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
2012		}
2013	}
2014
2015	return false;
2016}
2017
2018/*
2019 * If the parameter cmplz_region_redirect =true is passed, find the user's region, and redirect.
2020 */
2021function cmplz_maybe_auto_redirect() {
2022	let redirect = cmplz_get_url_parameter(window.location.href, 'cmplz_region_redirect');
2023	let region = cmplz_get_url_parameter(window.location.href, 'cmplz-region');
2024	if (redirect && !region) {
2025		let newUrl = window.location.href.split('#')[0] + '&cmplz-region=' + complianz.region;
2026		let hash = window.location.hash;
2027		window.location.href = newUrl + hash;
2028	}
2029}
2030
2031/*
2032 * wrapper to set consent for wp consent API. If consent API is not active, do nothing
2033 * @param type
2034 * @param value
2035 */
2036function cmplz_wp_set_consent(type, value) {
2037	//wp consent api integration
2038	if (typeof wp_set_consent == 'function') {
2039		wp_set_consent(type, value);
2040	}
2041}
2042
2043var cmplz_cookie_data = [];
2044var cmplzCleanCookieInterval;
2045function cmplz_start_clean(){
2046	if ( complianz.clean_cookies == 1 ) {
2047		//check if it's already stored
2048		if (typeof (Storage) !== "undefined" ) {
2049			cmplz_cookie_data = JSON.parse( sessionStorage.getItem('cmplz_cookie_data') );
2050		}
2051		//if not stored yet, load. As features in the user object can be changed on updates, we also check for the version
2052		if ( !cmplz_cookie_data || cmplz_cookie_data.length === 0 ) {
2053			let request = new XMLHttpRequest();
2054			request.open('GET', complianz.url+'cookie_data', true);
2055			request.setRequestHeader('Content-type', 'application/json');
2056			request.send();
2057			request.onload = function() {
2058				cmplz_cookie_data = JSON.parse(request.response);
2059				sessionStorage.setItem('cmplz_cookie_data', JSON.stringify(cmplz_cookie_data) );
2060				cmplz_setup_clean_interval();
2061			};
2062		} else {
2063			cmplz_setup_clean_interval();
2064		}
2065	}
2066}
2067
2068/*
2069* Execute the cleanup of cookies
2070*/
2071function cmplz_do_cleanup() {
2072	const consent_categories = ['preferences', 'statistics', 'marketing'];
2073	for (const category of consent_categories) {
2074		if (!cmplz_has_consent(category) && cmplz_cookie_data.hasOwnProperty(category)) {
2075			const services = cmplz_cookie_data[category];
2076
2077			for (const service in services) {
2078				if (!cmplz_has_service_consent(service, category)) {
2079					const cookies = services[service];
2080
2081					for (const item of cookies) {
2082						cmplz_clear_cookies(item);
2083						cmplz_clear_storage(item);
2084					}
2085				}
2086			}
2087		}
2088	}
2089}
2090
2091
2092function cmplz_setup_clean_interval(){
2093	// if the cookie data array is empty, return, nothing to do.
2094	if ( !cmplz_cookie_data ) {
2095		return;
2096	}
2097	//check if it's already activated
2098	if ( cmplzCleanCookieInterval ) {
2099		return;
2100	}
2101	//one straight away, then every second
2102	cmplz_do_cleanup();
2103	cmplzCleanCookieInterval = setInterval(cmplz_do_cleanup, 1000 );
2104}
2105
2106/*
2107 * Clear an item from either session or localstorage
2108 * @param item
2109 */
2110function cmplz_clear_storage(item){
2111	if (typeof (Storage) !== "undefined" ) {
2112		if ( localStorage.getItem(item) ) {
2113			localStorage.removeItem(item);
2114		}
2115		if ( sessionStorage.getItem(item) ) {
2116			sessionStorage.removeItem(item);
2117		}
2118	}
2119}
2120/*
2121 * Load revoke options
2122 */
2123
2124function cmplz_load_manage_consent_container() {
2125	//don't load manage html code in the block editor
2126	let manage_consent_container = document.querySelector('.cmplz-manage-consent-container');
2127	let is_block_editor = false;//document.querySelector('.wp-admin .cmplz-unlinked-mode');
2128	if ( manage_consent_container && !is_block_editor ) {
2129
2130		let request = new XMLHttpRequest();
2131		request.open('GET', complianz.url+'manage_consent_html?'+complianz.locale, true);
2132		request.setRequestHeader('Content-type', 'application/json');
2133		request.send();
2134		request.onload = function() {
2135			let html = JSON.parse(request.response);
2136			manage_consent_container.insertAdjacentHTML( 'beforeend', html );
2137			cmplz_sync_category_checkboxes();
2138			let nojavascript = document.querySelector('#cmplz-manage-consent-container-nojavascript')
2139			nojavascript.style.display = 'none';
2140			manage_consent_container.style.display = 'block';
2141			let event = new CustomEvent('cmplz_manage_consent_container_loaded');
2142			document.dispatchEvent(event);
2143		};
2144	}
2145}
2146
2147/*
2148 * Make slider radio's tabable
2149 */
2150
2151cmplz_add_event('keypress', '.cmplz-banner-slider label', function(e){
2152	let keycode = (e.keyCode ? e.keyCode : e.which);
2153	if (keycode == 32) {
2154		document.activeElement.click();
2155	}
2156});
2157
2158/*
2159 * Make close button closable with enter
2160 */
2161cmplz_add_event('keypress', '.cmplz-cookiebanner .cmplz-header .cmplz-close', function(e){
2162	let keycode = (e.keyCode ? e.keyCode : e.which);
2163	if ( keycode == 13 ) {
2164		document.activeElement.click();
2165	}
2166});
2167
2168/*
2169 * Compare two arrays
2170 * @param array
2171 * @returns {boolean}
2172 */
2173function cmplz_equals (array_1, array_2) {
2174	if ( !Array.isArray(array_1) ) {
2175		array_1 = Object.keys(array_1);
2176		array_2 = Object.keys(array_2);
2177	}
2178	// if the other array is a falsy value, return
2179	if (!array_1 || !array_2)
2180		return false;
2181
2182	// compare lengths - can save a lot of time
2183	if (array_1.length !== array_2.length)
2184		return false;
2185
2186	for (let i = 0, l=array_1.length; i < l; i++) {
2187		// Check if we have nested arrays
2188		if (array_1[i] instanceof Array && array_2[i] instanceof Array) {
2189			// recurse into the nested arrays
2190			if (!cmplz_equals(array_1[i], array_2[i]))
2191				return false;
2192		}
2193		else if (array_1[i] !== array_2[i]) {
2194			return false;
2195		}
2196	}
2197	return true;
2198}
2199
2200/*
2201* Copy all element atributes to the new element
2202*/
2203function cmplzCopyAttributes(source, target) {
2204	const excludes = ['type', 'data-service', 'data-category', 'async'];
2205	Array.from(source.attributes).forEach(attribute => {
2206		if (attribute.nodeName === 'data-script-type' && attribute.nodeValue === 'module') {
2207			target.setAttribute('type', 'module');
2208			target.removeAttribute('data-script-type');
2209		} else if (!excludes.includes(attribute.nodeName)) {
2210			target.setAttribute(attribute.nodeName, attribute.nodeValue);
2211		}
2212	});
2213}
2214
2215
2216/*
2217 * Hooked into jquery
2218 */
2219var cmplz_has_wp_video = document.querySelector('.cmplz-wp-video-shortcode');
2220var cmplz_times_checked = 0;
2221if ('undefined' != typeof window.jQuery) {
2222	jQuery(document).ready(function ($) {
2223		if ( cmplz_has_wp_video ) {
2224			document.addEventListener("cmplz_enable_category", function (consentData) {
2225				cmplz_activate_wp_video();
2226			});
2227
2228			let cmplzInterval = setInterval(function(){
2229				cmplz_times_checked+=1;
2230				if ( document.querySelector('.cmplz-wp-video-shortcode') && cmplz_times_checked<100) {
2231					cmplz_activate_wp_video();
2232				} else {
2233					clearInterval(cmplzInterval);
2234				}
2235			}, 500);
2236		}
2237
2238		/*
2239		 * WordPress legacy shortcode
2240		 */
2241		function cmplz_activate_wp_video(again) {
2242			if ( !document.querySelector('.cmplz-wp-video-shortcode') ) {
2243				return;
2244			}
2245			let categories = cmplz_accepted_categories();
2246			let services = cmplz_get_all_service_consents();
2247			let selectorVideo = '';
2248			let selectorVideos = [];
2249			for (let c_key in categories) {
2250				if (categories.hasOwnProperty(c_key)) {
2251					let category = categories[c_key];
2252					if (category==='functional') {
2253						break;
2254					}
2255					selectorVideos.push('.cmplz-wp-video-shortcode[data-category="'+category+'"]');
2256				}
2257			}
2258			for (let s_key in services) {
2259				if (services.hasOwnProperty(s_key)) {
2260					selectorVideos.push('.cmplz-wp-video-shortcode[data-service="' + s_key + '"]');
2261				}
2262			}
2263			selectorVideo = selectorVideos.join(',');
2264			let should_initialize_video = false;
2265			if ( selectorVideo.length>0 ) {
2266				document.querySelectorAll(selectorVideo).forEach(obj => {
2267					should_initialize_video = true;
2268					obj.setAttribute('controls', 'controls');
2269					obj.classList.add('wp-video-shortcode', 'cmplz-processed');
2270					obj.classList.remove('cmplz-wp-video-shortcode');
2271					obj.closest('.cmplz-wp-video').classList.remove('cmplz-wp-video');
2272
2273					let blocked_notice = obj.closest('.wp-video').querySelector('.cmplz-blocked-content-notice');
2274					if (blocked_notice) {
2275						blocked_notice.parentElement.removeChild(blocked_notice);
2276					}
2277					obj.classList.remove('cmplz-blocked-content-container');
2278				});
2279			}
2280
2281			if ( should_initialize_video ) {
2282				//in normal setup, this should work. e.g. with primavera theme not, then we init per element.
2283				if (window.wp.mediaelement) {
2284					window.wp.mediaelement.initialize()
2285				} else {
2286					let settings={};
2287					settings.videoWidth='100%';
2288					settings.videoHeight='100%';
2289					settings.enableAutosize=true;
2290					$( '.wp-video-shortcode' ).mediaelementplayer( settings );
2291				}
2292			}
2293		}
2294
2295		/*
2296		 * Activate fitvids on the parent element if active
2297		 *  a.o. Beaverbuilder
2298		 */
2299		document.querySelectorAll('.cmplz-video.cmplz-activated').forEach(obj => {
2300			cmplz_activate_fitvids(obj);
2301		});
2302
2303		document.addEventListener("cmplz_enable_category", function (e) {
2304			document.querySelectorAll('.cmplz-video.cmplz-activated').forEach(obj => {
2305				cmplz_activate_fitvids(obj);
2306			});
2307		});
2308
2309		function cmplz_activate_fitvids(obj){
2310			//turn obj into jquery object
2311			let $obj = $(obj);
2312			if (typeof $obj.parent().fitVids === 'function') {
2313				$obj.parent().fitVids();
2314			}
2315		}
2316	});
2317}

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.