Recently Written · git

sbm-sync

Sync server for sbm bookmark files: one small Go program, plain files, AGPL

git clone https://github.com/equwal/sbm-sync

Log | Files | Refs


search.js (2309 bytes)

1 // Live search for the bookmark pages of sbm Sync: the results change while
2 // you type, and Enter opens the first result, as in the sbm add-on.
3 // Ctrl+Enter opens it in a new tab. The server does the search: this script
4 // gets the page of the search and shows its results. Without this script the
5 // form still works.
6 "use strict";
7 
8 (() => {
9   const form = document.getElementById("search");
10   const results = document.getElementById("results");
11   if (!form || !results) return;
12   const box = form.elements.q;
13   const clears = results.dataset.empty === "clear"; // no results for no query
14   let timer = null; // the update that waits for a pause in the typing
15   let running = Promise.resolve(); // the newest update
16   let newest = 0; // the number of the newest update
17 
18   // update shows the results that the server gives for the form.
19   async function update() {
20     const n = ++newest;
21     const query = new URLSearchParams(new FormData(form)).toString();
22     let fresh = null;
23     if (!clears || box.value.trim() !== "") {
24       try {
25         const resp = await fetch("/bookmarks?" + query);
26         if (!resp.ok) return;
27         const page = new DOMParser().parseFromString(await resp.text(), "text/html");
28         fresh = page.getElementById("results");
29       } catch {
30         return;
31       }
32       if (fresh === null) return; // not signed in any more
33     }
34     if (n !== newest) return; // a newer update is on its way
35     results.replaceChildren(...(fresh === null ? [] : fresh.childNodes));
36     if (!clears) history.replaceState(null, "", "?" + query);
37   }
38 
39   function later() {
40     clearTimeout(timer);
41     timer = setTimeout(() => {
42       timer = null;
43       running = update();
44     }, 150);
45   }
46 
47   box.addEventListener("input", later);
48   form.addEventListener("change", (e) => {
49     if (e.target !== box) later();
50   });
51   box.addEventListener("keydown", async (e) => {
52     if (e.key !== "Enter" || e.isComposing) return;
53     e.preventDefault();
54     if (timer !== null) {
55       clearTimeout(timer);
56       timer = null;
57       running = update();
58     }
59     await running;
60     const first = results.querySelector("a.open");
61     if (first === null) form.submit();
62     else if (e.ctrlKey || e.metaKey) window.open(first.href, "_blank", "noopener");
63     else location.assign(first.href);
64   });
65 })();