PageSourceSearch

https://traiteurwille.de/wp-content/themes/traiteurwille/includes/scripts/generic.js?ver=1791598714

js traiteurwille.de collected 2026-10-10 02:18:44 UTC 100,731 bytes, 4,009 lines download raw bytes

1jQuery(function ($) {
2    // ===== CACHED HANDLES =====
3    var $win = $(window);
4    var $doc = $(document);
5    var $body = $('body');
6
7    // ===== SCROLL / HEADER =====
8    var oldScrollTop = $win.scrollTop();
9
10    function fixHeader() {
11        $body.toggleClass('--header-fixed', $win.scrollTop() >= $win.height());
12    }
13
14    $doc.ready(fixHeader);
15
16    $win.on('scroll', function () {
17        var scrollTop = $win.scrollTop();
18
19        if (scrollTop > oldScrollTop) {
20            // forward
21        } else {
22            // backward
23        }
24
25        fixHeader();
26        oldScrollTop = scrollTop;
27    });
28
29    // ===== BACK TO TOP =====
30    $doc.on('click', '#backtotop', function (e) {
31        e.preventDefault();
32        $('html, body').stop().animate({ scrollTop: 0 }, 500, 'swing');
33    });
34
35    // ===== DYNAMIC SUBMENU =====
36    function dynMenu() {
37        var $parent = $('.dynamic-submenu');
38
39        if (!$parent.length) {
40            return;
41        }
42
43        $.post(aup_params.ajaxurl, {
44            action: 'getdynamicsubmenu'
45        }).done(function (resp) {
46            $parent.append(resp);
47        });
48    }
49
50    dynMenu();
51
52    // ===== MOBILE MENU =====
53    function mobileMenu() {
54        if ($win.width() < 768) {
55            var $hasSubMenu = $('.menu-item-has-children');
56
57            if ($hasSubMenu.length) {
58                $hasSubMenu
59                    .not('._triggered')
60                    .addClass('_triggered')
61                    .prepend(
62                        '<span class="sub-menu-trigger" aria-hidden="true"></span>'
63                    );
64            }
65        }
66
67        $doc.on('click', '.sub-menu-trigger', function () {
68            $(this).parent().toggleClass('--open');
69        });
70
71        $doc.on('click', '.menu-toggle, .overflow', function () {
72            $body.toggleClass('--menu-open');
73        });
74    }
75
76    mobileMenu();
77
78    // ===== FAQ ACCORDION =====
79    function faqs() {
80        $doc.on('click', '.faqs-item__question', function () {
81            $(this).parent().toggleClass('--active');
82        });
83    }
84
85    faqs();
86
87    // ===== SWIPER HELPERS =====
88    function createSwiper(selector, settings) {
89        if (!$(selector).length || typeof Swiper === 'undefined') {
90            return null;
91        }
92
93        return new Swiper(selector, settings);
94    }
95
96    var commonSwiper = {
97        mousewheel: {
98            forceToAxis: true
99        },
100        pagination: {
101            el: '.swiper-pagination'
102        },
103        navigation: {
104            nextEl: '.swiper-button-next',
105            prevEl: '.swiper-button-prev'
106        }
107    };
108
109    function responsiveSwiper(selector, settings, breakpoint) {
110        breakpoint = typeof breakpoint === 'number' ? breakpoint : 768;
111
112        var instance = null;
113        var resizeFrame;
114
115        function toggle() {
116            if (!$(selector).length) {
117                return;
118            }
119
120            var windowWidth = $win.width();
121
122            if (windowWidth < breakpoint) {
123                if (instance) {
124                    if (typeof instance.enable === 'function') {
125                        instance.enable();
126                    }
127                } else {
128                    instance = createSwiper(selector, settings);
129                }
130            } else if (instance) {
131                instance.destroy(true, true);
132                instance = null;
133            }
134        }
135
136        function onResize() {
137            if (typeof cancelAnimationFrame === 'function') {
138                cancelAnimationFrame(resizeFrame);
139            }
140
141            if (typeof requestAnimationFrame === 'function') {
142                resizeFrame = requestAnimationFrame(toggle);
143            } else {
144                window.setTimeout(toggle, 100);
145            }
146        }
147
148        $doc.ready(toggle);
149        $win.on('resize', onResize);
150    }
151
152    // ===== LOCATIONS PREVIEW =====
153    responsiveSwiper(
154        '.js-swiper--locations-preview',
155        $.extend({}, commonSwiper, {
156            effect: 'slide',
157            slidesPerView: 'auto',
158            loop: false,
159            spaceBetween: 20
160        }),
161        768
162    );
163
164    // ===== GALLERY =====
165    createSwiper(
166        '.js-swiper--gallery',
167        $.extend({}, commonSwiper, {
168            effect: 'fade',
169            slidesPerView: 'auto',
170            loop: true,
171            autoHeight: true,
172            centeredSlides: true,
173            spaceBetween: 20,
174            autoplay: {
175                delay: 2000
176            }
177        })
178    );
179
180    // ===== GALLERY MODULE =====
181    createSwiper(
182        '.js-swiper--gallery-module',
183        $.extend({}, commonSwiper, {
184            effect: 'slide',
185            slidesPerView: 'auto',
186            loop: true,
187            autoHeight: false,
188            centeredSlides: true,
189            spaceBetween: 20,
190            autoplay: {
191                delay: 3000
192            }
193        })
194    );
195
196    // ===== TESTIMONIES =====
197    createSwiper(
198        '.js-swiper--testimonies',
199        $.extend({}, commonSwiper, {
200            effect: 'slide',
201            slidesPerView: 'auto',
202            loop: true,
203            autoHeight: true,
204            centeredSlides: true,
205            spaceBetween: 20
206        })
207    );
208
209    // ===== CATEGORIES PREVIEW =====
210    responsiveSwiper(
211        '.js-swiper--categories-preview',
212        $.extend({}, commonSwiper, {
213            effect: 'slide',
214            slidesPerView: 'auto',
215            loop: false,
216            spaceBetween: 20
217        }),
218        768
219    );
220
221    // ===== COUNTER PLUGIN =====
222    $.fn.countUpNumbers = function (options) {
223        var settings = $.extend(
224            {
225                duration: 2000,
226                intervalTime: 30
227            },
228            options
229        );
230
231        var steps = Math.ceil(
232            settings.duration / settings.intervalTime
233        );
234
235        function formatNumber(number) {
236            return number
237                .toString()
238                .replace(/\B(?=(\d{3})+(?!\d))/g, '.');
239        }
240
241        var items = this.map(function () {
242            var $element = $(this);
243            var finalValue = parseInt(
244                $element.text().replace(/\./g, ''),
245                10
246            );
247
248            if (isNaN(finalValue) || finalValue < 1) {
249                return null;
250            }
251
252            $element.text('0');
253
254            return {
255                el: $element,
256                finalVal: finalValue
257            };
258        }).get();
259
260        if (!items.length) {
261            return this;
262        }
263
264        var iteration = 0;
265
266        var timer = window.setInterval(function () {
267            iteration++;
268
269            var progress = iteration / steps;
270
271            if (progress > 1) {
272                progress = 1;
273            }
274
275            var easedProgress =
276                1 - Math.pow(1 - progress, 3);
277
278            items.forEach(function (item) {
279                item.el.text(
280                    formatNumber(
281                        Math.floor(
282                            item.finalVal * easedProgress
283                        )
284                    )
285                );
286            });
287
288            if (iteration >= steps) {
289                items.forEach(function (item) {
290                    item.el.text(
291                        formatNumber(item.finalVal)
292                    );
293                });
294
295                window.clearInterval(timer);
296            }
297        }, settings.intervalTime);
298
299        return this;
300    };
301
302    $('.counter-item__number').countUpNumbers({
303        duration: 1000,
304        intervalTime: 20
305    });
306
307    // ===== MAP =====
308    function initMap() {
309        var $map = $('#map');
310
311        if (!$map.length) {
312            return;
313        }
314
315        if (typeof maplibregl === 'undefined') {
316            if (window.console) {
317                console.error('MapLibre wurde nicht geladen.');
318            }
319
320            return;
321        }
322
323        $.post(aup_params.ajaxurl, {
324            action: 'getlocations'
325        }).done(function (resp) {
326            var locations;
327
328            try {
329                locations =
330                    typeof resp === 'string'
331                        ? JSON.parse(resp)
332                        : resp;
333            } catch (error) {
334                if (window.console) {
335                    console.error(
336                        'Ungültige Standortdaten:',
337                        error
338                    );
339                }
340
341                return;
342            }
343
344            if (
345                !locations ||
346                !locations.exclusive ||
347                !locations.partner
348            ) {
349                if (window.console) {
350                    console.error(
351                        'Standortdaten sind unvollständig.'
352                    );
353                }
354
355                return;
356            }
357
358            var map = new maplibregl.Map({
359                container: 'map',
360                style:
361                    'https://api.maptiler.com/maps/streets-v2-light/style.json?key=us3VTkjk4u0EnM3lTshr',
362                center: [10.0572475, 53.6028926],
363                zoom: 9
364            });
365
366            map.addControl(
367                new maplibregl.NavigationControl(),
368                'top-right'
369            );
370
371            map.on('load', function () {
372                Promise.all([
373                    map.loadImage(
374                        '/wp-content/themes/traiteurwille/assets/media/marker_exclusive.png'
375                    ),
376                    map.loadImage(
377                        '/wp-content/themes/traiteurwille/assets/media/marker.png'
378                    )
379                ])
380                    .then(function (images) {
381                        var imageExclusive = images[0];
382                        var imagePartner = images[1];
383
384                        map.addImage(
385                            'loc_exclusive',
386                            imageExclusive.data
387                        );
388
389                        map.addImage(
390                            'loc_partner',
391                            imagePartner.data
392                        );
393
394                        map.addSource(
395                            'locations_exclusive',
396                            {
397                                type: 'geojson',
398                                data: {
399                                    type:
400                                        'FeatureCollection',
401                                    features:
402                                    locations.exclusive
403                                }
404                            }
405                        );
406
407                        map.addSource(
408                            'locations_partner',
409                            {
410                                type: 'geojson',
411                                data: {
412                                    type:
413                                        'FeatureCollection',
414                                    features:
415                                    locations.partner
416                                }
417                            }
418                        );
419
420                        map.addLayer({
421                            id: 'locations_partner',
422                            type: 'symbol',
423                            source:
424                                'locations_partner',
425                            layout: {
426                                'icon-image':
427                                    'loc_partner',
428                                'icon-overlap':
429                                    'always',
430                                'icon-size': 0.8
431                            }
432                        });
433
434                        map.addLayer({
435                            id: 'locations_exclusive',
436                            type: 'symbol',
437                            source:
438                                'locations_exclusive',
439                            layout: {
440                                'icon-image':
441                                    'loc_exclusive',
442                                'icon-overlap':
443                                    'always',
444                                'icon-size': 0.8
445                            }
446                        });
447                    })
448                    .catch(function (error) {
449                        if (window.console) {
450                            console.error(
451                                'Kartenmarker konnten nicht geladen werden:',
452                                error
453                            );
454                        }
455                    });
456            });
457        }).fail(function (xhr, status, error) {
458            if (window.console) {
459                console.error(
460                    'Standorte konnten nicht geladen werden:',
461                    status,
462                    error
463                );
464            }
465        });
466    }
467
468    initMap();
469    // ===== NEWSLETTER FORM =====
470    function mailchimpForm() {
471        var $form = $('.newsletter-signup form');
472
473        if (!$form.length) {
474            return;
475        }
476
477        var $button = $form.find('button');
478        var $email = $form.find('#email');
479        var $dataPrivacy = $form.find(
480            '#newsletter-signup__dataprivacy'
481        );
482
483        function updateButton() {
484            var emailElement = $email.get(0);
485
486            var emailValid =
487                emailElement &&
488                typeof emailElement.checkValidity ===
489                'function'
490                    ? emailElement.checkValidity()
491                    : Boolean($email.val());
492
493            $button.prop(
494                'disabled',
495                !(
496                    $dataPrivacy.is(':checked') &&
497                    emailValid
498                )
499            );
500        }
501
502        $dataPrivacy.on('change', updateButton);
503        $email.on('input change', updateButton);
504
505        updateButton();
506
507        $form.on('submit', function (e) {
508            e.preventDefault();
509
510            $.post(
511                aup_params.ajaxurl,
512                $form.serialize()
513            )
514                .done(function (resp) {
515                    if (
516                        resp &&
517                        resp.success &&
518                        resp.data &&
519                        resp.data.redirect
520                    ) {
521                        window.location.href =
522                            '/' + resp.data.redirect;
523                    }
524                })
525                .fail(function (
526                    xhr,
527                    status,
528                    error
529                ) {
530                    if (window.console) {
531                        console.error(
532                            'Newsletter-Anmeldung fehlgeschlagen:',
533                            status,
534                            error
535                        );
536                    }
537                });
538        });
539    }
540
541    mailchimpForm();
542
543    // ===== FORM PARAMETER =====
544    function formularParameter() {
545        var $forms = $('form');
546
547        if (!$forms.length) {
548            return;
549        }
550
551        if (
552            typeof URLSearchParams === 'undefined'
553        ) {
554            return;
555        }
556
557        var searchParams =
558            new URLSearchParams(
559                window.location.search
560            );
561
562        var locationValue =
563            searchParams.get('l');
564
565        if (!locationValue) {
566            return;
567        }
568
569        $forms
570            .find('option')
571            .filter(function () {
572                return (
573                    String($(this).val()) ===
574                    String(locationValue)
575                );
576            })
577            .prop('selected', true);
578
579        $forms
580            .find(
581                'select[name="booking-location"]'
582            )
583            .prop('disabled', true)
584            .trigger('change');
585    }
586
587    formularParameter();
588
589    // ===== MULTISTEP FORM NAVIGATION =====
590    function formularNavigation() {
591        var $formular = $('.formular');
592
593        if (!$formular.length) {
594            return;
595        }
596
597        var $steps = $formular.find('.step');
598        var $total = $formular.find(
599            '.formular-form-pagination__total'
600        );
601        var $current = $formular.find(
602            '.formular-form-pagination__curr'
603        );
604
605        function setCounter(currentIndex) {
606            $total.text($steps.length);
607            $current.text(currentIndex + 1);
608        }
609
610        function getActiveStep() {
611            return $formular.find(
612                '.step--active'
613            );
614        }
615
616        function getActiveIndex() {
617            return $steps.index(
618                getActiveStep()
619            );
620        }
621
622        function showStep(index) {
623            if (
624                index < 0 ||
625                index >= $steps.length
626            ) {
627                return;
628            }
629
630            $steps.removeClass(
631                'step--active'
632            );
633
634            $steps
635                .eq(index)
636                .addClass('step--active');
637
638            setCounter(index);
639        }
640
641        setCounter(
642            Math.max(getActiveIndex(), 0)
643        );
644
645        $doc.on(
646            'click',
647            '.formular .btn--prev',
648            function (e) {
649
650                /*
651                 * #catering-request-form hat eine eigene Navigation.
652                 */
653                if (
654                    $(this).closest(
655                        '#catering-request-form'
656                    ).length
657                ) {
658                    return;
659                }
660
661                e.preventDefault();
662
663                var currentIndex =
664                    getActiveIndex();
665
666                if (currentIndex > 0) {
667                    showStep(
668                        currentIndex - 1
669                    );
670                }
671            }
672        );
673
674        function addFieldError($field) {
675            $field.addClass('--error');
676
677            var fieldName =
678                $field.attr('name');
679
680            if (fieldName) {
681                $formular
682                    .find(
683                        '[name="' +
684                        fieldName +
685                        '"]'
686                    )
687                    .addClass('--error');
688            }
689        }
690
691        function validateForm(
692            $button,
693            advanceStep
694        ) {
695            advanceStep =
696                advanceStep !== false;
697
698            var $step =
699                $button.closest('.step');
700
701            var $requiredFields =
702                $step.find(
703                    ':input[required]'
704                );
705
706            var isValid = true;
707            var checkedRadioGroups = {};
708
709            $formular
710                .find('.--error')
711                .removeClass('--error');
712
713            $requiredFields.each(
714                function () {
715                    var $field = $(this);
716                    var element = this;
717                    var type = (
718                        $field.attr('type') ||
719                        ''
720                    ).toLowerCase();
721
722                    var tagName =
723                        element.tagName
724                            .toLowerCase();
725
726                    if (type === 'radio') {
727                        var radioName =
728                            $field.attr(
729                                'name'
730                            );
731
732                        if (
733                            radioName &&
734                            !checkedRadioGroups[
735                                radioName
736                                ]
737                        ) {
738                            checkedRadioGroups[
739                                radioName
740                                ] = true;
741
742                            var $radioGroup =
743                                $step.find(
744                                    'input[type="radio"][name="' +
745                                    radioName +
746                                    '"]'
747                                );
748
749                            if (
750                                !$radioGroup.filter(
751                                    ':checked'
752                                ).length
753                            ) {
754                                isValid = false;
755                                addFieldError(
756                                    $radioGroup
757                                );
758                            }
759                        }
760
761                        return;
762                    }
763
764                    if (
765                        type === 'checkbox'
766                    ) {
767                        if (
768                            !$field.is(
769                                ':checked'
770                            )
771                        ) {
772                            isValid = false;
773                            addFieldError(
774                                $field
775                            );
776                        }
777
778                        return;
779                    }
780
781                    if (
782                        tagName === 'select'
783                    ) {
784                        if (!$field.val()) {
785                            isValid = false;
786                            addFieldError(
787                                $field
788                            );
789                        }
790
791                        return;
792                    }
793
794                    var rawValue =
795                        $field.val();
796
797                    var value =
798                        rawValue === null ||
799                        typeof rawValue ===
800                        'undefined'
801                            ? ''
802                            : String(
803                                rawValue
804                            ).trim();
805
806                    if (!value) {
807                        isValid = false;
808                        addFieldError(
809                            $field
810                        );
811
812                        return;
813                    }
814
815                    if (type === 'email') {
816                        var emailPattern =
817                            /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
818
819                        if (
820                            !emailPattern.test(
821                                value
822                            )
823                        ) {
824                            isValid =
825                                false;
826
827                            addFieldError(
828                                $field
829                            );
830                        }
831                    } else if (
832                        typeof element.checkValidity ===
833                        'function' &&
834                        !element.checkValidity()
835                    ) {
836                        isValid = false;
837                        addFieldError(
838                            $field
839                        );
840                    }
841                }
842            );
843
844            if (!isValid) {
845                setCounter(
846                    Math.max(
847                        getActiveIndex(),
848                        0
849                    )
850                );
851
852                $step
853                    .find('.formular-form-error')
854                    .addClass('--visible');
855
856                var $firstError =
857                    $step.find(
858                        '.--error'
859                    ).first();
860
861                if (
862                    $firstError.length &&
863                    typeof $firstError.get(0)
864                        .focus ===
865                    'function'
866                ) {
867                    $firstError
868                        .get(0)
869                        .focus();
870                }
871
872                return false;
873            }
874            $step
875                .find('.formular-form-error')
876                .removeClass('--visible');
877
878            if (advanceStep) {
879                var nextIndex =
880                    getActiveIndex() + 1;
881
882                if (
883                    nextIndex <
884                    $steps.length
885                ) {
886                    showStep(nextIndex);
887                }
888            }
889
890            return true;
891        }
892
893        $doc.on(
894            'click',
895            '.formular .btn--next',
896            function (e) {
897
898                /*
899                 * #catering-request-form hat eine eigene Prüfung.
900                 * Dieser Handler würde --error sofort wieder entfernen
901                 * und Choice-Groups ignorieren.
902                 */
903                if (
904                    $(this).closest(
905                        '#catering-request-form'
906                    ).length
907                ) {
908                    return;
909                }
910
911                e.preventDefault();
912
913                validateForm(
914                    $(this),
915                    true
916                );
917            }
918        );
919
920        $doc.on(
921            'click',
922            '.formular .btn--submit',
923            function (e) {
924                e.preventDefault();
925
926                var $button = $(this);
927
928                if (
929                    !validateForm(
930                        $button,
931                        false
932                    )
933                ) {
934                    return;
935                }
936
937                var $form =
938                    $button.closest('form');
939
940                if (!$form.length) {
941                    $form =
942                        $('.formular-form')
943                            .first();
944                }
945
946                var data =
947                    $form.serialize();
948
949                data +=
950                    '&action=sendformdata';
951
952                $button.prop(
953                    'disabled',
954                    true
955                );
956
957                $.post(
958                    aup_params.ajaxurl,
959                    data
960                )
961                    .done(function (resp) {
962                        var emailValue =
963                            $form
964                                .find(
965                                    '[name="email"]'
966                                )
967                                .val();
968
969                        var phoneValue =
970                            $form
971                                .find(
972                                    '[name="phone"]'
973                                )
974                                .val();
975
976                        var email =
977                            emailValue
978                                ? String(
979                                    emailValue
980                                )
981                                    .trim()
982                                    .toLowerCase()
983                                : '';
984
985                        var phone =
986                            phoneValue
987                                ? String(
988                                    phoneValue
989                                ).replace(
990                                    /[^\d+]/g,
991                                    ''
992                                )
993                                : '';
994
995                        var emailHashPromise =
996                            email
997                                ? sha256(email)
998                                : Promise.resolve(
999                                    ''
1000                                );
1001
1002                        var phoneHashPromise =
1003                            phone
1004                                ? sha256(phone)
1005                                : Promise.resolve(
1006                                    ''
1007                                );
1008
1009                        Promise.all([
1010                            emailHashPromise,
1011                            phoneHashPromise
1012                        ])
1013                            .then(
1014                                function (
1015                                    hashes
1016                                ) {
1017                                    var hashedEmail =
1018                                        hashes[0];
1019
1020                                    var hashedPhone =
1021                                        hashes[1];
1022
1023                                    var redirectUrl =
1024                                        '';
1025
1026                                    var responseType =
1027                                        resp &&
1028                                        resp.type
1029                                            ? resp.type
1030                                            : '';
1031
1032                                    var language =
1033                                        $(
1034                                            'html'
1035                                        ).attr(
1036                                            'lang'
1037                                        ) || '';
1038
1039                                    if (
1040                                        responseType ===
1041                                        'feedback'
1042                                    ) {
1043                                        redirectUrl =
1044                                            language.indexOf(
1045                                                'en'
1046                                            ) === 0
1047                                                ? '/en/feedback-thank-you'
1048                                                : '/feedback-danke';
1049                                    } else if (
1050                                        responseType ===
1051                                        'request'
1052                                    ) {
1053                                        redirectUrl =
1054                                            language.indexOf(
1055                                                'en'
1056                                            ) === 0
1057                                                ? '/en/contact-thank-you'
1058                                                : '/kontakt-danke';
1059                                    }
1060
1061                                    if (
1062                                        !redirectUrl
1063                                    ) {
1064                                        if (
1065                                            window.console
1066                                        ) {
1067                                            console.error(
1068                                                'Unbekannter Antworttyp:',
1069                                                resp
1070                                            );
1071                                        }
1072
1073                                        $button.prop(
1074                                            'disabled',
1075                                            false
1076                                        );
1077
1078                                        return;
1079                                    }
1080
1081                                    var url =
1082                                        new URL(
1083                                            redirectUrl,
1084                                            window
1085                                                .location
1086                                                .origin
1087                                        );
1088
1089                                    if (
1090                                        hashedEmail
1091                                    ) {
1092                                        url.searchParams.set(
1093                                            'ec_email',
1094                                            hashedEmail
1095                                        );
1096                                    }
1097
1098                                    if (
1099                                        hashedPhone
1100                                    ) {
1101                                        url.searchParams.set(
1102                                            'ec_phone',
1103                                            hashedPhone
1104                                        );
1105                                    }
1106
1107                                    window.location.href =
1108                                        url.toString();
1109                                }
1110                            )
1111                            .catch(
1112                                function (
1113                                    error
1114                                ) {
1115                                    $button.prop(
1116                                        'disabled',
1117                                        false
1118                                    );
1119
1120                                    if (
1121                                        window.console
1122                                    ) {
1123                                        console.error(
1124                                            'Hashing fehlgeschlagen:',
1125                                            error
1126                                        );
1127                                    }
1128                                }
1129                            );
1130                    })
1131                    .fail(function (
1132                        xhr,
1133                        status,
1134                        error
1135                    ) {
1136                        $button.prop(
1137                            'disabled',
1138                            false
1139                        );
1140
1141                        if (
1142                            window.console
1143                        ) {
1144                            console.error(
1145                                'Formularversand fehlgeschlagen:',
1146                                status,
1147                                error
1148                            );
1149                        }
1150                    });
1151            }
1152        );
1153
1154        $('#booking-location').on(
1155            'change',
1156            function () {
1157                var value =
1158                    $(this).val();
1159
1160                var $dependentFields =
1161                    $('.dependant-booking-location');
1162
1163                $dependentFields.hide();
1164                $dependentFields.prop(
1165                    'required',
1166                    false
1167                );
1168
1169                if (
1170                    value ===
1171                    'Eigene Location'
1172                ) {
1173                    $dependentFields.show();
1174                    $dependentFields.prop(
1175                        'required',
1176                        true
1177                    );
1178                }
1179            }
1180        );
1181
1182        $('#booking-location').trigger(
1183            'change'
1184        );
1185    }
1186
1187    formularNavigation();
1188    // ===== OBFUSCATE EMAILS =====
1189    $('.obfuscate').each(function () {
1190        function replaceObfuscatedEmail(
1191            value
1192        ) {
1193            return String(value || '')
1194                .replace('…', '@')
1195                .replace(/\.\.\./g, '@')
1196                .replace(
1197                    /\[(dot|punkt)\]/gi,
1198                    '.'
1199                )
1200                .replace(/\s+/g, '');
1201        }
1202
1203        var $element = $(this);
1204
1205        $element.attr(
1206            'href',
1207            replaceObfuscatedEmail(
1208                $element.attr('href')
1209            )
1210        );
1211
1212        $element.text(
1213            replaceObfuscatedEmail(
1214                $element.text()
1215            )
1216        );
1217    });
1218
1219    // ===== STARS RANKING =====
1220    $('input.stars').on(
1221        'change',
1222        function () {
1223            var rating = parseInt(
1224                $(this).val(),
1225                10
1226            );
1227
1228            rateStars(rating);
1229        }
1230    );
1231
1232    function rateStars(number) {
1233        var $stars =
1234            $('input.stars');
1235
1236        $stars.removeClass('active');
1237
1238        $stars.each(
1239            function (index) {
1240                if (index < number) {
1241                    $(this).addClass(
1242                        'active'
1243                    );
1244                }
1245            }
1246        );
1247    }
1248
1249    // ===== COMPLIANZ FIXES =====
1250    $('.cmplz-blocked-content-container')
1251        .each(function () {
1252            if (this.style) {
1253                this.style.setProperty(
1254                    'background-image',
1255                    'none',
1256                    'important'
1257                );
1258            }
1259        });
1260
1261    $('[role="menuitem"]').removeAttr(
1262        'role'
1263    );
1264
1265    $('.cmplz-link').attr(
1266        'aria-label',
1267        'Complianz Link'
1268    );
1269
1270    // ===== DEBUG =====
1271    var clickCount = 0;
1272    var lastClickTime = 0;
1273    var clickThreshold = 1000;
1274    var requiredClicks = 5;
1275
1276    $('.dbg').on('click', function () {
1277        var now = Date.now();
1278
1279        if (
1280            now - lastClickTime >
1281            clickThreshold
1282        ) {
1283            clickCount = 0;
1284        }
1285
1286        clickCount++;
1287        lastClickTime = now;
1288
1289        if (
1290            clickCount >= requiredClicks
1291        ) {
1292            clickCount = 0;
1293            dbgTriggered();
1294        }
1295    });
1296
1297    function dbgTriggered() {
1298        $('#salutation').val('Herr');
1299
1300        $('#fname').val(
1301            'Maximilian'
1302        );
1303
1304        $('#lname').val(
1305            'Mustermann'
1306        );
1307
1308        $('#email').val(
1309            '[email protected]'
1310        );
1311
1312        $('#phone').val(
1313            '+49 178 123 4567'
1314        );
1315
1316        $('#booking-type').val(
1317            'Privat'
1318        );
1319
1320        $('#booking-budget').val(
1321            'ab 2.000 EUR (Mindestumsatz)'
1322        );
1323
1324        $('#booking-location')
1325            .val('Eigene Location')
1326            .trigger('change');
1327
1328        $(
1329            '#booking-location-description'
1330        ).val(
1331            'Musterstraße 101, 12345 Musterhausen'
1332        );
1333
1334        $('#date').val(
1335            '2026-12-01'
1336        );
1337
1338        $('#booking-subject').val(
1339            'Familienfeier'
1340        );
1341
1342        $('#time-start').val(
1343            '09:00'
1344        );
1345
1346        $('#time-end').val(
1347            '23:55'
1348        );
1349
1350        $('#guests').val('100');
1351
1352        $('#guests-children').val(
1353            '10'
1354        );
1355
1356        $('#service-menu').prop(
1357            'checked',
1358            true
1359        );
1360
1361        $('#service-fingerfood').prop(
1362            'checked',
1363            true
1364        );
1365
1366        $('#service-personal').prop(
1367            'checked',
1368            true
1369        );
1370
1371        $('#service-fotograf').prop(
1372            'checked',
1373            true
1374        );
1375
1376        $('#service-questions').val(
1377            'Bitte berücksichtigen Sie folgendes'
1378        );
1379
1380        $('#connection').val(
1381            'Google'
1382        );
1383
1384        $('#data-privacy').prop(
1385            'checked',
1386            true
1387        );
1388
1389        $('#newsletter').prop(
1390            'checked',
1391            true
1392        );
1393    }
1394
1395    // ===== POPUP CLOSE =====
1396    $doc.on(
1397        'click',
1398        '.popup-close',
1399        function () {
1400            $(this)
1401                .closest(
1402                    '.popup, .popup-wrapper, .popup-container'
1403                )
1404                .addClass('--hidden');
1405        }
1406    );
1407
1408    // ===== SHA-256 =====
1409    function sha256(value) {
1410        if (
1411            typeof TextEncoder ===
1412            'undefined' ||
1413            !window.crypto ||
1414            !window.crypto.subtle
1415        ) {
1416            return Promise.reject(
1417                new Error(
1418                    'Web Crypto API ist nicht verfügbar.'
1419                )
1420            );
1421        }
1422
1423        var buffer =
1424            new TextEncoder().encode(
1425                value
1426            );
1427
1428        return window.crypto.subtle
1429            .digest('SHA-256', buffer)
1430            .then(
1431                function (hashBuffer) {
1432                    var hashArray =
1433                        Array.prototype.slice.call(
1434                            new Uint8Array(
1435                                hashBuffer
1436                            )
1437                        );
1438
1439                    return hashArray
1440                        .map(
1441                            function (
1442                                byte
1443                            ) {
1444                                return (
1445                                    '00' +
1446                                    byte.toString(
1447                                        16
1448                                    )
1449                                ).slice(-2);
1450                            }
1451                        )
1452                        .join('');
1453                }
1454            );
1455    }
1456
1457    var servicesSwiper = createSwiper(
1458        '.js-swiper--services-module',
1459        $.extend({}, commonSwiper, {
1460            effect: 'fade',
1461            slidesPerView: 1,
1462            loop: false,
1463            allowTouchMove: false,
1464            speed: 500
1465        })
1466    );
1467
1468    $('.services-overview-list-item__title, .services-overview-list-item__toggle').on('click', function () {
1469
1470        var $item = $(this).closest('.services-overview-list-item');
1471        var $module = $item.closest('.services-overview');
1472        var $items = $module.find('.services-overview-list-item');
1473
1474        // Aktives Item bleibt immer geöffnet
1475        if ($item.hasClass('--active')) {
1476            return;
1477        }
1478
1479        var index = $items.index($item);
1480
1481        // Bisher aktives Item schließen
1482        $items
1483            .filter('.--active')
1484            .removeClass('--active')
1485            .find('.services-overview-list-item__wrap')
1486            .stop(true, true)
1487            .slideUp(300);
1488
1489        // Neues Item öffnen
1490        $item
1491            .addClass('--active')
1492            .find('.services-overview-list-item__wrap')
1493            .stop(true, true)
1494            .slideDown(300);
1495
1496        // Passendes Bild anzeigen
1497        if (servicesSwiper) {
1498            servicesSwiper.slideTo(index, 500);
1499        }
1500    });
1501
1502    $('.announcement').each(function () {
1503
1504        var $announcement = $(this);
1505        var $items = $announcement.find('ul > li');
1506
1507        if (!$items.length) {
1508            return;
1509        }
1510
1511        if ($items.length === 1) {
1512            $items.eq(0).addClass('--active');
1513            return;
1514        }
1515
1516        var currentIndex = 0;
1517
1518        var timer = null;
1519        var startedAt = 0;
1520        var remainingTime = 0;
1521
1522        var transitionDuration = 500;
1523
1524        // Etwas ruhiger für Website-/Marketingtexte
1525        var wordsPerMinute = 180;
1526
1527        var minimumReadingTime = 3000;
1528        var maximumReadingTime = 10000;
1529
1530
1531        function getReadingTime($item) {
1532
1533            var text = $.trim($item.text());
1534
1535            if (!text.length) {
1536                return minimumReadingTime;
1537            }
1538
1539            var words = text
1540                .split(/\s+/)
1541                .filter(function (word) {
1542                    return word.length > 0;
1543                })
1544                .length;
1545
1546            var readingTime =
1547                words * (60000 / wordsPerMinute);
1548
1549            readingTime = Math.max(
1550                readingTime,
1551                minimumReadingTime
1552            );
1553
1554            readingTime = Math.min(
1555                readingTime,
1556                maximumReadingTime
1557            );
1558
1559            return Math.round(readingTime);
1560        }
1561
1562
1563        function scheduleNext(duration) {
1564
1565            clearTimeout(timer);
1566
1567            remainingTime = duration;
1568            startedAt = Date.now();
1569
1570            timer = window.setTimeout(function () {
1571                showNext();
1572            }, duration);
1573        }
1574
1575
1576        function showItem(index) {
1577
1578            var $current = $items.eq(currentIndex);
1579            var $next = $items.eq(index);
1580
1581            $current
1582                .removeClass('--active')
1583                .addClass('--leave');
1584
1585            $next
1586                .removeClass('--leave')
1587                .addClass('--active');
1588
1589            window.setTimeout(function () {
1590                $current.removeClass('--leave');
1591            }, transitionDuration);
1592
1593            currentIndex = index;
1594
1595            scheduleNext(
1596                getReadingTime($next)
1597            );
1598        }
1599
1600
1601        function showNext() {
1602
1603            var nextIndex = currentIndex + 1;
1604
1605            if (nextIndex >= $items.length) {
1606                nextIndex = 0;
1607            }
1608
1609            showItem(nextIndex);
1610        }
1611
1612
1613        function pause() {
1614
1615            if (!timer) {
1616                return;
1617            }
1618
1619            clearTimeout(timer);
1620
1621            timer = null;
1622
1623            var elapsed =
1624                Date.now() - startedAt;
1625
1626            remainingTime = Math.max(
1627                0,
1628                remainingTime - elapsed
1629            );
1630        }
1631
1632
1633        function resume() {
1634
1635            if (timer) {
1636                return;
1637            }
1638
1639            if (remainingTime <= 0) {
1640                showNext();
1641                return;
1642            }
1643
1644            scheduleNext(remainingTime);
1645        }
1646
1647
1648        // Initialzustand
1649        $items
1650            .removeClass('--active --leave')
1651            .eq(0)
1652            .addClass('--active');
1653
1654        scheduleNext(
1655            getReadingTime($items.eq(0))
1656        );
1657
1658
1659        // Pause bei Hover
1660        $announcement.on('mouseenter', pause);
1661        $announcement.on('mouseleave', resume);
1662
1663    });
1664
1665    function initCateringRequestForm(formSelector) {
1666
1667        var $form = jQuery(formSelector);
1668
1669        if (!$form.length) {
1670            return;
1671        }
1672
1673        /*
1674         * Verhindert doppelte Initialisierung.
1675         */
1676        if ($form.data('catering-form-initialized')) {
1677            return;
1678        }
1679
1680        $form.data('catering-form-initialized', true);
1681
1682
1683        var currentStep = 1;
1684        var totalSteps = $form.find('.step').length;
1685
1686
1687        /*
1688         * ============================================================
1689         * HELPERS
1690         * ============================================================
1691         */
1692
1693        function getRequestType() {
1694
1695            return $form
1696                .find('input[name="request_type"]:checked')
1697                .val() || '';
1698        }
1699
1700
1701        function includesCatering() {
1702
1703            var type = getRequestType();
1704
1705            return (
1706                type === 'catering' ||
1707                type === 'catering_location'
1708            );
1709        }
1710
1711
1712        function includesLocationSearch() {
1713
1714            var type = getRequestType();
1715
1716            return (
1717                type === 'location' ||
1718                type === 'catering_location'
1719            );
1720        }
1721
1722
1723        function setRequired(selector, required) {
1724
1725            $form
1726                .find(selector)
1727                .prop('required', required);
1728        }
1729
1730
1731        /*
1732         * Eingabefelder in ausgeblendeten Conditional-Bereichen
1733         * deaktivieren.
1734         *
1735         * Wichtig bei verschachtelten Conditional-Bereichen.
1736         */
1737        function syncConditionalDisabledState() {
1738
1739            $form
1740                .find('[data-condition] :input')
1741                .each(function () {
1742
1743                    var $input = jQuery(this);
1744
1745                    var isHidden =
1746                        $input.closest('[data-condition][hidden]').length > 0;
1747
1748                    $input.prop(
1749                        'disabled',
1750                        isHidden
1751                    );
1752
1753                });
1754        }
1755
1756
1757        function showConditional(selector) {
1758
1759            $form
1760                .find(selector)
1761                .prop('hidden', false);
1762
1763            syncConditionalDisabledState();
1764        }
1765
1766
1767        function hideConditional(selector) {
1768
1769            $form
1770                .find(selector)
1771                .prop('hidden', true);
1772
1773            syncConditionalDisabledState();
1774        }
1775
1776
1777        function clearStepErrors($step) {
1778
1779            $step
1780                .find('.choice-group')
1781                .removeClass('--error');
1782
1783            $step
1784                .find('.field')
1785                .removeClass('--error');
1786
1787            $step
1788                .find(':input')
1789                .removeClass('--error');
1790
1791            $step
1792                .find('.formular-form-error')
1793                .removeClass('--visible');
1794        }
1795
1796
1797        /*
1798         * ============================================================
1799         * STEP HANDLING
1800         * ============================================================
1801         */
1802
1803        function showStep(step, scrollToForm) {
1804
1805            if (
1806                step < 1 ||
1807                step > totalSteps
1808            ) {
1809                return;
1810            }
1811
1812
1813            currentStep = step;
1814
1815
1816            $form
1817                .find('.step')
1818                .removeClass('step--active');
1819
1820
1821            $form
1822                .find(
1823                    '.step[data-step="' + step + '"]'
1824                )
1825                .addClass('step--active');
1826
1827
1828            $form
1829                .find('.formular-form-pagination__curr')
1830                .text(step);
1831
1832
1833            $form
1834                .find('.formular-form-pagination__total')
1835                .text(totalSteps);
1836
1837
1838            $form
1839                .find('.formular-form-error')
1840                .removeClass('--visible');
1841
1842
1843            if (scrollToForm !== false) {
1844
1845                jQuery('html, body')
1846                    .stop(true)
1847                    .animate(
1848                        {
1849                            scrollTop: Math.max(
1850                                0,
1851                                $form.offset().top - 40
1852                            )
1853                        },
1854                        250
1855                    );
1856
1857            }
1858        }
1859
1860
1861        /*
1862         * ============================================================
1863         * VALIDATION
1864         * ============================================================
1865         *
1866         * Required Radio-Gruppen:
1867         *
1868         * <div
1869         *   class="field choice-group"
1870         *   data-required-choice="true"
1871         * >
1872         *
1873         * Die einzelnen Radios brauchen KEIN required.
1874         */
1875
1876        function validateStep(stepNumber) {
1877
1878            var $step = $form.find(
1879                '.step[data-step="' + stepNumber + '"]'
1880            );
1881
1882
1883            if (!$step.length) {
1884                return false;
1885            }
1886
1887
1888            var valid = true;
1889            var $firstInvalid = null;
1890
1891
1892            /*
1893             * ============================================================
1894             * RESET
1895             * ============================================================
1896             */
1897
1898            $step
1899                .find('.choice-group')
1900                .removeClass('--error')
1901                .attr('aria-invalid', 'false');
1902
1903
1904            $step
1905                .find('.field')
1906                .removeClass('--error');
1907
1908
1909            $step
1910                .find(':input')
1911                .removeClass('--error')
1912                .removeAttr('aria-invalid');
1913
1914
1915            $step
1916                .find('.formular-form-error')
1917                .removeClass('--visible');
1918
1919
1920            /*
1921             * ============================================================
1922             * REQUIRED CHOICE GROUPS
1923             * ============================================================
1924             */
1925
1926            $step
1927                .find(
1928                    '.choice-group[data-required-choice="true"]'
1929                )
1930                .each(function () {
1931
1932                    var $group =
1933                        jQuery(this);
1934
1935
1936                    /*
1937                     * Versteckte Conditionals überspringen.
1938                     */
1939                    if (
1940                        $group.is('[hidden]') ||
1941                        $group.closest('[hidden]').length
1942                    ) {
1943                        return;
1944                    }
1945
1946
1947                    var $radios =
1948                        $group.find(
1949                            'input[type="radio"]:not(:disabled)'
1950                        );
1951
1952
1953                    if (!$radios.length) {
1954                        return;
1955                    }
1956
1957
1958                    var hasSelection =
1959                        $radios.filter(':checked').length > 0;
1960
1961
1962                    $group.attr(
1963                        'aria-required',
1964                        'true'
1965                    );
1966
1967
1968                    if (!hasSelection) {
1969
1970                        valid = false;
1971
1972
1973                        $group
1974                            .addClass('--error')
1975                            .attr(
1976                                'aria-invalid',
1977                                'true'
1978                            );
1979
1980
1981                        if (!$firstInvalid) {
1982                            $firstInvalid = $group;
1983                        }
1984
1985                    }
1986
1987                });
1988
1989
1990            /*
1991             * ============================================================
1992             * NORMALE REQUIRED INPUTS
1993             * ============================================================
1994             */
1995
1996            $step
1997                .find(
1998                    ':input[required]:not(:disabled)'
1999                )
2000                .not(
2001                    'input[type="radio"]'
2002                )
2003                .each(function () {
2004
2005                    var $input =
2006                        jQuery(this);
2007
2008
2009                    if (
2010                        $input.closest('[hidden]').length
2011                    ) {
2012                        return;
2013                    }
2014
2015
2016                    var rawValue = $input.val();
2017
2018                    var isEmpty = $input.is(
2019                        'input[type="checkbox"]'
2020                    )
2021                        ? !$input.is(':checked')
2022                        : (
2023                            rawValue === null ||
2024                            typeof rawValue === 'undefined' ||
2025                            String(rawValue).trim() === ''
2026                        );
2027
2028
2029                    var violatesConstraint =
2030                        typeof this.checkValidity === 'function' &&
2031                        !this.checkValidity();
2032
2033
2034                    if (!isEmpty && !violatesConstraint) {
2035
2036                        $input.attr(
2037                            'aria-invalid',
2038                            'false'
2039                        );
2040
2041                        return;
2042                    }
2043
2044
2045                    valid = false;
2046
2047
2048                    var $field = $input.closest('.field');
2049
2050
2051                    if (!$field.length) {
2052                        $field = $input;
2053                    }
2054
2055
2056                    /*
2057                     * Klasse auf Input und Wrapper,
2058                     * damit Border und Label unabhängig
2059                     * vom älteren .field-CSS rot werden.
2060                     */
2061                    $input
2062                        .addClass('--error')
2063                        .attr('aria-invalid', 'true');
2064
2065                    $field.addClass('--error');
2066
2067
2068                    if (!$firstInvalid) {
2069                        $firstInvalid = $field;
2070                    }
2071
2072                });
2073
2074
2075            /*
2076             * ============================================================
2077             * INVALID
2078             * ============================================================
2079             */
2080
2081            if (!valid) {
2082
2083                $step
2084                    .find('.formular-form-error')
2085                    .addClass('--visible');
2086
2087
2088                if (
2089                    $firstInvalid &&
2090                    $firstInvalid.length
2091                ) {
2092
2093                    jQuery('html, body')
2094                        .stop(true)
2095                        .animate(
2096                            {
2097                                scrollTop:
2098                                    Math.max(
2099                                        0,
2100                                        $firstInvalid.offset().top - 100
2101                                    )
2102                            },
2103                            250
2104                        );
2105
2106                }
2107
2108
2109                return false;
2110            }
2111
2112
2113            return true;
2114        }
2115
2116
2117        /*
2118         * ============================================================
2119         * REQUEST TYPE
2120         * ============================================================
2121         */
2122
2123        function updateRequestType() {
2124
2125            var type = getRequestType();
2126
2127
2128            /*
2129             * Catering-Bereich anzeigen.
2130             */
2131            if (
2132                type === 'catering' ||
2133                type === 'catering_location'
2134            ) {
2135
2136                showConditional(
2137                    '[data-condition="catering-section"]'
2138                );
2139
2140            } else {
2141
2142                hideConditional(
2143                    '[data-condition="catering-section"]'
2144                );
2145
2146            }
2147
2148
2149            /*
2150             * Bei reiner Location-Suche macht
2151             * "Eigene Location" keinen Sinn.
2152             */
2153            var $existingLocationOption = $form.find(
2154                '[data-option="existing-location"]'
2155            );
2156
2157
2158            if (type === 'location') {
2159
2160                $existingLocationOption
2161                    .prop('hidden', true);
2162
2163
2164                if (
2165                    $form
2166                        .find(
2167                            'input[name="location_status"]:checked'
2168                        )
2169                        .val() === 'existing'
2170                ) {
2171
2172                    $form
2173                        .find(
2174                            'input[name="location_status"]'
2175                        )
2176                        .prop('checked', false);
2177
2178                }
2179
2180            } else {
2181
2182                $existingLocationOption
2183                    .prop('hidden', false);
2184
2185            }
2186
2187
2188            updateLocationStatus();
2189            updateInfrastructure();
2190            updateTasting();
2191        }
2192
2193
2194        /*
2195         * ============================================================
2196         * OCCASION
2197         * ============================================================
2198         */
2199
2200        function updateOccasion() {
2201
2202            var occasion = $form
2203                .find('#occasion')
2204                .val();
2205
2206
2207            /*
2208             * Sonstiger Anlass.
2209             */
2210            if (occasion === 'other') {
2211
2212                showConditional(
2213                    '[data-condition="occasion-other"]'
2214                );
2215
2216                setRequired(
2217                    '#occasion_other',
2218                    true
2219                );
2220
2221            } else {
2222
2223                setRequired(
2224                    '#occasion_other',
2225                    false
2226                );
2227
2228                hideConditional(
2229                    '[data-condition="occasion-other"]'
2230                );
2231
2232            }
2233
2234
2235            /*
2236             * Unternehmen.
2237             */
2238            if (
2239                occasion === 'business' ||
2240                occasion === 'christmas' ||
2241                occasion === 'conference' ||
2242                occasion === 'fair'
2243            ) {
2244
2245                showConditional(
2246                    '[data-condition="company"]'
2247                );
2248
2249            } else {
2250
2251                hideConditional(
2252                    '[data-condition="company"]'
2253                );
2254
2255            }
2256
2257
2258            updateTasting();
2259        }
2260
2261
2262        /*
2263         * ============================================================
2264         * DATE
2265         * ============================================================
2266         */
2267
2268        function updateDateStatus() {
2269
2270            var status = $form
2271                .find(
2272                    'input[name="date_status"]:checked'
2273                )
2274                .val();
2275
2276
2277            /*
2278             * Reset.
2279             */
2280            setRequired(
2281                '#event_date',
2282                false
2283            );
2284
2285
2286            hideConditional(
2287                '[data-condition="date-fixed"]'
2288            );
2289
2290            hideConditional(
2291                '[data-condition="date-flexible"]'
2292            );
2293
2294            hideConditional(
2295                '[data-condition="alternative-date"]'
2296            );
2297
2298
2299            /*
2300             * Fixes Datum.
2301             */
2302            if (status === 'fixed') {
2303
2304                showConditional(
2305                    '[data-condition="date-fixed"]'
2306                );
2307
2308                setRequired(
2309                    '#event_date',
2310                    true
2311                );
2312
2313
2314                if (
2315                    $form
2316                        .find('#has_alternative_date')
2317                        .is(':checked')
2318                ) {
2319
2320                    showConditional(
2321                        '[data-condition="alternative-date"]'
2322                    );
2323
2324                }
2325
2326            }
2327
2328
2329            /*
2330             * Flexibles Datum.
2331             */
2332            if (status === 'flexible') {
2333
2334                showConditional(
2335                    '[data-condition="date-flexible"]'
2336                );
2337
2338            }
2339        }
2340
2341
2342        /*
2343         * ============================================================
2344         * CHILDREN
2345         * ============================================================
2346         */
2347
2348        function updateChildren() {
2349
2350            var status = $form
2351                .find(
2352                    'input[name="children_status"]:checked'
2353                )
2354                .val();
2355
2356
2357            if (status === 'yes') {
2358
2359                showConditional(
2360                    '[data-condition="children"]'
2361                );
2362
2363            } else {
2364
2365                hideConditional(
2366                    '[data-condition="children"]'
2367                );
2368
2369            }
2370        }
2371
2372
2373        /*
2374         * ============================================================
2375         * LOCATION
2376         * ============================================================
2377         */
2378
2379        function updateLocationStatus() {
2380
2381            var status = $form
2382                .find(
2383                    'input[name="location_status"]:checked'
2384                )
2385                .val();
2386
2387
2388            /*
2389             * Reset.
2390             */
2391            setRequired(
2392                '#event_location_city',
2393                false
2394            );
2395
2396
2397            hideConditional(
2398                '[data-condition="existing-location"]'
2399            );
2400
2401            hideConditional(
2402                '[data-condition="location-search"]'
2403            );
2404
2405            hideConditional(
2406                '[data-condition="partner-location"]'
2407            );
2408
2409
2410            /*
2411             * Eigene Location.
2412             */
2413            if (status === 'existing') {
2414
2415                showConditional(
2416                    '[data-condition="existing-location"]'
2417                );
2418
2419                setRequired(
2420                    '#event_location_city',
2421                    true
2422                );
2423
2424            }
2425
2426
2427            /*
2428             * Location gesucht.
2429             */
2430            if (status === 'search') {
2431
2432                showConditional(
2433                    '[data-condition="location-search"]'
2434                );
2435
2436            }
2437
2438
2439            /*
2440             * Partnerlocation.
2441             */
2442            if (status === 'partner') {
2443
2444                showConditional(
2445                    '[data-condition="partner-location"]'
2446                );
2447
2448            }
2449
2450
2451            /*
2452             * Location-Wünsche.
2453             */
2454            if (
2455                status === 'search' ||
2456                status === 'partner' ||
2457                includesLocationSearch()
2458            ) {
2459
2460                showConditional(
2461                    '[data-condition="location-wishes"]'
2462                );
2463
2464            } else {
2465
2466                hideConditional(
2467                    '[data-condition="location-wishes"]'
2468                );
2469
2470            }
2471
2472
2473            updateInfrastructure();
2474        }
2475
2476
2477        /*
2478         * ============================================================
2479         * INFRASTRUCTURE / LOGISTICS
2480         * ============================================================
2481         */
2482
2483        function updateInfrastructure() {
2484
2485            var locationStatus = $form
2486                .find(
2487                    'input[name="location_status"]:checked'
2488                )
2489                .val();
2490
2491
2492            if (
2493                includesCatering() &&
2494                locationStatus === 'existing'
2495            ) {
2496
2497                showConditional(
2498                    '[data-condition="infrastructure"]'
2499                );
2500
2501                showConditional(
2502                    '[data-condition="logistics"]'
2503                );
2504
2505            } else {
2506
2507                hideConditional(
2508                    '[data-condition="infrastructure"]'
2509                );
2510
2511                hideConditional(
2512                    '[data-condition="logistics"]'
2513                );
2514
2515            }
2516        }
2517
2518
2519        /*
2520         * ============================================================
2521         * TASTING
2522         * ============================================================
2523         */
2524
2525        function updateTasting() {
2526
2527            var occasion = $form
2528                .find('#occasion')
2529                .val();
2530
2531
2532            if (
2533                occasion === 'wedding' &&
2534                includesCatering()
2535            ) {
2536
2537                showConditional(
2538                    '[data-condition="tasting"]'
2539                );
2540
2541            } else {
2542
2543                hideConditional(
2544                    '[data-condition="tasting"]'
2545                );
2546
2547            }
2548        }
2549
2550
2551        /*
2552         * ============================================================
2553         * CONTACT PREFERENCE
2554         * ============================================================
2555         */
2556
2557        function updateContactPreference() {
2558
2559            var value = $form
2560                .find(
2561                    'input[name="contact_preference"]:checked'
2562                )
2563                .val();
2564
2565
2566            var requiresPhone =
2567                value === 'phone' ||
2568                value === 'both';
2569
2570
2571            /*
2572             * Telefon wird bei Telefon / Beides Pflichtfeld.
2573             */
2574            setRequired(
2575                '#phone',
2576                requiresPhone
2577            );
2578
2579
2580            if (requiresPhone) {
2581
2582                showConditional(
2583                    '[data-condition="callback-time"]'
2584                );
2585
2586            } else {
2587
2588                hideConditional(
2589                    '[data-condition="callback-time"]'
2590                );
2591
2592
2593                /*
2594                 * Bestehenden Telefon-Fehler entfernen,
2595                 * wenn auf E-Mail gewechselt wird.
2596                 */
2597                $form
2598                    .find('#phone')
2599                    .removeClass('--error');
2600
2601            }
2602        }
2603
2604
2605        /*
2606         * ============================================================
2607         * STEP NAVIGATION
2608         * ============================================================
2609         */
2610
2611        $form.off('.cateringRequestValidation');
2612
2613
2614        $form.on(
2615            'change.cateringRequestValidation input.cateringRequestValidation',
2616            ':input',
2617            function () {
2618
2619                var $input =
2620                    jQuery(this);
2621
2622
2623                /*
2624                 * ========================================================
2625                 * RADIO
2626                 * ========================================================
2627                 */
2628
2629                if (
2630                    $input.is(
2631                        'input[type="radio"]'
2632                    )
2633                ) {
2634
2635                    var $group =
2636                        $input.closest(
2637                            '.choice-group'
2638                        );
2639
2640
2641                    if (
2642                        $group.is(
2643                            '[data-required-choice="true"]'
2644                        )
2645                    ) {
2646
2647                        var hasSelection =
2648                            $group
2649                                .find(
2650                                    'input[type="radio"]:not(:disabled):checked'
2651                                )
2652                                .length > 0;
2653
2654
2655                        if (hasSelection) {
2656
2657                            $group
2658                                .removeClass('--error')
2659                                .attr(
2660                                    'aria-invalid',
2661                                    'false'
2662                                );
2663
2664                        }
2665
2666                    }
2667
2668
2669                    /*
2670                     * Radios selbst bekommen keinen Error.
2671                     */
2672                    $input
2673                        .removeClass('--error')
2674                        .removeAttr('aria-invalid');
2675
2676
2677                    return;
2678                }
2679
2680
2681                /*
2682                 * ========================================================
2683                 * CHECKBOX
2684                 * ========================================================
2685                 */
2686
2687                if (
2688                    $input.is(
2689                        'input[type="checkbox"]'
2690                    )
2691                ) {
2692
2693                    if ($input[0].checkValidity()) {
2694
2695                        $input
2696                            .removeClass('--error')
2697                            .attr(
2698                                'aria-invalid',
2699                                'false'
2700                            );
2701
2702
2703                        $input
2704                            .closest('.field')
2705                            .removeClass('--error');
2706
2707                    }
2708
2709
2710                    return;
2711                }
2712
2713
2714                /*
2715                 * ========================================================
2716                 * STANDARD FIELD
2717                 * ========================================================
2718                 */
2719
2720                if ($input[0].checkValidity()) {
2721
2722                    $input
2723                        .removeClass('--error')
2724                        .attr(
2725                            'aria-invalid',
2726                            'false'
2727                        );
2728
2729                    $input
2730                        .closest('.field')
2731                        .removeClass('--error');
2732
2733                }
2734
2735            }
2736        );
2737
2738
2739        /*
2740         * ============================================================
2741         * REQUIRED CHOICE GROUP ERROR RESET
2742         * ============================================================
2743         */
2744
2745        $form.on(
2746            'change.cateringRequest',
2747            '.choice-group[data-required-choice="true"] input[type="radio"]',
2748            function () {
2749
2750                var $group = jQuery(this).closest(
2751                    '.choice-group[data-required-choice="true"]'
2752                );
2753
2754
2755                var hasSelection =
2756                    $group
2757                        .find(
2758                            'input[type="radio"]:not(:disabled):checked'
2759                        )
2760                        .length > 0;
2761
2762
2763                if (hasSelection) {
2764
2765                    $group
2766                        .removeClass('--error')
2767                        .attr('aria-invalid', 'false');
2768
2769                }
2770
2771            }
2772        );
2773
2774
2775        /*
2776         * ============================================================
2777         * GENERIC ERROR RESET
2778         * ============================================================
2779         */
2780
2781        $form.on(
2782            'input.cateringRequest change.cateringRequest',
2783            ':input',
2784            function () {
2785
2786                var $input = jQuery(this);
2787
2788
2789                /*
2790                 * Radios:
2791                 * niemals --error auf das Input selbst.
2792                 */
2793                if (
2794                    $input.is(
2795                        'input[type="radio"]'
2796                    )
2797                ) {
2798
2799                    $input.removeClass('--error');
2800
2801                    return;
2802                }
2803
2804
2805                /*
2806                 * Checkbox.
2807                 */
2808                if (
2809                    $input.is(
2810                        'input[type="checkbox"]'
2811                    )
2812                ) {
2813
2814                    if (
2815                        $input[0].checkValidity()
2816                    ) {
2817
2818                        $input
2819                            .closest('.field')
2820                            .removeClass('--error');
2821
2822                        $input.removeClass('--error');
2823
2824                    }
2825
2826                    return;
2827                }
2828
2829
2830                /*
2831                 * Normale Felder.
2832                 */
2833                if (
2834                    $input[0].checkValidity()
2835                ) {
2836
2837                    $input.removeClass('--error');
2838
2839                    $input
2840                        .closest('.field')
2841                        .removeClass('--error');
2842
2843                }
2844
2845            }
2846        );
2847
2848
2849        /*
2850         * ============================================================
2851         * REQUEST TYPE
2852         * ============================================================
2853         */
2854
2855        $form.on(
2856            'change.cateringRequest',
2857            'input[name="request_type"]',
2858            function () {
2859
2860                updateRequestType();
2861
2862            }
2863        );
2864
2865
2866        /*
2867         * ============================================================
2868         * OCCASION
2869         * ============================================================
2870         */
2871
2872        $form.on(
2873            'change.cateringRequest',
2874            '#occasion',
2875            function () {
2876
2877                updateOccasion();
2878
2879            }
2880        );
2881
2882
2883        /*
2884         * ============================================================
2885         * DATE STATUS
2886         * ============================================================
2887         */
2888
2889        $form.on(
2890            'change.cateringRequest',
2891            'input[name="date_status"]',
2892            function () {
2893
2894                updateDateStatus();
2895
2896            }
2897        );
2898
2899
2900        /*
2901         * ============================================================
2902         * ALTERNATIVE DATE
2903         * ============================================================
2904         */
2905
2906        $form.on(
2907            'change.cateringRequest',
2908            '#has_alternative_date',
2909            function () {
2910
2911                if (
2912                    jQuery(this).is(':checked')
2913                ) {
2914
2915                    showConditional(
2916                        '[data-condition="alternative-date"]'
2917                    );
2918
2919                } else {
2920
2921                    hideConditional(
2922                        '[data-condition="alternative-date"]'
2923                    );
2924
2925                }
2926
2927            }
2928        );
2929
2930
2931        /*
2932         * ============================================================
2933         * CHILDREN
2934         * ============================================================
2935         */
2936
2937        $form.on(
2938            'change.cateringRequest',
2939            'input[name="children_status"]',
2940            function () {
2941
2942                updateChildren();
2943
2944            }
2945        );
2946
2947
2948        /*
2949         * ============================================================
2950         * LOCATION
2951         * ============================================================
2952         */
2953
2954        $form.on(
2955            'change.cateringRequest',
2956            'input[name="location_status"]',
2957            function () {
2958
2959                updateLocationStatus();
2960
2961            }
2962        );
2963
2964
2965        /*
2966         * ============================================================
2967         * CATERING PHASES
2968         * ============================================================
2969         */
2970
2971        $form.on(
2972            'change.cateringRequest',
2973            'input[name="multiple_catering_phases"]',
2974            function () {
2975
2976                if (
2977                    jQuery(this).val() === 'yes'
2978                ) {
2979
2980                    showConditional(
2981                        '[data-condition="catering-phases"]'
2982                    );
2983
2984                } else {
2985
2986                    hideConditional(
2987                        '[data-condition="catering-phases"]'
2988                    );
2989
2990                }
2991
2992            }
2993        );
2994
2995
2996        /*
2997         * Programmpunkt hinzufügen.
2998         */
2999        $form.on(
3000            'click.cateringRequest',
3001            '#add-catering-phase',
3002            function () {
3003
3004                var phaseIndex =
3005                    $form
3006                        .find('.catering-phase')
3007                        .length + 1;
3008
3009
3010                var html = `
3011                <fieldset class="catering-phase">
3012
3013                    <div class="field">
3014
3015                        <input
3016                            type="time"
3017                            id="phase-time-${phaseIndex}"
3018                            name="phase_time[]"
3019                        >
3020
3021                        <label for="phase-time-${phaseIndex}">
3022                            Uhrzeit
3023                        </label>
3024
3025                    </div>
3026
3027
3028                    <div class="field">
3029
3030                        <input
3031                            type="text"
3032                            id="phase-service-${phaseIndex}"
3033                            name="phase_service[]"
3034                        >
3035
3036                        <label for="phase-service-${phaseIndex}">
3037                            Leistung
3038                        </label>
3039
3040                    </div>
3041
3042
3043                    <div class="field">
3044
3045                        <input
3046                            type="number"
3047                            id="phase-guests-${phaseIndex}"
3048                            name="phase_guests[]"
3049                            min="1"
3050                        >
3051
3052                        <label for="phase-guests-${phaseIndex}">
3053                            Gäste ca.
3054                        </label>
3055
3056                    </div>
3057
3058
3059                    <button
3060                        type="button"
3061                        class="remove-catering-phase"
3062                    >
3063                        Entfernen
3064                    </button>
3065
3066                </fieldset>
3067            `;
3068
3069
3070                $form
3071                    .find('#catering-phases-container')
3072                    .append(html);
3073
3074            }
3075        );
3076
3077
3078        /*
3079         * Programmpunkt entfernen.
3080         */
3081        $form.on(
3082            'click.cateringRequest',
3083            '.remove-catering-phase',
3084            function () {
3085
3086                jQuery(this)
3087                    .closest('.catering-phase')
3088                    .remove();
3089
3090            }
3091        );
3092
3093
3094        /*
3095         * ============================================================
3096         * DIET / ALLERGIES
3097         * ============================================================
3098         */
3099
3100        function updateDiet() {
3101
3102            var $dietInputs = $form.find(
3103                'input[name="diet[]"]'
3104            );
3105
3106
3107            var hasAllergies =
3108                $dietInputs
3109                    .filter('[value="allergies"]')
3110                    .is(':checked');
3111
3112
3113            if (hasAllergies) {
3114
3115                showConditional(
3116                    '[data-condition="allergies"]'
3117                );
3118
3119            } else {
3120
3121                hideConditional(
3122                    '[data-condition="allergies"]'
3123                );
3124
3125            }
3126        }
3127
3128
3129        $form.on(
3130            'change.cateringRequest',
3131            'input[name="diet[]"]',
3132            function () {
3133
3134                var $changed = jQuery(this);
3135
3136
3137                if ($changed.is(':checked')) {
3138
3139                    /*
3140                     * "Keine" und "Noch nicht bekannt"
3141                     * sind exklusive Optionen.
3142                     */
3143                    if (
3144                        $changed.val() === 'none' ||
3145                        $changed.val() === 'unknown'
3146                    ) {
3147
3148                        $form
3149                            .find('input[name="diet[]"]')
3150                            .not($changed)
3151                            .prop('checked', false);
3152
3153                    } else {
3154
3155                        $form
3156                            .find(
3157                                'input[name="diet[]"][value="none"],' +
3158                                'input[name="diet[]"][value="unknown"]'
3159                            )
3160                            .prop('checked', false);
3161
3162                    }
3163
3164                }
3165
3166
3167                updateDiet();
3168
3169            }
3170        );
3171
3172
3173        /*
3174         * ============================================================
3175         * DRINKS
3176         * ============================================================
3177         */
3178
3179        function updateDrinks() {
3180
3181            var value = $form
3182                .find(
3183                    'input[name="drinks_service"]:checked'
3184                )
3185                .val();
3186
3187
3188            if (
3189                value === 'complete' ||
3190                value === 'partial'
3191            ) {
3192
3193                showConditional(
3194                    '[data-condition="drinks-options"]'
3195                );
3196
3197            } else {
3198
3199                hideConditional(
3200                    '[data-condition="drinks-options"]'
3201                );
3202
3203            }
3204        }
3205
3206
3207        $form.on(
3208            'change.cateringRequest',
3209            'input[name="drinks_service"]',
3210            function () {
3211
3212                updateDrinks();
3213
3214            }
3215        );
3216
3217
3218        /*
3219         * ============================================================
3220         * STAFF
3221         * ============================================================
3222         */
3223
3224        function updateStaff() {
3225
3226            var value = $form
3227                .find(
3228                    'input[name="staff_required"]:checked'
3229                )
3230                .val();
3231
3232
3233            if (value === 'yes') {
3234
3235                showConditional(
3236                    '[data-condition="staff-options"]'
3237                );
3238
3239            } else {
3240
3241                hideConditional(
3242                    '[data-condition="staff-options"]'
3243                );
3244
3245            }
3246        }
3247
3248
3249        $form.on(
3250            'change.cateringRequest',
3251            'input[name="staff_required"]',
3252            function () {
3253
3254                updateStaff();
3255
3256            }
3257        );
3258
3259
3260        /*
3261         * ============================================================
3262         * EQUIPMENT
3263         * ============================================================
3264         */
3265
3266        function updateEquipment() {
3267
3268            var value = $form
3269                .find(
3270                    'input[name="equipment_required"]:checked'
3271                )
3272                .val();
3273
3274
3275            if (value === 'yes') {
3276
3277                showConditional(
3278                    '[data-condition="equipment-options"]'
3279                );
3280
3281            } else {
3282
3283                hideConditional(
3284                    '[data-condition="equipment-options"]'
3285                );
3286
3287            }
3288        }
3289
3290
3291        $form.on(
3292            'change.cateringRequest',
3293            'input[name="equipment_required"]',
3294            function () {
3295
3296                updateEquipment();
3297
3298            }
3299        );
3300
3301
3302        /*
3303         * ============================================================
3304         * CONTACT PREFERENCE
3305         * ============================================================
3306         */
3307
3308        $form.on(
3309            'change.cateringRequest',
3310            'input[name="contact_preference"]',
3311            function () {
3312
3313                updateContactPreference();
3314
3315            }
3316        );
3317
3318
3319        /*
3320         * ============================================================
3321         * SOURCE
3322         * ============================================================
3323         */
3324
3325        function updateSource() {
3326
3327            var source = $form
3328                .find('#source')
3329                .val();
3330
3331
3332            if (source === 'other') {
3333
3334                showConditional(
3335                    '[data-condition="source-other"]'
3336                );
3337
3338            } else {
3339
3340                hideConditional(
3341                    '[data-condition="source-other"]'
3342                );
3343
3344            }
3345        }
3346
3347
3348        $form.on(
3349            'change.cateringRequest',
3350            '#source',
3351            function () {
3352
3353                updateSource();
3354
3355            }
3356        );
3357
3358
3359        /*
3360         * ============================================================
3361         * STEP BUTTONS
3362         *
3363         * stopPropagation, damit der ältere Handler
3364         * auf .formular .btn--next diese Buttons nicht
3365         * zusätzlich (und ohne Choice-Groups) prüft.
3366         * ============================================================
3367         */
3368
3369        $form.on(
3370            'click.cateringRequest',
3371            '.next-step',
3372            function (event) {
3373
3374                event.preventDefault();
3375                event.stopPropagation();
3376
3377
3378                if (!validateStep(currentStep)) {
3379                    return;
3380                }
3381
3382
3383                showStep(currentStep + 1);
3384
3385            }
3386        );
3387
3388
3389        $form.on(
3390            'click.cateringRequest',
3391            '.prev-step',
3392            function (event) {
3393
3394                event.preventDefault();
3395                event.stopPropagation();
3396
3397
3398                showStep(currentStep - 1);
3399
3400            }
3401        );
3402
3403
3404        $form.on(
3405            'click.cateringRequest',
3406            'button[type="submit"]',
3407            function (event) {
3408
3409                event.stopPropagation();
3410
3411            }
3412        );
3413
3414
3415        /*
3416         * ============================================================
3417         * DEBUG
3418         * 5 schnelle Klicks auf die Schrittanzeige.
3419         * ============================================================
3420         */
3421
3422        var debugClickCount = 0;
3423        var debugLastClick = 0;
3424
3425
3426        function revealDebugHiddenFields() {
3427
3428            $form
3429                .find('input[type="hidden"]')
3430                .each(function () {
3431
3432                    var $input = jQuery(this);
3433
3434                    var fieldName =
3435                        $input.attr('name') ||
3436                        $input.attr('id') ||
3437                        'hidden';
3438
3439
3440                    $input
3441                        .attr('type', 'text')
3442                        .addClass('is-debug-hidden')
3443                        .attr('autocomplete', 'off');
3444
3445
3446                    if (
3447                        $input
3448                            .parent()
3449                            .hasClass('field--debug-hidden')
3450                    ) {
3451                        return;
3452                    }
3453
3454
3455                    var $wrap = jQuery(
3456                        '<div class="field field--debug-hidden"></div>'
3457                    );
3458
3459                    var $label = jQuery('<label></label>')
3460                        .text('hidden · ' + fieldName);
3461
3462
3463                    if ($input.attr('id')) {
3464                        $label.attr(
3465                            'for',
3466                            $input.attr('id')
3467                        );
3468                    }
3469
3470
3471                    $input.before($wrap);
3472                    $wrap.append($input);
3473                    $wrap.append($label);
3474
3475                });
3476
3477        }
3478
3479
3480        function fillDebugSample() {
3481
3482            $form
3483                .find('#request-catering-location')
3484                .prop('checked', true);
3485
3486            $form.find('#occasion').val('wedding');
3487
3488            $form
3489                .find('#date-fixed')
3490                .prop('checked', true);
3491
3492            $form.find('#event_date').val('2026-12-12');
3493
3494            $form
3495                .find('#has_alternative_date')
3496                .prop('checked', true);
3497
3498            $form
3499                .find('#alternative_date')
3500                .val('2026-12-19');
3501
3502            $form.find('#guest_count').val('80');
3503
3504            $form
3505                .find('#children-yes')
3506                .prop('checked', true);
3507
3508            $form.find('#children_count').val('12');
3509
3510            $form
3511                .find('#location-existing')
3512                .prop('checked', true);
3513
3514            $form
3515                .find('#event_location_name')
3516                .val('Gutshof Beispiel');
3517
3518            $form
3519                .find('#event_location_city')
3520                .val('Köln');
3521
3522            $form
3523                .find(
3524                    '#location-feature-indoor, #location-feature-outdoor, #location-feature-green'
3525                )
3526                .prop('checked', true);
3527
3528            $form
3529                .find('#catering-buffet, #catering-menu')
3530                .prop('checked', true);
3531
3532            $form
3533                .find('#phases-yes')
3534                .prop('checked', true);
3535
3536            $form
3537                .find('input[name="phase_time[]"]')
3538                .first()
3539                .val('18:00');
3540
3541            $form
3542                .find('input[name="phase_service[]"]')
3543                .first()
3544                .val('Empfang und Dinner');
3545
3546            $form
3547                .find('input[name="phase_guests[]"]')
3548                .first()
3549                .val('80');
3550
3551            $form
3552                .find('#diet-vegetarian, #diet-allergies')
3553                .prop('checked', true);
3554
3555            $form
3556                .find('#allergies_text')
3557                .val('Nüsse, Laktose');
3558
3559            $form
3560                .find('#drinks-complete')
3561                .prop('checked', true);
3562
3563            $form
3564                .find('#drink-soft, #drink-wine, #drink-sparkling')
3565                .prop('checked', true);
3566
3567            $form
3568                .find('#staff-yes')
3569                .prop('checked', true);
3570
3571            $form
3572                .find('#staff-service, #staff-bar')
3573                .prop('checked', true);
3574
3575            $form
3576                .find('#equipment-yes')
3577                .prop('checked', true);
3578
3579            $form
3580                .find('#eq-dishes, #eq-glasses, #eq-tables')
3581                .prop('checked', true);
3582
3583            $form
3584                .find('#service-decoration, #service-floristry')
3585                .prop('checked', true);
3586
3587            $form.find('#budget').val('5000-7500');
3588
3589            $form
3590                .find('#planning_status')
3591                .val('planning');
3592
3593            $form
3594                .find('#infra-kitchen, #infra-power, #infra-access')
3595                .prop('checked', true);
3596
3597            $form
3598                .find('#log-delivery, #log-setup, #log-full-service')
3599                .prop('checked', true);
3600
3601            $form
3602                .find('#tasting-yes')
3603                .prop('checked', true);
3604
3605            $form
3606                .find('#additional_notes')
3607                .val(
3608                    'Hochzeit im kleinen Kreis, gerne mit Gartenempfang.'
3609                );
3610
3611            $form.find('#first_name').val('Maximilian');
3612            $form.find('#last_name').val('Mustermann');
3613            $form.find('#email').val('[email protected]');
3614            $form.find('#phone').val('+49 178 1234567');
3615
3616            $form
3617                .find('#contact-both')
3618                .prop('checked', true);
3619
3620            $form
3621                .find('#callback_time')
3622                .val('afternoon');
3623
3624            $form.find('#source').val('recommendation');
3625
3626            $form
3627                .find('#privacy')
3628                .prop('checked', true);
3629
3630
3631            updateRequestType();
3632            updateOccasion();
3633            updateDateStatus();
3634            updateChildren();
3635            updateLocationStatus();
3636            updateInfrastructure();
3637            updateTasting();
3638            updateDiet();
3639            updateDrinks();
3640            updateStaff();
3641            updateEquipment();
3642            updateContactPreference();
3643            updateSource();
3644
3645
3646            if (
3647                $form
3648                    .find('input[name="multiple_catering_phases"]:checked')
3649                    .val() === 'yes'
3650            ) {
3651
3652                showConditional(
3653                    '[data-condition="catering-phases"]'
3654                );
3655
3656            }
3657
3658
3659            revealDebugHiddenFields();
3660
3661
3662            $form
3663                .find('.--error')
3664                .removeClass('--error');
3665
3666            $form
3667                .find('[aria-invalid="true"]')
3668                .attr('aria-invalid', 'false');
3669
3670            $form
3671                .find('.formular-form-error')
3672                .removeClass('--visible');
3673
3674        }
3675
3676
3677        $form.on(
3678            'click.cateringRequest',
3679            '.formular-form-pagination',
3680            function (event) {
3681
3682                event.preventDefault();
3683
3684
3685                var now = Date.now();
3686
3687
3688                if (now - debugLastClick > 800) {
3689                    debugClickCount = 0;
3690                }
3691
3692
3693                debugClickCount++;
3694                debugLastClick = now;
3695
3696
3697                if (window.getSelection) {
3698                    window.getSelection().removeAllRanges();
3699                }
3700
3701
3702                if (debugClickCount < 5) {
3703                    return;
3704                }
3705
3706
3707                debugClickCount = 0;
3708                fillDebugSample();
3709
3710            }
3711        );
3712
3713
3714        /*
3715         * ============================================================
3716         * FORM RESET
3717         * ============================================================
3718         */
3719
3720        var clearOnReturn = false;
3721
3722
3723        function resetCateringForm() {
3724
3725            var formElement = $form.get(0);
3726
3727            if (formElement) {
3728                formElement.reset();
3729            }
3730
3731
3732            $form
3733                .find('#catering-phases-container .catering-phase')
3734                .not(':first')
3735                .remove();
3736
3737
3738            $form
3739                .find('.--error')
3740                .removeClass('--error');
3741
3742            $form
3743                .find('[aria-invalid="true"]')
3744                .attr('aria-invalid', 'false');
3745
3746            $form
3747                .find('.formular-form-error')
3748                .removeClass('--visible');
3749
3750
3751            $form
3752                .find('button[type="submit"]')
3753                .prop('disabled', false)
3754                .text('Unverbindliche Anfrage senden');
3755
3756
3757            currentStep = 1;
3758            initialiseState();
3759        }
3760
3761
3762        /*
3763         * Zurück aus dem Verlauf soll keine ausgefüllte
3764         * Anfrage wieder anzeigen.
3765         */
3766        window.addEventListener('pageshow', function (event) {
3767
3768            if (event.persisted && clearOnReturn) {
3769                resetCateringForm();
3770            }
3771
3772        });
3773
3774
3775        /*
3776         * ============================================================
3777         * SUBMIT
3778         * ============================================================
3779         */
3780
3781        $form.on(
3782            'submit.cateringRequest',
3783            function (event) {
3784
3785                var invalidStep = null;
3786
3787
3788                /*
3789                 * Alle Steps prüfen.
3790                 */
3791                for (
3792                    var step = 1;
3793                    step <= totalSteps;
3794                    step++
3795                ) {
3796
3797                    if (!validateStep(step)) {
3798
3799                        invalidStep = step;
3800                        break;
3801
3802                    }
3803
3804                }
3805
3806
3807                event.preventDefault();
3808
3809
3810                if (invalidStep !== null) {
3811
3812                    showStep(
3813                        invalidStep,
3814                        false
3815                    );
3816
3817
3818                    /*
3819                     * Nach dem Anzeigen nochmals validieren,
3820                     * damit Scrollposition + Fehlerzustände stimmen.
3821                     */
3822                    validateStep(invalidStep);
3823
3824
3825                    return false;
3826                }
3827
3828
3829                var $submit = $form.find(
3830                    'button[type="submit"]'
3831                );
3832
3833                var submitLabel = $submit.text();
3834
3835
3836                $submit
3837                    .prop('disabled', true)
3838                    .text('Anfrage wird gesendet …');
3839
3840
3841                var formData = new FormData(
3842                    $form.get(0)
3843                );
3844
3845
3846                jQuery.ajax({
3847                    url: (window.aup_params && aup_params.ajaxurl)
3848                        ? aup_params.ajaxurl
3849                        : $form.attr('action'),
3850                    method: 'POST',
3851                    data: formData,
3852                    processData: false,
3853                    contentType: false
3854                }).done(function (resp) {
3855
3856                    if (!resp || resp.msg !== 'OK' || resp.type !== 'catering26') {
3857
3858                        $submit
3859                            .prop('disabled', false)
3860                            .text(submitLabel);
3861
3862                        $form
3863                            .find('.step--active .formular-form-error')
3864                            .text('Die Anfrage konnte nicht gesendet werden. Bitte versuchen Sie es erneut.')
3865                            .addClass('--visible');
3866
3867                        return;
3868                    }
3869
3870
3871                    var emailValue = $form.find('[name="email"]').val();
3872                    var phoneValue = $form.find('[name="phone"]').val();
3873                    var email = emailValue
3874                        ? String(emailValue).trim().toLowerCase()
3875                        : '';
3876                    var phone = phoneValue
3877                        ? String(phoneValue).replace(/[^\d+]/g, '')
3878                        : '';
3879
3880                    resetCateringForm();
3881                    clearOnReturn = true;
3882                    var language = jQuery('html').attr('lang') || '';
3883                    var redirectUrl = language.indexOf('en') === 0
3884                        ? '/en/contact-thank-you'
3885                        : '/kontakt-danke';
3886
3887
3888                    Promise.all([
3889                        email ? sha256(email) : Promise.resolve(''),
3890                        phone ? sha256(phone) : Promise.resolve('')
3891                    ]).then(function (hashes) {
3892
3893                        var url = new URL(
3894                            redirectUrl,
3895                            window.location.origin
3896                        );
3897
3898                        if (hashes[0]) {
3899                            url.searchParams.set('ec_email', hashes[0]);
3900                        }
3901
3902                        if (hashes[1]) {
3903                            url.searchParams.set('ec_phone', hashes[1]);
3904                        }
3905
3906                        window.location.href = url.toString();
3907
3908                    }).catch(function () {
3909
3910                        window.location.href = redirectUrl;
3911
3912                    });
3913
3914                }).fail(function () {
3915
3916                    $submit
3917                        .prop('disabled', false)
3918                        .text(submitLabel);
3919
3920                    $form
3921                        .find('.step--active .formular-form-error')
3922                        .text('Die Anfrage konnte nicht gesendet werden. Bitte versuchen Sie es erneut.')
3923                        .addClass('--visible');
3924
3925                });
3926
3927            }
3928        );
3929
3930
3931        /*
3932         * ============================================================
3933         * INITIAL STATE
3934         * ============================================================
3935         */
3936
3937        function initialiseState() {
3938
3939            syncConditionalDisabledState();
3940
3941            updateRequestType();
3942            updateOccasion();
3943            updateDateStatus();
3944            updateChildren();
3945            updateLocationStatus();
3946
3947            updateInfrastructure();
3948            updateTasting();
3949
3950            updateDiet();
3951            updateDrinks();
3952            updateStaff();
3953            updateEquipment();
3954
3955            updateContactPreference();
3956            updateSource();
3957
3958
3959            /*
3960             * Catering Phases.
3961             */
3962            var phases = $form
3963                .find(
3964                    'input[name="multiple_catering_phases"]:checked'
3965                )
3966                .val();
3967
3968
3969            if (phases === 'yes') {
3970
3971                showConditional(
3972                    '[data-condition="catering-phases"]'
3973                );
3974
3975            } else {
3976
3977                hideConditional(
3978                    '[data-condition="catering-phases"]'
3979                );
3980
3981            }
3982
3983
3984            /*
3985             * Keine Error-Klasse direkt auf Radios.
3986             */
3987            $form
3988                .find('input[type="radio"]')
3989                .removeClass('--error');
3990
3991
3992            /*
3993             * Initial Step.
3994             */
3995            showStep(
3996                currentStep,
3997                false
3998            );
3999
4000
4001            syncConditionalDisabledState();
4002        }
4003
4004
4005        initialiseState();
4006    }
4007    initCateringRequestForm('#catering-request-form');
4008
4009});

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.