1(() => { 2 if (window.felcosContactModalReady) return; 3 4 const triggers = document.querySelectorAll('[data-contact-modal]'); 5 if (!triggers.length) return; 6 window.felcosContactModalReady = true; 7 8 const config = { 9 endpoint: 'contact.php', 10 email: '[email protected]', 11 phone: '+49 (621) 159 69 107', 12 phoneHref: 'tel:+4962115969107', 13 privacyUrl: '/datenschutz' 14 }; 15 16 const modal = document.createElement('div'); 17 modal.className = 'contact-modal'; 18 modal.hidden = true; 19 modal.innerHTML = ` 20 <div class="contact-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="contact-modal-title"> 21 <div class="contact-modal__header"> 22 <div> 23 <span class="contact-modal__kicker">FelCos IT Kontakt</span> 24 <h2 id="contact-modal-title">Beratung oder Demo</h2> 25 <p>Schick uns kurz die wichtigsten Eckdaten. Direkte Kontaktwege stehen hier ebenfalls bereit.</p> 26 </div> 27 <button class="contact-modal__close" type="button" aria-label="Kontaktformular schlieÃen">×</button> 28 </div> 29 <div class="contact-modal__body"> 30 <aside class="contact-modal__direct" aria-label="Direkte Kontaktdaten"> 31 <h3>Direkt erreichbar</h3> 32 <p>Für schnelle Rückfragen oder eine konkrete Terminabstimmung.</p> 33 <a class="contact-modal__contact-link" href="${config.phoneHref}"><span>☎</span><span>${config.phone}</span></a> 34 <a class="contact-modal__contact-link" href="mailto:${config.email}"><span>@</span><span>${config.email}</span></a> 35 <div class="contact-modal__hub-note" aria-label="HuB Web-ERP Hinweis"> 36 <img src="assets/img/hub-logo-header.svg" alt="HuB - The essentials. Nothing more."> 37 <strong>HuB â Web-ERP für wachsende KMU.</strong> 38 <span>Vom Team bis Company. Alle Module des aktuellen Releases. Ohne unnötigen Ballast.</span> 39 <ul class="contact-modal__facts" aria-label="FelCos IT Keyfacts"> 40 <li>Ãber 25 Jahre ERP-Erfahrung</li> 41 <li>Beratung, Programmierung & Prozessoptimierung</li> 42 <li>Direkter persönlicher Ansprechpartner</li> 43 </ul> 44 </div> 45 </aside> 46 <form class="contact-modal__form" action="${config.endpoint}" method="post"> 47 <h3>Anfrage vorbereiten</h3> 48 <label class="contact-modal__website">Website<input name="website" tabindex="-1" autocomplete="off"></label> 49 <div class="contact-modal__grid"> 50 <label>Name<input name="name" autocomplete="name" required></label> 51 <label>Firma<input name="company" autocomplete="organization"></label> 52 </div> 53 <div class="contact-modal__grid"> 54 <label>E-Mail<input name="email" type="email" autocomplete="email" required></label> 55 <label>Telefon<input name="phone" autocomplete="tel"></label> 56 </div> 57 <label>Thema 58 <select name="topic"> 59 <option>HuB Demo</option> 60 <option>HuB Beratung</option> 61 <option>Lexware Office Integration</option> 62 <option>caniasERP Anfrage</option> 63 <option>Förderung</option> 64 <option>Vereins-Aktion 2026</option> 65 <option>Web-Applikation</option> 66 <option>Allgemeine Anfrage</option> 67 </select> 68 </label> 69 <label>Nachricht<textarea name="message" maxlength="2500" placeholder="Worum geht es und wann passt ein Rückruf?"></textarea></label> 70 <label class="contact-modal__consent"><input name="privacy" type="checkbox" required><span>Ich stimme zu, dass FelCos IT meine Angaben zur Bearbeitung der Anfrage verarbeitet. Hinweise stehen in der <a href="${config.privacyUrl}">Datenschutzerklärung</a>.</span></label> 71 <div class="contact-modal__actions"> 72 <button class="contact-modal__submit" type="submit">Kontaktanfrage senden</button> 73 <a class="contact-modal__fallback" href="mailto:${config.email}">Oder direkt mailen</a> 74 </div>
75 <p class="contact-modal__status" role="status" aria-live="polite"></p> 76 </form> 77 </div> 78 </div>`; 79 document.body.append(modal); 80 81 const form = modal.querySelector('form'); 82 const topic = modal.querySelector('select[name="topic"]'); 83 const closeButton = modal.querySelector('.contact-modal__close'); 84 const submitButton = modal.querySelector('.contact-modal__submit'); 85 const status = modal.querySelector('.contact-modal__status'); 86 let previousFocus = null; 87 88 const setStatus = (message = '', type = '') => { 89 status.textContent = message; 90 status.dataset.status = type; 91 }; 92 93 const openModal = (event) => { 94 event.preventDefault(); 95 const trigger = event.currentTarget; 96 previousFocus = document.activeElement; 97 if (trigger.dataset.contactTopic) topic.value = trigger.dataset.contactTopic; 98 setStatus(); 99 modal.hidden = false; 100 document.body.classList.add('contact-modal-open'); 101 closeButton.focus(); 102 }; 103 104 const closeModal = () => { 105 modal.hidden = true; 106 document.body.classList.remove('contact-modal-open'); 107 previousFocus?.focus(); 108 }; 109 110 form.addEventListener('submit', async (event) => { 111 event.preventDefault(); 112 if (!form.reportValidity()) return; 113 114 if (window.location.protocol === 'file:') { 115 setStatus('Bitte öffne die Seite über den Webspace oder einen lokalen PHP-Webserver. Direkt als Datei kann contact.php nicht ausgeführt werden.', 'error'); 116 return; 117 } 118 119 const formData = new FormData(form); 120 const payload = Object.fromEntries(formData.entries()); 121 payload.privacy = formData.get('privacy') === 'on'; 122 123 submitButton.disabled = true; 124 submitButton.textContent = 'Wird gesendet...'; 125 setStatus('Anfrage wird sicher übermittelt.', 'pending'); 126 127 try { 128 const response = await fetch(config.endpoint, { 129 method: 'POST', 130 headers: { 131 Accept: 'application/json', 132 'Content-Type': 'application/json' 133 }, 134 body: JSON.stringify(payload) 135 }); 136 const result = await response.json().catch(() => ({})); 137 if (!response.ok || !result.ok) { 138 throw new Error(result.message || 'Die Anfrage konnte gerade nicht gesendet werden.'); 139 } 140 141 window.felcosTrack?.('contact_submit', { topic: payload.topic || 'unknown' }); 142 form.reset(); 143 topic.value = payload.topic || 'HuB Demo'; 144 setStatus(result.message || 'Danke, deine Anfrage wurde gesendet.', 'success'); 145 } catch (error) { 146 setStatus(error.message || 'Die Anfrage konnte gerade nicht gesendet werden. Bitte nutze E-Mail oder Telefon.', 'error'); 147 } finally { 148 submitButton.disabled = false; 149 submitButton.textContent = 'Kontaktanfrage senden'; 150 } 151 }); 152 153 triggers.forEach((trigger) => trigger.addEventListener('click', openModal)); 154 closeButton.addEventListener('click', closeModal); 155 modal.addEventListener('click', (event) => { 156 if (event.target === modal) closeModal(); 157 }); 158 document.addEventListener('keydown', (event) => { 159 if (event.key === 'Escape' && !modal.hidden) closeModal(); 160 }); 161})();
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.