/* Zoeken in de bovenbalk: live resultaten (search_live) onder het veld; Enter opent de volledige zoekpagina.
* Pijltjes lopen door de resultaten, Escape sluit, "/" springt naar het zoekveld. */
(() => {
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
document.querySelectorAll('form.tsearch').forEach((form) => {
const input = form.querySelector('input[name=q]');
const box = form.querySelector('.tsearch-results');
let timer = null, ctrl = null, last = '';
const items = () => [...box.querySelectorAll('a')];
const close = () => { box.classList.remove('open'); input.setAttribute('aria-expanded', 'false'); };
const open = () => { if (box.innerHTML) { box.classList.add('open'); input.setAttribute('aria-expanded', 'true'); } };
const render = (data) => {
box.innerHTML = data.groups.length
? data.groups.map((g) => `
${esc(g.label)}
` + g.items.map((i) =>
`${esc(i.title)}${i.sub ? `${esc(i.sub)}` : ''}`).join('')).join('')
+ `Alle resultaten voor “${esc(data.q)}”`
: `Niets gevonden voor “${esc(data.q)}”.
`;
open();
};
input.addEventListener('input', () => {
clearTimeout(timer);
const v = input.value.trim();
if (v.length < 2) { box.innerHTML = ''; close(); return; }
timer = setTimeout(() => {
if (v === last && box.innerHTML) { open(); return; }
if (ctrl) ctrl.abort();
ctrl = new AbortController();
fetch(`${form.dataset.live}?q=${encodeURIComponent(v)}`, { signal: ctrl.signal, headers: { Accept: 'application/json' } })
.then((r) => (r.ok ? r.json() : null))
.then((d) => { if (d && input.value.trim() === v) { last = v; render(d); } })
.catch(() => {});
}, 180);
});
form.addEventListener('keydown', (e) => {
const list = items(), at = list.indexOf(document.activeElement);
if (e.key === 'ArrowDown' && list.length) { e.preventDefault(); open(); list[Math.min(at + 1, list.length - 1)].focus(); }
else if (e.key === 'ArrowUp' && list.length) { e.preventDefault(); (at <= 0 ? input : list[at - 1]).focus(); }
else if (e.key === 'Escape') { close(); input.focus(); }
});
input.addEventListener('focus', () => { if (input.value.trim().length >= 2) open(); });
document.addEventListener('click', (e) => { if (!form.contains(e.target)) close(); });
// mobiel: het veld krijgt de focus zodra het uitklapmenu opengaat
const toggle = form.closest('.dropdown')?.querySelector('[data-bs-toggle="dropdown"]');
if (toggle) toggle.addEventListener('shown.bs.dropdown', () => input.focus());
});
document.addEventListener('keydown', (e) => {
if (e.key !== '/' || e.ctrlKey || e.metaKey || e.altKey || e.target.closest('input, textarea, select, [contenteditable]')) return;
const input = [...document.querySelectorAll('form.tsearch input[name=q]')].find((i) => i.offsetParent);
if (input) { e.preventDefault(); input.focus(); input.select(); }
});
})();