PageSourceSearch

https://www.fit-star.de/_assets/9cf6576c6fbffeb8186a9a45e4d2c8c3/JavaScripts/FitstarMain.js?1776855279

js fit-star.de collected 2026-10-04 00:00:50 UTC 16,036 bytes, 568 lines download raw bytes

1// from: /Resources/Private/Layouts/Page/Default.html
2const pathsElement = document.getElementById('js-paths');
3const modulePath = pathsElement.dataset.module;
4const vendorPath = pathsElement.dataset.vendor;
5const imagePath = pathsElement.dataset.images;
6const lottiePath = pathsElement.dataset.lottie;
7
8
9// ----- MOBILE MENU -------------------------------------------------------//
10
11const initMenu = async () => {
12
13    const menu = document.querySelector('#navDrop');
14
15    if (!menu) return;
16
17    const { default: Navi } = await import(`${modulePath}/Menu.js`)
18
19    Navi();
20
21};
22
23
24
25// ----- SCROLLLISTENER -------------------------------------------------------//
26
27const initScrolledListener = () => {
28
29    document.addEventListener('scroll', () => {
30
31        if (window.scrollY > 50) {
32            document.body.classList.add('js-scrolled');
33        } else {
34            document.body.classList.remove('js-scrolled');
35        }
36    });
37};
38
39
40// ----- SCROLLLISTENER -------------------------------------------------------//
41
42const initHeaderButton = () => {
43
44    const stage = document.querySelector('.stage--hideButton');
45
46    if (!stage) return;
47
48    document.body.classList.add('js-hideButton');
49
50    document.addEventListener('scroll', () => {
51
52        const { bottom } = stage.getBoundingClientRect();
53
54        if (bottom < 0) {
55            document.body.classList.remove('js-hideButton');
56        } else {
57            document.body.classList.add('js-hideButton');
58        }
59    });
60};
61
62
63// ----- Accordion -------------------------------------------------------//
64
65const initAccordions = async () => {
66
67    const accordionSelector = '.js-accordion';
68
69    if (!document.querySelector(accordionSelector)) return;
70
71    const { default: initAccordion } = await import(`${modulePath}/Accordion.js`);
72
73    initAccordion(accordionSelector);
74
75};
76
77// ----- Countdown -------------------------------------------------------//
78
79const initCountdowns = async () => {
80
81    const countdownSelector = '.js-countdown';
82
83    if (!document.querySelector(countdownSelector)) return;
84
85    const { default: initCountdown } = await import(`${modulePath}/Countdown.js`);
86
87    initCountdown(countdownSelector);
88
89};
90
91const initTestimonials = async () => {
92
93    const swiperSelector = '.swiper.testimonials__container'
94    const swiperContainer = document.querySelector(swiperSelector);
95
96    if (!swiperContainer) return;
97
98    const { Swiper } = await import(`${vendorPath}/swiper-bundle.esm.browser.min.js`);
99
100    const swiper = new Swiper(swiperSelector, {
101        initialSlide: 0,
102        slidesPerView: 1,
103        loop: false,
104        autoplay: false,
105        pagination: {
106            el: '.swiper-pagination',
107            clickable: true
108        },
109        navigation: {
110            nextEl: '.swiper-button--next',
111            prevEl: '.swiper-button--prev',
112        }
113    });
114};
115
116
117// ----- image Slider ---------------------------------------------------//
118
119const initImageSlider = async () => {
120
121    const swiperSelector = '.swiper.imageSlider'
122    const swiperContainers = document.querySelectorAll(swiperSelector);
123
124    if (!swiperContainers[0]) return;
125
126    const { Swiper } = await import(`${vendorPath}/swiper-bundle.esm.browser.min.js`);
127
128
129    swiperContainers.forEach((element, i) => {
130
131        const buttonPrev = document.querySelectorAll('.swiper ~ .swiper-button2--prev');
132        const buttonNext = document.querySelectorAll('.swiper ~ .swiper-button2--next');
133        const autoplay = (element.dataset.autoplay === '1');
134        const loop = (element.dataset.loop === '1');
135
136        const swiper = new Swiper(element, {
137            initialSlide: 0,
138            slidesPerView: 1,
139            loop: loop,
140            autoplay: autoplay,
141            pagination: {
142                el: '.swiper-pagination',
143                clickable: true
144            },
145            navigation: {
146                prevEl: buttonPrev[i],
147                nextEl: buttonNext[i]
148            }
149        });
150    });
151};
152
153
154// ----- teasers Slider ---------------------------------------------------//
155
156const initTeaserSlider = async () => {
157
158    const swiperSelector = '.swiper.teasers'
159    const swiperContainer = document.querySelector(swiperSelector);
160
161    if (!swiperContainer) return;
162
163    const { Swiper } = await import(`${vendorPath}/swiper-bundle.esm.browser.min.js`);
164
165    const swiper = new Swiper(swiperSelector, {
166        initialSlide: 0,
167        slidesPerView: 1.2,
168        loop: false,
169        autoplay: false,
170        spaceBetween: 20,
171        pagination: {
172            el: '.swiper-pagination',
173            clickable: true
174        },
175        breakpoints: {
176            769: {
177                slidesPerView: 2.2
178            },
179            1200: {
180                slidesPerView: 3
181            }
182        }
183    });
184};
185
186
187// ----- Form Input Label Animation ---------------------------------------------------//
188const initFormInputs = async () => {
189
190    const inputSelector = '.form-control';
191
192    if (!document.querySelector(inputSelector)) return;
193
194    const { default: initInputs } = await import(`${modulePath}/FormInput.js`);
195
196    initInputs(inputSelector);
197};
198
199
200// Custom Select -------------------------------------------------------//
201
202const initFormSelects = async () => {
203
204    const formSelects = document.querySelectorAll('.custom-select');
205
206    if (!formSelects) return;
207
208    const { default: CSelect } = await import(`${vendorPath}/cselect.js`);
209
210
211    for (const select of formSelects) {
212
213        let search = false;
214
215        if (select.nextElementSibling?.classList.contains('cSelect')) return;
216
217        if (select.classList.contains('custom-select--search')) {
218            search = true;
219        }
220
221        new CSelect(select, {
222            search: search,
223            animated: true,
224        });
225    }
226
227    initFormSelectsZIndex();
228};
229
230
231const initFormSelectsZIndex = () => {
232
233    // prevent custom selects overlapping each other
234
235    const formSelects = document.querySelectorAll('.cSelect__default');
236
237    formSelects.forEach((element, i) => {
238
239        const selectDrop = element.nextElementSibling;
240        const zIndex = 1000 - i * 5;
241
242        element.style.zIndex = zIndex;
243        selectDrop.style.zIndex = zIndex - 1;
244    })
245
246};
247
248const initFormUploads = () => {
249
250    const formUploads = document.querySelectorAll('.file-input input');
251
252    formUploads.forEach((element, i) => {
253
254        element.addEventListener('change', () => {
255
256            const fileName = element.value.replace(/\.[^/.]+$/, "").split('\\').pop();
257            const uploadField = element.parentNode.parentNode;
258
259            if (fileName !== '') {
260                uploadField.querySelector('.file-name').innerHTML = fileName;
261                uploadField.querySelector('.placeholder').classList.add('hidden');
262                uploadField.querySelector('label').classList.add('filled');
263            }
264        });
265    })
266};
267
268
269const initButtonChat = async () => {
270
271    const chatButtons = document.querySelectorAll('.button--chat');
272
273    if (!chatButtons) return;
274
275    const { waitForElement: waitForElement } = await import(`${modulePath}/Utils.js`);
276
277    waitForElement('#launcher').then((elm) => {
278
279        const chatIFrame = document.querySelector('#launcher');
280
281        chatButtons.forEach(element => {
282            element.addEventListener('click', (event) => {
283                const c = chatIFrame.contentDocument || chatIFrame.contentWindow.document;
284                c.querySelector('button[data-garden-id]').click();
285            });
286        })
287    });
288}
289
290
291const initCopyToClipboard = async () => {
292
293    const clipboardButtons = document.querySelectorAll('.copyToClipboard');
294
295    if (!clipboardButtons[0]) return;
296
297    clipboardButtons.forEach(element => {
298
299        const clipboardContainer = element.querySelector('.copyToClipboardText') || element;
300        const clipboardText = clipboardContainer.innerHTML;
301
302        element.addEventListener('click', (event) => {
303            navigator.clipboard.writeText(clipboardText);
304
305            alert('"' + clipboardText + '" wurde in die Zwischenablage kopiert.');
306        });
307    })
308}
309
310
311// fix for safari browser back issues
312const initBrowserBackFix = async () => {
313    window.onpageshow = function (event) {
314        if (event.persisted) {
315            window.location.reload();
316        }
317    };
318}
319const initLottie = async () => {
320    const lottieContainers = document.querySelectorAll('.lottie-animation');
321
322    if (lottieContainers[0]) {
323        const { DotLottie } = await import(`${vendorPath}/dotlottie.js`);
324        const { default: InitLottie } = await import(`${modulePath}/Lottie.js`);
325        const lottieInstance = new InitLottie(lottieContainers);
326
327        lottieContainers.forEach(element => {
328            const filePath = element.dataset.path;
329            new DotLottie({
330                autoplay: true,
331                loop: true,
332                canvas: element,
333                src: filePath,
334            });
335        });
336
337        lottieInstance.init();
338    }
339};
340const initFocus = () => {
341
342    document.addEventListener('keydown', function (e) {
343        if (e.key === 'Tab') {
344            document.body.classList.remove('js-hideOutline');
345        }
346    });
347
348    document.addEventListener('mousedown', function () {
349        document.body.classList.add('js-hideOutline');
350    });
351};
352
353const initGlobalLottie = async () => {
354    // CustomEvent from register-new.js
355    document.addEventListener('FdLightboxContentLoaded', async (event) => {
356        // Container
357        const lottieContainers = document.querySelectorAll('.globalLottie');
358        // Import necessary libraries
359        const { DotLottie } = await import(`${vendorPath}/dotlottie.js`);
360        const { default: InitGlobalLottie } = await import(`${modulePath}/GlobalLottie.js`);
361
362        // Add more animations
363        const lottieAnimation = new InitGlobalLottie([
364            `${lottiePath}sad1.json`,
365            `${lottiePath}sad2.json`,
366            `${lottiePath}sad3.json`,
367        ]);
368
369        lottieContainers.forEach(element => {
370            setTimeout(() => {
371                const filePath = lottieAnimation.init();
372                new DotLottie({
373                    autoplay: true,
374                    loop: true,
375                    canvas: element,
376                    src: filePath,
377                });
378
379                // Remove with transition
380                setTimeout(() => {
381                    element.style.transition = "opacity 1s";
382                    element.style.opacity = 0;
383                    setTimeout(() => {
384                        element.style.display = "none";
385                    }, 1000); //transition time
386                }, 4000);
387            }, 500); // delay  0.5s
388        });
389    });
390};
391
392
393
394// ----- ABKey DataLayer -------------------------------------------------------//
395const initAbKeyDataLayer = () => {
396    const links = document.querySelectorAll('a[data-abkey]');
397
398    links.forEach(link => {
399        link.addEventListener('click', (e) => {
400            const abKey = link.dataset.abkey;
401            if (abKey) {
402                e.preventDefault();
403                window.dataLayer = window.dataLayer || [];
404                window.dataLayer.push({
405                    'event': abKey,
406                    'url': link.href
407                });      
408               
409                setTimeout(() => {
410                    window.location.href = link.href;
411                    console.log(window.dataLayer);
412                }, 100);
413            }
414        });
415    });
416};
417
418
419function initABTesting(options = {}) {
420    const {
421        selector = '[data-ab-variant]',
422        useSessionStorage = true,
423        sessionStorageKey = 'abUser'
424    } = options;
425
426    // Guard: nicht doppelt initialisieren
427    if (window.__abTestingInitialized) {
428        return window.__abUserVariant || null;
429    }
430    window.__abTestingInitialized = true;
431
432    // Weniger als 2 Varianten-Buttons: ersten sichtbar machen, kein A/B-Testing
433    const allVariantButtons = document.querySelectorAll(selector);
434    if (allVariantButtons.length < 2) {
435        if (allVariantButtons.length === 1) {
436            allVariantButtons[0].removeAttribute('hidden');
437        }
438        return null;
439    }
440
441    const normalizeVariant = (value) => {
442        return (value === 'A' || value === 'B') ? value : null;
443    };
444
445    const pickRandomVariant = () => {
446        // 50/50: B in 50% der Fälle
447        return (Math.random() < 0.5) ? 'B' : 'A';
448    };
449
450    const getVariantFromDataLayer = () => {
451        const dl = window.dataLayer;
452        if (!Array.isArray(dl)) return null;
453
454        // Von hinten suchen (letzter Stand ist meist maßgeblich)
455        for (let i = dl.length - 1; i >= 0; i--) {
456            const item = dl[i];
457            if (item && typeof item === 'object' && 'abUser' in item) {
458                return normalizeVariant(item.abUser);
459            }
460        }
461        return null;
462    };
463
464    const hasAbUserInDataLayer = () => {
465        const dl = window.dataLayer;
466        if (!Array.isArray(dl)) return false;
467
468        for (let i = dl.length - 1; i >= 0; i--) {
469            const item = dl[i];
470            if (item && typeof item === 'object' && 'abUser' in item) {
471                return true;
472            }
473        }
474        return false;
475    };
476
477    // 1) Primär: Server-Session
478    let abUser = normalizeVariant(window?.fdsession?.abUser);
479
480    // 2) Falls schon im dataLayer vorhanden: übernehmen (und später NICHT nochmal pushen)
481    if (!abUser) {
482        abUser = getVariantFromDataLayer();
483    }
484
485    // 3) Session-only Persistenz (kein Cookie)
486    if (!abUser && useSessionStorage) {
487        try {
488            abUser = normalizeVariant(window.sessionStorage.getItem(sessionStorageKey));
489        } catch (e) {}
490    }
491
492    // 4) abUser bleibt null – Variante wird erst unten zufällig gewählt,
493    //    aber erst bei Klick registriert
494
495    // Wurde der Nutzer bereits registriert (aus Session/DataLayer/SessionStorage)?
496    const alreadyRegistered = !!abUser;
497
498    // Falls noch nicht registriert: zufällige Variante nur für die Anzeige wählen
499    if (!abUser) {
500        abUser = pickRandomVariant();
501    }
502
503    // Buttons je nach Variante ein-/ausblenden
504    document.querySelectorAll(selector).forEach((el) => {
505        const variant = el.getAttribute('data-ab-variant');
506        if (variant === abUser) {
507            el.removeAttribute('hidden');
508
509            // Erst bei Klick registrieren, wenn noch nicht registriert
510            if (!alreadyRegistered) {
511                el.addEventListener('click', () => {
512                    if (useSessionStorage) {
513                        try {
514                            window.sessionStorage.setItem(sessionStorageKey, abUser);
515                        } catch (e) {}
516                    }
517                    if (!hasAbUserInDataLayer()) {
518                        window.dataLayer = window.dataLayer || [];
519                        window.dataLayer.push({
520                            abUser: abUser
521                        });
522                    }
523                }, { once: true });
524            }
525        } else {
526            el.setAttribute('hidden', 'hidden');
527        }
528    });
529
530    // dataLayer nur setzen, wenn Nutzer bereits vorher registriert war
531    if (alreadyRegistered && !hasAbUserInDataLayer()) {
532        window.dataLayer = window.dataLayer || [];
533        window.dataLayer.push({
534            abUser: abUser
535        });
536    }
537
538    window.__abUserVariant = abUser;
539    return abUser;
540}
541
542
543
544// ----- INIT -------------------------------------------------------//
545const initMain = () => {
546    initMenu();
547    initScrolledListener();
548    initAccordions();
549    initCountdowns();
550    initTestimonials();
551    initImageSlider();
552    initTeaserSlider();
553    initFormInputs();
554    initFormSelects();
555    initFormUploads();
556    initButtonChat();
557    initHeaderButton();
558    initCopyToClipboard();
559    initBrowserBackFix();
560    initLottie();
561    initFocus();
562    initGlobalLottie();
563    initABTesting();
564    initAbKeyDataLayer();
565};
566
567
568initMain();

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.