1(function (w, d) { 2 'use strict'; 3 var config = w.klaroConfig; 4 if (!config || w.biertheaterConsent) return; 5 6 var states = Object.create(null); 7 var initialized = Object.create(null); 8 var loaded = Object.create(null); 9 var pendingLoads = Object.create(null); 10 var readyLoads = Object.create(null); 11 var configuredTags = Object.create(null); 12 var measurementIds = Object.create(null); 13 var contentsquareRequested = false; 14 var contentsquareStopped = false; 15 var services = config.services || []; 16 var trackingReady = config.__trackingEnabled && /^GTM-[A-Z0-9]+$/.test(config.__gtmContainerId || ''); 17 var trackingServices = services.filter(function (service) { 18 return ['gtm', 'ga4', 'google_ads'].indexOf(service.__adapter) !== -1; 19 }); 20 21 function dataLayer() { 22 w.dataLayer = w.dataLayer || []; 23 return w.dataLayer; 24 } 25 26 function gtag() { dataLayer().push(arguments); } 27 28 function snapshot() { 29 var result = {}; 30 services.forEach(function (service) { result[service.name] = states[service.name] === true; }); 31 return result; 32 } 33 34 function hasConsent(name) { return states[name] === true; } 35 36 function registerGoogleMeasurementId(id, serviceName) { 37 if (!/^G-[A-Z0-9]+$/.test(id)) return; 38 measurementIds[id] = serviceName || 'google-analytics'; 39 w['ga-disable-' + id] = !hasConsent(measurementIds[id]); 40 } 41 42 w.biertheaterConsent = { 43 hasConsent: hasConsent, 44 getStates: snapshot, 45 registerGoogleMeasurementId: registerGoogleMeasurementId, 46 startContentsquare: startContentsquare 47 }; 48 49 function load(id, src, callback) { 50 if (loaded[id]) { 51 if (callback && readyLoads[id]) callback(); 52 else if (callback && pendingLoads[id]) pendingLoads[id].push(callback); 53 return; 54 } 55 if (d.getElementById(id)) { loaded[id] = true; return; } 56 loaded[id] = true; 57 pendingLoads[id] = callback ? [callback] : []; 58 var script = d.createElement('script'); 59 script.id = id; 60 script.async = true; 61 script.src = src; 62 script.onload = function () { 63 readyLoads[id] = true; 64 var callbacks = pendingLoads[id]; 65 delete pendingLoads[id]; 66 callbacks.forEach(function (done) { done(); }); 67 }; 68 // No automatic retries: a failed request must not multiply tags or events. 69 script.onerror = function () { 70 delete pendingLoads[id]; 71 if (w.console) w.console.warn('Consent: Dienst konnte nicht geladen werden.', id); 72 }; 73 d.head.appendChild(script); 74 } 75 76 function modes() { 77 var analytics = false, ads = false; 78 trackingServices.forEach(function (service) { 79 if (!hasConsent(service.name)) return; 80 if (service.name === 'google-analytics' || service.__adapter === 'ga4') analytics = true; 81 if (service.name === 'google-ads' || service.__adapter === 'google_ads') ads = true; 82 }); 83 return { 84 analytics_storage: analytics ? 'granted' : 'denied', 85 ad_storage: ads ? 'granted' : 'denied', 86 ad_user_data: ads ? 'granted' : 'denied', 87 ad_personalization: ads ? 'granted' : 'denied' 88 }; 89 } 90 91 function contentsquareCommand(command) { 92 w._uxa = w._uxa || []; 93 w._uxa.push(command); 94 } 95 96 function startContentsquare() { 97 var service = services.filter(function (item) { return item.name === 'contentsquare' && item.__adapter === 'contentsquare'; })[0]; 98 var tagId = service && (service.__adapterConfig || {}).tagId; 99 if (!config.__trackingEnabled || !hasConsent('contentsquare') || typeof tagId !== 'string' || !/^[a-f0-9]{13}$/.test(tagId) || contentsquareStopped) return false; 100 // Respect an independent Contentsquare opt-out until an explicit new choice. 101 if (/(^|;\s*)_cs_optout=/.test(d.cookie || '')) return false; 102 if (contentsquareRequested) return true; 103 contentsquareRequested = true; 104 contentsquareCommand(['setPIISelectors', {PIISelectors: ['form, input, textarea, select, [contenteditable], [data-cs-mask]']}]); 105 contentsquareCommand(['replay:consent:unanonymized:withdrawn']); 106 contentsquareCommand(['excludeURLforReplay', '^[^/]+/(?:[^/?#]+/)*(?:kontakt|c
106ontact|casting)(?:[/?#]|$)']); 107 contentsquareCommand(['setQuery', '']); 108 load('biertheater-contentsquare', 'https://t.contentsquare.net/uxa/' + tagId + '.js', function () { 109 if (!hasConsent('contentsquare') || contentsquareStopped) { 110 contentsquareCommand(['optout']); 111 // A re-grant during download applies on the next navigation, never by re-initializing this tag. 112 if (hasConsent('contentsquare')) eraseCookies([['_cs_optout', '/', '']], true); 113 } 114 }); 115 return true; 116 } 117 118 function updateContentsquare(accepted, first) { 119 if (!accepted && contentsquareRequested) { 120 contentsquareStopped = true; 121 contentsquareCommand(['optout']); 122 } 123 if (accepted && !first) eraseCookies([['_cs_optout', '/', '']], true); 124 } 125 126 function publish(service, accepted) { 127 dataLayer().push({ 128 event: 'klaro-' + service.name + (accepted ? '-accepted' : '-declined'), 129 klaroConsents: snapshot() 130 }); 131 } 132 133 function applyTracking(service, accepted) { 134 var adapter = service.__adapter; 135 var cfg = service.__adapterConfig || {}; 136 if (!trackingReady) return; 137 138 if (adapter === 'ga4') registerGoogleMeasurementId(cfg.measurementId, service.name); 139 Object.keys(measurementIds).forEach(function (id) { 140 w['ga-disable-' + id] = !hasConsent(measurementIds[id]); 141 }); 142 if (service.name === 'meta-pixel' && typeof w.fbq === 'function') { 143 w.fbq('consent', accepted ? 'grant' : 'revoke'); 144 } 145 // Disable already initialized tags before notifying Google's consent runtime. 146 gtag('consent', 'update', modes()); 147 dataLayer().push({klaroConsents: snapshot()}); 148 149 if (adapter === 'gtm' && accepted) { 150 var container = config.__gtmContainerId; 151 var id = 'biertheater-gtm-' + container; 152 if (!loaded[id] && !d.getElementById(id)) { 153 dataLayer().push({'gtm.start': new Date().getTime(), event: 'gtm.js'}); 154 load(id, 'https://www.googletagmanager.com/gtm.js?id=' + encodeURIComponent(container)); 155 } 156 } 157 if (adapter === 'ga4' && accepted && /^G-[A-Z0-9]+$/.test(cfg.measurementId || '')) { 158 load('biertheater-ga4-' + cfg.measurementId, 'https://www.googletagmanager.com/gtag/js?id=' + encodeURIComponent(cfg.measurementId), function () { 159 if (!hasConsent(service.name) || configuredTags[cfg.measurementId]) return; 160 configuredTags[cfg.measurementId] = true; 161 gtag('js', new Date()); 162 gtag('config', cfg.measurementId, {allow_google_signals: false, allow_ad_personalization_signals: false}); 163 }); 164 } 165 if (adapter === 'google_ads' && accepted && /^AW-[0-9]+$/.test(cfg.conversionId || '')) { 166 load('biertheater-ads-' + cfg.conversionId, 'https://www.googletagmanager.com/gtag/js?id=' + encodeURIComponent(cfg.conversionId), function () { 167 if (!hasConsent(service.name) || configuredTags[cfg.conversionId]) return; 168 configuredTags[cfg.conversionId] = true; 169 gtag('js', new Date()); 170 gtag('config', cfg.conversionId); 171 }); 172 } 173 publish(service, accepted); 174 } 175 176 function cookiePattern(pattern) { 177 return new RegExp('^' + String(pattern).replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$'); 178 } 179 180 function eraseCookies(rules, allowContentsquareOptout) {
181 rules.forEach(function (rule) { 182 var regex = cookiePattern(rule[0]); 183 var paths = [rule[1] || '/', '/']; 184 var hosts = ['', rule[2] || '', w.location.hostname]; 185 var labels = w.location.hostname.split('.'); 186 for (var i = 0; i < labels.length - 1; i++) hosts.push('.' + labels.slice(i).join('.')); 187 (d.cookie || '').split(';').forEach(function (part) { 188 var name = part.split('=')[0].trim(); 189 // Matomo and the consent selection are intentionally outside this cleanup. 190 if (!name || /^_pk_/.test(name) || name === config.cookieName || !regex.test(name)) return; 191 // These store privacy choices, not analytics identifiers. 192 if (name === '_cs_c' || (name === '_cs_optout' && !allowContentsquareOptout)) return; 193 paths.forEach(function (path) { 194 hosts.forEach(function (host) { 195 d.cookie = name + '=; Max-Age=0; path=' + path + (host ? '; domain=' + host : ''); 196 }); 197 }); 198 }); 199 }); 200 } 201 202 function expert(code, accepted, app, service) { 203 if (!code) return; 204 try { 205 Function('consent', 'app', 'service', '"use strict";\n' + code)(accepted, app, service); 206 } catch (error) { 207 if (w.console) w.console.error('Consent callback', service.name, error); 208 } 209 } 210 211 if (trackingReady && trackingServices.length) { 212 w.gtag = w.gtag || gtag; 213 gtag('consent', 'default', { 214 analytics_storage: 'denied', ad_storage: 'denied', 215 ad_user_data: 'denied', ad_personalization: 'denied' 216 }); 217 } 218 219 services.forEach(function (service) { 220 states[service.name] = false; 221 var callbacks = service.__callbacks || {}; 222 var cookies = service.__cookies || []; 223 var tracking = trackingServices.indexOf(service) !== -1; 224 var contentsquare = service.name === 'contentsquare' && service.__adapter === 'contentsquare'; 225 // Klaro must not delete wildcard/third-party cookies independently of our scoped cleanup. 226 service.cookies = []; 227 service.callback = function (accepted, app) { 228 accepted = !!accepted && (!tracking || trackingReady) && (!contentsquare || !!config.__trackingEnabled); 229 var first = !initialized[service.name]; 230 if (!first && states[service.name] === accepted) return; 231 initialized[service.name] = true; 232 states[service.name] = accepted; 233 if (contentsquare) updateContentsquare(accepted, first); 234 if (first) expert(callbacks.init, accepted, app, service); 235 if (tracking) applyTracking(service, accepted); 236 if (contentsquare && accepted) startContentsquare(); 237 if (!accepted) eraseCookies(cookies); 238 expert(accepted ? callbacks.accept : callbacks.decline, accepted, app, service); 239 }; 240 }); 241 242 function installToggle() { 243 if (!config.__showToggleButton || d.getElementById('klaro_toggle_dialog')) return; 244 var button = d.createElement('button'); 245 button.id = 'klaro_toggle_dialog'; 246 button.className = 'klaro_toggle_dialog'; 247 button.type = 'button'; 248 button.setAttribute('data-klaro-open', ''); 249 button.setAttribute('aria-label', 'Datenschutz-Einstellungen öffnen'); 250 button.setAttribute('aria-haspopup', 'dialog'); 251 button.title = 'Datenschutz-Einstellungen öffnen'; 252 d.body.appendChild(button); 253 function refresh() { 254 button.hidden = !!d.querySelector('#klaro .cookie-modal,#klaro .cookie-notice'); 255 } 256 refresh(); 257 new MutationObserver(refresh).observe(d.getElementById('klaro') || d.body, {childList: true, subtree: true}); 258 } 259 260 d.addEventListener('click', function (event) { 261 var trigger = event.target.closest && event.target.closest('[data-klaro-open]'); 262 if (!trigger) return; 263 event.preventDefault(); 264 if (w.klaro && w.klaro.show) w.klaro.show(config, true); 265 }); 266 if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', installToggle); 267 else installToggle(); 268})(window, document);
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.