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.