1var fbxMainAjaxUrl = "/assets/init.php"; 2 3jQuery(document).ready(function() { 4 fbxSetCookies(); 5}); 6 7// Globale Helper-Funktion: zuerst Ableitung aus dem URL-Pfad (sichtbare Seite gewinnt), 8// sonst Cookie `fbx-lang`. So verhindern wir z. B. âlu-deâ auf /de durch ein veraltetes Cookie nach /lu/de. 9// Rückgabe: lang_slug wie "de-de", "nl-nl", "be-fr", "lu-nl" oder "". 10window.fbxGetLangSlug = function fbxGetLangSlug(pathname) { 11 const readCookie = (name) => { 12 const m = document.cookie.match(new RegExp("(?:^|;\\s*)" + name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + "=([^;]+)")); 13 if (!m) return ""; 14 try { return decodeURIComponent(m[1]); } catch (e) { return m[1]; } 15 }; 16 17 const clean = String(pathname || window.location.pathname || "/").replace(/^\/+|\/+$/g, ""); 18 if (clean) { 19 const parts = clean.split("/").map((p) => String(p).toLowerCase()).filter(Boolean); 20 const first = parts[0] || ""; 21 const second = parts[1] || ""; 22 23 if (/^[a-z]{2}-[a-z]{2}$/.test(first)) return first; 24 if (first === "lu" && ["de", "fr", "nl"].includes(second)) return `lu-${second}`; 25 if (first === "be" && ["fr", "nl"].includes(second)) return `be-${second}`; 26 27 const shortMap = { 28 de: "de-de", 29 nl: "nl-nl", 30 fr: "fr-fr", 31 pl: "pl-pl", 32 at: "de-at", 33 lu: "lu-fr", 34 be: "be-nl", 35 lu: "lu-de", 36 be: "be-fr", 37 }; 38 const fromPath = shortMap[first] || ""; 39 if (fromPath) return fromPath; 40 } 41 42 let langSlug = String(readCookie("fbx-lang") || "").toLowerCase().trim(); 43 if (/^[a-z]{2}-[a-z]{2}$/.test(langSlug)) return langSlug; 44 45 return ""; 46}; 47 48// Baut die URL zur Buchungsstrecke (hauptsache.net) anhand des lang_slug. 49// Beispiele: 50// - de-de -> /de/de/buchung/{YYYYMMDD}/anfrage 51// - de-at -> /at/de/buchung/{YYYYMMDD}/anfrage 52// - fr-fr -> /fr/fr/reservation/{YYYYMMDD}/demande 53// - lu-fr -> /lu/fr/reservation/{YYYYMMDD}/demande 54// - be-fr -> /be/fr/reservation/{YYYYMMDD}/demande 55// - nl-nl -> /nl/nl/boeking/{YYYYMMDD}/aanvraag 56// - be-nl -> /be/nl/boeking/{YYYYMMDD}/aanvraag 57// - pl-pl -> /pl/pl/rezerwacja/{YYYYMMDD}/zapytanie 58window.fbxBuildBookingUrl = function fbxBuildBookingUrl(langSlug, dateApp, baseUrl) { 59 const cleanLang = String(langSlug || "").toLowerCase().trim(); 60 const cleanDate = String(dateApp || "").trim(); // erwartet YYYYMMDD 61 const cfg = (window.fbxConfig || {}); 62 const baseCandidate = baseUrl || cfg.bookingBaseUrl || ""; 63 const base = String(baseCandidate).replace(/\/+$/, ""); 64 if (!base) return ""; 65 66 const marketPrefixByLangSlug = { 67 "de-de": "de/de", 68 "de-at": "at/de", 69 "fr-fr": "fr/fr", 70 "lu-fr": "lu/fr", 71 "be-fr": "be/fr", 72 "nl-nl": "nl/nl", 73 "be-nl": "be/nl", 74 "pl-pl": "pl/pl", 75 // sinnvolle Defaults für evtl. weitere Märkte 76 "lu-de": "lu/de", 77 "lu-nl": "lu/nl", 78 }; 79 80 const routeByLanguage = { 81 "de": { booking: "buchung", request: "anfrage" }, 82 "fr": { booking: "reservation", request: "demande" }, 83 "nl": { booking: "boeking", request: "aanvraag" }, 84 "pl": { booking: "rezerwacja", request: "zapytanie" }, 85 }; 86 87 const prefix = marketPrefixByLangSlug[cleanLang] || "de/de"; 88 // Sprachanteil kommt aus dem zweiten Segment von xx-yy (z. B. be-fr -> fr) 89 const langPart = cleanLang.split("-")[1] || "de"; 90 const route = routeByLanguage[langPart] || routeByLanguage["de"]; 91 92 // Falls date nicht passt, trotzdem URL bauen (Caller kann validieren) 93 return `${base}/${prefix}/${route.booking}/${cleanDate}/${route.request}`; 94}; 95 96// Liefert den effektiv zu verwendenden Language-Slug: 97// 1) aus sichtbarem URL-Pfad ableiten (sichtbare Seite gewinnt) 98// 2) window.fbxConfig.langSlug (vom Server/Template gesetzt) 99// 3) Fallback über Cookie in fbxGetLangSlug 100window.fbxGetEffectiveLangSlug = function fbxGetEffectiveLangSlug() { 101 const fromPathOrCookie = (window.fbxGetLangSlug && window.fbxGetLangSlug(window.location.pathname)) || ""; 102 if (/^[a-z]{2}-[a-z]{2}$/.test(fromPathOrCookie)) return fromPathOrCookie; 103 104 const cfg = (window.fbxConfig || {}); 105 const fromCfg = String(cfg.langSlug || "").toLowerCase().trim(); 106 if (/^[a-z]{2}-[a-z]{2}$/.test(fromCfg)) return fromCfg; 107 return fromPathOrCookie; 108}; 109 110// Baut Booking-URL basierend auf fbxConfig (ohne dass Templates Base-URLs hardcoden müssen) 111window.fbxGetBookingUrl = function fbxGetBookingUrl(dateApp) { 112 const cfg = (window.fbxConfig || {}); 113 const baseUrl = cfg.bookingBaseUrl || ""; 114 const langSlug = window.fbxGetEffectiveLangSlug ? window.fbxGetEffectiveLangSlug() : ""; 115 return window.fbxBuildBookingUrl ? window.fbxBuildBookingUrl(langSlug, dateApp, baseUrl) : ""; 116}; 117 118// Setzt action auf das Booking-Formular und submit 119window.fbxSubmitBooking = function fbxSubmitBooking(dateApp) { 120 const url = window.fbxGetBookingUrl ? window.fbxGetBookingUrl(dateApp) : ""; 121 if (!url) return false; 122 const $form = jQuery("form#fbx_event_booking"); 123 if ($form.length === 0) return false; 124 $form.attr("action", url); 125 $form.submit(); 126 return true; 127}; 128 129function fbxSetCookies() { 130 const parseParams = (querystring) => { 131 const params = new URLSearchParams(querystring); 132 const obj = {}; 133 for (const key of params.keys()) {
134 if (params.getAll(key).length > 1) { 135 obj[key] = params.getAll(key); 136 } else { 137 obj[key] = params.get(key); 138 } 139 } 140 return obj; 141 }; 142 const url = new URL(window.location.href); 143 const postObj = parseParams(window.location.search); 144 // Locale/Market-Info zusätzlich mitsenden, damit init.php die richtige Sprache/Land setzen kann 145 postObj["lang_slug"] = (window.fbxGetLangSlug && window.fbxGetLangSlug(window.location.pathname)) || ""; 146 postObj["path"] = window.location.pathname || "/"; 147 148 jQuery.ajax({ 149 url: fbxMainAjaxUrl, 150 type: "post", 151 /*data: parseParams(window.location.search),*/ 152 data: postObj, 153 success: function(response) { 154 console.log(response); 155 }, 156 error: function(xhr) { 157 console.log(xhr); 158 } 159 }); 160} 161 162 163 164$(document).ready(function() { 165 166 /** ---------------------------- // 167 * @group viewport trigger script 168 * for adding or removing classes from elements in view within viewport 169 */ 170 171 var $animationElements = $('.animation-element'); 172 var $window = $(window); 173 174 // ps: Let's FIRST disable triggering on small devices! 175 var isMobile = window.matchMedia("only screen and (max-width: 768px)"); 176 if (isMobile.matches) { 177 $animationElements.removeClass('animation-element'); 178 } 179 180 function checkIfInView() { 181 182 var windowHeight = $window.height(); 183 var windowTopPosition = $window.scrollTop(); 184 var windowBottomPosition = (windowTopPosition + windowHeight); 185 186 $.each($animationElements, function () { 187 var $element = $(this); 188 var elementHeight = $element.outerHeight(); 189 var elementTopPosition = $element.offset().top; 190 var elementBottomPosition = (elementTopPosition + elementHeight); 191 192 //check to see if this current container is within viewport 193 if ((elementBottomPosition >= windowTopPosition) && 194 (elementTopPosition <= windowBottomPosition)) { 195 $element.addClass('in-view'); 196 } else { 197 $element.removeClass('in-view'); 198 } 199 }); 200 } 201 202 $window.on('scroll resize', checkIfInView); 203 $window.trigger('scroll'); 204 }); 205 206 207 $(document).ready(function() { 208 209 /** ---------------------------- // 210 * @group viewport trigger script 211 * for adding or removing classes from elements in view within viewport 212 */ 213 214 var $animationElements = $('.bg-item3'); 215 var $window = $(window); 216 217 // ps: Let's FIRST disable triggering on small devices! 218 var isMobile = window.matchMedia("only screen and (max-width: 768px)"); 219 if (isMobile.matches) { 220 $animationElements.removeClass('start'); 221 } 222 223 function checkIfInView() { 224 225 var windowHeight = $window.height(); 226 var windowTopPosition = $window.scrollTop(); 227 var windowBottomPosition = (windowTopPosition + windowHeight); 228 229 $.each($animationElements, function () { 230 var $element = $(this); 231 var elementHeight = $element.outerHeight(); 232 var elementTopPosition = $element.offset().top; 233 var elementBottomPosition = (elementTopPosition + elementHeight); 234 235 //check to see if this current container is within viewport 236 if ((elementBottomPosition >= windowTopPosition) && 237 (elementTopPosition <= windowBottomPosition)) { 238 $element.addClass('start'); 239 } else { 240 $element.removeClass('start'); 241 } 242 }); 243 } 244 245 $window.on('scroll resize', checkIfInView); 246 $window.trigger('scroll'); 247 248 var video = document.getElementById("logo-video"); 249 250 if (video) { 251 video.muted = true; 252 video.playsInline = true; 253 254 video.addEventListener("canplay", function () { 255 setTimeout(function () { 256 video.play().catch(function (error) { 257 console.warn("Autoplay wurde blockiert:", error); 258 }); 259 }, 2000); 260 }); 261 262 // Optional: fallback für ältere Browser 263 video.load(); 264 } 265 266}); 267 268/* 269console.log("A"); 270function onUc(ev){ console.log("ucEvent gesehen:", ev); } 271window.addEventListener("ucEvent", onUc); 272document.addEventListener("ucEvent", onUc); 273console.log("B"); 274*/ 275 276document.addEventListener('DOMContentLoaded', function () { 277 const mobileMenu = document.getElementById('navbarNav'); 278 279 if (!mobileMenu) return; 280 281 mobileMenu.addEventListener('show.bs.collapse', function (e) { 282 if (e.target !== mobileMenu) return; 283 document.body.classList.add('menu-open'); 284 }); 285 286 mobileMenu.addEventListener('hide.bs.collapse', function (e) { 287 if (e.target !== mobileMenu) return; 288 document.body.classList.remove('menu-open'); 289 }); 290}); 291 292 293window.addEventListener("ucEvent", function (e) { 294 if (!e.detail || e.detail.event !== "consent_status") return; 295 296 const hostname = window.location.hostname; 297 const tld = hostname.split('.').pop(); 298 const path = window.location.pathname; 299
300 /* Crisp Chat (nur Guide-URLs) */ 301 const allowedPaths = ["/fr/guide", "/nl/handleiding", "/pl/instrukcja", "/lu/fr/guide", "/lu/de/anleitung", "/be/fr/guide", "/be/nl/handleiding"]; 302 if (allowedPaths.includes(path)) { 303 const $buttons = jQuery(".open_crisp"); 304 305 function getConsentMessage(currentPath) { 306 if (currentPath === "/fr/guide" || currentPath === "/lu/fr/guide" || currentPath === "/be/fr/guide") { 307 return "Veuillez accepter l'utilisation du service Crisp dans les paramètres de confidentialité."; 308 } 309 310 if (currentPath === "/nl/handleiding" || currentPath === "/be/nl/handleiding") { 311 return "Accepteer het gebruik van de Crisp-service in de privacyinstellingen."; 312 } 313 314 if (currentPath === "/pl/instrukcja") { 315 return "Zaakceptuj korzystanie z usÅugi Crisp w ustawieniach prywatnoÅci."; 316 } 317 318 if (currentPath === "/lu/de/anleitung") { 319 return "Bitte akzeptiere die Nutzung von Crisp in den Datenschutz-Einstellungen."; 320 } 321 322 return "Please accept the use of the Crisp service in the privacy settings."; 323 } 324 325 function hasCrispConsent(detail) { 326 if (detail["Crisp Chat"] === true) { 327 return true; 328 } 329 330 if (Array.isArray(detail.services)) { 331 const crispService = detail.services.find(function (service) { 332 return service.name === "Crisp Chat"; 333 }); 334 335 return !!(crispService && crispService.consent && crispService.consent.given); 336 } 337 338 return false; 339 } 340 341 function loadCrisp() { 342 window.$crisp = window.$crisp || []; 343 344 if (!window.CRISP_WEBSITE_ID) { 345 window.CRISP_WEBSITE_ID = "3a50a659-5310-4a22-a29f-a9ce481b8e05"; 346 } 347 348 const existingScript = document.querySelector( 349 'script[src="https://client.crisp.chat/l.js"]' 350 ); 351 352 if (!existingScript) { 353 const script = document.createElement("script"); 354 script.src = "https://client.crisp.chat/l.js"; 355 script.async = true; 356 document.head.appendChild(script); 357 } 358 } 359 360 const crispConsent = hasCrispConsent(e.detail); 361 362 $buttons.off("click.crisp"); 363 364 if (crispConsent) { 365 loadCrisp(); 366 367 $buttons.on("click.crisp", function (event) { 368 event.preventDefault(); 369 window.$crisp = window.$crisp || []; 370 window.$crisp.push(["do", "chat:open"]); 371 }); 372 } else { 373 $buttons.on("click.crisp", function (event) { 374 event.preventDefault(); 375 376 alert(getConsentMessage(path)); 377 378 if (window.__ucCmp && typeof window.__ucCmp.showSecondLayer === "function") { 379 window.__ucCmp.showSecondLayer(); 380 } 381 }); 382 } 383 } 384 385 /* Superchat */ 386 if (e.detail['Superchat'] === true) { 387 const styles = document.createElement("style"); 388 styles.innerHTML = '#superchat-widget { width: calc(120px + 20px) !important; height: calc(120px + 20px) !important; max-width: calc(640px + 1px) !important; position: fixed !important; bottom: 35px !important; background: transparent !important; border: 0 !important; -webkit-transition-property: none !important; -moz-transition-property: none !important; -o-transition-property: none !important; transition-property: none !important; } #superchat-widget.open { width: 100% !important; height: 100% !important; } #superchat-widget.placement-left { left: 0 !important; } #superchat-widget.placement-right { right: 0 !important; } #superchat-widget.greeting:not(.open) { width: calc(400px + 20px) !important; height: calc(280px + 20px) !important; } @media(min-width: 640px) { #superchat-widget.open { height: calc(660px + 20px) !important; } } @media (max-width:649px) { #superchat-widget { bottom: 0px !important; } }'; 389 styles.setAttribute("id", "SuperchatCSS"); 390 document.body.appendChild(styles); 391 const element = document.createElement("iframe"); 392 element.style.zIndex = "10000";
393 element.id = "superchat-widget"; 394 element.classList.add("placement-right"); 395 element.src = 'https://widget.superchat.de/v2?applicationKey=WCA36v4Zg7dwEA7Or5oGYJpMKq'; 396 document.body.appendChild(element); 397 function handleMessage(event) { 398 if (event.origin !== "https://widget.superchat.de" || event.data.type !== "v2:controller-state-change") { 399 return; 400 } 401 element.classList.toggle("open", event.data.details.isOpen); 402 element.classList.toggle("greeting", event.data.details.isShowingGreeting); 403 } 404 window.addEventListener("message", handleMessage, false); 405 } else { 406 jQuery("#SuperchatCSS").remove(); 407 jQuery("#superchat-widget").remove(); 408 } 409 410 /* Trusted Shops Badge */ 411 if (e.detail['Trusted Shops'] === true && tld == "de") { 412 var s = document.getElementsByTagName('script')[0]; 413 var script = document.createElement('script'); 414 script.src = '//widgets.trustedshops.com/js/XD6167D9815CD3E0043452FB0AB48CC64.js'; 415 script.setAttribute("id", "TSBadgeJS"); 416 script.setAttribute("data-desktop-y-offset", "0"); 417 script.setAttribute("data-mobile-y-offset", "0"); 418 script.setAttribute("data-desktop-disable-reviews", "false"); 419 script.setAttribute("data-desktop-enable-custom", "false"); 420 script.setAttribute("data-desktop-position", "left"); 421 script.setAttribute("data-desktop-custom-width", "156"); 422 script.setAttribute("data-desktop-enable-fadeout", "false"); 423 script.setAttribute("data-disable-mobile", "false"); 424 script.setAttribute("data-disable-trustbadge", "false"); 425 script.setAttribute("data-mobile-custom-width", "156"); 426 script.setAttribute("data-mobile-disable-reviews", "false"); 427 script.setAttribute("data-mobile-enable-custom", "false"); 428 script.setAttribute("data-mobile-position", "left"); 429 script.setAttribute("charset", "UTF-8"); 430 script.setAttribute("id", "TSBadge"); 431 432 s.parentNode.insertBefore(script, s); 433 434 } else { 435 jQuery("script#TSBadgeJS").remove(); 436 jQuery("#TSBadge").remove(); 437 jQuery("#trustbadge-iframe").remove(); 438 jQuery("[id^='trustbadge-container']").remove(); 439 } 440 441 /* Trusted Shops Widget */ 442 if (e.detail['Trusted Shops'] === true && tld == "de") { 443 var s = document.getElementsByTagName('script')[0]; 444 var script = document.createElement('script'); 445 script.setAttribute("id", "TSWidgetJS"); 446 script.src = 'https://integrations.etrusted.com/applications/widget.js/v2'; 447 s.parentNode.insertBefore(script, s); 448 449 jQuery(".fbxTrustedShopsWidget .script .wpb_wrapper").html('<etrusted-widget data-etrusted-widget-id="wdg-b6482e95-2c33-4bfb-a6cf-10551f0eef6f"></etrusted-widget>'); 450 } else { 451 jQuery(".fbxTrustedShopsWidget .script .wpb_wrapper").html('<span class="text-white">An dieser Stelle werden Kundenbewertungen über ein TrustedShops Widget eingebunden, wenn du der Nutzung zugestimmt hast. Du hast der Verwendung <strong>nicht zugestimmt</strong>, kannst das aber jederzeit in den <a class="text-orange" href="#" onclick="UC_UI.showSecondLayer();" style="text-decoration:underline;">Cookie Einstellungen</a> ändern!</div>'); 452 jQuery("#TSWidgetJS").remove(); 453 } 454 455 /* Juicer */ 456 if (jQuery("body").hasClass("juicer")) { 457 if (e.detail['Juicer'] === true) { 458 var s = document.getElementsByTagName('script')[0]; 459 var script = document.createElement('script'); 460 script.src = '//assets.juicer.io/embed.js'; 461 script.setAttribute("id", "JuicerJS"); 462 s.parentNode.insertBefore(script, s); 463 464 var style = document.createElement('link'); 465 style.rel = 'stylesheet'; 466 style.id = 'JuicerCSS'; 467 style.type = 'text/css'; 468 style.href = '//assets.juicer.io/embed.css'; 469 document.head.appendChild(style); 470 471 jQuery('.juicer-content').html('<ul class="juicer-feed" data-feed-id="foboxy" data-origin="embed-code"></ul></div>'); 472 jQuery('.juicer-dsgvo').hide(); 473 if(jQuery("body").hasClass("init")) { 474 window.location.reload(); 475 } 476 jQuery("body").addClass("init"); 477 } else { 478 jQuery("script#JuicerJS, style#JuicerCSS").remove(); 479 jQuery('.juicer-dsgvo').show(); 480 jQuery('.juicer-content').hide(); 481 } 482 } 483});
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.