61 lines
2.5 KiB
JavaScript
61 lines
2.5 KiB
JavaScript
(() => {
|
|
const modal = document.getElementById('globalSearchModal');
|
|
const input = document.getElementById('globalSearchInput');
|
|
const results = document.getElementById('globalSearchResults');
|
|
if (!modal || !input || !results) return;
|
|
|
|
const labels = modal.dataset;
|
|
let groups = {};
|
|
try { groups = JSON.parse(labels.groups || '{}'); } catch {}
|
|
let timer;
|
|
let request;
|
|
const escapeText = value => {
|
|
const node = document.createElement('span');
|
|
node.textContent = String(value ?? '');
|
|
return node.innerHTML;
|
|
};
|
|
const notice = (message, danger = false) =>
|
|
'<div class="' + (danger ? 'text-danger' : 'text-muted') + ' text-center py-4">' + escapeText(message) + '</div>';
|
|
|
|
document.addEventListener('keydown', event => {
|
|
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'k') {
|
|
event.preventDefault();
|
|
bootstrap.Modal.getOrCreateInstance(modal).show();
|
|
}
|
|
});
|
|
modal.addEventListener('shown.bs.modal', () => input.focus());
|
|
input.addEventListener('input', () => {
|
|
clearTimeout(timer);
|
|
const query = input.value.trim();
|
|
if (query.length < 2) {
|
|
results.innerHTML = notice(labels.hint);
|
|
return;
|
|
}
|
|
timer = setTimeout(async () => {
|
|
request?.abort();
|
|
request = new AbortController();
|
|
results.innerHTML = notice(labels.loading);
|
|
try {
|
|
const response = await fetch('/search.json?q=' + encodeURIComponent(query), {
|
|
signal: request.signal,
|
|
headers: {Accept: 'application/json'}
|
|
});
|
|
if (!response.ok) throw new Error('Search failed');
|
|
const data = await response.json();
|
|
const entries = Object.entries(data.groups || {});
|
|
if (!entries.length) {
|
|
results.innerHTML = notice(labels.none);
|
|
return;
|
|
}
|
|
results.innerHTML = entries.map(([group, items]) =>
|
|
'<div class="mb-3"><div class="subheader mb-1">' + escapeText(groups[group] || group) + '</div>' +
|
|
items.map(item => '<a class="list-group-item list-group-item-action" href="' + escapeText(item.url) + '"><strong>' + escapeText(item.title) + '</strong><div class="text-muted small">' + escapeText(item.detail) + '</div></a>').join('') +
|
|
'</div>'
|
|
).join('') + '<a class="btn btn-outline-primary" href="/search?q=' + encodeURIComponent(query) + '">' + escapeText(labels.allResults) + '</a>';
|
|
} catch (error) {
|
|
if (error.name !== 'AbortError') results.innerHTML = notice(labels.error, true);
|
|
}
|
|
}, 220);
|
|
});
|
|
})();
|
|
|