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.