1(function () { 2'use strict'; 3var header = document.getElementById('site-header'); 4if (header) { 5var onScroll = function () { header.classList.toggle('scrolled', window.scrollY > 8); }; 6document.addEventListener('scroll', onScroll, { passive: true }); 7onScroll(); 8} 9var megaToggle = document.getElementById('megaToggle'); 10var megaMenu = document.getElementById('mega-menu'); 11var megaClose = document.getElementById('megaClose'); 12if (megaToggle && megaMenu) { 13var openMega = function () { 14megaMenu.hidden = false; 15megaToggle.setAttribute('aria-expanded', 'true'); 16megaToggle.setAttribute('aria-label', 'Hauptmenü schlieÃen'); 17document.body.classList.add('pd-mega-open'); 18var firstLink = megaMenu.querySelector('a, button'); 19if (firstLink) { 20setTimeout(function () { firstLink.focus({ preventScroll: true }); }, 100); 21} 22}; 23var closeMega = function (returnFocus) { 24megaMenu.hidden = true; 25megaToggle.setAttribute('aria-expanded', 'false'); 26megaToggle.setAttribute('aria-label', 'Hauptmenü öffnen'); 27document.body.classList.remove('pd-mega-open'); 28if (returnFocus !== false) { 29megaToggle.focus({ preventScroll: true }); 30} 31}; 32megaToggle.addEventListener('click', function () { 33if (megaMenu.hidden) openMega(); 34else closeMega(); 35}); 36if (megaClose) { 37megaClose.addEventListener('click', function () { closeMega(); }); 38} 39document.addEventListener('keydown', function (e) { 40if (e.key === 'Escape' && !megaMenu.hidden) { 41closeMega(); 42} 43}); 44document.addEventListener('click', function (e) { 45if (megaMenu.hidden) return; 46if (!megaMenu.contains(e.target) && !megaToggle.contains(e.target)) { 47closeMega(false); 48} 49}); 50megaMenu.addEventListener('click', function (e) { 51var link = e.target.closest('a'); 52if (link && link.getAttribute('href')) { 53closeMega(false); 54} 55}); 56megaMenu.addEventListener('keydown', function (e) { 57if (e.key !== 'Tab' || megaMenu.hidden) return; 58var focusables = megaMenu.querySelectorAll('a[href], button:not([disabled])'); 59if (!focusables.length) return; 60var first = focusables[0]; 61var last = focusables[focusables.length - 1]; 62if (e.shiftKey && document.activeElement === first) { 63e.preventDefault(); last.focus(); 64} else if (!e.shiftKey && document.activeElement === last) { 65e.preventDefault(); first.focus(); 66} 67}); 68} 69var roi = document.getElementById('pdRoiCalculator'); 70if (roi) { 71var cfg = {}; 72Object.keys(roi.dataset).forEach(function (k) { 73if (k.indexOf('pd') === 0) { 74var key = k.substring(2).replace(/^[A-Z]/, function (c) { return c.toLowerCase(); }) 75.replace(/([A-Z])/g, function (c) { return '_' + c.toLowerCase(); }); 76var v = parseFloat(roi.dataset[k]); 77cfg[key] = isNaN(v) ? roi.dataset[k] : v; 78} 79}); 80var fmt = function (n) { 81if (n < 0) n = 0; 82return n.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + ' â¬'; 83}; 84var getVal = function (key) { 85var el = roi.querySelector('[data-roi-input="' + key + '"]'); 86if (!el) return 0; 87if (el.tagName === 'SELECT') return el.value; 88var n = parseFloat(el.value); 89return isNaN(n) ? 0 : n; 90}; 91var setOut = function (key, value) { 92roi.querySelectorAll('[data-roi-out="' + key + '"]').forEach(function (el) { 93el.textContent = value; 94}); 95}; 96var isPillarActive = function (name) { 97var cb = roi.querySelector('[data-roi-pillar="' + name + '"]'); 98return cb && cb.checked; 99}; 100var toggleSection = function (name, active) { 101var sec = roi.querySelector('[data-roi-section="' + name + '"]'); 102if (sec) sec.hidden = !active; 103}; 104var renderBreakdown = function (rows) { 105var container = roi.querySelector('[data-roi-out="breakdown"]'); 106if (!container) return; 107if (!rows.length) { 108container.innerHTML = '<p style="text-align:center;color:var(--ink-500);padding:1rem;">Wähle mindestens eine Säule aus, um deine Kosten zu sehen.</p>'; 109return; 110} 111container.innerHTML = rows.map(function (r) { 112return '<div class="roi-breakdown-row">' 113+ '<div class="roi-breakdown-label">' + r.label + '</div>' 114+ '<div class="roi-breakdown-detail">' + r.detail + '</div>' 115+ '<div class="roi-breakdown-amount">' + fmt(r.amount) + '</div>' 116+ '</div>'; 117}).join(''); 118}; 119var calc = function () { 120var sg = isPillarActive('stadtgutscheine'); 121var tk = isPillarActive('tickets'); 122var gb = isPillarActive('gutscheinbuecher'); 123var cp = isPillarActive('coupons'); 124toggleSection('stadtgutscheine', sg); 125toggleSection('tickets', tk);
126toggleSection('gutscheinbuecher', gb); 127toggleSection('coupons', cp); 128var bundleActive = sg && tk && gb; 129var bundlePct = cfg.bundle_discount_pct || 10; 130var bundleFactor = bundleActive ? (1 - bundlePct / 100) : 1; 131var bundleSuffix = bundleActive ? ' (inkl. ' + bundlePct + ' % Bündel-Rabatt)' : ''; 132var pdYear = 0; 133var rows = []; 134if (sg) { 135var sgVol = getVal('stadtgutscheine_volume'); 136var sgSetup = cfg.stadtgutscheine_setup_pd; 137var sgLaufend = sgVol * 12 * cfg.stadtgutscheine_pct_pd / 100 * bundleFactor; 138var sgPd = sgSetup + sgLaufend; 139pdYear += sgPd; 140rows.push({ 141label: 'Stadtgutscheine', 142detail: fmt(sgSetup) + ' Setup einmalig + ' + cfg.stadtgutscheine_pct_pd + ' % von ' + fmt(sgVol) + '/Mt' + bundleSuffix, 143amount: sgPd 144}); 145} 146if (tk) { 147var tkCount = getVal('tickets_count'); 148var tkPd = tkCount * 12 * cfg.tickets_per_ticket_pd * bundleFactor; 149pdYear += tkPd; 150rows.push({ 151label: 'Online-Ticketing', 152detail: '0 ⬠Setup + ' + cfg.tickets_per_ticket_pd.toString().replace('.', ',') + ' ⬠à ' + tkCount + ' Tickets à 12' + bundleSuffix, 153amount: tkPd 154}); 155} 156if (gb) { 157var gbVol = getVal('gutscheinbuecher_volume'); 158var gbSetup = cfg.gutscheinbuecher_setup_pd; 159var gbProvision = gbVol * cfg.gutscheinbuecher_provision_pd / 100 * bundleFactor; 160var gbPd = gbSetup + gbProvision; 161pdYear += gbPd; 162rows.push({ 163label: 'Gutscheinbücher', 164detail: fmt(gbSetup) + ' Setup einmalig + ' + cfg.gutscheinbuecher_provision_pd + ' % von ' + fmt(gbVol) + ' Buchverkauf/Jahr' + bundleSuffix, 165amount: gbPd 166}); 167} 168if (cp) { 169var cpTier = getVal('coupons_tier') || 'basic'; 170var monthlyPrice = cfg['coupons_' + cpTier + '_price'] || 9.90; 171var cpPd = monthlyPrice * 12; 172pdYear += cpPd; 173var tierLabel = cpTier.charAt(0).toUpperCase() + cpTier.slice(1); 174rows.push({ 175label: 'Coupons (' + tierLabel + ')', 176detail: monthlyPrice.toString().replace('.', ',') + ' â¬/Mt à 12', 177amount: cpPd 178}); 179} 180renderBreakdown(rows); 181setOut('pd_year', rows.length ? fmt(pdYear) : 'â'); 182var note = roi.querySelector('[data-roi-out="bundle_note"]'); 183if (note) note.hidden = !bundleActive; 184}; 185roi.querySelectorAll('input, select').forEach(function (el) { 186el.addEventListener('input', calc); 187el.addEventListener('change', calc); 188}); 189document.querySelectorAll('[data-roi-preset]').forEach(function (btn) { 190btn.addEventListener('click', function () { 191try { 192var preset = JSON.parse(btn.getAttribute('data-roi-preset')); 193roi.querySelectorAll('[data-roi-pillar]').forEach(function (cb) { cb.checked = false; }); 194(preset.pillars || []).forEach(function (p) { 195var cb = roi.querySelector('[data-roi-pillar="' + p + '"]'); 196if (cb) cb.checked = true; 197}); 198Object.keys(preset).forEach(function (k) { 199if (k === 'pillars') return; 200var el = roi.querySelector('[data-roi-input="' + k + '"]'); 201if (el) el.value = preset[k]; 202}); 203calc(); 204var resEl = roi.querySelector('.roi-breakdown'); 205if (resEl) resEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); 206} catch (e) { } 207}); 208}); 209calc(); 210} 211var gallery = document.getElementById('gallery'); 212if (gallery) { 213var galImgs = gallery.querySelectorAll('img'); 214var galIdx = 0; 215var setGalSlide = function (i) { 216var N = galImgs.length; 217galIdx = (i + N) % N; 218galImgs.forEach(function (img, n) { 219var rel = (n - galIdx + N) % N; 220img.classList.remove('s-active','s-next','s-far','s-prev','s-prev-far','s-hidden'); 221if (rel === 0) img.classList.add('s-active'); 222else if (rel === 1) img.classList.add('s-next'); 223else if (rel === 2) img.classList.add('s-far'); 224else if (rel === N - 1) img.classList.add('s-prev'); 225else if (rel === N - 2) img.classList.add('s-prev-far'); 226else img.classList.add('s-hidden'); 227img.setAttribute('aria-hidden', rel !== 0 ? 'true' : 'false'); 228}); 229}; 230if (galImgs.length) setGalSlide(0); 231var galTimer; 232var startGalTimer = function () { 233galTimer = setInterval(function () { setGalSlide(galIdx + 1); }, 3200); 234}; 235var restartGalTimer = function () { clearInterval(galTimer); startGalTimer(); }; 236var prevBtn = document.getElementById('gal-prev'); 237var nextBtn = document.getElementById('gal-next'); 238if (prevBtn) prevBtn.addEventListener('click', function () { setGalSlide(galIdx - 1); restartGalTimer(); }); 239if (nextBtn) nextBtn.addEventListener('click', function () { setGalSlide(galIdx + 1); restartGalTimer(); }); 240if (galImgs.length && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 241startGalTimer(); 242} 243} 244if ('IntersectionObserver' in window) { 245var io = new IntersectionObserver(function (entries) {
246entries.forEach(function (e) { 247if (e.isIntersecting) { 248e.target.classList.add('in'); 249io.unobserve(e.target); 250} 251}); 252}, { threshold: 0.12 }); 253document.querySelectorAll('.reveal').forEach(function (el) { io.observe(el); }); 254} else { 255document.querySelectorAll('.reveal').forEach(function (el) { el.classList.add('in'); }); 256} 257function pdFieldError(field, msg) { 258field.setAttribute('aria-invalid', msg ? 'true' : 'false'); 259var span = field.id ? document.getElementById(field.id + '-error') : null; 260if (span) { span.textContent = msg || ''; } 261} 262var PD_FIELD_HINTS = { 263pd_firma: 'den Namen deines Unternehmens', 264pd_rechtsform: 'die Rechtsform (z. B. GmbH, GbR, Einzelunternehmen)', 265pd_vorname: 'deinen Vornamen', 266pd_nachname: 'deinen Nachnamen', 267pd_strasse: 'StraÃe und Hausnummer', 268pd_plz: 'die Postleitzahl', 269pd_ort: 'den Ort', 270pd_telefon: 'eine Telefonnummer für Rückfragen', 271pd_email: 'deine E-Mail-Adresse', 272pd_name: 'deinen Namen', 273pd_stadt: 'deine Stadt', 274pd_typ: 'aus, was für ein Anbieter du bist' 275}; 276function pdValidationMessage(field) { 277var v = field.validity; 278if (!v || v.valid) { return ''; } 279var hint = PD_FIELD_HINTS[field.name] || 'dieses Feld'; 280if (v.valueMissing) { 281return field.tagName === 'SELECT' 282? 'Bitte wähle ' + hint + '.' 283: 'Bitte gib ' + hint + ' an.'; 284} 285if (v.typeMismatch && field.type === 'email') { 286return 'Bitte gib eine gültige E-Mail-Adresse ein, z. B. [email protected].'; 287} 288return field.validationMessage || 'Bitte prüfe deine Eingabe.'; 289} 290var bookingForm = document.querySelector('form.booking'); 291if (bookingForm) { 292var bSteps = Array.prototype.slice.call(bookingForm.querySelectorAll('.booking-step')); 293var bProg = Array.prototype.slice.call(bookingForm.querySelectorAll('.booking-progress li')); 294var bPrev = bookingForm.querySelector('[data-prev]'); 295var bNext = bookingForm.querySelector('[data-next]'); 296var bNav = bookingForm.querySelector('.booking-nav'); 297var bTotal = bSteps.length; 298var bCurrent = 1; 299var eur = function (n) { 300return n.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' â¬'; 301}; 302var selectedPillars = function () { 303return Array.prototype.slice.call( 304bookingForm.querySelectorAll('input[name="pd_saeule[]"]:checked') 305); 306}; 307var couponChecked = function () { 308var c = bookingForm.querySelector('input[name="pd_saeule[]"][value="coupons"]'); 309return c && c.checked; 310}; 311var couponTier = function () { 312return bookingForm.querySelector('input[name="pd_coupon_tier"]:checked'); 313}; 314var renderCosts = function () { 315var picks = selectedPillars(); 316var keys = picks.map(function (el) { return el.value; }); 317var bundle = keys.indexOf('stadtgutscheine') >= 0 318&& keys.indexOf('tickets') >= 0 319&& keys.indexOf('gutscheinbuecher') >= 0; 320var line = function (label, value) { 321return '<span class="booking-product-line">' 322+ '<span>' + label + '</span><span>' + value + '</span></span>'; 323}; 324var html = ''; 325picks.forEach(function (el) { 326var name = el.getAttribute('data-label'); 327var setup = parseFloat(el.getAttribute('data-setup')) || 0; 328var lines = line('Einmalige Einrichtung', eur(setup) + ' zzgl. MwSt'); 329if (el.value === 'coupons') { 330var t = couponTier(); 331if (t) { 332var m = parseFloat(t.getAttribute('data-monthly')) || 0; 333name += ' â Tarif ' + t.getAttribute('data-label'); 334lines += line('Monatliche Gebühr', eur(m) + ' zzgl. MwSt'); 335} 336} else { 337var v = el.getAttribute('data-variabel'); 338if (v) {
339if (bundle) { v += ' (inkl. 10 % Bündel-Rabatt)'; } 340lines += line('Laufende Gebühr', v); 341} 342} 343html += '<div class="booking-product">' 344+ '<span class="booking-product-name">' + name + '</span>' 345+ lines + '</div>'; 346}); 347if (picks.length) { 348if (bundle) { 349html += '<p class="booking-product-note">Der 10-%-Bündel-Rabatt auf die ' 350+ 'laufenden Gebühren von Stadtgutscheinen, Tickets und Gutscheinbüchern ' 351+ 'ist oben je Modul berücksichtigt. Lokale Coupons sind ausgenommen.</p>'; 352} 353} else { 354html = '<p class="booking-empty">Noch kein Modul gewählt.</p>'; 355} 356var costList = bookingForm.querySelector('[data-cost-list]'); 357if (costList) { costList.innerHTML = html; } 358var summary = bookingForm.querySelector('[data-summary]'); 359if (summary) { 360summary.innerHTML = picks.length 361? '<h3>Deine Buchung â Modul für Modul</h3>' + html 362: html; 363} 364}; 365var syncStep2 = function () { 366var hasCoupons = couponChecked(); 367var tierBox = bookingForm.querySelector('[data-needs="coupons"]'); 368var emptyBox = bookingForm.querySelector('[data-needs="not-coupons"]'); 369if (tierBox) tierBox.hidden = !hasCoupons; 370if (emptyBox) emptyBox.hidden = hasCoupons; 371}; 372var showError = function (key, show) { 373var el = bookingForm.querySelector('[data-error="' + key + '"]'); 374if (el) el.hidden = !show; 375}; 376var focusFirstInvalid = function () { 377var bad = bSteps[2].querySelector('[aria-invalid="true"]'); 378if (bad) { bad.focus(); } 379}; 380var validateStep = function (n) { 381if (n === 1) { 382var ok = selectedPillars().length > 0; 383showError('saeule', !ok); 384return ok; 385} 386if (n === 3) { 387var fields = bSteps[2].querySelectorAll('input, select, textarea'); 388var firstInvalid = null; 389for (var i = 0; i < fields.length; i++) { 390var f = fields[i]; 391if (f.checkValidity()) { 392pdFieldError(f, ''); 393} else { 394pdFieldError(f, pdValidationMessage(f)); 395if (!firstInvalid) { firstInvalid = f; } 396} 397} 398return !firstInvalid; 399} 400if (n === 4) { 401var agb = bookingForm.querySelector('input[name="pd_agb"]'); 402var ds = bookingForm.querySelector('input[name="pd_datenschutz"]'); 403var ok2 = agb && agb.checked && ds && ds.checked; 404showError('consent', !ok2); 405return ok2; 406} 407return true; 408}; 409var showStep = function (n, skipScroll) { 410bCurrent = n; 411bSteps.forEach(function (s) { 412s.hidden = parseInt(s.getAttribute('data-step'), 10) !== n; 413}); 414bProg.forEach(function (p) { 415var pn = parseInt(p.getAttribute('data-step'), 10); 416p.classList.toggle('is-active', pn === n); 417p.classList.toggle('is-done', pn < n); 418}); 419bPrev.hidden = n === 1; 420bNext.hidden = n === bTotal; 421if (n === 2) syncStep2(); 422if (n === 2 || n === 4) renderCosts(); 423if (!skipScroll) { 424var panel = bookingForm.querySelector('.booking-step[data-step="' + n + '"]'); 425if (panel) panel.scrollIntoView({ behavior: 'smooth', block: 'start' }); 426} 427}; 428bNext.addEventListener('click', function () { 429if (!validateStep(bCurrent)) { 430if (bCurrent === 3) { focusFirstInvalid(); } 431return; 432} 433if (bCurrent < bTotal) showStep(bCurrent + 1); 434}); 435bPrev.addEventListener('click', function () { 436if (bCurrent > 1) showStep(bCurrent - 1); 437}); 438bookingForm.addEventListener('change', function (e) { 439if (e.target.name === 'pd_saeule[]' || e.target.name === 'pd_coupon_tier') { 440renderCosts(); 441} 442}); 443bookingForm.addEventListener('submit', function (e) { 444if (!validateStep(1)) { e.preventDefault(); showStep(1); return; } 445if (!validateStep(3)) { e.preventDefault(); showStep(3); focusFirstInvalid(); return; } 446if (!validateStep(4)) { e.preventDefault(); showStep(4); return; } 447}); 448Array.prototype.forEach.call( 449bSteps[2].querySelectorAll('input, select, textarea'), 450function (field) { 451var clear = function () { 452if (field.validity.valid) { pdFieldError(field, ''); } 453}; 454field.addEventListener('input', clear); 455field.addEventListener('change', clear); 456} 457); 458if (bNav) bNav.hidden = false; 459showStep(1, true); 460} 461var leadForm = document.querySelector('form.lead-form'); 462if (leadForm) { 463Array.prototype.forEach.call( 464leadForm.querySelectorAll('input[id], select[id], textarea[id]'), 465function (field) { 466field.addEventListener('invalid', function (e) { 467e.preventDefault(); 468pdFieldError(field, pdValidationMessage(field)); 469}); 470var clear = function () { 471if (field.validity.valid) { pdFieldError(field, ''); } 472}; 473field.addEventListener('input', clear); 474field.addEventListener('change', clear); 475} 476); 477} 478var leadStatus = document.querySelector('.lead-status'); 479if (leadStatus && typeof leadStatus.focus === 'function') { 480leadStatus.focus(); 481} 482})();
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.