PageSourceSearch

https://broadbandswitch.uk/assets/js/router-search.js

js broadbandswitch.uk collected 2026-10-04 08:38:43 UTC 3,334 bytes, 87 lines download raw bytes

1/*
2 * BroadbandSwitch.uk - Router Help Hub search
3 * Progressive enhancement: loads the JSON index and filters as the user types.
4 * No external dependencies.  Degrades gracefully if fetch fails.
5 *
6 * Cursor: adjust INDEX_URL to the deployed path of data/router-search-index.json.
7 */
8(function () {
9  "use strict";
10  var INDEX_URL = "/data/router-search-index.json"; // <-- Cursor: set to deployed path
11  var input = document.getElementById("router-search-input");
12  var results = document.getElementById("router-search-results");
13  if (!input || !results) return;
14
15  var data = [];
16  var ready = false;
17
18  fetch(INDEX_URL)
19    .then(function (r) { return r.ok ? r.json() : Promise.reject(r.status); })
20    .then(function (json) { data = json; ready = true; })
21    .catch(function () {
22      var hint = document.getElementById("search-hint");
23      if (hint) hint.textContent = "Search is unavailable right now.  Please browse by provider and brand below.";
24    });
25
26  function score(item, q) {
27    var hay = (item.title + " " + (item.keywords || []).join(" ")).toLowerCase();
28    if (hay.indexOf(q) === -1) return 0;
29    var s = 0;
30    if (item.title.toLowerCase().indexOf(q) === 0) s += 100;        // title starts with query
31    else if (item.title.toLowerCase().indexOf(q) !== -1) s += 60;   // title contains
32    (item.keywords || []).forEach(function (k) {
33      if (k.toLowerCase() === q) s += 40;
34      else if (k.toLowerCase().indexOf(q) === 0) s += 20;
35    });
36    // prefer guide pages slightly over individual models for broad queries
37    if (item.type && item.type.indexOf("guide") !== -1) s += 5;
38    return s;
39  }
40
41  function render(items, q) {
42    results.innerHTML = "";
43    if (!items.length) {
44      var li = document.createElement("li");
45      li.className = "search-empty";
46      li.textContent = 'No match for "' + q + '".  Try your ISP, brand or model number.';
47      results.appendChild(li);
48      return;
49    }
50    items.slice(0, 8).forEach(function (item) {
51      var li = document.createElement("li");
52      li.className = "search-result";
53      li.setAttribute("role", "option");
54      var ip = item.login_ip && item.login_ip.indexOf("Not") === -1 ? item.login_ip : "";
55      li.innerHTML =
56        '<a href="' + item.url + '">' +
57        '<span class="sr-title">' + item.title + '</span>' +
58        (ip ? '<span class="sr-ip"><code>' + ip.split(" ")[0] + '</code></span>' : '') +
59        '<span class="sr-type">' + (item.type || '') + '</span>' +
60        '</a>';
61      results.appendChild(li);
62    });
63  }
64
65  var t;
66  input.addEventListener("input", function () {
67    clearTimeout(t);
68    t = setTimeout(function () {
69      var q = input.value.trim().toLowerCase();
70      if (!ready || q.length < 2) { results.innerHTML = ""; return; }
71      var scored = data
72        .map(function (item) { return { item: item, s: score(item, q) }; })
73        .filter(function (x) { return x.s > 0; })
74        .sort(function (a, b) { return b.s - a.s; })
75        .map(function (x) { return x.item; });
76      render(scored, q);
77    }, 120);
78  });
79
80  // keyboard: Enter jumps to top result
81  input.addEventListener("keydown", function (e) {
82    if (e.key === "Enter") {
83      var first = results.querySelector(".search-result a");
84      if (first) { window.location.href = first.getAttribute("href"); }
85    }
86  });
87})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.