PageSourceSearch

https://www.foboxy.com/assets/js/script.js

js foboxy.com collected 2026-10-09 17:08:34 UTC 19,818 bytes, 483 lines download raw bytes

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.