// 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');
}
});