// wir.fgk.org – kleine Hilfen ohne externe Bibliotheken document.addEventListener('submit', function (ev) { var msg = ev.target.getAttribute('data-confirm'); if (msg && !window.confirm(msg)) ev.preventDefault(); }); (function () { var btn = document.querySelector('.nav-toggle'); var nav = document.getElementById('mainnav'); if (!btn || !nav) return; btn.addEventListener('click', function () { var open = nav.classList.toggle('open'); btn.setAttribute('aria-expanded', open ? 'true' : 'false'); }); })(); // Tag/Nacht-Umschaltung in der Fußzeile: Wahl wird im Browser gespeichert, sonst gilt die Geräteeinstellung (function () { var btns = document.querySelectorAll('[data-theme-set]'); if (!btns.length) return; var root = document.documentElement; var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null; function current() { var t = root.getAttribute('data-theme'); if (t === 'dark' || t === 'light') return t; return media && media.matches ? 'dark' : 'light'; } function update() { var c = current(); for (var i = 0; i < btns.length; i++) btns[i].setAttribute('aria-pressed', btns[i].getAttribute('data-theme-set') === c ? 'true' : 'false'); } for (var i = 0; i < btns.length; i++) btns[i].addEventListener('click', function () { var next = this.getAttribute('data-theme-set'); root.setAttribute('data-theme', next); try { localStorage.setItem('theme', next); } catch (e) {} update(); }); if (media && media.addEventListener) media.addEventListener('change', update); update(); })(); // Teilen: Link kopieren und Teilen-Menü des Geräts (mit Foto, wenn möglich) document.addEventListener('click', function (ev) { var copy = ev.target.closest('[data-copy]'); if (copy) { var text = copy.getAttribute('data-copy'); var label = copy.querySelector('span'); var done = function () { copy.classList.add('copied'); if (label) { var old = label.textContent; label.textContent = 'Kopiert!'; setTimeout(function () { label.textContent = old; copy.classList.remove('copied'); }, 2000); } }; if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(done, function () { window.prompt('Link kopieren:', text); }); } else { window.prompt('Link kopieren:', text); } return; } var nat = ev.target.closest('.share-native'); if (nat && navigator.share) { var data = { title: nat.dataset.shareTitle, text: nat.dataset.shareText, url: nat.dataset.shareUrl }; var go = function (d) { navigator.share(d).catch(function () {}); }; if (nat.dataset.sharePhoto && navigator.canShare) { fetch(nat.dataset.sharePhoto).then(function (r) { return r.blob(); }).then(function (b) { var file = new File([b], 'idee.jpg', { type: b.type || 'image/jpeg' }); var withFile = Object.assign({ files: [file] }, data); go(navigator.canShare(withFile) ? withFile : data); }).catch(function () { go(data); }); } else { go(data); } } }); document.querySelectorAll('.share-native').forEach(function (b) { if (navigator.share) b.hidden = false; }); // Dateigröße schon vor dem Hochladen prüfen (z. B. Videos max. 10 MB) document.addEventListener('change', function (ev) { var inp = ev.target; if (!inp.matches('input[type=file][data-maxsize]') || !inp.files || !inp.files[0]) return; var max = parseInt(inp.getAttribute('data-maxsize'), 10); if (inp.files[0].size > max) { window.alert('Die Datei ist ' + (inp.files[0].size / 1048576).toFixed(1).replace('.', ',') + ' MB groß. Erlaubt sind höchstens ' + Math.round(max / 1048576) + ' MB.'); inp.value = ''; } }); // Aufklappen (z. B. Formular „Neue Abstimmung“) document.addEventListener('click', function (ev) { var btn = ev.target.closest('[data-toggle]'); if (!btn) return; var el = document.getElementById(btn.getAttribute('data-toggle')); if (!el) return; el.hidden = !el.hidden; if (!el.hidden) { var f = el.querySelector('input:not([type=hidden])'); if (f) f.focus(); } }); // Marktplatz: Videos in den Karten automatisch abspielen (stumm, Schleife), // aber nur solange sie sichtbar sind – spart Datenvolumen und Akku. // Bei „Bewegung reduzieren“ in den Systemeinstellungen bleibt das Vorschaubild stehen. (function () { var vids = document.querySelectorAll('video.autoplay-video'); if (!vids.length) return; var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; vids.forEach(function (v) { v.muted = true; v.addEventListener('playing', function () { v.parentNode.classList.add('playing'); }); v.addEventListener('pause', function () { v.parentNode.classList.remove('playing'); }); }); if (reduce) { vids.forEach(function (v) { if (!v.poster) v.preload = 'metadata'; }); return; } var play = function (v) { if (v.readyState < 3) v.parentNode.classList.add('video-loading'); if (v.preload === 'none') v.preload = 'auto'; var p = v.play(); if (p && p.catch) p.catch(function () {}); }; if (!('IntersectionObserver' in window)) { vids.forEach(play); return; } var io = new IntersectionObserver(function (entries) { entries.forEach(function (en) { if (en.isIntersecting) play(en.target); else en.target.pause(); }); }, { threshold: 0.4 }); vids.forEach(function (v) { io.observe(v); }); })(); // Ladeanzeige über dem Vorschaubild, bis das Video abspielbereit ist (function () { document.querySelectorAll('.video-loading video').forEach(function (v) { var box = v.parentNode; var done = function () { box.classList.remove('video-loading'); }; var wait = function () { box.classList.add('video-loading'); }; if (v.readyState >= 2) done(); v.addEventListener('loadeddata', done); v.addEventListener('canplay', done); v.addEventListener('playing', done); v.addEventListener('waiting', wait); v.addEventListener('error', done, true); // Auf der Projektseite (Video lädt erst beim Klick) kein Kreisel, solange nichts geladen wird if (v.preload === 'metadata' && !v.autoplay) { v.addEventListener('loadedmetadata', done); setTimeout(function () { if (v.readyState >= 1 || v.networkState === 1) done(); }, 1500); } }); // Marktplatz: Kreisel erst zeigen, wenn das Video wirklich zu laden beginnt document.querySelectorAll('.card-video.video-loading').forEach(function (box) { var v = box.querySelector('video'); if (v && v.preload === 'none') box.classList.remove('video-loading'); if (v) v.addEventListener('loadstart', function () { if (v.preload !== 'none' && v.readyState < 2) box.classList.add('video-loading'); }); }); })(); // Drag & Drop für Datei-Felder: Datei hineinziehen oder klicken, mit Vorschau für Bilder (function () { if (!window.DataTransfer || !window.FileReader) return; var canSet = true; try { new DataTransfer(); } catch (e) { canSet = false; } if (!canSet) return; function fmtSize(b) { return b >= 1048576 ? (b / 1048576).toFixed(1).replace('.', ',') + ' MB' : Math.max(1, Math.round(b / 1024)) + ' KB'; } function accepts(inp, file) { var acc = (inp.getAttribute('accept') || '').split(',').map(function (s) { return s.trim().toLowerCase(); }).filter(Boolean); if (!acc.length) return true; var name = file.name.toLowerCase(), type = (file.type || '').toLowerCase(); return acc.some(function (a) { if (a.charAt(0) === '.') return name.slice(-a.length) === a; if (a.slice(-2) === '/*') return type.indexOf(a.slice(0, -1)) === 0; return type === a; }); } function kind(inp) { var acc = inp.getAttribute('accept') || ''; if (/^image\//.test(acc) && acc.indexOf('video') < 0 && acc.indexOf('.pdf') < 0) return 'Bild'; if (/^video\//.test(acc)) return 'Video'; return 'Datei'; } function enhance(inp) { if (inp.closest('.dropzone')) return; var zone = document.createElement('div'); zone.className = 'dropzone'; var what = kind(inp); zone.innerHTML = '
' + '' + what + ' hierher ziehen oder klicken zum Auswählen
' + ''; inp.parentNode.insertBefore(zone, inp); zone.appendChild(inp); inp.classList.add('dz-input'); var empty = zone.querySelector('.dz-empty'), box = zone.querySelector('.dz-file'), thumb = zone.querySelector('.dz-thumb'), name = zone.querySelector('.dz-name'); function show() { var f = inp.files && inp.files[0]; if (thumb.src) { URL.revokeObjectURL(thumb.src); thumb.removeAttribute('src'); } if (!f) { box.hidden = true; empty.hidden = false; zone.classList.remove('has-file'); return; } name.textContent = f.name + ' · ' + fmtSize(f.size); thumb.hidden = !/^image\//.test(f.type); if (!thumb.hidden) thumb.src = URL.createObjectURL(f); box.hidden = false; empty.hidden = true; zone.classList.add('has-file'); } inp.addEventListener('change', function () { setTimeout(show, 0); }); var depth = 0; zone.addEventListener('dragenter', function (ev) { ev.preventDefault(); depth++; zone.classList.add('is-over'); }); zone.addEventListener('dragover', function (ev) { ev.preventDefault(); ev.dataTransfer.dropEffect = 'copy'; }); zone.addEventListener('dragleave', function () { if (--depth <= 0) { depth = 0; zone.classList.remove('is-over'); } }); zone.addEventListener('drop', function (ev) { ev.preventDefault(); depth = 0; zone.classList.remove('is-over'); var f = ev.dataTransfer.files && ev.dataTransfer.files[0]; if (!f) return; if (!accepts(inp, f)) { window.alert('Diese Datei passt hier nicht: ' + f.name); return; } var dt = new DataTransfer(); dt.items.add(f); inp.files = dt.files; inp.dispatchEvent(new Event('change', { bubbles: true })); }); zone.querySelector('.dz-remove').addEventListener('click', function () { inp.value = ''; inp.dispatchEvent(new Event('change', { bubbles: true })); }); // Bild aus der Zwischenablage einfügen, wenn das Feld fokussiert ist zone.addEventListener('paste', function (ev) { var items = (ev.clipboardData && ev.clipboardData.files) || []; if (!items.length || !accepts(inp, items[0])) return; ev.preventDefault(); var dt = new DataTransfer(); dt.items.add(items[0]); inp.files = dt.files; inp.dispatchEvent(new Event('change', { bubbles: true })); }); } document.querySelectorAll('input[type=file]').forEach(enhance); // Dateien, die neben ein Feld fallen, nicht im Browser öffnen window.addEventListener('dragover', function (ev) { if (!ev.target.closest || !ev.target.closest('.dropzone')) ev.preventDefault(); }); window.addEventListener('drop', function (ev) { if (!ev.target.closest || !ev.target.closest('.dropzone')) ev.preventDefault(); }); })(); // Akkordeon: geöffnete Bereiche merken und per Link (#thema-…) aufklappen (function () { var KEY = 'ez-acc'; function load() { try { return JSON.parse(localStorage.getItem(KEY) || '{}') || {}; } catch (e) { return {}; } } function save(o) { try { localStorage.setItem(KEY, JSON.stringify(o)); } catch (e) {} } var state = load(); document.querySelectorAll('details.acc[data-remember]').forEach(function (d) { var k = d.getAttribute('data-remember'); if (state[k] === 1) d.open = true; if (state[k] === 0) d.open = false; d.addEventListener('toggle', function () { var s = load(); s[k] = d.open ? 1 : 0; save(s); }); }); function openHash() { if (!location.hash || location.hash.length < 2) return; var el = document.getElementById(decodeURIComponent(location.hash.slice(1))); if (!el) return; var d = el.tagName === 'DETAILS' ? el : el.closest('details'); if (d) { d.open = true; setTimeout(function () { el.scrollIntoView({ block: 'start' }); }, 0); } } openHash(); window.addEventListener('hashchange', openHash); // Formular in einem Akkordeon über einen Knopf öffnen (data-toggle) → Akkordeon mit öffnen document.addEventListener('click', function (ev) { var a = ev.target.closest('a[href^="#"]'); if (a) { var t = document.getElementById(a.getAttribute('href').slice(1)); if (t && t.tagName === 'DETAILS') t.open = true; } }); })(); // Admin: Reihenfolge per Ziehen oder ↑/↓ (Liste #order-list) (function () { var list = document.getElementById('order-list'); if (!list) return; var dragged = null; function renumber() { list.querySelectorAll('.order-item').forEach(function (li, i) { li.querySelector('.order-num').textContent = i + 1; }); list.closest('form').classList.add('is-dirty'); } list.addEventListener('dragstart', function (ev) { dragged = ev.target.closest('.order-item'); if (!dragged) return; dragged.classList.add('is-dragging'); ev.dataTransfer.effectAllowed = 'move'; try { ev.dataTransfer.setData('text/plain', ''); } catch (e) {} }); list.addEventListener('dragend', function () { if (dragged) dragged.classList.remove('is-dragging'); dragged = null; renumber(); }); list.addEventListener('dragover', function (ev) { if (!dragged) return; ev.preventDefault(); var over = ev.target.closest('.order-item'); if (!over || over === dragged) return; var r = over.getBoundingClientRect(); list.insertBefore(dragged, ev.clientY > r.top + r.height / 2 ? over.nextSibling : over); }); list.addEventListener('drop', function (ev) { ev.preventDefault(); }); list.addEventListener('click', function (ev) { var b = ev.target.closest('[data-move]'); if (!b) return; var li = b.closest('.order-item'); if (b.getAttribute('data-move') === '-1' && li.previousElementSibling) list.insertBefore(li, li.previousElementSibling); else if (b.getAttribute('data-move') === '1' && li.nextElementSibling) list.insertBefore(li.nextElementSibling, li); b.focus(); renumber(); }); window.addEventListener('beforeunload', function (ev) { var f = list.closest('form'); if (f.classList.contains('is-dirty') && !f.dataset.submitting) { ev.preventDefault(); ev.returnValue = ''; } }); list.closest('form').addEventListener('submit', function () { this.dataset.submitting = '1'; }); })(); // Hilfe: Suche über alle Themen, alle auf-/zuklappen (function () { var input = document.getElementById('help-search'); if (!input) return; var items = [].slice.call(document.querySelectorAll('.help-item')); var empty = document.getElementById('help-empty'); items.forEach(function (d) { d._text = d.textContent.toLowerCase(); }); function clearMarks(root) { root.querySelectorAll('mark.help-hit').forEach(function (m) { m.replaceWith(document.createTextNode(m.textContent)); }); root.normalize(); } function mark(root, word) { var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT), nodes = [], n; while ((n = walker.nextNode())) nodes.push(n); nodes.forEach(function (node) { var i = node.data.toLowerCase().indexOf(word); if (i < 0) return; var hit = node.splitText(i); hit.splitText(word.length); var m = document.createElement('mark'); m.className = 'help-hit'; m.textContent = hit.data; hit.replaceWith(m); }); } var t; input.addEventListener('input', function () { clearTimeout(t); t = setTimeout(function () { var q = input.value.trim().toLowerCase(), words = q.split(/\s+/).filter(Boolean), shown = 0; items.forEach(function (d) { clearMarks(d); var ok = words.every(function (w) { return d._text.indexOf(w) >= 0; }); d.classList.toggle('is-hidden', !ok); if (ok) shown++; if (q && ok) { d.open = true; words.forEach(function (w) { if (w.length > 1) mark(d, w); }); } if (!q) d.open = false; }); document.querySelectorAll('.help-section').forEach(function (s) { s.classList.toggle('is-hidden', !s.querySelector('.help-item:not(.is-hidden)')); }); empty.hidden = shown > 0; }, 120); }); document.addEventListener('click', function (ev) { var b = ev.target.closest('[data-help]'); if (!b) return; var open = b.getAttribute('data-help') === 'open'; items.forEach(function (d) { if (!d.classList.contains('is-hidden')) d.open = open; }); }); })(); // Foto in voller Größe (Knopf .photo-zoom oder Klick aufs Foto) (function () { var dlg = null; function open(src, caption) { if (!dlg) { dlg = document.createElement('dialog'); dlg.className = 'lightbox'; dlg.innerHTML = '
'; document.body.appendChild(dlg); dlg.addEventListener('click', function (ev) { if (ev.target === dlg || ev.target.closest('.lightbox-close') || ev.target.tagName === 'IMG') dlg.close(); }); } dlg.querySelector('img').src = src; dlg.querySelector('img').alt = caption || ''; dlg.querySelector('figcaption').textContent = caption || ''; if (dlg.showModal) dlg.showModal(); else window.open(src, '_blank'); } document.addEventListener('click', function (ev) { var b = ev.target.closest('[data-lightbox]'); if (!b) { var img = ev.target.closest('.project-photo img'); if (img) b = img.parentNode.querySelector('[data-lightbox]'); } if (!b) return; ev.preventDefault(); open(b.getAttribute('data-lightbox'), b.getAttribute('data-caption')); }); })(); // Projektvorschau: mit den Pfeiltasten ← → blättern document.addEventListener('keydown', function (ev) { if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey) return; if (ev.key !== 'ArrowLeft' && ev.key !== 'ArrowRight') return; var t = ev.target; if (t.closest && t.closest('input, textarea, select, [contenteditable], video, dialog')) return; var a = document.querySelector('.market-nav a[rel="' + (ev.key === 'ArrowLeft' ? 'prev' : 'next') + '"]'); if (a) location.href = a.href; }); // Terminfindung: Tag im Kalender anklicken → Datum ins Formular übernehmen document.addEventListener('click', function (ev) { var a = ev.target.closest('.meet-grid a[data-day]'); if (!a) return; var input = document.getElementById('md'); if (!input) return; ev.preventDefault(); input.value = a.getAttribute('data-day'); document.querySelectorAll('.meet-grid td.is-selected').forEach(function (td) { td.classList.remove('is-selected'); }); a.parentNode.classList.add('is-selected'); var from = document.getElementById('mf'); document.getElementById('termin-neu').scrollIntoView({ block: 'nearest', behavior: 'smooth' }); if (from) from.focus(); }); // Erinnerung: Tage-Feld nur bei „ein“ (function () { function sync(f) { var s = f.querySelector('select[name=mode]'), d = f.querySelector('input[name=days]'); if (s && d) d.disabled = s.value === 'aus'; } document.querySelectorAll('.remind-form').forEach(sync); document.addEventListener('change', function (ev) { var f = ev.target.closest('.remind-form'); if (f) sync(f); }); })(); // Bewerbung beantworten: Bestätigung beim Ablehnen, Text bei Rückfrage nötig document.addEventListener('click', function (ev) { var b = ev.target.closest('button[data-confirm-btn], button[data-needs-text]'); if (!b) return; var f = b.form, ta = f && f.querySelector('textarea[name=reply]'); if (b.hasAttribute('data-needs-text') && ta && ta.value.trim() === '') { ev.preventDefault(); window.alert(b.getAttribute('data-needs-text')); ta.focus(); return; } if (b.hasAttribute('data-confirm-btn') && !window.confirm(b.getAttribute('data-confirm-btn'))) ev.preventDefault(); }); // Auswahlfelder, die sofort speichern (z. B. Foto-Darstellung im Admin-Bereich) document.addEventListener('change', function (e) { var el = e.target; if (el && el.matches && el.matches('select[data-autosubmit]') && el.form) { if (el.form.requestSubmit) el.form.requestSubmit(); else el.form.submit(); } }); // Vorschaubilder, die nicht erzeugt werden konnten (z. B. beschädigte PDF), ausblenden document.addEventListener('error', function (e) { var el = e.target; if (el && el.tagName === 'IMG' && el.hasAttribute('data-hide-broken')) { var box = el.closest('.doc-thumb') || el; box.style.display = 'none'; } }, true); // Aushang drucken: PDF im Vorschaufenster drucken, sonst in neuem Tab öffnen document.addEventListener('click', function (e) { var b = e.target.closest && e.target.closest('[data-print-frame]'); if (!b) return; var f = document.getElementById(b.getAttribute('data-print-frame')); var small = window.matchMedia && window.matchMedia('(max-width: 700px)').matches; try { if (!f || small) throw new Error('kein Rahmen'); f.contentWindow.focus(); f.contentWindow.print(); } catch (err) { window.open(b.getAttribute('data-print-url'), '_blank'); } });