munin/static/similar.js
2026-10-19 10:13:56 +02:00

96 lines
3.6 KiB
JavaScript

// „Gibt es schon?" — zeigt beim Anlegen eines Eintrags vorhandene Einträge mit
// ähnlichem Titel an (AUSBAUSTUFEN.md 5.7).
//
// Agenten bekommen bei "remember" eine Rückfrage, wenn es schon einen
// ähnlichen Eintrag gibt. Menschen im Formular bekommen denselben Hinweis,
// nur ohne Sperre: Die Liste erscheint unter dem Titel, speichern lässt sich
// trotzdem. Geprüft wird auf dem Server mit derselben Logik wie bei MCP
// (internal/duplicates), damit beide Wege gleich urteilen.
//
// Gefragt wird erst, wenn eine Weile nicht getippt wurde (delay), nicht
// bei jedem Buchstaben. Antworten, die zu spät für den aktuellen Titel
// kommen, werden verworfen.
(function () {
"use strict";
var titleInput = document.querySelector("input[data-similar-url]");
var box = document.querySelector("[data-similar-list]");
if (!titleInput || !box) {
return;
}
var list = box.querySelector("ul");
var slugField = document.querySelector("input[name=slug]");
var url = titleInput.getAttribute("data-similar-url");
// Die Adresse eines Eintrags: aus der Such-URL ".../entries/similar" wird
// ".../e/<slug>".
var entryBase = url.replace(/entries\/similar$/, "e/");
var delay = 400; // Millisekunden ohne Tippen, bevor gefragt wird
var timeoutId = null;
var lastQuery = "";
function show(candidates) {
list.textContent = "";
candidates.forEach(function (k) {
var li = document.createElement("li");
var a = document.createElement("a");
a.href = entryBase + encodeURIComponent(k.slug);
a.target = "_blank";
a.rel = "noopener";
a.textContent = k.title;
li.appendChild(a);
// Die gemeinsamen Wörter zeigen, warum der Eintrag hier steht.
// Ist es nur ein allgemeines Wort, sieht man sofort: anderes Thema.
if (k.shared && k.shared.length) {
var reason = document.createElement("span");
reason.className = "help";
reason.textContent = " — gemeinsam: " + k.shared.join(", ");
li.appendChild(reason);
}
list.appendChild(li);
});
box.hidden = candidates.length === 0;
}
function ask() {
var t = titleInput.value.trim();
var s = slugField ? slugField.value.trim() : "";
var cacheKey = t + "\u0000" + s;
if (cacheKey === lastQuery) {
return;
}
lastQuery = cacheKey;
if (t.length < 3) {
show([]);
return;
}
var address = url + "?title=" + encodeURIComponent(t) + "&slug=" + encodeURIComponent(s);
fetch(address, { credentials: "same-origin", headers: { "Accept": "application/json" } })
.then(function (answer) { return answer.ok ? answer.json() : []; })
.then(function (candidates) {
// Inzwischen weitergetippt? Dann gehört die Antwort zu einem
// alten Titel.
if (cacheKey === lastQuery) {
show(candidates || []);
}
})
.catch(function () { /* nur ein Hinweis — ohne ihn geht es auch */ });
}
function askLater() {
clearTimeout(timeoutId);
timeoutId = setTimeout(ask, delay);
}
titleInput.addEventListener("input", askLater);
if (slugField) {
slugField.addEventListener("input", askLater);
}
// Vorbelegter Titel (z.B. nach einem Fehler zurück im Formular).
if (titleInput.value.trim() !== "") {
ask();
}
})();