www/scripts/fuzzy.js

144 lines
4.6 KiB
JavaScript
Raw Normal View History

2021-10-01 05:23:35 +00:00
(() => {
2021-09-24 04:08:37 +00:00
let searchField = document.querySelector("#search");
2021-10-01 05:23:35 +00:00
if (searchField === null)
return;
2021-09-24 04:08:37 +00:00
let client = new XMLHttpRequest();
client.open("GET", "/pages.json");
client.onreadystatechange = () => {
if (client.readyState === 4)
fuzzyInit(client.responseText);
2021-06-03 01:57:01 +00:00
}
client.send();
2021-09-24 04:08:37 +00:00
searchField.focus();
2021-10-01 05:23:35 +00:00
})();
2021-06-03 01:57:01 +00:00
function fuzzyInit(pagesFileName) {
if (pagesFileName == "")
2021-06-03 01:57:01 +00:00
return;
2021-09-24 04:08:37 +00:00
let searchField = document.querySelector("#search");
let resultBlock = document.querySelector("#results");
if (searchField === null || resultBlock === null)
return;
2021-06-03 01:57:01 +00:00
var pages;
try {
pages = JSON.parse(pagesFileName);
2021-06-03 01:57:01 +00:00
} catch (e) {
console.error(e);
document.body.innerHTML = e.message;
2021-06-03 01:57:01 +00:00
return;
}
2021-10-01 06:03:22 +00:00
pages.sort((x, y) => {return ('' + x.title).localeCompare(y.title)});
2021-06-03 01:57:01 +00:00
2021-09-24 04:08:37 +00:00
searchField.addEventListener("keyup", (e) => {
2021-10-01 05:23:35 +00:00
let searchValue = searchField.value ? searchField.value.toLowerCase() : "";
2021-09-24 04:08:37 +00:00
2021-10-01 05:23:35 +00:00
if (e.code === "Enter") {
if (resultBlock.childNodes === null
|| resultBlock.childNodes[0] === null
|| resultBlock.childNodes[0].href === undefined)
2021-09-24 04:08:37 +00:00
return;
2021-10-01 05:23:35 +00:00
window.location = resultBlock.childNodes[0].href;
return;
}
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
// help
if (searchValue === "?" || searchValue === "help") {
resultBlock.innerHTML = "<h2>Help</h2>Enter a page or directory name.<br>If do not know any, clear the search field to list everything.<br> Using the <code>Enter</code> key would take you to the first page in list, if the list is not empty.<br>Alternatively, use the <code>Up</code> and <code>Down</code> arrow keys to select the page you want and use the <code>Enter</code> key.<br>Use <code>Backspace</code> to go back to the search."
return;
}
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
let results = [];
for (const [i, page] of pages.entries()) {
ret = fuzzySearch(page.title, searchValue);
if (ret === null)
continue;
results.push({formatted: ret.formatted, link: page.link, score: ret.score});
}
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
results.sort((x, y) => {return x.score - y.score});
2021-06-03 01:57:01 +00:00
2021-10-01 05:23:35 +00:00
resultBlock.innerHTML = "";
for (const res of results) {
linkBlock = document.createElement("a");
linkBlock.classList.add("hyperlink");
linkBlock.href = res.link;
linkBlock.innerHTML = `<div class="name">${res.formatted}</div><div class="link">${res.link}</div>`;
resultBlock.appendChild(linkBlock);
2021-06-03 01:57:01 +00:00
}
2021-10-01 05:23:35 +00:00
if (results.length <= 0)
resultBlock.innerHTML = "Unknown command or no matching pages found."
});
2021-09-24 04:08:37 +00:00
document.body.addEventListener("keydown", (e) => {
2021-10-01 05:23:35 +00:00
if (e.code === "Backspace") {
searchField.focus();
}
2021-09-24 04:08:37 +00:00
if (e.code === "ArrowDown" || e.code === "ArrowUp") {
if (resultBlock.childNodes === null)
return;
resultNodes = resultBlock.childNodes;
if (resultNodes.length <= 1)
return;
let currNode = document.activeElement;
if (searchField === currNode) {
e.preventDefault();
2021-09-24 04:08:37 +00:00
if (e.code === "ArrowDown")
resultNodes[0].focus();
else
resultNodes[resultNodes.length - 1].focus();
return;
}
if (Array.from(resultNodes).indexOf(currNode) < 0)
return;
e.preventDefault();
2021-09-24 04:08:37 +00:00
if (e.code === "ArrowDown")
if (currNode.nextElementSibling === null)
searchField.focus();
else
currNode.nextElementSibling.focus();
else if (e.code === "ArrowUp")
if (currNode.previousElementSibling === null)
searchField.focus();
else
currNode.previousElementSibling.focus();
return;
}
});
2021-06-03 01:57:01 +00:00
}
2021-09-24 04:08:37 +00:00
function fuzzySearch(findIn, find) {
let search = find.replace(/\s/g, "");
2021-06-03 01:57:01 +00:00
search = search.toLowerCase();
2021-09-24 04:08:37 +00:00
let tokens = findIn.split('');
2021-06-03 01:57:01 +00:00
let pc = 0;
let score = 0;
for (const [i, ch] of tokens.entries()) {
2021-09-24 04:08:37 +00:00
if (ch.toLowerCase() === search[pc]) {
2021-06-03 01:57:01 +00:00
score += i - pc;
tokens[i] = `<span class="highlight">${ch}</span>`;
pc++;
if (search.length < pc)
return null;
2021-06-03 01:57:01 +00:00
}
}
2021-09-24 04:08:37 +00:00
if (search.length === pc)
return {formatted: tokens.join(''), score: (score / search.length)};
2021-06-03 01:57:01 +00:00
2021-09-24 04:08:37 +00:00
return null;
2021-06-03 01:57:01 +00:00
}