{
if (isDebug) console.log(...args);
};
// IP-Adresse und Geodaten abrufen
let userIP = null;
let userLatitude = localStorage.getItem("userLatitude") || null;
let userLongitude = localStorage.getItem("userLongitude") || null;
let cityName = localStorage.getItem("aktuelle_location") || null;
let recordIdGlobal;
let projektElementGlobal;
let projektKundenkennungGlobal;
let colorButtonsHex;
let colorButtonsText;
let colorCTAHex;
let colorCTAText;
let logoMaxWidth;
let chatstreifen;
let FacebookPixels;
let apiUrl = null;
let widget = "https://cdn.voiceflow.com/widget-next/bundle.mjs";
let endChatBtn = null;
let xCloseBtn = null;
let background_header = null;
let API = {
"Projekt ID": "66a4df3db0a9c60f49d8b423",
"Kundenkennung": "recb9W1Gu2Abu2",
"Facebook_Data_Id": "", // Stelle sicher, dass dies eine gültige Pixel-ID ist
"Color_Buttons_Hex": "#004080",
"Color_Button_Text_Hex": "white",
"Color_CTA_Hex": "#28a745",
"Color_CTA_Text": "white"
};
function getApiUrl() {
const scripts = document.getElementsByTagName('script');
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
if (script.src.includes('carlos_snippet.js')) {
return script.getAttribute('data-api-url');
}
}
return null;
}
apiUrl = getApiUrl();
log(apiUrl);
async function fetchData(apiUrl) {
try {
const response = await fetch(apiUrl, {
method: 'GET',
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Fehler beim Abrufen der Daten.');
}
const data = await response.json();
console.log('Abgerufene Daten:', data);
return data;
} catch (error) {
console.error('Fetch-Fehler:', error);
// Optional: Benutzer über den Fehler informieren
log(`Fehler: ${error.message}`);
return null;
}
}
async function getUserIP() {
try {
const response = await fetch("https://api.ipify.org?format=json");
const data = await response.json();
userIP = data.ip;
} catch (error) {
console.error("Fehler beim Abrufen der IP-Adresse:", error);
}
}
async function getLocation() {
try {
const response = await fetch("https://ipapi.co/json/");
const data = await response.json();
userLatitude = data.latitude;
userLongitude = data.longitude;
localStorage.setItem("userLatitude", userLatitude);
localStorage.setItem("userLongitude", userLongitude);
if (data?.city) {
cityName = data.city;
localStorage.setItem("aktuelle_location", cityName);
}
} catch (error) {
console.error("Fehler beim Abfragen des Standorts:", error);
}
}
function isDesktop() {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
return (
/Windows|Mac OS X|Linux/.test(userAgent) &&
!(
"ontouchstart" in window ||
navigator.maxTouchPoints > 0 ||
window.innerWidth < 800
)
);
}
function getUtmParameters() {
const params = new URLSearchParams(window.location.search);
if (window.location.hash.includes("?")) {
const hashParams = new URLSearchParams(
window.location.hash.split("?")[1]
);
for (const p of hashParams) {
params.set(p[0], p[1]);
}
}
return params;
}
const browserId =
localStorage.getItem("browserId") ||
(localStorage.setItem("browserId", crypto.randomUUID()), crypto.randomUUID());
async function befuellenUndAnzeigen(apiUrl) {
if (apiUrl) {
API = await fetchData(apiUrl);
}
// ProjektID
if (API) {
const projektElement = API?.["Projekt ID"];
if (projektElement) {
projektElementGlobal = projektElement.trim();
log("ProjektId gesetzt:", projektElementGlobal);
}
// Kundenkennung
const kennungElement = API?.Kundenkennung;
if (kennungElement) {
projektKundenkennungGlobal = kennungElement.trim();
log("Kundenkennung gesetzt:", projektKundenkennungGlobal);
}
// Color_Buttons_Hex
const colorButtonsHexElement = API?.Color_Buttons_Hex || "#004080";
if (colorButtonsHexElement){
colorButtonsHex = colorButtonsHexElement;
log("colorButtonsHex", colorButtonsHex);
}
const colorButtonsTextHexElement = API?.Color_Button_Text_Hex || "white";
if (colorButtonsTextHexElement){
colorButtonsText = colorButtonsTextHexElement;
log("colorButtonsText", colorButtonsText);
}
const colorCTAHexElement = API?.Color_CTA_Hex || "#28a745";
if (colorCTAHexElement){
colorCTAHex = colorCTAHexElement;
log("colorCTAHex", colorCTAHex);
}
// Color_CTA_Text_Hex
const colorCTAHexTextElement = API?.Color_CTA_Text || "white";
if (colorCTAHexTextElement){
colorCTAText = colorCTAHexTextElement;
log("colorCTAText", colorCTAText);
}
if(API?.Facebook_Data_Id) {
initializeFacebookPixel(API?.Facebook_Data_Id);
}
}
await initializeVoiceflowChat();
}
function isValidPixelID(pixelID) {
return /^\d{8,}$/.test(pixelID); // Prüft, ob es sich um eine Zahl handelt, die mindestens 8 Stellen hat
}
// Facebook Pixel initialisieren
function initializeFacebookPixel(pixelID) {
if (!isValidPixelID(pixelID)) {
console.warn("Ungültige oder fehlende Pixel-ID:", pixelID);
return; // Blockiert die Initialisierung bei ungültiger ID
}
log("Initialisiere Facebook Pixel mit ID:", pixelID);
!function(f, b, e, v, n, t, s) {
if (f.fbq) return;
n = f.fbq = function() {
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
};
if (!f._fbq) f._fbq = n;
n.push = n;
n.loaded = !0;
n.version = '2.0';
n.queue = [];
t = b.createElement(e);
t.async = !0;
t.src = v;
s = b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t, s);
}(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', pixelID);
fbq('track', 'PageView');
window.fbqInitialized = true; // Initialisierungs-Flag setzen
}
// Falsche Pixel-Skripte entfernen
function deleteFalsePixels(validPixelID) {
log("Starte Bereinigung von Pixel-Skripten...");
document.querySelectorAll('script[src*="fbevents.js"]').forEach(script => {
if (!validPixelID || !script.src.includes(validPixelID)) {
console.warn("Entferne fehlerhaftes Pixel-Skript oder Platzhalter:", script.src);
script.remove();
} else {
log("Valides Pixel-Skript gefunden:", script.src);
}
});
// Entferne auch die globale `fbq`, falls sie mit einem Platzhalter initialisiert wurde
if (!validPixelID && window.fbq) {
console.warn("Platzhalter `fbq` gefunden. Entferne Instanz...");
delete window.fbq;
delete window._fbq;
window.fbqInitialized = false;
}
}
async function initializeVoiceflowChat() {
const urlParams = getUtmParameters();
const isTest = urlParams.get("test") === "1";
const incomingUserID = urlParams.get("user_id");
let user_id = incomingUserID || localStorage.getItem("user_id");
// Wenn keine user_id bekannt → neue ID erstellen
if (!user_id) {
user_id = 'guest_' + Math.random().toString(36).substring(2, 10);
}
if (incomingUserID) {
localStorage.setItem("user_id", user_id);
}
const storedID = localStorage.getItem("user_id");
const hasRealStoredID = storedID && !storedID.startsWith("guest_");
const persistenceMode = isTest
? 'memory'
: (incomingUserID || hasRealStoredID)
? 'localStorage'
: 'memory';
console.log("persistenceMode", persistenceMode);
console.log("Tatsächliche user_id", user_id);
console.log("incoming_id", incomingUserID);
if (!userLatitude || !userLongitude) {
// Falls keine Daten vorhanden sind, erneut abfragen
await Promise.all([getUserIP(), getLocation()]);
} else {
// Standortdaten liegen bereits vor, IP dennoch neu laden
await getUserIP();
}
userLatitude = localStorage.getItem("userLatitude");
userLongitude = localStorage.getItem("userLongitude");
cityName = localStorage.getItem("aktuelle_location");
log("In initializeVoiceflowChat nach getLocation:", {
userLatitude,
userLongitude,
cityName: localStorage.getItem("aktuelle_location")
});
(function (d, t) {
const v = d.createElement(t);
const s = d.getElementsByTagName(t)[0];
v.src = widget;
v.type = "text/javascript";
v.async = true;
v.onload = function () {
const FacebookPixelExtension = {
name: "FacebookPixel",
type: "effect",
match: ({ trace }) => trace.type === "FacebookPixel",
effect: ({ trace }) => {
log("FacebookPixelEffect ausgelöst:", trace);
const { event, params } = trace.payload;
if (event) {
if (typeof fbq === "function") {
fbq('track', event, params || {});
} else {
console.error("Facebook Pixel (fbq) ist nicht definiert. Stelle sicher, dass der Pixel-Code korrekt geladen ist.");
}
} else {
console.error("Kein 'event'-Feld in der FacebookPixel-Payload gefunden.");
}
}
}
const YouTubeStopEmbedExtension = {
name: "YouTubeStopEmbed",
type: "response",
match: ({ trace }) => trace.type === "YouTubeStopEmbed",
render: ({ trace, element }) => {
log("=== Render gestartet ===", { trace });
// Payload verarbeiten
let payload;
try {
payload = typeof trace.payload === "string" ? JSON.parse(trace.payload) : trace.payload;
log("Payload erfolgreich geparst:", payload);
} catch (error) {
console.error("Fehler beim Parsen der Payload:", error);
return;
}
let { videoUrl, width, height, maxDuration } = payload;
// Überprüfen der notwendigen Parameter
if (!videoUrl || !width || !height || !maxDuration) {
console.error("Fehlende Parameter in der Payload:", payload);
return;
}
// Sicherstellen, dass maxDuration eine Zahl ist
maxDuration = Number(maxDuration);
if (isNaN(maxDuration)) {
console.error("maxDuration ist keine gültige Zahl:", payload.maxDuration);
return;
}
log("Dauerlimit festgelegt auf (Sekunden):", maxDuration);
const separator = videoUrl.includes('?') ? '&' : '?';
const finalVideoUrl = `${videoUrl}${separator}enablejsapi=1&rel=0&modestbranding=1&showinfo=0`;
// **Globale Player-Instanz und iframe verwalten**
if (!window.YTPlayer) {
window.YTPlayer = {
playerInstance: null,
iframe: null,
};
}
// **Vorheriges iframe und Player-Instanz entfernen**
if (window.YTPlayer.iframe && window.YTPlayer.playerInstance) {
log("Vorheriges iframe und Player-Instanz gefunden. Zerstöre und entferne sie.");
try {
window.YTPlayer.playerInstance.destroy();
log("Alte Player-Instanz erfolgreich zerstört.");
} catch (error) {
console.error("Fehler beim Zerstören der alten Player-Instanz:", error);
}
if (window.YTPlayer.iframe.parentElement) {
window.YTPlayer.iframe.parentElement.removeChild(window.YTPlayer.iframe);
log("Altes iframe erfolgreich entfernt.");
}
window.YTPlayer = {
playerInstance: null,
iframe: null,
};
}
// iFrame erstellen und hinzufügen (ohne 'end'-Parameter)
const iframe = document.createElement("iframe");
iframe.id = "player";
iframe.width = width;
iframe.height = height;
iframe.src = finalVideoUrl; // 'end' entfernt
iframe.frameBorder = "0";
iframe.allow =
"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
iframe.allowFullscreen = true;
element.appendChild(iframe);
//document.body.appendChild(iframe); // Append to body globally
// Speichern des iframes in der globalen Variable
window.YTPlayer.iframe = iframe;
log("iFrame erstellt und hinzugefügt:", iframe);
// Variablen zur Verwaltung des Timers und der abgespielten Zeit
let player = null; // YT.Player Instanz
let timer = null; // setInterval ID
// Funktion zum Stoppen des Videos
const enforceStop = () => {
log("=== enforceStop aufgerufen ===");
try {
player.stopVideo();
log(`Video gestoppt über 'stopVideo' bei maxDuration: ${maxDuration} Sekunden`);
} catch (error) {
console.error("Fehler beim Stoppen des Videos:", error);
}
if (timer) {
clearInterval(timer);
log("Intervall gestoppt in enforceStop.");
timer = null;
}
};
// Funktion zum Starten des Timers
const startTimer = () => {
if (timer) {
console.warn("Intervall bereits gestartet. Verhindere doppeltes Starten.");
return;
}
log("Timer gestartet.");
timer = setInterval(() => {
const currentTime = player.getCurrentTime();
log("Aktuelle Zeit:", currentTime, "Sekunden");
if (currentTime >= maxDuration) {
log("MaxDuration erreicht oder überschritten.");
enforceStop();
}
}, 1000); // Überprüfung jede Sekunde
log("Intervall gestartet.");
};
// Funktion zum Stoppen des Timers
const stopTimer = () => {
if (timer) {
clearInterval(timer);
log("Intervall gestoppt.");
timer = null;
}
};
// Funktion zum Zurücksetzen des Timers (bei Neustart)
const resetTimer = () => {
stopTimer();
log("Timer zurückgesetzt.");
startTimer();
};
// Event-Listener für `postMessage`, um sicherzustellen, dass stopVideo empfangen wird
const messageListener = (event) => {
// Überprüfe, ob die Nachricht vom aktuellen iFrame kommt
if (event.source !== iframe.contentWindow) return;
log("=== Received message ===", event.data);
// Hier kann weiter geprüft werden, ob stopVideo korrekt verarbeitet wurde
};
// Verhindern, dass der Listener mehrfach hinzugefügt wird
if (!window.messageListenerAdded) {
window.addEventListener("message", messageListener);
window.messageListenerAdded = true;
log("Message Listener hinzugefügt.");
}
// Funktion zum Laden der YouTube IFrame API
const loadYouTubeAPI = () => {
return new Promise((resolve, reject) => {
// Überprüfen, ob die API bereits geladen ist
if (typeof YT !== "undefined" && typeof YT.Player !== "undefined") {
log("YouTube IFrame API ist bereits geladen.");
resolve();
return;
}
log("YouTube IFrame API wird geladen...");
const script = document.createElement("script");
script.src = "https://www.youtube.com/iframe_api";
script.async = true;
script.defer = true;
script.onload = () => {
log("YouTube IFrame API Skript erfolgreich geladen.");
resolve();
};
script.onerror = (error) => {
console.error("Fehler beim Laden des YouTube IFrame API Skripts:", error);
reject(error);
};
document.body.appendChild(script);
});
};
// Queue für YouTube IFrame API Ready Callbacks
if (!window.YTReadyCallbacks) {
window.YTReadyCallbacks = [];
log("YTReadyCallbacks Queue initialisiert.");
}
// Definiere onYouTubeIframeAPIReady nur einmal
if (!window.onYouTubeIframeAPIReadyDefined) {
window.onYouTubeIframeAPIReady = () => {
log("=== onYouTubeIframeAPIReady aufgerufen ===");
window.YTReady = true; // Setze das Flag, dass die API bereit ist
window.YTReadyCallbacks.forEach((callback) => {
try {
callback();
} catch (error) {
console.error("Fehler im YTReadyCallback:", error);
}
});
window.YTReadyCallbacks = []; // Leere die Warteschlange
};
window.onYouTubeIframeAPIReadyDefined = true;
log("onYouTubeIframeAPIReady definiert.");
}
// Funktion zum Hinzufügen eines Callbacks zur Queue
const addYTReadyCallback = (callback) => {
if (window.YTReady) {
log("API ist bereits bereit. Führe Callback sofort aus.");
callback();
} else {
window.YTReadyCallbacks.push(callback);
log("YTReadyCallback hinzugefügt.");
}
};
// Callback-Funktion zur Erstellung des Players
addYTReadyCallback(() => {
log("YTReadyCallback: YT.Player wird erstellt.");
// Überprüfen, ob YT.Player verfügbar ist
if (typeof YT === "undefined" || typeof YT.Player !== "function") {
console.error("YT.Player ist nach onYouTubeIframeAPIReady immer noch nicht verfügbar.");
return;
}
log("YT.Player ist verfügbar. Erstelle den Player...");
// YouTube-Player erstellen
player = new YT.Player(iframe, {
events: {
onReady: () => {
log("Player ist bereit. Starte Video.");
try {
//player.playVideo();
player.setPlaybackQuality('hd720');
player.addEventListener('onPlaybackQualityChange', function(event) {
if (event.data !== 'hd720') {
player.setPlaybackQuality('hd720');
}
});
log("player.playVideo() aufgerufen.");
//startTimer(); // Timer starten, wenn das Video spielt
} catch (error) {
console.error("Fehler beim Aufrufen von player.playVideo():", error);
}
},
onStateChange: (event) => {
log("Player Status geändert:", event.data);
if (event.data === YT.PlayerState.PLAYING) {
log("Video läuft. Startet Überwachung.");
startTimer();
} else if (event.data === YT.PlayerState.PAUSED) {
log("Video pausiert. Stoppe Überwachung.");
stopTimer();
} else if (event.data === YT.PlayerState.ENDED) {
log("Video beendet. Stoppe Überwachung.");
stopTimer();
}
},
},
});
// Verknüpfen der Player-Instanz mit dem iframe für späteres Zerstören
window.YTPlayer.playerInstance = player;
log("YouTube-Player erstellt:", player);
});
// YouTube-API laden und den Player initialisieren
loadYouTubeAPI()
.then(() => {
log("YouTube IFrame API wurde erfolgreich geladen und ist bereit.");
// Die Erstellung des Players wird durch die YTReadyCallbacks übernommen
})
.catch((error) => {
console.error("Fehler beim Laden der YouTube IFrame API:", error);
});
},
};
const chat = window.voiceflow.chat;
window.voiceflow.chat
.load({
verify: { projectID: `${projektElementGlobal}`},
launch: {
event: {
type: "launch",
payload: {
t: window.location.hostname + window.location.pathname,
ur_l: window.location.href,
p: isDesktop() ? "Desktop" : "Mobil",
u: urlParams.get("utm_content"),
us: urlParams.get("utm_source"),
ut: urlParams.get("utm_term"),
uc: urlParams.get("utm_campaign"),
um: urlParams.get("utm_medium"),
bereich: urlParams.get("bereich"),
stelle: urlParams.get("stelle"),
fbclid: urlParams.get("fbclid"),
map: urlParams.get("map"),
ref_empf: urlParams.get("ref_empf"),
empf: urlParams.get("empf"),
lat: localStorage.getItem("userLatitude"),
long: localStorage.getItem("userLongitude"),
ip: userIP,
agent: navigator.userAgent,
browser_id: browserId,
aktuelle_location: localStorage.getItem("aktuelle_location"),
rec: `${projektKundenkennungGlobal}`,
},
},
},
url: "https://general-runtime.voiceflow.com",
versionID: "production",
userID: user_id,
assistant: {
persistence: persistenceMode,
extensions: [YouTubeStopEmbedExtension, FacebookPixelExtension],
stylesheet: `data:text/css;base64,LnZmcmMtd2lkZ2V0IHsKICBmb250LXNpemU6IDE2cHg7Cn0KCi52ZnJjLXdpZGdldC0tY2hhdCAudmZyYy1zeXN0ZW0tcmVzcG9uc2V7bWFyZ2luLWJvdHRvbToxMnB4fUBtZWRpYShtaW4td2lkdGg6MTIwMHB4KXsuYy1maWtsb28sIC5fMXdrcTduZjMuXzF3a3E3bmY2IHt3aWR0aDo1ODBweH19CgpkaXY+ZGl2LnZmcmMtd2lkZ2V0LS1jaGF0LmMtZmlrbG9vPmRpdj5hcnRpY2xlPm1haW4+ZGl2PmRpdi52ZnJjLXN5c3RlbS1yZXNwb25zZS0tbGlzdC5jLWlUeGFDSz5kaXY+c2VjdGlvbj5tYWluPmgzIHsKICBtYXJnaW4tYm90dG9tOiAxLjVlbTsgLyogMjRweCAqLwogIGZvbnQtc2l6ZTogMWVtOyAvKiAxNnB4ICovCiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIHdvcmQtYnJlYWs6IGJyZWFrLXdvcmQ7CiAgd2hpdGUtc3BhY2U6IG5vcm1hbDsKICBsaW5lLWhlaWdodDogMS41Owp9CiN2ZmNoYXQ+ZGl2PmFydGljbGU+bWFpbj5kaXYudmZyYy11c2VyLXJlc3BvbnNlLmMta21admNHPmRpdj5kaXYudmZyYy1tZXNzYWdlLmMtZ01wTmtZLnZmcmMtbWVzc2FnZS0tY2hhdC5jLVBKTFYuYy1QSkxWLWJPc2FuZy1mcm9tLXVzZXIgewogIGJhY2tncm91bmQtY29sb3I6ICMwMDRlOWQ7CiAgYm9yZGVyLXJhZGl1czogMC42MjVlbTsgLyogMTBweCAqLwp9CiN2ZmNoYXQ+ZGl2PmFydGljbGU+Zm9vdGVyPmJ1dHRvbiB7CiAgZm9udC1zaXplOiAwOwp9CiN2ZmNoYXQ+ZGl2PmFydGljbGU+Zm9vdGVyPmJ1dHRvbjo6YWZ0ZXIgewogIGNvbnRlbnQ6ICJKZXR6dCBzdGFydGVuIjsKICBmb250LXNpemU6IDAuOTM3NWVtOyAvKiAxNXB4ICovCn0KZGl2PmRpdi52ZnJjLXdpZGdldC0tbGF1bmNoZXIuYy1QSkxWPmRpdj5kaXYuYy1pSHFFUFQ+ZGl2Om50aC1jaGlsZCgyKSwKZGl2PmRpdi52ZnJjLXdpZGdldC0tbGF1bmNoZXIuYy1QSkxWPmRpdj5kaXYuYy1pSHFFUFQ+ZGl2Om50aC1jaGlsZCgxKSB7CiAgYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7CiAgY29sb3I6IHdoaXRlOwp9Ci52ZmNyLWJ1dHRvbiB7CiAgdGV4dC1hbGlnbjogY2VudGVyICFpbXBvcnRhbnQ7Cn0KLncyOTE3cDMgaW1nLAoudmZyYy1jYXJkIGltZyB7CiAgd2lkdGg6IDI1MHB4OyAvKiAyNTBweCAqLwogIGhlaWdodDogMTBlbTsgLyogMTYwcHggKi8KICBvYmplY3QtZml0OiBjb3ZlcjsKICBib3JkZXItcmFkaXVzOiAxMHB4OyAvKiAxMHB4ICovCn0KLnZmcmMtYnV0dG9uIHsKICBkaXNwbGF5OiBmbGV4OwogIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7CiAgd2lkdGg6IDEwMCU7CiAgcGFkZGluZzogMTVweDsgLyogMTVweCAqLwogIGJveC1zaXppbmc6IGJvcmRlci1ib3g7CiAgdGV4dC1hbGlnbjogY2VudGVyICFpbXBvcnRhbnQ7CiAgZm9udC1zaXplOiAwLjllbTsgLyogZW50c3ByaWNodCB2b3JoZXIgMC44NXJlbSAqLwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGh5cGhlbnM6IGF1dG87CiAgd2hpdGUtc3BhY2U6IG5vcm1hbDsKICB3b3JkLXdyYXA6IGJyZWFrLXdvcmQ7CiAgbWFyZ2luLXRvcDogYXV0bzsKICBsaW5lLWhlaWdodDogMS40Owp9Ci52ZnJjLWNhcmQgewogIGRpc3BsYXk6IGZsZXg7CiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgaGVpZ2h0OiAxMDAlICFpbXBvcnRhbnQ7CiAgd2lkdGg6IDI1MHB4OyAvKiAyNTBweCAqLwp9Ci52ZnJjLWNhcmQtLWNvbnRlbnQgewogIGZsZXg6IDE7CiAgZGlzcGxheTogZmxleDsKICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICBoZWlnaHQ6IDEwMCU7CiAgdGV4dC1hbGlnbjogbGVmdDsKICBvdmVyZmxvdzogdmlzaWJsZTsKICBwYWRkaW5nOiAxMHB4OyAvKiAxMHB4ICovCn0KLnZmcmMtY2FyZC0tZGVzY3JpcHRpb24gewogIGZsZXg6IDE7CiAgcGFkZGluZzogMCAxMHB4OyAvKiAwIDEwcHggKi8KICBoeXBoZW5zOiBhdXRvOwogIHdvcmQtd3JhcDogYnJlYWstd29yZDsKICB0ZXh0LWFsaWduOiBsZWZ0OwogIG1heC13aWR0aDogMTAwJTsKICBtYXJnaW4tYm90dG9tOiAxMHB4OyAvKiAxMHB4ICovCiAgb3ZlcmZsb3c6IHZpc2libGU7CiAgd2hpdGUtc3BhY2U6IG5vcm1hbDsKICBmb250LXNpemU6IDE0cHg7IC8qIDE0cHggKi8KICBsaW5lLWhlaWdodDogMS40Owp9Ci52ZnJjLWJ1dHRvbltsYWJlbD0nSGllciBCZXdlcmJlbiddIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMWRiZjczOwogIGNvbG9yOiB3aGl0ZTsKfQoudmZyYy1idXR0b25bbGFiZWw9J0VuZCBDaGF0J10gewogIGZvbnQtc2l6ZTogMDsKICBjb2xvcjogdHJhbnNwYXJlbnQ7Cn0KLnZmcmMtYnV0dG9uW2xhYmVsPSdFbmQgQ2hhdCddOjphZnRlciB7CiAgY29udGVudDogJ0Fzc2lzdGVudCBiZWVuZGVuJzsKICBmb250LXNpemU6IDAuOWVtOyAvKiAxNXB4IHVtZ2VyZWNobmV0IHp1dm9yIDAuODVyZW0gYnp3LiAxNXB4ICovCiAgY29sb3I6IHdoaXRlOwp9Ci52ZnJjLWJ1dHRvbltsYWJlbD0nQ2FuY2VsJ10gewogIGZvbnQtc2l6ZTogMDsKICBjb2xvcjogdHJhbnNwYXJlbnQ7Cn0KLnZmcmMtYnV0dG9uW2xhYmVsPSdDYW5jZWwnXTo6YWZ0ZXIgewogIGNvbnRlbnQ6ICdBYmJyZWNoZW4nOwogIGZvbnQtc2l6ZTogMC45ZW07IC8qIDE1cHggKi8KICBjb2xvcjogYmxhY2s7Cn0KLnZmcmMtY2FyZC0taGVhZGVyLAoudmZyYy1jYXJkLS1kZXNjcmlwdGlvbiB7CiAgd2hpdGUtc3BhY2U6IG5vcm1hbDsKICB3b3JkLXdyYXA6IGJyZWFrLXdvcmQ7CiAgb3ZlcmZsb3ctd3JhcDogYnJlYWstd29yZDsKICBvdmVyZmxvdzogdmlzaWJsZTsKfQoudmZyYy13aWRnZXQtLWNoYXQgLnZmcmMtY2Fyb3VzZWwgLnZmcmMtY2FyZC0tY29udGVudCB7CiAgZGlzcGxheTogZmxleDsKICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICBhbGlnbi1pdGVtczogc3RyZXRjaDsKICBvdmVyZmxvdzogdmlzaWJsZTsKICBoZWlnaHQ6IDEwMCU7Cn0KLnZmcmMtY2FyZCB7CiAgZGlzcGxheTogZmxleDsKICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICBoZWlnaHQ6IDEwMCU7CiAgYm94LXNpemluZzogYm9yZGVyLWJveDsKfQoudmZyYy1jYXJkLS1jb250ZW50IHsKICBkaXNwbGF5OiBmbGV4OwogIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogIGZsZXgtZ3JvdzogMTsKICBwYWRkaW5nOiAxMHB4OyAvKiAxMHB4ICovCn0KLnZmcmMtY2FyZC0tZGVzY3JpcHRpb24gewogIG1hcmdpbi1ib3R0b206IGF1dG87CiAgdGV4dC1hbGlnbjogbGVmdDsKICBmb250LXNpemU6IDE0cHg7IC8qIDE0cHggKi8KICBsaW5lLWhlaWdodDogMS40OwogIGZsZXgtZ3JvdzogMTsKICBkaXNwbGF5OiBmbGV4OwogIGFsaWduLWl0ZW1zOiBmbGV4LXN0YXJ0Owp9Ci52ZnJjLWJ1dHRvbiB7CiAgZGlzcGxheTogZmxleDsKICBhbGlnbi1pdGVtczogY2VudGVyOwogIGp1c3RpZnktY29udGVudDogY2VudGVyOwogIHBhZGRpbmc6IDEwcHg7IC8qIDEwcHggKi8KICBmb250LXNpemU6IDAuOWVtOyAvKiAxNnB4IC0+IDFlbSB3dXJkZSB2b3JoZXIsIGhpZXIgYmxlaWJ0IDAuODVlbSBsYXV0IFZvcmdhYmUgKi8KICBmb250LXdlaWdodDogYm9sZDsKICB3aWR0aDogMTAwJTsKICBtaW4taGVpZ2h0OiA3MHB4OyAvKiA3MHB4ICovCiAgYm94LXNpemluZzogYm9yZGVyLWJveDsKICBib3JkZXItcmFkaXVzOiA4cHg7IC8qIDhweCAqLwp9Ci52ZnJjLWNhcm91c2VsIC52ZnJjLWNhcmQgewogIGRpc3BsYXk6IGZsZXg7CiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgaGVpZ2h0OiAxMDAlOwp9Ci5jLWtmeUl4VCB7CiAgd2hpdGUtc3BhY2U6IG5vcm1hbDsKfQoudmZyYy1oZWFkZXItLWJ1dHRvbjpmaXJzdC1vZi10eXBlIHsKICBkaXNwbGF5OiBub25lOwp9Ci52ZnJjLWNoYXQtaW5wdXQgdGV4dGFyZWEgewogIGJvcmRlcjogMnB4IHNvbGlkICMwMDRlOWQ7IC8qIDJweCAqLwogIGJvcmRlci1yYWRpdXM6IDhweDsgLyogOHB4ICovCiAgcGFkZGluZzogMTJweDsgLyogMTJweCAqLwogIGZvbnQtc2l6ZTogMTRweDsgLyogMTRweCAqLwogIGxpbmUtaGVpZ2h0OiAxLjU7CiAgYm94LXNoYWRvdzogMCAwLjEyNWVtIDAuMzc1ZW0gcmdiYSgwLCAwLCAwLCAwLjEpOyAvKiAwLCAycHgsIDZweCAqLwogIGJhY2tncm91bmQtY29sb3I6ICNmMGY4ZmY7CiAgdHJhbnNpdGlvbjogYWxsIDAuM3MgZWFzZTsKICBib3JkZXItY29sb3I6ICMwNmM7CiAgYm94LXNoYWRvdzogMCAwIDAuNWVtIHJnYmEoMCwgMTAyLCAyMDQsIDAuNyk7IC8qIDhweCAqLwogIGJhY2tncm91bmQtY29sb3I6ICNmZmY7Cn0KLnZmcmMtY2hhdC1pbnB1dCB0ZXh0YXJlYTpmb2N1cyB7CiAgb3V0bGluZTogMDsKICBib3JkZXItY29sb3I6ICMwNmM7CiAgYm94LXNoYWRvdzogMCAwIDAuNWVtIHJnYmEoMCwgMTAyLCAyMDQsIDAuNyk7IC8qIDhweCAqLwogIGJhY2tncm91bmQtY29sb3I6ICNmZmY7CiAgYm9kZXItc2l6ZS1yaWdodDogMDsKfQoudmZyYy1jaGF0LWlucHV0IHRleHRhcmVhIHsKICBvdmVyZmxvdzogaGlkZGVuOwogIHJlc2l6ZTogbm9uZTsKfQoudmZyYy1hdmF0YXIsIHsKICBvcGFjaXR5OiAwOwp9Ci52ZnJjLWFzc2lzdGFudC1pbmZvLS10aXRsZSB7CiAgZGlzcGxheTogbm9uZTsKfQpodG1sIHsKICBmb250LXNpemU6IDFlbTsgLyogMTZweCAqLwp9Ci52ZnJjLWNhcmQgewogIHdpZHRoOiAyNzBweDsgLyogMjcwcHggKi8KfQoudzI5MTdwMyBpbWcsCi52ZnJjLWNhcmQgaW1nIHsKICB3aWR0aDogMjcwcHg7IC8qIDI3MHB4ICovCiAgaGVpZ2h0OiAxNjBweDsgLyogMTYwcHggKi8KICBvYmplY3QtZml0OiBjb3ZlcjsKICBvYmplY3QtcG9zaXRpb246IHRvcDsKfQoudmZyYy1zeXN0ZW0tcmVzcG9uc2UgaDMgewogIGZvbnQtc2l6ZTogMS4yZW07CiAgbGluZS1oZWlnaHQ6IDEuNTsKICBmb250LXdlaWdodDogYm9sZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KLnZmcmMtY2FyZC0tZGVzY3JpcHRpb24gewogIGZvbnQtc2l6ZTogMC44ZW07IC8qIGF1cyAwLjhyZW0gKi8KICBsaW5lLWhlaWdodDogMS41Owp9Ci52ZnJjLWJ1dHRvbiB7CiAgZm9udC1zaXplOiAwLjllbTsgLyogYXVzIDAuODVyZW0gKi8KICBwYWRkaW5nOiAwLjc1ZW07CiAgaGVpZ2h0OiBhdXRvOwp9Ci52ZnJjLWhlYWRlciB7CiAgbWluLWhlaWdodDogODBweDsgLyogODBweCAqLwp9CkBtZWRpYShtYXgtaGVpZ2h0OjkwMHB4KSB7CiAgLnZmcmMtaW1hZ2UgewogICAgaGVpZ2h0OiAxNjBweDsgLyogMTYwcHggKi8KICB9CiAgLnZmY3ItaGVhZGVyLXRpdGVsLCAuYy1rZnlJeFQgewogICAgZm9udC1zaXplOiAwLjkwZW07CiAgICBsaW5lLWhlaWdodDogMS4zOwogIH0KICAudmZyYy1zeXN0ZW0tcmVzcG9uc2UgaDMsIC52ZnJjLWNhcmQtLWhlYWRlciB7CiAgICBmb250LXNpemU6IDAuOWVtOyAvKiBhdXMgMC45cmVtICovCiAgICBsaW5lLWhlaWdodDogMS41OwogIH0KICAudmZyYy1jYXJkLS1kZXNjcmlwdGlvbiB7CiAgICBmb250LXNpemU6IDAuOGVtOyAvKiBhdXMgMC44cmVtICovCiAgICBsaW5lLWhlaWdodDogMS40OwogIH0KICAudmZyYy1idXR0b24gewogICAgZm9udC1zaXplOiAwLjg1ZW07IC8qIGF1cyAwLjg1cmVtICovCiAgICBwYWRkaW5nOiAwLjYyNWVtOwogIH0KfQpAbWVkaWEobWF4LWhlaWdodDo3ODBweCkgewogIC53MjkxN3AzIGltZywKICAudmZyYy1jYXJkIGltZyB7CiAgICBoZWlnaHQ6IDE0MHB4OyAvKiAxNDBweCAqLwogIH0KICAudmZyYy1zeXN0ZW0tcmVzcG9uc2UgaDMsIC52ZnJjLWNhcmQtLWhlYWRlciB7CiAgICBmb250LXNpemU6IDFlbTsKICAgIGxpbmUtaGVpZ2h0OiAxLjQ7CiAgfQogIC52ZnJjLWNhcmQtLWRlc2NyaXB0aW9uIHsKICAgIGZvbnQtc2l6ZTogMC44NWVtOwogICAgbGluZS1oZWlnaHQ6IDEuMzsKICB9CiAgLnZmcmMtaGVhZGVyIHsKICAgIG1pbi1oZWlnaHQ6IDcwcHg7IC8qIDcwcHggKi8KICB9CiAgLnZmY3ItaGVhZGVyLXRpdGVsIC5jLWtmeUl4VCB7CiAgICBmb250LXNpemU6IDAuODBlbTsKICAgIGxpbmUtaGVpZ2h0OiAxLjI7CiAgfQogIC52ZnJjLWJ1dHRvbiB7CiAgICBmb250LXNpemU6IDAuOWVtOyAvKiBhdXMgMC44NXJlbSAqLwogICAgcGFkZGluZzogMC41ZW07CiAgfQp9CkBtZWRpYShtYXgtaGVpZ2h0OjY4MHB4KSB7CiAgLnZmcmMtaW1hZ2UgewogICAgaGVpZ2h0OiAxMjBweDsgLyogMTIwcHggKi8KICB9CiAgLnZmcmMtc3lzdGVtLXJlc3BvbnNlIGgzLCAudmZyYy1jYXJkLS1oZWFkZXIgewogICAgZm9udC1zaXplOiAwLjllbTsgLyogYXVzIC44Mzc1cmVtICovCiAgICBsaW5lLWhlaWdodDogMS4zOwogIH0KICAudmZyYy1oZWFkZXIgewogICAgbWluLWhlaWdodDogNjVweDsgLyogNjVweCAqLwogIH0KCgogIC52ZmNyLWhlYWRlci10aXRlbCAuYy1rZnlJeFQsIC52ZnJjLWhlYWRlci0tdGl0bGUuanlpdWVpNCB7CiAgICBmb250LXNpemU6IDE0cHg7CiAgICBsaW5lLWhlaWdodDogMS4yOwogIH0KICAudmZyYy1jYXJkLS1kZXNjcmlwdGlvbiB7CiAgICBmb250LXNpemU6IDAuOGVtOyAvKiBhdXMgMC44cmVtICovCiAgICBsaW5lLWhlaWdodDogMS4zOwogIH0KICAudmZyYy1idXR0b24gewogICAgZm9udC1zaXplOiAwLjg1ZW07IC8qIGF1cyAwLjg1cmVtICovCiAgICBwYWRkaW5nOiAwLjVlbTsKICB9Cn0KLnZmcmMtc3lzdGVtLXJlc3BvbnNlIGgzLCAudmZyYy1jYXJkLS1oZWFkZXIgewogIHBhZGRpbmctYm90dG9tOiAxZW07IC8qIDEuMHJlbSAqLwp9CkBtZWRpYShtaW4td2lkdGg6MTIwMHB4KSB7CiAgLnZmcmMtd2lkZ2V0LS1jaGF0IC52ZnJjLXN5c3RlbS1yZXNwb25zZSB7CiAgICBmb250LXNpemU6IDEuMTVlbTsKICB9CiAgLnZmcmMtc3lzdGVtLXJlc3BvbnNlIGgzLCAudmZyYy1jYXJkLS1oZWFkZXIgewogICAgZm9udC1zaXplOiAxLjA1ZW07CiAgICBsaW5lLWhlaWdodDogMS40ZW07CiAgfQogIC52ZnJjLWNhcmQtLWRlc2NyaXB0aW9uIHsKICAgIGZvbnQtc2l6ZTogMC44ZW07IC8qIGF1cyAwLjhyZW0gKi8KICB9Cn0KLnZmcmMtaGVhZGVyLS10aXRsZSB7CiAgZm9udC13ZWlnaHQ6IHVuc2V0ICFpbXBvcnRhbnQ7CiAgbGluZS1oZWlnaHQ6IHVuc2V0ICFpbXBvcnRhbnQ7CiAgd2hpdGUtc3BhY2U6IHVuc2V0ICFpbXBvcnRhbnQ7Cn0KLncyOTE3cDMgaW1nLAoudmZyYy1jYXJkIGltZywKLnZmcmMtaW1hZ2UgewogIG9iamVjdC1maXQ6IGNvdmVyOwogIG9iamVjdC1wb3NpdGlvbjogdG9wIGNlbnRlcjsKICB3aWR0aDogMTAwJTsKfQoudmZyYy1jYXJkLS1oZWFkZXIsIC52ZnJjLWNhcmQtLWRlc2NyaXB0aW9uIHsKICBtYXJnaW4tdG9wOiAwLjYyNWVtOyAvKiAxMHB4ICovCn0KLnZmcmMtY2Fyb3VzZWwtLWJ1dHRvbiB7CiAgYm9yZGVyLXJhZGl1czogMTAwJTsKfQp2ZnJjLWhlYWRlci0tYnV0dG9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB1bnNldDsKfQoudmZyYy1zeXN0ZW0tcmVzcG9uc2UtLWFjdGlvbnMuZzNkcWZkNiB7CiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKfQoudzI5MTdwMyB7CiAgYWxpZ24taXRlbXM6IHVuc2V0Owp9Cg==`,
},
})
.then(() => {
log('JavaScript code started executing...');
/* 2 | Socket still & leise aufbauen */
chat.open({ disableAnimation: true }).then(() => {
/* 3 | SERVER-STATE auslösen – korrektes Schema! */
chat.interact({
type: 'text',
payload: 'Ah, perfekt – dann lass uns hier direkt weitermachen 🚀'
});
/* 4 | Bubble sofort wieder schliessen (kein Blinken) */
chat.close({ disableAnimation: true });
})
const shadowHost_ = document.querySelector('#voiceflow-chat'); // z.B.
console.log("shadowHost_",shadowHost_);
// 2. Auf das Shadow Root zugreifen (nur möglich, wenn es 'open' ist)
const shadowRoot_ = shadowHost_.shadowRoot;
log("shadowRoot_",shadowRoot_);
const interval_2 = setInterval(() => {
const allProactives = shadowRoot_.querySelectorAll(
'.vfrc-proactive._6r9xee0._6r9xee1 ._6r9xee3.o231sh1'
);
log('Aktueller Fund:', allProactives.length, 'Elemente');
// Beispiel: Wir warten, bis wir mind. 2 Kindelemente gefunden haben
if (allProactives.length >= 2) {
// Hintergrund/Schriftfarbe für alle gefundene Elemente setzen
allProactives.forEach(elem => {
elem.style.backgroundColor = colorButtonsHex;
elem.style.color = colorButtonsText;
log('Eingefärbtes Element:', elem);
});
// Intervall beenden, damit es nicht unendlich weiterprüft
clearInterval(interval_2);
console.log('Fertig! Intervall beendet.');
}
}, 300); // alle 300ms prüfen
// Funktion, um Buttons zu stylen
const styleButton = (button, color = `${colorCTAHex}`, textColor = `${colorCTAText}`) => {
log('Styling button:', button.textContent);
button.style.backgroundColor = color;
button.style.color = textColor;
button.style.fontWeight = 'bold';
log('Button styled successfully:', button.textContent);
};
// Funktion, um Buttons auf Relevanz zu prüfen
const isRelevantButton = (button) => {
if (!button) return false;
const text = button.textContent.trim();
log(`Checking if button is relevant: "${text}"`);
const hasIconChild = !!button.querySelector('.vfrc-icon'); // 👈 hier die neue Prüfung
return (
button.classList.contains('vfrc-button') &&
!hasIconChild && // 👈 Button wird ausgeschlossen, wenn Icon drin ist
(text.includes('Bewerben') || text === 'Weiter' || text.includes('Stellen im Umkreis')) &&
!text.includes('Minimieren') &&
!text.includes('X')
);
};
// Funktion, um Knoten und Subknoten zu prüfen
const traverseNodes = (node) => {
if (node.tagName === 'BUTTON') {
const text = node.textContent.trim();
if (isRelevantButton(node)) {
console.log('Relevant button found:', text);
styleButton(node);
} else if (!text.includes('Minimieren') && !text.includes('X')) {
log('Irrelevant button found:', text);
if (!node.querySelector('.vfrc-icon')){
styleButton(node, `${colorButtonsHex}`, `${colorButtonsText}`); // Dunkleres Blau für irrelevante Buttons
}
}
} else if (node.children && node.children.length > 0) {
Array.from(node.children).forEach((child) => traverseNodes(child));
} else {
log('Node ignored:', node);
}
};
// Funktion, um bestehende Buttons im ShadowRoot zu stylen
const styleExistingButtons = (shadowRoot) => {
if (!shadowRoot) {
log('ShadowRoot not found. Exiting styleExistingButtons.');
return;
}
log('Styling existing buttons in ShadowRoot...');
const buttons = shadowRoot.querySelectorAll('button');
log('Found existing buttons:', buttons);
buttons.forEach((button) => {
const text = button.textContent.trim();
const hasIconChild = !!button.querySelector('.vfrc-icon'); // 👈 auch hier
if (isRelevantButton(button) && !hasIconChild) {
log('Styling existing relevant button:', text);
styleButton(button);
} else if (!hasIconChild && !text.includes('Minimieren') && !text.includes('X')) {
log('Styling existing irrelevant button:', text);
styleButton(button, `${colorButtonsHex}`, `${colorButtonsText}`);
}
});
};
// Funktion, um neue Buttons im ShadowRoot zu beobachten
const observeChatButtons = (shadowRoot) => {
if (!shadowRoot) {
log('ShadowRoot not found. Exiting observeChatButtons.');
return;
}
log('Starting button observer for ShadowRoot...');
const observer = new MutationObserver((mutations) => {
log('DOM mutation detected in ShadowRoot:', mutations);
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
log('Added node detected:', node);
traverseNodes(node); // Traverse durch alle Subknoten
});
});
});
// Beobachte den ShadowRoot
observer.observe(shadowRoot, {
childList: true,
subtree: true,
});
log('Button observer is active for ShadowRoot.');
};
// Hauptfunktion, um den Chat und ShadowRoot zu prüfen
const initializeChatStyling = () => {
log('Initializing chat styling...');
const checkInterval = setInterval(() => {
log('Running checkChatVisibility...');
const chatElement = document.querySelector('#voiceflow-chat');
if (!chatElement) {
log('Chat element not found yet.');
return;
}
const shadowRoot = chatElement.shadowRoot;
if (!shadowRoot) {
log('ShadowRoot not available yet.');
return;
}
log('ShadowRoot found. Styling buttons...');
clearInterval(checkInterval); // Stoppe die weitere Prüfung
// Style vorhandene Buttons
styleExistingButtons(shadowRoot);
// Starte die Beobachtung neuer Buttons
observeChatButtons(shadowRoot);
}, 500); // Prüfung alle 500ms
};
// Starte den gesamten Prozess
initializeChatStyling();
if (
urlParams.has("utm_content") ||
urlParams.has("ref_empf") ||
urlParams.has("empf") ||
urlParams.has("bereich") ||
urlParams.has("stelle") ||
urlParams.has("map") ||
window.location.href.includes("hallo")
) {
window.voiceflow.chat.open();
if (!isDesktop()) {
disableBodyScroll();
}
} else {
window.voiceflow.chat.proactive.push(
{
type: "text",
payload: {
message: "Finde jetzt deinen Traumjob",
},
},
{
type: "text",
payload: { message: "Hier klicken, um mehr zu erfahren" },
}
);
setTimeout(function() {
window.voiceflow.chat.proactive.clear();
}, 10000);
}
startChatObserver();
checkChatVisibility();
});
};
s.parentNode.insertBefore(v, s);
})(document, "script");
}
befuellenUndAnzeigen(apiUrl);
let chatObserver = null;
let isChatOpen = false; // Globale Zustandsvariable
let scrollTop = 0;
// Flag zur Vermeidung von Endlosschleifen
let isInternalChange = false;
/**
* Einrichtung des MutationObservers
*/
function startChatObserver() {
log("=== startChatObserver() - MutationObserver wird eingerichtet ===");
// Prüfen, ob ein Observer bereits läuft
if (chatObserver) {
// log("startChatObserver - Observer läuft bereits.");
return;
}
chatObserver = new MutationObserver((mutationsList) => {
// Wenn gerade interne Änderungen vorgenommen werden, Mutationen ignorieren
if (isInternalChange) {
// log("MutationObserver - Ignoriere Mutationen während interner Änderungen.");
return;
}
// log("=== MutationObserver Callback TRIGGERED ===");
for (let mutation of mutationsList) {
// log("Mutation TYPE:", mutation.type);
if (mutation.type === "childList" || mutation.type === "attributes") {
// Logik zur Prüfung
checkChatVisibility();
}
}
});
chatObserver.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["style", "class"],
});
log("=== startChatObserver() - MutationObserver läuft jetzt ===");
}
/**
* Funktion zur Überprüfung der Chat-Sichtbarkeit
*/
function checkChatVisibility() {
// log("checkChatVisibility - Prüfung gestartet.");
const voiceflowChat = document.querySelector("#voiceflow-chat");
if (!voiceflowChat) {
console.log("checkChatVisibility - Kein Element mit ID voiceflow-chat gefunden.");
if (isChatOpen) {
enableBodyScroll();
isChatOpen = false;
}
return;
}
const shadowRoot = voiceflowChat.shadowRoot;
const headerElement = shadowRoot.querySelector('.vfrc-header');
// Falls gefunden, Hintergrundfarbe setzen
if (headerElement) {
headerElement.style.backgroundColor = background_header; // Beispiel: Rot
}
if (!shadowRoot) {
console.log("checkChatVisibility - Kein Shadow DOM in voiceflow-chat gefunden.");
if (isChatOpen) {
enableBodyScroll();
isChatOpen = false;
}
return;
}
if(widget == "https://cdn.voiceflow.com/widget-next/bundle.mjs") {
endChatBtn =
Array.from(shadowRoot.querySelectorAll('button')).find((btn) => {
const pathEl = btn.querySelector('svg path');
return pathEl && pathEl.getAttribute('d')?.includes('M17.7478');
})
// Neu: X-Button prüfen (Button, der ein .vfrc-icon-Kind hat)
xCloseBtn = Array.from(shadowRoot.querySelectorAll('button')).find((btn) => {
const pathEl = btn.querySelector('svg path');
return pathEl && pathEl.getAttribute('d')?.includes('M17.7478');
});
} else {
xCloseBtn = shadowRoot.querySelector('path#vf-close')?.closest('button');
endChatBtn = shadowRoot.querySelector('button[label="End Chat"]');
}
// Suche der Message im ShadowRoot
let startchat = null;
setTimeout(() => {
startchat = shadowRoot.querySelector(".vfrc-system-response");
if (startchat) {
console.log("startchat ist da", startchat);
if (!isDesktop()) {
disableBodyScroll();
// Event-Listener für den 'End Chat'-Button hinzufügen
addEndChatButtonListener(endChatBtn);
// Event-Listener für den 'X'-Button hinzufügen
addXButtonListener(shadowRoot);
isChatOpen = true;
} else {
// log("checkChatVisibility - 'End Chat'-Button NICHT gefunden, Chat ist geschlossen.");
if (isChatOpen) {
isChatOpen = false;
if (scrollDisabled) {
enableBodyScroll();
}
}
}
}
}, 300);
}
/**
* Fügt einen Event-Listener zum 'End Chat'-Button hinzu
*/
function addEndChatButtonListener(endChatBtn) {
if (!endChatBtn.dataset.listenerAdded) {
endChatBtn.dataset.listenerAdded = "true";
endChatBtn.addEventListener("click", () => {
console.log("End Chat Button wurde geklickt!");
enableBodyScroll();
});
// log("addEndChatButtonListener - Event-Listener hinzugefügt.");
} else {
// log("addEndChatButtonListener - Event-Listener bereits hinzugefügt.");
}
}
/**
* Fügt einen Event-Listener zum 'X'-Button hinzu
*/
function addXButtonListener(shadowRoot) {
const xCloseBtn = shadowRoot.querySelector('path#vf-close')?.closest("button");
if (xCloseBtn) {
// log("addXButtonListener - X-Button gefunden:", xCloseBtn);
if (!xCloseBtn.dataset.listenerAdded) {
xCloseBtn.dataset.listenerAdded = "true";
xCloseBtn.addEventListener("click", () => {
log("X-Button geklickt => enableBodyScroll()");
enableBodyScroll();
});
} else {
}
} else {
}
}
// Scroll-Funktionen
let scrollDisabled = false;
function disableBodyScroll() {
console.log("disableBodyScroll - Scrollen wird deaktiviert.");
// Flag setzen, um Änderungen zu kennzeichnen
isInternalChange = true;
scrollTop = window.scrollY || document.documentElement.scrollTop || 0;
document.body.style.position = "fixed";
//document.body.style.top = `-${scrollTop}px`;
document.body.style.top = `0px`;
document.body.style.width = "100%";
console.log(`disableBodyScroll - Status: scrollTop=${scrollTop}`);
scrollDisabled = true;
// Flag zurücksetzen nach einer kurzen Verzögerung
setTimeout(() => {
isInternalChange = false;
}, 0);
}
function enableBodyScroll() {
// log("enableBodyScroll - Scrollen wird aktiviert.");
// Flag setzen, um Änderungen zu kennzeichnen
isInternalChange = true;
document.body.style.position = "";
document.body.style.top = "";
document.body.style.width = "";
window.scrollTo(0, scrollTop || 0);
// log("enableBodyScroll - Status: Scroll aktiviert.");
scrollDisabled = false;
// Flag zurücksetzen nach einer kurzen Verzögerung
setTimeout(() => {
isInternalChange = false;
}, 0);
}
} // Ende: if (Cookiebot && Cookiebot.consent && Cookiebot.consent.preferences)
})(); // Ende des IIFE