(function () { // --- (1) 50Hertz-Schrift laden --- document.head.insertAdjacentHTML( 'beforeend', '' ); // --- (2) Minimal-CSS + Basisverankerung unten links --- const style = document.createElement('style'); style.innerHTML = ` .hidden { display: none !important; } /* Flash animation */ @keyframes flash { 0%, 14.28%, 28.57%, 42.86%, 57.14%, 71.43%, 85.71%, 100% { background-color: #ffffff; } 7.14%, 21.43%, 35.71%, 50%, 64.29%, 78.57%, 92.86% { background-color: #1f2937; } } /* Container: immer unten links; feintuning via JS (Reposition) */ #chat-widget-container { position: fixed; left: 16px; /* wird vom JS dynamisch angepasst */ bottom: 16px; /* wird vom JS dynamisch angepasst */ z-index: 2147483639; /* unterhalb UC-Dialoge (UC nutzt 2147483640+) */ display: flex; flex-direction: column; gap: 8px; } #chat-bubble { width: 64px; height: 64px; background-color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background-color 0.3s ease; color: #f06d1a; box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px rgba(0, 0, 0, 0.14), 0px 1px 18px rgba(0, 0, 0, 0.12); border: none; padding: 0; animation: flash 3s ease-in-out; outline: none; } #chat-bubble:hover { background-color: #f5f5f5; } #chat-bubble:focus { outline: none; } /* Inner div inherits height from button (like Usercentrics pattern) */ #chat-bubble .sc-fqkvVR { height: inherit; width: 100%; display: flex; align-items: center; justify-content: center; } #chat-bubble svg { width: 60%; height: 60%; color: #f06d1a; } /* Popup: erscheint oberhalb der Bubble, linksbündig zum Container */ #chat-popup { position: absolute; left: 0; /* statt zentriert */ bottom: 80px; /* oberhalb der Bubble */ width: 384px; height: 70vh; max-height: 70vh; background-color: #fff; border-radius: 8px; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); display: flex; flex-direction: column; font-size: 14px; transition: all .3s; overflow: hidden; } /* Close-Button über dem iframe - OBEN RECHTS */ #close-popup { position: absolute; top: 8px; right: 8px; z-index: 10; background: rgba(31, 41, 55, 0.8); border: none; color: white; cursor: pointer; padding: 6px; border-radius: 4px; transition: background-color 0.2s; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; } #close-popup:hover { background: rgba(31, 41, 55, 0.9); } #close-popup svg { width: 18px; height: 18px; } #chat-messages { flex: 1; padding: 0; border-radius: 8px; overflow: hidden; } @media (max-width: 768px) { #chat-popup { position: fixed; top: 0; left: 0; width: 100%; height: 100%; border-radius: 0; } #chat-bubble { width: 44px; height: 44px; } } `; document.head.appendChild(style); // --- (3) Container erzeugen --- const chatWidgetContainer = document.createElement('div'); chatWidgetContainer.id = 'chat-widget-container'; document.body.appendChild(chatWidgetContainer); // --- (4) Markup injizieren --- chatWidgetContainer.innerHTML = `
`; // --- (4.5) Language override and iframe setup (after DOM insertion) --- // Override der Browser-Language Object.defineProperty(navigator, 'language', { value: 'de-DE', configurable: true }); Object.defineProperty(navigator, 'languages', { value: ['de-DE'], configurable: true }); // Fallback: Sprache per postMessage an den iFrame senden setTimeout(() => { const iframe = document.getElementById('copilotFrame'); if (iframe && iframe.contentWindow) { try { iframe.contentWindow.postMessage({ locale: 'de-DE' }, '*'); } catch (e) { console.log('Could not send postMessage to iframe:', e); } } }, 1000); // --- (5) Events --- const chatBubble = document.getElementById('chat-bubble'); const chatPopup = document.getElementById('chat-popup'); const closePopup = document.getElementById('close-popup'); chatBubble.addEventListener('click', togglePopup); closePopup.addEventListener('click', togglePopup); function togglePopup() { chatPopup.classList.toggle('hidden'); } // -------------------------------------------------------------------------- // (6) >>> UC‑AWARE POSITIONIERUNG (immer unten links; außer UC ist dort → rechts daneben) // -------------------------------------------------------------------------- // Konfiguration für die Position const BASE_LEFT = 16; // px, wenn UC nicht unten links sitzt const BASE_BOTTOM = 16; // px, wenn UC nicht unten links sitzt const SPACING = 12; // px Abstand zwischen UC-Trigger und unserem Container function getUcTrigger() { const host = document.querySelector('#usercentrics-root'); const root = host && host.shadowRoot; if (!root) return null; // Bevorzugt: stabile Selektoren let btn = root.querySelector('button[data-testid*="privacy"]') || root.querySelector('button[aria-label*="Privacy" i]') || root.querySelector('button[aria-label*="Datenschutz" i]'); // Fallback-Heuristik: sichtbarer fixed-Button in einer Ecke if (!btn) { btn = Array.from(root.querySelectorAll('button')).find(el => { const cs = getComputedStyle(el); const fixed = cs.position === 'fixed'; const corner = (parseInt(cs.bottom) >= 0 || parseInt(cs.top) >= 0) && (parseInt(cs.left) >= 0 || parseInt(cs.right) >= 0); const visible = el.offsetWidth > 0 && el.offsetHeight > 0 && cs.visibility !== 'hidden' && cs.display !== 'none'; return fixed && corner && visible; }); } return btn || null; } function isBottomLeft(rect) { const isLeft = rect.left < (window.innerWidth - rect.right); const isBelow = (window.innerHeight - rect.bottom) < rect.top; return isLeft && isBelow; } function overlaps(a, b, margin = 0) { return !( a.right + margin < b.left || a.left > b.right + margin || a.bottom + margin < b.top || a.top > b.bottom + margin ); } function setContainer(leftPx, bottomPx) { chatWidgetContainer.style.left = Math.max(0, Math.round(leftPx)) + 'px'; chatWidgetContainer.style.right = ''; // sicherstellen, dass links verwendet wird chatWidgetContainer.style.bottom = Math.max(0, Math.round(bottomPx)) + 'px'; chatWidgetContainer.style.top = ''; } function reposition() { // Default: unten links auf Basiswerte setContainer(BASE_LEFT, BASE_BOTTOM); const ucBtn = getUcTrigger(); if (!ucBtn) return; const ucRect = ucBtn.getBoundingClientRect(); // Wenn der UC-Trigger unten links sitzt: rechts daneben, gleiche Höhe if (isBottomLeft(ucRect)) { const desiredLeft = ucRect.right + SPACING; const desiredBottom = window.innerHeight - ucRect.bottom; setContainer(desiredLeft, desiredBottom); // Fallback, falls rechts daneben kein Platz (z.B. kleiner Viewport) const myRect = chatWidgetContainer.getBoundingClientRect(); const noRoomRight = myRect.right > window.innerWidth - 4; const collides = overlaps(myRect, ucRect, 4); if (noRoomRight || collides) { // dezent oberhalb stapeln, bündig zur linken UC-Kante const newLeft = ucRect.left; const newBottom = (window.innerHeight - ucRect.top) + SPACING; setContainer(newLeft, newBottom); } } } // Initiale Positionierung requestAnimationFrame(reposition); // Auf UC‑DOM‑Änderungen reagieren (UC rendert im Shadow DOM) function attachObservers() { const host = document.querySelector('#usercentrics-root'); if (!host) return; new MutationObserver(reposition) .observe(host, { attributes: true, childList: true, subtree: true }); if (host.shadowRoot) { new MutationObserver(reposition) .observe(host.shadowRoot, { attributes: true, childList: true, subtree: true }); } } // Mehrfach versuchen, bis UC geladen ist let tries = 0; const t = setInterval(() => { attachObservers(); reposition(); if (++tries > 20 || document.querySelector('#usercentrics-root')) clearInterval(t); }, 250); // Viewport-Änderungen berücksichtigen window.addEventListener('resize', reposition); window.addEventListener('orientationchange', reposition); document.addEventListener('visibilitychange', () => { if (!document.hidden) setTimeout(reposition, 50); }); })();