const $ = (id) => document.getElementById(id); const store = chrome.storage.sync; let links = []; let editing = false; let editId = null; const uid = () => Math.random().toString(36).slice(2, 10); function normalizeUrl(raw) { raw = raw.trim(); if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) raw = "http://" + raw; try { return new URL(raw).href; } catch { return null; } } async function load() { const data = await store.get("links"); links = data.links || []; render(); } const save = () => store.set({ links }); function render() { const q = $("search").value.trim().toLowerCase(); const list = $("list"); list.textContent = ""; const shown = links.filter((l) => !q || (l.name + " " + l.url).toLowerCase().includes(q)); for (const l of shown) { const li = document.createElement("li"); const a = document.createElement("a"); a.href = l.url; a.target = "_blank"; a.rel = "noopener"; a.innerHTML = ''; a.querySelector(".ico").textContent = l.icon || "▣"; a.querySelector(".name").textContent = l.name; a.querySelector(".url").textContent = l.url.replace(/^https?:\/\//, "").replace(/\/$/, ""); if (editing) a.addEventListener("click", (e) => e.preventDefault()); li.append(a); const eb = document.createElement("div"); eb.className = "edit-btns"; eb.append( btn("[✎]", () => openForm(l)), btn("[↑]", () => move(l.id, -1)), btn("[↓]", () => move(l.id, 1)), Object.assign(btn("[✕]", () => remove(l.id)), { className: "del" }) ); li.append(eb); list.append(li); } $("empty").hidden = links.length > 0 || !!editId; $("count").textContent = links.length ? `${links.length} Einträge` : ""; document.body.classList.toggle("editing", editing); for (const id of ["add", "export", "import"]) $(id).hidden = !editing; $("editToggle").textContent = editing ? "[FERTIG]" : "[EDIT]"; } function btn(label, fn) { const b = document.createElement("button"); b.textContent = label; b.addEventListener("click", fn); return b; } async function move(id, dir) { const i = links.findIndex((l) => l.id === id); const j = i + dir; if (j < 0 || j >= links.length) return; [links[i], links[j]] = [links[j], links[i]]; await save(); render(); } async function remove(id) { links = links.filter((l) => l.id !== id); await save(); render(); } function openForm(link) { editId = link ? link.id : "new"; $("fIcon").value = link?.icon || ""; $("fName").value = link?.name || ""; $("fUrl").value = link?.url || ""; $("form").hidden = false; $("fName").focus(); } function closeForm() { editId = null; $("form").hidden = true; render(); } $("form").addEventListener("submit", async (e) => { e.preventDefault(); const url = normalizeUrl($("fUrl").value); if (!url) { $("fUrl").focus(); return; } const entry = { name: $("fName").value.trim(), url, icon: $("fIcon").value.trim() }; if (editId === "new") links.push({ id: uid(), ...entry }); else Object.assign(links.find((l) => l.id === editId), entry); await save(); closeForm(); }); $("cancel").addEventListener("click", closeForm); $("add").addEventListener("click", () => openForm()); $("search").addEventListener("input", render); $("editToggle").addEventListener("click", () => { editing = !editing; if (!editing) { editId = null; $("form").hidden = true; } render(); }); // Enter im Suchfeld öffnet den ersten Treffer $("search").addEventListener("keydown", (e) => { if (e.key === "Enter" && !editing) $("list").querySelector("a")?.click(); }); $("export").addEventListener("click", () => { const blob = new Blob([JSON.stringify(links, null, 2)], { type: "application/json" }); const a = Object.assign(document.createElement("a"), { href: URL.createObjectURL(blob), download: "lablinks.json" }); a.click(); URL.revokeObjectURL(a.href); }); $("import").addEventListener("click", () => $("file").click()); $("file").addEventListener("change", async (e) => { const file = e.target.files[0]; if (!file) return; try { const arr = JSON.parse(await file.text()); links = arr .filter((x) => x && x.name && x.url) .map((x) => ({ id: uid(), name: String(x.name), url: normalizeUrl(String(x.url)) || String(x.url), icon: x.icon || "" })); await save(); render(); } catch { alert("Ungültige JSON-Datei."); } e.target.value = ""; }); load(); $("search").focus();