PageSourceSearch

https://www.mediashow.cz/wp-content/plugins/adsimple-cookie-manage…/js/local-banner.js?ver=2.2.1-1790299038

js mediashow.cz collected 2026-09-25 01:17:38 UTC 56,219 bytes, 1,426 lines download raw bytes

1/* Local fallback cookie banner script (English code comments) */
2(function () {
3  if (typeof window === 'undefined') return;
4
5  var cfg = window.ascmLocalConfig || {};
6  var cookieName = cfg.consentCookieName || 'as_cm_consent';
7  var ttlDays = cfg.consentTtlDays || 180;
8  var categories = Array.isArray(cfg.categories) ? cfg.categories.slice() : [];
9  if (!categories.length) {
10    categories = [
11      { key: 'required', label: (cfg.texts && cfg.texts.required_label) || 'Necessary', locked: true, default: true, description: (cfg.texts && cfg.texts.required_description) || '' },
12      { key: 'statistics', label: (cfg.texts && cfg.texts.statistics_label) || 'Statistics', locked: false, default: false, description: (cfg.texts && cfg.texts.statistics_description) || '' },
13      { key: 'marketing', label: (cfg.texts && cfg.texts.marketing_label) || 'Marketing', locked: false, default: false, description: (cfg.texts && cfg.texts.marketing_description) || '' }
14    ];
15  }
16
17  function setCookie(name, value, days) {
18    var expires = '';
19    if (days) {
20      var date = new Date();
21      date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
22      expires = '; expires=' + date.toUTCString();
23    }
24    document.cookie = name + '=' + encodeURIComponent(value) + expires + '; path=/; SameSite=Lax';
25  }
26
27  function getCookie(name) {
28    var nameEQ = name + '=';
29    var ca = document.cookie.split(';');
30    for (var i = 0; i < ca.length; i++) {
31      var c = ca[i];
32      while (c.charAt(0) === ' ') c = c.substring(1, c.length);
33      if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
34    }
35    return null;
36  }
37
38  function getStoredConsent() {
39    try {
40      var raw = getCookie(cookieName);
41      if (!raw) return null;
42      return JSON.parse(raw);
43    } catch (e) {
44      return null;
45    }
46  }
47
48  function storeConsent(consentMap) {
49    var payload = {
50      v: 1,
51      ts: Date.now(),
52      consent: consentMap
53    };
54    setCookie(cookieName, JSON.stringify(payload), ttlDays);
55    emitConsentChange(consentMap);
56  }
57
58  function emitConsentChange(consentMap) {
59    if (typeof window === 'undefined') {
60      return;
61    }
62    try {
63      var detail = { consent: consentMap };
64      var event;
65      if (typeof window.CustomEvent === 'function') {
66        event = new CustomEvent('ascm:consent-change', { detail: detail });
67      } else {
68        event = document.createEvent('CustomEvent');
69        event.initCustomEvent('ascm:consent-change', true, true, detail);
70      }
71      window.dispatchEvent(event);
72    } catch (error) {
73      // ignore
74    }
75    updateGoogleConsentMode(consentMap);
76  }
77
78  /* ---- Google Consent Mode v2 ---- */
79  var gcmCfg = cfg.googleConsentMode || {};
80  var gcmEnabled = gcmCfg.enabled === true || gcmCfg.enabled === '1';
81
82  function ensureGtag() {
83    window.dataLayer = window.dataLayer || [];
84    if (typeof window.gtag !== 'function') {
85      window.gtag = function () { window.dataLayer.push(arguments); };
86    }
87  }
88
89  function mapConsentToGcm(consentMap) {
90    var stats = consentMap && consentMap.statistics ? 'granted' : 'denied';
91    var marketing = consentMap && consentMap.marketing ? 'granted' : 'denied';
92    return {
93      ad_storage: marketing,
94      ad_user_data: marketing,
95      ad_personalization: marketing,
96      analytics_storage: stats,
97      functionality_storage: 'granted',
98      personalization_storage: stats,
99      security_storage: 'granted'
100    };
101  }
102
103  function initGoogleConsentMode() {
104    if (!gcmEnabled) return;
105    ensureGtag();
106    var defaultState = gcmCfg.defaultConsent === 'granted' ? 'granted' : 'denied';
107    var stored = getStoredConsent();
108    if (stored && stored.consent) {
109      window.gtag('consent', 'default', mapConsentToGcm(stored.consent));
110    } else {
111      window.gtag('consent', 'default', {
112        ad_storage: defaultState,
113        ad_user_data: defaultState,
114        ad_personalization: defaultState,
115        analytics_storage: defaultState,
116        functionality_storage: 'granted',
117        personalization_storage: defaultState,
118        security_storage: 'granted',
119        wait_for_update: 500
120      });
121    }
122  }
123
124  function updateGoogleConsentMode(consentMap) {
125    if (!gcmEnabled) return;
126    ensureGtag();
127    window.gtag('consent', 'update', mapConsentToGcm(consentMap));
128  }
129
130  initGoogleConsentMode();
131
132  function buildDefaultConsentMap() {
133    var map = {};
134    categories.forEach(function (c) {
135      var key = c.key;
136      if (!key) return;
137      if (c.locked) {
138        map[key] = true;
139      } else {
140        map[key] = !!c.default;
141      }
142    });
143    return map;
144  }
145
146  function createEl(tag, opts) {
147    var el = document.createElement(tag);
148    opts = opts || {};
149    if (opts.className) el.className = opts.className;
150    if (opts.text) el.textContent = opts.text;
151    if (opts.html) el.innerHTML = opts.html;
152    if (opts.attrs) Object.keys(opts.attrs).forEach(function (k) { el.setAttribute(k, opts.attrs[k]); });
153    if (opts.style && typeof opts.style === 'object') {
154      Object.keys(opts.style).forEach(function (key) {
155        el.style[key] = opts.style[key];
156      });
157    }
158    return el;
159  }
160
161  function parseHexColor(value) {
162    if (typeof value !== 'string') {
163      return null;
164    }
165    var hex = value.trim();
166    if (hex.charAt(0) === '#') {
167      hex = hex.substring(1);
168    }
169    if (hex.length === 3 || hex.length === 4) {
170      hex = hex.split('').map(function (ch) { return ch + ch; }).join('');
171    }
172    if (hex.length === 6 || hex.length === 8) {
173      var r = parseInt(hex.substring(0, 2), 16);
174      var g = parseInt(hex.substring(2, 4), 16);
175      var b = parseInt(hex.substring(4, 6), 16);
176      var a = 1;
177      if (hex.length === 8) {
178        a = parseInt(hex.substring(6, 8), 16) / 255;
179      }
180      if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
181        return { r: r, g: g, b: b, a: isNaN(a) ? 1 : a };
182      }
183    }
184    return null;
185  }
186
187  function computeOverlayBackground(effects) {
188    var baseColor = null;
189    if (effects && typeof effects.popup_overlay_color === 'string' && effects.popup_overlay_color.trim() !== '') {
190      baseColor = effects.popup_overlay_color;
191    }
192    if (!baseColor) {
193      return {
194        rgba: null,
195        alpha: 0,
196        background: null,
197      };
198    }
199    var parsed = parseHexColor(baseColor) || { r: 15, g: 23, b: 42, a: 1 };
200    var opacity = 0.5;
201    if (effects && typeof effects.popup_overlay_opacity !== 'undefined') {
202      var rawOpacity = Number(effects.popup_overlay_opacity);
203      if (!isNaN(rawOpacity)) {
204        opacity = Math.max(0, Math.min(100, rawOpacity)) / 100;
205      }
206    }
207    var overlayAlpha = Math.max(0, Math.min(1, opacity * (typeof parsed.a === 'number' ? parsed.a : 1)));
208
209    return {
210      rgba: parsed,
211      alpha: overlayAlpha,
212      background: 'rgba(' + parsed.r + ',' + parsed.g + ',' + parsed.b + ',' + overlayAlpha + ')',
213    };
214  }
215
216  var injectedScopes = {};
217
218  function injectCustomColorStyles(scopeClass, colors) {
219    if (!scopeClass || !colors || typeof colors !== 'object') {
220      return;
221    }
222
223    if (injectedScopes[scopeClass]) {
224      return;
225    }
226
227    function safe(value) {
228      return typeof value === 'string' && value.trim() !== '' ? value.trim() : '';
229    }
230
231    var cssParts = [];
232    var popupBg = safe(colors.popup);
233    var popupText = safe(colors.popupText);
234    var popupElements = safe(colors.popupElements);
235    var popupLink = safe(colors.popupLink);
236    var popupLinkHover = safe(colors.popupLinkHover);
237
238    var primaryBg = safe(colors.button);
239    var primaryText = safe(colors.buttonText);
240    var primaryBorder = safe(colors.buttonBorder);
241    if (!primaryBorder && primaryBg) {
242      primaryBorder = primaryBg;
243    }
244    var primaryBgHover = safe(colors.buttonHover);
245    var primaryTextHover = safe(colors.buttonTextHover);
246    var primaryBorderHover = safe(colors.buttonBorderHover);
247    if (!primaryBorderHover && primaryBgHover) {
248      primaryBorderHover = primaryBgHover;
249    }
250
251    var secondaryBg = safe(colors.necessaryCookiesOnlyButton);
252    var secondaryText = safe(colors.necessaryCookiesOnlyButtonText);
253    var secondaryBorder = safe(colors.necessaryCookiesOnlyButtonBorder);
254    if (!secondaryBorder && secondaryBg) {
255      secondaryBorder = secondaryBg;
256    }
257    var secondaryBgHover = safe(colors.necessaryCookiesOnlyButtonHover);
258    var secondaryTextHover = safe(colors.necessaryCookiesOnlyButtonTextHover);
259    var secondaryBorderHover = safe(colors.necessaryCookiesOnlyButtonBorderHover);
260    if (!secondaryBorderHover && secondaryBgHover) {
261      secondaryBorderHover = secondaryBgHover;
262    }
263    var checkboxColor = safe(colors.checkboxColor);
264
265    if (popupBg) {
266      cssParts.push('.cc-window.' + scopeClass + ' { background-color: ' + popupBg + '; }');
267      cssParts.push('.cc-settings-modal.' + scopeClass + ' { background-color: ' + popupBg + '; }');
268    }
269
270    if (popupText) {
271      cssParts.push('.cc-window.' + scopeClass + ', .cc-window.' + scopeClass + ' .cc-message { color: ' + popupText + '; }');
272      cssParts.push('.cc-settings-modal.' + scopeClass + ' { color: ' + popupText + '; }');
273    }
274
275    if (popupElements) {
276      cssParts.push('.cc-window.' + scopeClass + ' .cc-header { color: ' + popupElements + '; }');
277      cssParts.push('.cc-settings-modal.' + scopeClass + ' .cc-settings-title { color: ' + popupElements + '; }');
278    }
279
280    if (popupLink) {
281      cssParts.push('.cc-window.' + scopeClass + ' .cc-link { color: ' + popupLink + '; }');
282    }
283    if (popupLinkHover) {
284      cssParts.push('.cc-window.' + scopeClass + ' .cc-link:hover, .cc-window.' + scopeClass + ' .cc-link:focus { color: ' + popupLinkHover + '; }');
285    }
286
287    if (primaryBg || primaryText || primaryBorder) {
288      var primaryRule = '.cc-window.' + scopeClass + ' .cc-btn.cc-allow { ';
289      if (primaryBg) primaryRule += 'background-color: ' + primaryBg + '; ';
290      if (primaryText) primaryRule += 'color: ' + primaryText + '; ';
291      if (primaryBorder) primaryRule += 'border-color: ' + primaryBorder + '; ';
292      primaryRule += '}';
293      cssParts.push(primaryRule);
294    }
295
296    if (primaryBgHover || primaryTextHover || primaryBorderHover) {
297      var primaryHoverRule = '.cc-window.' + scopeClass + ' .cc-btn.cc-allow:hover, .cc-window.' + scopeClass + ' .cc-btn.cc-allow:focus { ';
298      if (primaryBgHover) primaryHoverRule += 'background-color: ' + primaryBgHover + '; ';
299      if (primaryTextHover) primaryHoverRule += 'color: ' + primaryTextHover + '; ';
300      if (primaryBorderHover) primaryHoverRule += 'border-color: ' + primaryBorderHover + '; ';
301      primaryHoverRule += '}';
302      cssParts.push(primaryHoverRule);
303    }
304
305    if (secondaryBg || secondaryText || secondaryBorder) {
306      var secondaryRule = '.cc-window.' + scopeClass + ' .cc-btn.cc-deny, .cc-window.' + scopeClass + ' .cc-btn.cc-settings { ';
307      if (secondaryBg) secondaryRule += 'background-color: ' + secondaryBg + '; ';
308      if (secondaryText) secondaryRule += 'color: ' + secondaryText + '; ';
309      if (secondaryBorder) secondaryRule += 'border-color: ' + secondaryBorder + '; ';
310      secondaryRule += '}';
311      cssParts.push(secondaryRule);
312    }
313
314    if (secondaryBgHover || secondaryTextHover || secondaryBorderHover) {
315      var secondaryHoverRule = '.cc-window.' + scopeClass + ' .cc-btn.cc-deny:hover, .cc-window.' + scopeClass + ' .cc-btn.cc-deny:focus, .cc-window.' + scopeClass + ' .cc-btn.cc-settings:hover, .cc-window.' + scopeClass + ' .cc-btn.cc-settings:focus { ';
316      if (secondaryBgHover) secondaryHoverRule += 'background-color: ' + secondaryBgHover + '; ';
317      if (secondaryTextHover) secondaryHoverRule += 'color: ' + secondaryTextHover + '; ';
318      if (secondaryBorderHover) secondaryHoverRule += 'border-color: ' + secondaryBorderHover + '; ';
319      secondaryHoverRule += '}';
320      cssParts.push(secondaryHoverRule);
321    }
322
323    if (checkboxColor) {
324      cssParts.push('.cc-settings-modal.' + scopeClass + ' .cc-switch input:checked + .cc-switch__slider { background-color: ' + checkboxColor + '; border-color: ' + checkboxColor + '; }');
325    }
326
327    if (!cssParts.length) {
328      injectedScopes[scopeClass] = true;
329      return;
330    }
331
332    var styleId = 'cc-colors-' + scopeClass;
333    var styleEl = document.getElementById(styleId);
334    if (!styleEl) {
335      styleEl = document.createElement('style');
336      styleEl.id = styleId;
337      document.head.appendChild(styleEl);
338    }
339    styleEl.textContent = cssParts.join('\n');
340    injectedScopes[scopeClass] = true;
341  }
342
343  function extractColorsFromCssVariables(vars) {
344    if (!vars || typeof vars !== 'object') {
345      return null;
346    }
347    var map = {};
348    var assign = function (targetKey, varName) {
349      var value = vars[varName];
350      if (typeof value === 'string' && value.trim() !== '') {
351        map[targetKey] = value.trim();
352      }
353    };
354
355    assign('popup', '--ascm-popup-bg');
356    assign('popupText', '--ascm-popup-text');
357    assign('popupElements', '--ascm-popup-elements');
358    assign('popupLink', '--ascm-popup-link');
359    assign('popupLinkHover', '--ascm-popup-link-hover');
360    assign('checkboxColor', '--ascm-checkbox-color');
361    assign('button', '--ascm-button-primary-bg');
362    assign('buttonHover', '--ascm-button-primary-hover');
363    assign('buttonText', '--ascm-button-primary-text');
364    assign('buttonTextHover', '--ascm-button-primary-text-hover');
365    assign('buttonBorder', '--ascm-button-primary-border');
366    assign('buttonBorderHover', '--ascm-button-primary-border-hover');
367    assign('necessaryCookiesOnlyButton', '--ascm-button-secondary-bg');
368    assign('necessaryCookiesOnlyButtonHover', '--ascm-button-secondary-hover');
369    assign('necessaryCookiesOnlyButtonText', '--ascm-button-secondary-text');
370    assign('necessaryCookiesOnlyButtonTextHover', '--ascm-button-secondary-text-hover');
371    assign('necessaryCookiesOnlyButtonBorder', '--ascm-button-secondary-border');
372    assign('necessaryCookiesOnlyButtonBorderHover', '--ascm-button-secondary-border-hover');
373
374    return Object.keys(map).length ? map : null;
375  }
376
377  function applyCssVariables(element) {
378    if (!element || !cfg.cssVariables || typeof element.style === 'undefined') {
379      return;
380    }
381    Object.keys(cfg.cssVariables).forEach(function (key) {
382      element.style.setProperty(key, cfg.cssVariables[key]);
383    });
384  }
385
386  function renderBanner(showBannerImmediately) {
387    var root = document.getElementById('ascm-banner-root');
388    if (!root) return;
389
390    var storedConsent = getStoredConsent();
391    var current = storedConsent && storedConsent.consent && typeof storedConsent.consent === 'object'
392      ? storedConsent.consent
393      : buildDefaultConsentMap();
394
395    var rawPos = (cfg.position || 'bottom').toString();
396    var sanitizedPos = rawPos.replace(/[^a-z0-9\-]/gi, '').toLowerCase();
397    if (!sanitizedPos) {
398      sanitizedPos = 'bottom';
399    }
400
401    var layoutStyle = cfg.layout_style === 'edgeless' ? 'edgeless' : 'classic';
402    var customColorsEnabled =
403      cfg.custom_colors_enabled === true ||
404      cfg.custom_colors_enabled === '1' ||
405      cfg.custom_colors_enabled === 1;
406    var rawThemeKey = typeof cfg.theme === 'string' && cfg.theme.trim() !== '' ? cfg.theme.trim() : 'theme1';
407    var themeKey = customColorsEnabled ? 'theme1' : rawThemeKey;
408    var buttonLayoutKey = typeof cfg.layout === 'string' && cfg.layout.trim() !== '' ? cfg.layout.trim() : 'classic';
409    var isModalPosition = sanitizedPos === 'popup';
410    var isBannerLayout = sanitizedPos === 'top' || sanitizedPos === 'bottom' || sanitizedPos === 'top-push';
411    var isFloatingLayout = !isBannerLayout;
412    var consentType = (cfg.type || 'opt-out').toString();
413    var removeShadow = cfg.popup_effects && String(cfg.popup_effects.popup_shadow) === '0';
414    var overlayInfo = computeOverlayBackground(cfg.popup_effects);
415    var overlayShouldApply = overlayInfo && overlayInfo.background && overlayInfo.alpha > 0;
416    if (!isModalPosition && overlayShouldApply) {
417      var overlayColorValue = '';
418      if (cfg.popup_effects && typeof cfg.popup_effects.popup_overlay_color === 'string') {
419        overlayColorValue = cfg.popup_effects.popup_overlay_color.trim();
420      }
421      var overlayOpacityValue = NaN;
422      if (cfg.popup_effects && typeof cfg.popup_effects.popup_overlay_opacity !== 'undefined') {
423        overlayOpacityValue = Number(cfg.popup_effects.popup_overlay_opacity);
424      }
425      var overlayOpacityChanged = !isNaN(overlayOpacityValue) && overlayOpacityValue !== 50;
426      if (!overlayOpacityChanged && overlayColorValue === '') {
427        overlayShouldApply = false;
428      }
429    }
430
431    var overlayElement = null;
432    if (!isModalPosition) {
433      var existingOverlay = root.querySelector('.cc-overlay');
434      if (!existingOverlay && overlayShouldApply) {
435        existingOverlay = createEl('div', { className: 'cc-overlay' });
436      }
437      if (existingOverlay) {
438        if (!existingOverlay.parentNode) {
439          root.appendChild(existingOverlay);
440        }
441        if (overlayShouldApply && overlayInfo && overlayInfo.background) {
442          existingOverlay.style.background = overlayInfo.background;
443        } else {
444          existingOverlay.style.background = '';
445        }
446        existingOverlay.style.display = 'none';
447        overlayElement = overlayShouldApply ? existingOverlay : null;
448      }
449    } else {
450      var overlayToHide = root.querySelector('.cc-overlay');
451      if (overlayToHide) {
452        overlayToHide.style.display = 'none';
453      }
454    }
455
456    function setOverlayVisibility(visible) {
457      if (!overlayElement) {
458        return;
459      }
460      overlayElement.style.display = visible && overlayShouldApply ? 'block' : 'none';
461    }
462
463    var settingsCfg = cfg.settings_button && typeof cfg.settings_button === 'object' ? cfg.settings_button : {};
464    var settingsLabel = settingsCfg.label || (cfg.texts && cfg.texts.policy) || 'Privacy settings';
465    var showIconDesktop = settingsCfg.iconDesktopShow === '1';
466    var badgeMediaQuery = typeof window !== 'undefined' && typeof window.matchMedia === 'function'
467      ? window.matchMedia('(max-width: 640px)')
468      : null;
469
470    var scopeClass = 'cc-scope-' + Math.random().toString(36).slice(2, 10);
471    var colorOverrideClass = null;
472    var colorSource = null;
473    if (customColorsEnabled && cfg.custom_colors) {
474      colorSource = cfg.custom_colors;
475    } else if (!customColorsEnabled) {
476      colorSource = extractColorsFromCssVariables(cfg.cssVariables);
477    }
478    if (colorSource) {
479      colorOverrideClass = 'cc-color-override-' + Math.abs(JSON.stringify(colorSource).split('').reduce(function (acc, ch) {
480        acc = ((acc << 5) - acc) + ch.charCodeAt(0);
481        return acc | 0;
482      }, 0));
483      injectCustomColorStyles(scopeClass, colorSource);
484    }
485
486    var bannerClasses = ['cc-window', 'cc-window-styles-' + themeKey, 'cc-window-button-layout-' + buttonLayoutKey, 'cc-type-' + consentType, 'cc-theme-' + layoutStyle, scopeClass];
487    if (isModalPosition) {
488      bannerClasses.push('cc-window__modal', 'cc-window-white-label');
489    } else if (isBannerLayout) {
490      bannerClasses.push('cc-banner');
491    } else if (isFloatingLayout) {
492      bannerClasses.push('cc-floating');
493    }
494    if (colorOverrideClass) {
495      bannerClasses.push(colorOverrideClass);
496    }
497    if (!isModalPosition) {
498      switch (sanitizedPos) {
499        case 'top':
500          bannerClasses.push('cc-top');
501          break;
502        case 'bottom':
503          bannerClasses.push('cc-bottom');
504          break;
505        case 'bottom-left':
506          bannerClasses.push('cc-bottom', 'cc-left');
507          break;
508        case 'bottom-right':
509          bannerClasses.push('cc-bottom', 'cc-right');
510          break;
511        case 'top-left':
512          bannerClasses.push('cc-top', 'cc-left');
513          break;
514        case 'top-right':
515          bannerClasses.push('cc-top', 'cc-right');
516          break;
517        case 'top-push':
518          bannerClasses.push('cc-top', 'cc-top-fixed');
519          break;
520        default:
521          bannerClasses.push('cc-bottom');
522          break;
523      }
524    }
525
526    var banner = createEl('div', { className: bannerClasses.join(' ') });
527    banner.setAttribute('role', 'dialog');
528    banner.setAttribute('aria-label', 'cookieconsent');
529    banner.setAttribute('aria-describedby', 'cookieconsent:desc');
530    banner.setAttribute('data-position', sanitizedPos);
531    banner.setAttribute('data-nosnippet', 'true');
532    banner.setAttribute('aria-live', 'polite');
533    if (isModalPosition) {
534      banner.setAttribute('aria-modal', 'true');
535    }
536
537    if (removeShadow) {
538      banner.style.boxShadow = 'none';
539      banner.style.filter = 'none';
540    }
541    if (isModalPosition) {
542      if (overlayShouldApply && overlayInfo && overlayInfo.background) {
543        banner.style.setProperty('background', overlayInfo.background, 'important');
544        banner.style.setProperty('background-color', overlayInfo.background, 'important');
545      } else {
546        // Explicitly set to transparent to override CSS default
547        banner.style.setProperty('background', 'transparent', 'important');
548        banner.style.setProperty('background-color', 'transparent', 'important');
549      }
550    }
551
552    if (overlayElement && overlayElement.parentNode !== root) {
553      root.appendChild(overlayElement);
554    }
555
556    root.appendChild(banner);
557
558    if (overlayElement) {
559      root.insertBefore(overlayElement, banner);
560    }
561
562    setOverlayVisibility(showBannerImmediately);
563
564    if (sanitizedPos === 'top-push') {
565      banner.style.position = 'fixed';
566      banner.style.top = '0';
567      banner.style.left = '0';
568      banner.style.right = '0';
569      banner.style.margin = '0';
570    }
571
572    var container = banner;
573    var usesTabbedContent = isModalPosition || sanitizedPos === 'bottom';
574    var tabHinweis = null;
575    var tabSettings = null;
576    var tabPanelNotice = null;
577    var tabPanelSettings = null;
578    if (isModalPosition) {
579      var modalClasses = ['cc-modal', 'cc-modal__shadow', 'cc-window', 'cc-floating', 'cc-type-' + consentType, 'cc-theme-' + layoutStyle, 'cc-popup', scopeClass];
580      if (removeShadow) {
581        modalClasses = modalClasses.filter(function (className) { return className !== 'cc-modal__shadow'; });
582      }
583      if (colorOverrideClass) {
584        modalClasses.push(colorOverrideClass);
585      }
586      var modal = createEl('div', { className: modalClasses.join(' ') });
587      container.appendChild(modal);
588      container = modal;
589      if (removeShadow) {
590        modal.style.boxShadow = 'none';
591        modal.style.filter = 'none';
592      }
593    }
594
595    var closeButton = null;
596    if (isModalPosition) {
597      closeButton = createEl('div', {
598        className: 'cc-modal-close cc-details-toggle cc-details-toogle',
599        attrs: { 'data-type': 'additional', role: 'button', tabindex: '0' }
600      });
601      closeButton.style.display = 'none';
602      closeButton.appendChild(document.createElement('span'));
603      closeButton.appendChild(document.createElement('span'));
604      container.appendChild(closeButton);
605    }
606
607  function createActionButton(className, ariaLabel, labelEntries) {
608    var anchor = createEl('a', {
609      className: className,
610      attrs: {
611        role: 'button',
612        tabindex: '0',
613        'aria-label': ariaLabel
614      },
615      style: {
616        textDecoration: 'none'
617      }
618    });
619      anchor.style.pointerEvents = 'auto';
620      anchor.style.cursor = 'pointer';
621      var entries = Array.isArray(labelEntries) && labelEntries.length ? labelEntries : [{ type: 'hinweis', text: ariaLabel }];
622      entries.forEach(function (entry, index) {
623        if (!entry || typeof entry.text !== 'string') {
624          return;
625        }
626        var span = createEl('span', { text: entry.text });
627        if (entry.type) {
628          span.setAttribute('data-type', entry.type);
629        }
630        if (entry.hidden || index > 0) {
631          span.style.display = 'none';
632        }
633        anchor.appendChild(span);
634      });
635      return anchor;
636    }
637
638    function renderTextWithPlaceholders(rawText) {
639      var text = typeof rawText === 'string' ? rawText : '';
640
641      text = text.replace(/\{cookie-einstellungen\}([\s\S]*?)\{\/cookie-einstellungen\}/gi, function (_, inner) {
642        var label = inner && inner.trim() ? inner : ((cfg.texts && cfg.texts.policy) || 'Privacy preferences');
643        return '<a class="cc-link cc-settings-link" role="button" tabindex="0" data-tab="einstellungen">' + label + '</a>';
644      });
645
646      text = text.replace(/\{erforderliche-cookies\}([\s\S]*?)\{\/erforderliche-cookies\}/gi, function (_, inner) {
647        var label = inner && inner.trim() ? inner : ((cfg.texts && cfg.texts.necessary_cookies_only) || 'Necessary only');
648        return '<a class="cc-link cc-necessary-link" role="button" tabindex="0">' + label + '</a>';
649      });
650
651      return text;
652    }
653
654    var message = createEl('div', { className: 'cc-message' });
655    var titleText = (cfg.texts && cfg.texts.welcome_title) || 'Cookies & Privacy';
656    if (usesTabbedContent) {
657      var heading = createEl('div', { className: 'cc-modal__heading' });
658      var headingNotice = createEl('div', { attrs: { 'data-type': 'hinweis' }, text: titleText });
659      var headingSettings = createEl('div', { attrs: { 'data-type': 'einstellungen' }, text: (cfg.texts && cfg.texts.title) || titleText });
660      headingSettings.style.display = 'none';
661      var headingTcf = createEl('div', { attrs: { 'data-type': 'tcf' }, text: (cfg.texts && cfg.texts.tcf_heading) || 'TCF-Datenmanager' });
662      headingTcf.style.display = 'none';
663      heading.appendChild(headingNotice);
664      heading.appendChild(headingSettings);
665      heading.appendChild(headingTcf);
666      message.appendChild(heading);
667    } else if (titleText) {
668      var header = createEl('span', { className: 'cc-header', text: titleText });
669      message.appendChild(header);
670      message.appendChild(document.createElement('br'));
671    }
672    var descText = (cfg.texts && cfg.texts.message) || 'Choose which categories to allow.';
673    var introText = (cfg.texts && cfg.texts.welcome_message) || 'We use cookies to improve our website and your experience. You can review and adjust your preferences at any time.';
674    if (usesTabbedContent) {
675      var modalText = createEl('div', { className: 'cc-modal__text', attrs: { id: 'cookieconsent:desc' } });
676      var tabsWrapper = createEl('div', { className: 'cc-modal__tabs cc-modal__main-tabs', attrs: { role: 'tablist' } });
677      tabHinweis = createEl('div', {
678        className: 'cc-modal__tabs-heading_item active',
679        attrs: {
680          role: 'tab',
681          'aria-selected': 'true',
682          tabindex: '0',
683          'aria-controls': scopeClass + '-content-hinweis',
684          id: scopeClass + '-tab-hinweis',
685          'data-tab': 'hinweis'
686        },
687        text: (cfg.texts && cfg.texts.heading_notice) || 'Notice'
688      });
689      tabsWrapper.appendChild(tabHinweis);
690      tabSettings = createEl('div', {
691        className: 'cc-modal__tabs-heading_item',
692        attrs: {
693          role: 'tab',
694          'aria-selected': 'false',
695          tabindex: '0',
696          'aria-controls': scopeClass + '-content-einstellungen',
697          id: scopeClass + '-tab-einstellungen',
698          'data-tab': 'einstellungen'
699        },
700        text: (cfg.texts && cfg.texts.heading_settings) || 'Settings'
701      });
702      tabsWrapper.appendChild(tabSettings);
703      var tabsContent = createEl('div', { className: 'cc-modal__tabs-content' });
704      tabPanelNotice = createEl('div', {
705        className: 'cc-modal__tab-panel active',
706        attrs: { id: scopeClass + '-content-hinweis', role: 'tabpanel', 'aria-labelledby': scopeClass + '-tab-hinweis' }
707      });
708      var noticeParagraph = createEl('p', {});
709      noticeParagraph.innerHTML = renderTextWithPlaceholders(introText);
710      tabPanelNotice.appendChild(noticeParagraph);
711      tabsContent.appendChild(tabPanelNotice);
712      tabPanelSettings = createEl('div', {
713        className: 'cc-modal__tab-panel',
714        attrs: { id: scopeClass + '-content-einstellungen', role: 'tabpanel', 'aria-labelledby': scopeClass + '-tab-einstellungen' }
715      });
716      tabPanelSettings.appendChild(createEl('p', { text: descText }));
717      tabsContent.appendChild(tabPanelSettings);
718      modalText.appendChild(tabsWrapper);
719      modalText.appendChild(tabsContent);
720      message.appendChild(modalText);
721    } else {
722      var bodySpan = createEl('span', {});
723      bodySpan.innerHTML = renderTextWithPlaceholders(introText);
724      bodySpan.id = 'cookieconsent:desc';
725      message.appendChild(bodySpan);
726    }
727
728    var compliance = createEl('div', { className: 'cc-compliance cc-highlight' });
729    var buttonWrapper = createEl('div', { className: 'cc-button_wrapper' });
730    var allowText = (cfg.texts && cfg.texts.allow) || 'Accept all';
731    var allowSelectedText = (cfg.texts && cfg.texts.allow_selected) || ((cfg.texts && cfg.texts.allow) || 'Save selection');
732    var btnAllow = createActionButton('cc-btn cc-allow', allowText, [
733      { type: 'hinweis', text: allowText },
734      { type: 'einstellungen', text: allowSelectedText, hidden: true },
735      { type: 'tcf', text: allowSelectedText, hidden: true }
736    ]);
737    var necessaryText = (cfg.texts && cfg.texts.necessary_cookies_only) || 'Necessary only';
738    var btnNecessary = createActionButton('cc-btn cc-deny cc-deny-custom', necessaryText, [
739      { type: 'hinweis', text: necessaryText }
740    ]);
741    btnNecessary.id = 'cookieDenyButton';
742    var policyText = (cfg.texts && cfg.texts.policy) || 'Privacy preferences';
743    var btnSettings = createActionButton('cc-btn cc-settings cc-details-toggle cc-details-toogle', policyText, [
744      { type: 'hinweis', text: policyText }
745    ]);
746    btnSettings.setAttribute('data-tab', 'einstellungen');
747    btnSettings.setAttribute('aria-expanded', 'false');
748    btnSettings.setAttribute('aria-controls', 'cookieDetails');
749    buttonWrapper.appendChild(btnAllow);
750    buttonWrapper.appendChild(btnNecessary);
751    compliance.appendChild(buttonWrapper);
752
753    if (cfg.links && (cfg.links.privacy || cfg.links.imprint)) {
754      var complianceFooter = createEl('div', { className: 'cc-compliance__footer' });
755      var footerLinks = createEl('div', { className: 'cc-footer__links' });
756      if (cfg.links.imprint) {
757        footerLinks.appendChild(createEl('a', {
758          text: cfg.links.imprintLabel || 'Legal notice',
759          attrs: { href: cfg.links.imprint, target: '_self' },
760          style: { textDecoration: 'none' }
761        }));
762      }
763      if (cfg.links.privacy) {
764        footerLinks.appendChild(createEl('a', {
765          text: cfg.links.privacyLabel || 'Privacy policy',
766          attrs: { href: cfg.links.privacy, target: '_self' },
767          style: { textDecoration: 'none' }
768        }));
769      }
770      complianceFooter.appendChild(footerLinks);
771      compliance.appendChild(complianceFooter);
772    }
773
774    container.appendChild(message);
775    var details = createEl('div', { className: 'cc-details', style: 'display: none;' });
776    details.id = 'cookieDetails';
777    var detailsBody = createEl('div', { className: 'cc-details-body' });
778    details.appendChild(detailsBody);
779    var detailsWrapperNotice = createEl('div', { className: 'cc-details-wrapper', attrs: { id: 'hinweis' } });
780    var detailsWrapperSettings = createEl('div', { className: 'cc-details-wrapper', attrs: { id: 'einstellungen' } });
781    var detailsWrapperTcf = createEl('div', { className: 'cc-details-wrapper', attrs: { id: 'tcf' } });
782    detailsWrapperTcf.style.display = 'none';
783    detailsWrapperTcf.appendChild(createEl('div', { text: 'Laden...' }));
784    detailsBody.appendChild(detailsWrapperNotice);
785    detailsBody.appendChild(detailsWrapperSettings);
786    detailsBody.appendChild(detailsWrapperTcf);
787    container.appendChild(details);
788    container.appendChild(compliance);
789
790    if (closeButton) {
791      closeButton.setAttribute('data-tab', 'hinweis');
792    }
793
794    var dataTypeElements = Array.prototype.slice.call(container.querySelectorAll('[data-type]'));
795    var detailsToggleElements = Array.prototype.slice.call(container.querySelectorAll('.cc-details-toggle, .cc-details-toogle'));
796    var categoryEntries = [];
797    var selectAllCheckbox = null;
798    var currentView = 'hinweis';
799
800    function toggleDataTypeVisibility(targetType) {
801      dataTypeElements.forEach(function (node) {
802        var type = node.getAttribute('data-type');
803        if (!type) {
804          return;
805        }
806        node.style.display = type === targetType ? '' : 'none';
807      });
808    }
809
810    function toggleCategoryDetails(entry, forceOpen) {
811      var shouldOpen = typeof forceOpen === 'boolean' ? forceOpen : !entry.menuItem.classList.contains('cc-selected');
812      if (shouldOpen) {
813        entry.menuItem.classList.add('cc-selected');
814        entry.menuItem.setAttribute('aria-expanded', 'true');
815        entry.content.classList.add('cc-selected');
816        entry.content.style.display = 'block';
817      } else {
818        entry.menuItem.classList.remove('cc-selected');
819        entry.menuItem.setAttribute('aria-expanded', 'false');
820        entry.content.classList.remove('cc-selected');
821        entry.content.style.display = 'none';
822      }
823    }
824
825    function updateSelectAllState() {
826      if (!selectAllCheckbox) {
827        return;
828      }
829      var eligible = categoryEntries.filter(function (entry) { return !entry.locked; });
830      if (!eligible.length) {
831        selectAllCheckbox.indeterminate = false;
832        selectAllCheckbox.checked = true;
833        return;
834      }
835      var checkedCount = eligible.reduce(function (count, entry) {
836        return count + (entry.checkbox.checked ? 1 : 0);
837      }, 0);
838      selectAllCheckbox.indeterminate = checkedCount > 0 && checkedCount < eligible.length;
839      selectAllCheckbox.checked = checkedCount === eligible.length;
840    }
841
842    function setView(view) {
843      var targetView = view || 'hinweis';
844      currentView = targetView;
845
846      if (tabHinweis && tabSettings) {
847        var isHinweis = targetView === 'hinweis';
848        var isSettings = targetView === 'einstellungen';
849        tabHinweis.classList.toggle('active', isHinweis);
850        tabHinweis.setAttribute('aria-selected', isHinweis ? 'true' : 'false');
851        tabSettings.classList.toggle('active', isSettings);
852        tabSettings.setAttribute('aria-selected', isSettings ? 'true' : 'false');
853        if (targetView === 'tcf') {
854          tabHinweis.classList.remove('active');
855          tabHinweis.setAttribute('aria-selected', 'false');
856          tabSettings.classList.remove('active');
857          tabSettings.setAttribute('aria-selected', 'false');
858        }
859      }
860      if (tabPanelNotice && tabPanelSettings) {
861        var panelHinweis = targetView === 'hinweis';
862        var panelSettings = targetView === 'einstellungen';
863        tabPanelNotice.classList.toggle('active', panelHinweis);
864        tabPanelNotice.setAttribute('aria-hidden', panelHinweis ? 'false' : 'true');
865        tabPanelSettings.classList.toggle('active', panelSettings);
866        tabPanelSettings.setAttribute('aria-hidden', panelSettings ? 'false' : 'true');
867        if (!panelHinweis && !panelSettings) {
868          tabPanelNotice.classList.remove('active');
869          tabPanelSettings.classList.remove('active');
870          tabPanelNotice.setAttribute('aria-hidden', 'true');
871          tabPanelSettings.setAttribute('aria-hidden', 'true');
872        }
873      }
874
875      if (details) {
876        if (targetView === 'hinweis') {
877          details.style.display = 'none';
878        } else {
879          details.style.display = 'block';
880        }
881      }
882      if (detailsWrapperNotice) {
883        detailsWrapperNotice.style.display = targetView === 'hinweis' ? '' : 'none';
884      }
885      if (detailsWrapperSettings) {
886        detailsWrapperSettings.style.display = targetView === 'einstellungen' ? '' : 'none';
887      }
888      if (detailsWrapperTcf) {
889        detailsWrapperTcf.style.display = targetView === 'tcf' ? '' : 'none';
890      }
891      if (closeButton) {
892        closeButton.style.display = targetView === 'hinweis' ? 'none' : '';
893      }
894      if (btnSettings) {
895        btnSettings.setAttribute('aria-expanded', targetView === 'hinweis' ? 'false' : 'true');
896      }
897      if (btnAllow) {
898        btnAllow.style.width = targetView === 'einstellungen' ? '100%' : '';
899        btnAllow.style.display = targetView === 'einstellungen' ? 'block' : '';
900        btnAllow.classList.toggle('cc-btn--fullwidth', targetView === 'einstellungen');
901      }
902      if (btnNecessary) {
903        btnNecessary.style.display = targetView === 'einstellungen' ? 'none' : '';
904      }
905      toggleDataTypeVisibility(targetView);
906      if (targetView === 'hinweis') {
907        categoryEntries.forEach(function (entry) {
908          toggleCategoryDetails(entry, false);
909        });
910      } else if (targetView === 'einstellungen') {
911        updateSelectAllState();
912      }
913    }
914
915    setView('hinweis');
916
917    function attachTabListeners(tabEl) {
918      if (!tabEl) {
919        return;
920      }
921      tabEl.addEventListener('click', handleToggleActivation);
922      tabEl.addEventListener('keydown', function (event) {
923        var key = event.key || event.code;
924        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
925          handleToggleActivation.call(tabEl, event);
926        }
927      });
928    }
929
930    attachTabListeners(tabHinweis);
931    attachTabListeners(tabSettings);
932
933    function handleToggleActivation(event) {
934      if (event) {
935        event.preventDefault();
936      }
937      var target = event && event.currentTarget ? event.currentTarget : this;
938      var targetTab = target && target.getAttribute ? target.getAttribute('data-tab') : null;
939      setView(targetTab || 'einstellungen');
940    }
941
942    detailsToggleElements.forEach(function (toggle) {
943      toggle.addEventListener('click', handleToggleActivation);
944      toggle.addEventListener('keydown', function (event) {
945        var key = event.key || event.code;
946        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
947          handleToggleActivation.call(toggle, event);
948        }
949      });
950    });
951
952    var inlineSettingsLinks = container.querySelectorAll('.cc-settings-link');
953    inlineSettingsLinks.forEach(function (link) {
954      link.style.pointerEvents = 'auto';
955      link.style.cursor = 'pointer';
956      link.addEventListener('click', handleToggleActivation);
957      link.addEventListener('keydown', function (event) {
958        var key = event.key || event.code;
959        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
960          handleToggleActivation.call(link, event);
961        }
962      });
963    });
964
965    var inlineNecessaryLinks = container.querySelectorAll('.cc-necessary-link');
966    inlineNecessaryLinks.forEach(function (link) {
967      link.style.pointerEvents = 'auto';
968      link.style.cursor = 'pointer';
969      link.addEventListener('click', function (event) {
970        event.preventDefault();
971        btnNecessary.click();
972      });
973      link.addEventListener('keydown', function (event) {
974        var key = event.key || event.code;
975        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
976          event.preventDefault();
977          btnNecessary.click();
978        }
979      });
980    });
981
982    var selectAllLabel = (cfg.texts && cfg.texts.select_all_categories) || 'Allow all categories';
983    var selectAllItem = createEl('div', { className: 'cc-details-menu-item cc-details-menu-item_empty', attrs: { tabindex: '0' } });
984    var selectAllItemLabel = createEl('label', {});
985    selectAllItemLabel.appendChild(createEl('span', { text: selectAllLabel }));
986    selectAllCheckbox = createEl('input', { attrs: { type: 'checkbox', name: 'select_all_categories' } });
987    selectAllItemLabel.appendChild(selectAllCheckbox);
988    var selectAllMark = createEl('span', { className: 'cc-modal__checkbox-mark', attrs: { tabindex: '0' } });
989    selectAllMark.appendChild(createEl('span', { className: 'cc-modal__checkbox-mark-bg' }));
990    selectAllMark.appendChild(createEl('span', { className: 'cc-modal__checkbox-mark-tick' }));
991    selectAllItemLabel.appendChild(selectAllMark);
992    selectAllItem.appendChild(selectAllItemLabel);
993    detailsWrapperSettings.appendChild(selectAllItem);
994
995    function buildCategoryId(key) {
996      if (!key) {
997        return 'cookieCategory';
998      }
999      return 'cookieCategory' + key.charAt(0).toUpperCase() + key.slice(1);
1000    }
1001
1002    categories.forEach(function (c) {
1003      var baseId = buildCategoryId(c.key);
1004      var menuItem = createEl('div', {
1005        className: 'cc-details-menu-item',
1006        attrs: { tabindex: '0', id: baseId + 'MenuItem', 'data-category-id': baseId }
1007      });
1008      if (current[c.key]) {
1009        menuItem.setAttribute('checked', 'checked');
1010      }
1011      menuItem.setAttribute('aria-expanded', 'false');
1012      var label = createEl('label', {});
1013      var toggleIcon = createEl('i', { className: 'summary-arrow', attrs: { 'aria-hidden': 'true' } });
1014      label.appendChild(toggleIcon);
1015      label.appendChild(createEl('span', { text: c.label || c.key }));
1016      var checkbox = createEl('input', { attrs: { type: 'checkbox', id: baseId, name: c.key, 'data-key': c.key } });
1017      checkbox.checked = !!current[c.key];
1018      if (c.locked) {
1019        checkbox.disabled = true;
1020      }
1021      label.appendChild(checkbox);
1022      var mark = createEl('span', { className: 'cc-modal__checkbox-mark', attrs: { tabindex: '0' } });
1023      mark.appendChild(createEl('span', { className: 'cc-modal__checkbox-mark-bg' }));
1024      mark.appendChild(createEl('span', { className: 'cc-modal__checkbox-mark-tick' }));
1025      label.appendChild(mark);
1026      menuItem.appendChild(label);
1027      detailsWrapperSettings.appendChild(menuItem);
1028
1029      var detailContent = createEl('div', { className: 'cc-detail
1029s-content', attrs: { id: baseId + 'Content' } });
1030      detailContent.style.display = 'none';
1031      if (c.description) {
1032        detailContent.appendChild(createEl('p', { text: c.description }));
1033      }
1034      
1035      // Add cookies table for this category
1036      var blockingCfg = cfg.blocking || {};
1037      var allCookies = blockingCfg.cookies || [];
1038      var categoryCookies = allCookies.filter(function(cookie) {
1039        return cookie.category === c.key || (c.key === 'unclassified' && !cookie.category);
1040      });
1041      
1042      if (categoryCookies.length > 0) {
1043        var cookieSection = createEl('div', { className: 'cc-cookie-section' });
1044        var cookieTable = createEl('table', { className: 'cc-cookie-table' });
1045        var cookieHeader = createEl('tr');
1046        cookieHeader.appendChild(createEl('th', { text: (cfg.texts && cfg.texts.cookie_name) || 'NAME' }));
1047        cookieHeader.appendChild(createEl('th', { text: (cfg.texts && cfg.texts.cookie_provider) || 'PROVIDER' }));
1048        cookieHeader.appendChild(createEl('th', { text: (cfg.texts && cfg.texts.cookie_purpose) || 'PURPOSE' }));
1049        cookieHeader.appendChild(createEl('th', { text: (cfg.texts && cfg.texts.cookie_expires) || 'EXPIRES' }));
1050        cookieHeader.appendChild(createEl('th', { text: (cfg.texts && cfg.texts.cookie_type) || 'TYPE' }));
1051        cookieTable.appendChild(cookieHeader);
1052        
1053        categoryCookies.forEach(function(cookie) {
1054          var row = createEl('tr');
1055          row.appendChild(createEl('td', { text: cookie.name || '' }));
1056          row.appendChild(createEl('td', { text: cookie.provider || '' }));
1057          row.appendChild(createEl('td', { text: cookie.purpose || '' }));
1058          row.appendChild(createEl('td', { text: cookie.expires || ((cfg.texts && cfg.texts.cookie_session) || 'Session') }));
1059          row.appendChild(createEl('td', { text: cookie.type || 'HTTP' }));
1060          cookieTable.appendChild(row);
1061        });
1062        
1063        cookieSection.appendChild(cookieTable);
1064        detailContent.appendChild(cookieSection);
1065      }
1066      
1067      // Add external hosts section for this category
1068      var allHosts = blockingCfg.hosts || [];
1069      var categoryHosts = allHosts.filter(function(host) {
1070        return host.category === c.key || (c.key === 'unclassified' && !host.category);
1071      });
1072      
1073      if (categoryHosts.length > 0) {
1074        var hostsSection = createEl('div', { className: 'cc-hosts-section' });
1075        var hostsTitle = createEl('p', { className: 'cc-hosts-title', text: (cfg.texts && cfg.texts.external_hosts_title) || 'External Resource Domains' });
1076        var hostsDesc = createEl('p', { className: 'cc-hosts-description', text: (cfg.texts && cfg.texts.external_hosts_description) || 'Below is a list of external resource domains used on our website that may process personal data.' });
1077        hostsSection.appendChild(hostsTitle);
1078        hostsSection.appendChild(hostsDesc);
1079        
1080        var hostsTable = createEl('table', { className: 'cc-hosts-table' });
1081        var hostsHeader = createEl('tr');
1082        hostsHeader.appendChild(createEl('th', { text: 'URL' }));
1083        hostsTable.appendChild(hostsHeader);
1084        
1085        categoryHosts.forEach(function(host) {
1086          var row = createEl('tr');
1087          row.appendChild(createEl('td', { text: host.host || '' }));
1088          hostsTable.appendChild(row);
1089        });
1090        
1091        hostsSection.appendChild(hostsTable);
1092        detailContent.appendChild(hostsSection);
1093      }
1094      
1095      detailsWrapperSettings.appendChild(detailContent);
1096
1097      categoryEntries.push({
1098        key: c.key,
1099        checkbox: checkbox,
1100        menuItem: menuItem,
1101        content: detailContent,
1102        locked: !!c.locked
1103      });
1104    });
1105
1106    categoryEntries.forEach(function (entry) {
1107      toggleCategoryDetails(entry, false);
1108      entry.menuItem.addEventListener('click', function (event) {
1109        if (event && event.target && (
1110          event.target.tagName === 'INPUT' ||
1111          (event.target.closest && event.target.closest('.cc-modal__checkbox-mark'))
1112        )) {
1113          return;
1114        }
1115        event.preventDefault();
1116        toggleCategoryDetails(entry);
1117      });
1118      entry.menuItem.addEventListener('keydown', function (event) {
1119        var key = event.key || event.code;
1120        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
1121          event.preventDefault();
1122          toggleCategoryDetails(entry);
1123        }
1124      });
1125      entry.checkbox.addEventListener('change', function () {
1126        current[entry.key] = !!entry.checkbox.checked;
1127        updateSelectAllState();
1128      });
1129    });
1130
1131    if (selectAllCheckbox) {
1132      selectAllCheckbox.addEventListener('change', function () {
1133        var targetChecked = !!selectAllCheckbox.checked;
1134        selectAllCheckbox.indeterminate = false;
1135        categoryEntries.forEach(function (entry) {
1136          if (entry.locked) {
1137            return;
1138          }
1139          entry.checkbox.checked = targetChecked;
1140          current[entry.key] = targetChecked;
1141        });
1142        updateSelectAllState();
1143      });
1144      updateSelectAllState();
1145    }
1146
1147
1148    var bannerRemoved = false;
1149    var badgeContainer = null;
1150    var badge = null;
1151    var badgeTextEl = null;
1152    var badgeIconEl = null;
1153    var badgeMediaQueryListenerAttached = false;
1154
1155    function showBanner() {
1156      if (banner) {
1157        banner.style.display = isModalPosition ? 'flex' : '';
1158      }
1159      setOverlayVisibility(true);
1160      bannerRemoved = false;
1161    }
1162
1163    function hideBanner() {
1164      if (banner) {
1165        banner.style.display = 'none';
1166      }
1167      setOverlayVisibility(false);
1168      bannerRemoved = true;
1169    }
1170
1171    function showBadge() {
1172      if (!badge) {
1173        createBadge();
1174      }
1175      if (badgeContainer) {
1176        badgeContainer.style.display = 'block';
1177      }
1178      if (badge) {
1179        badge.classList.add('cc-active');
1180      }
1181      updateBadgeDisplayMode();
1182    }
1183
1184    function hideBadge() {
1185      if (badgeContainer) {
1186        badgeContainer.style.display = 'none';
1187      }
1188      if (badge) {
1189        badge.classList.remove('cc-active');
1190      }
1191    }
1192
1193    function updateBadgeDisplayMode() {
1194      if (!badge) {
1195        return;
1196      }
1197      var isMobile = badgeMediaQuery ? badgeMediaQuery.matches : false;
1198      var showIcon = showIconDesktop || isMobile;
1199      if (badgeIconEl) {
1200        badgeIconEl.style.display = showIcon ? '' : 'none';
1201      }
1202      if (badgeTextEl) {
1203        badgeTextEl.style.display = showIcon ? 'none' : 'inline-block';
1204      }
1205      badge.classList.toggle('cc-icon', showIcon);
1206    }
1207
1208    function openSettings(event) {
1209      if (event && typeof event.preventDefault === 'function') {
1210        event.preventDefault();
1211      }
1212      showBanner();
1213      setView('einstellungen');
1214      hideBadge();
1215    }
1216
1217    function getBadgePositionClass() {
1218      return 'cc-bottom cc-left';
1219    }
1220
1221    function createBadge() {
1222      if (badge) {
1223        return;
1224      }
1225
1226      var badgeLabel = settingsLabel;
1227      var textColor = settingsCfg.iconColor && settingsCfg.iconColor !== '' ? settingsCfg.iconColor : '#ffffff';
1228      var backgroundColor = settingsCfg.iconBackground && settingsCfg.iconBackground !== '' ? settingsCfg.iconBackground : '#57ca85';
1229      var bodyClasses = ['cc-body'];
1230      var badgePositionClass = getBadgePositionClass();
1231      if (badgePositionClass.indexOf('cc-left') !== -1) {
1232        bodyClasses.push('cc-body-left');
1233      } else if (badgePositionClass.indexOf('cc-right') !== -1) {
1234        bodyClasses.push('cc-body-right');
1235      } else {
1236        bodyClasses.push('cc-body-center');
1237      }
1238      badgeContainer = createEl('div', { className: bodyClasses.join(' ') });
1239      badgeContainer.style.display = 'none';
1240
1241      badge = createEl('div', {
1242        className: 'cc-revoke ' + badgePositionClass + ' ' + scopeClass,
1243        attrs: { role: 'button', tabindex: '0', 'aria-label': badgeLabel }
1244      });
1245
1246      var badgeText = createEl('div', { className: 'cc-revoke-text cc-popup cc-animate' + (colorOverrideClass ? ' ' + colorOverrideClass : '') });
1247      badgeText.textContent = badgeLabel;
1248      badgeText.style.backgroundColor = backgroundColor;
1249      badgeText.style.color = textColor;
1250
1251      var badgeIcon = createEl('div', { className: 'cc-revoke-icon', attrs: { 'data-tooltip': badgeLabel } });
1252      badgeIcon.style.backgroundColor = backgroundColor;
1253
1254      var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1255      svg.setAttribute('width', '50');
1256      svg.setAttribute('height', '50');
1257      svg.setAttribute('viewBox', '0 0 50 50');
1258      svg.setAttribute('fill', 'none');
1259      var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1260      rect.setAttribute('width', '50');
1261      rect.setAttribute('height', '50');
1262      rect.setAttribute('rx', '25');
1263      rect.setAttribute('fill', backgroundColor);
1264      svg.appendChild(rect);
1265      var path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1266      path1.setAttribute('d', 'M25.7514 11.3516C25.2514 11.1016 24.6889 11.1016 24.1889 11.3516L13.1264 16.0391C12.4389 16.2891 11.9389 16.9766 12.0014 17.7266C12.0014 23.6016 14.4389 33.9766 24.1889 38.6641C24.6889 38.9141 25.2514 38.9141 25.7514 38.6641C35.5014 33.9766 37.9389 23.6016 38.0014 17.7266C38.0014 16.9766 37.5014 16.2891 36.8139 16.0391L25.7514 11.3516ZM23.4389 9.47656C24.4389 9.03906 25.5639 9.03906 26.5639 9.47656L37.5639 14.1641C38.9389 14.7891 40.0014 16.1016 40.0014 17.7266C39.9389 23.9766 37.3764 35.2891 26.6264 40.4766C25.5639 40.9766 24.3764 40.9766 23.3139 40.4766C12.5639 35.2891 10.0014 23.9766 10.0014 17.7266C9.9389 16.1016 11.0014 14.7891 12.3764 14.1641L23.4389 9.47656Z');
1267      path1.setAttribute('fill', '#ffffff');
1268      svg.appendChild(path1);
1269      var path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1270      path2.setAttribute('d', 'M22.1366 28.2572H18.125M22.1366 20.2192H18.125M25.9167 19.8958L27.3581 21.6851L30.875 18.125M25.3927 25.9536L30.2544 30.875M25.3927 30.875L30.2544 25.9536');
1271      path2.setAttribute('stroke', '#ffffff');
1272      path2.setAttribute('stroke-width', '2');
1273      path2.setAttribute('stroke-linecap', 'round');
1274      path2.setAttribute('stroke-linejoin', 'round');
1275      svg.appendChild(path2);
1276      badgeIcon.appendChild(svg);
1277
1278      badge.appendChild(badgeText);
1279      badge.appendChild(badgeIcon);
1280
1281      badgeTextEl = badgeText;
1282      badgeIconEl = badgeIcon;
1283
1284      applyCssVariables(badgeText);
1285      applyCssVariables(badgeIcon);
1286      applyCssVariables(badge);
1287      applyCssVariables(badgeContainer);
1288
1289      var offset = parseFloat(settingsCfg.offsetSettingsTabPosition);
1290      var clampedOffset = isNaN(offset) ? 1 : Math.max(0, Math.min(90, offset));
1291      var offsetValue = clampedOffset + '%';
1292
1293      badge.style.top = '';
1294      badge.style.bottom = '0';
1295      badge.style.left = offsetValue;
1296      badge.style.right = '';
1297      badge.style.transform = '';
1298
1299      badgeContainer.style.top = '';
1300      badgeContainer.style.bottom = '0';
1301      badgeContainer.style.left = '0';
1302      badgeContainer.style.right = '';
1303      badgeContainer.style.transform = '';
1304
1305      badge.addEventListener('click', function (event) {
1306        openSettings(event);
1307        badge.blur();
1308      });
1309      badge.addEventListener('keydown', function (event) {
1310        var key = event.key || event.code;
1311        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
1312          openSettings(event);
1313          badge.blur();
1314        }
1315      });
1316
1317      badgeContainer.appendChild(badge);
1318      document.body.appendChild(badgeContainer);
1319
1320      updateBadgeDisplayMode();
1321      if (badgeMediaQuery && !badgeMediaQueryListenerAttached) {
1322        if (typeof badgeMediaQuery.addEventListener === 'function') {
1323          badgeMediaQuery.addEventListener('change', updateBadgeDisplayMode);
1324        } else if (typeof badgeMediaQuery.addListener === 'function') {
1325          badgeMediaQuery.addListener(updateBadgeDisplayMode);
1326        }
1327        badgeMediaQueryListenerAttached = true;
1328      }
1329    }
1330
1331    if (closeButton) {
1332      closeButton.addEventListener('click', function (event) {
1333        if (event && typeof event.preventDefault === 'function') {
1334          event.preventDefault();
1335        }
1336        setView('hinweis');
1337      });
1338      closeButton.addEventListener('keydown', function (event) {
1339        var key = event.key || event.code;
1340        if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
1341          event.preventDefault();
1342          setView('hinweis');
1343        }
1344      });
1345    }
1346
1347    function gatherCurrentPreferences() {
1348      var map = {};
1349      categoryEntries.forEach(function (entry) {
1350        if (entry.locked) {
1351          map[entry.key] = true;
1352        } else {
1353          map[entry.key] = !!entry.checkbox.checked;
1354        }
1355      });
1356      return map;
1357    }
1358
1359    btnAllow.addEventListener('click', function (event) {
1360      if (event && typeof event.preventDefault === 'function') {
1361        event.preventDefault();
1362      }
1363      btnAllow.blur();
1364      var map;
1365      if (currentView === 'einstellungen') {
1366        map = gatherCurrentPreferences();
1367      } else {
1368        map = {};
1369        categories.forEach(function (c) { map[c.key] = true; });
1370      }
1371      storeConsent(map);
1372      hideBanner();
1373      showBadge();
1374    });
1375
1376    btnNecessary.addEventListener('click', function (event) {
1377      if (event && typeof event.preventDefault === 'function') {
1378        event.preventDefault();
1379      }
1380      btnNecessary.blur();
1381      var map = {};
1382      categories.forEach(function (c) { map[c.key] = !!c.locked; });
1383      storeConsent(map);
1384      hideBanner();
1385      showBadge();
1386    });
1387
1388    // Expose minimal API
1389    window.ascmConsent = {
1390      get: function () { return getStoredConsent(); },
1391      set: function (map) {
1392        storeConsent(map || gatherCurrentPreferences());
1393        hideBanner();
1394        showBadge();
1395      },
1396      showSettings: function () {
1397        openSettings();
1398      }
1399    };
1400
1401    createBadge();
1402    if (!showBannerImmediately) {
1403      hideBanner();
1404      showBadge();
1405    } else {
1406      hideBadge();
1407    }
1408  }
1409
1410  function init() {
1411    try {
1412      var stored = getStoredConsent();
1413      renderBanner(!stored);
1414    } catch (e) {
1415      // swallow
1416    }
1417  }
1418
1419  if (document.readyState === 'loading') {
1420    document.addEventListener('DOMContentLoaded', init);
1421  } else {
1422    init();
1423  }
1424})();
1425
1426

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.