blob: 02f18140e0d2d31da8cd3d74f19db95ef3a4f8c9 (
plain) (
tree)
|
|
// Copyright (c) 2021 Johannes Stoelp
// file: search.js
const formatResultPreview = (body, terms) => {
let body_idx = 0;
// Collect indexes into `body` that match any term.
let matches = body.toLowerCase().split(" ").map(word => {
let idx = body_idx;
// Update global index into body.
body_idx += word.length + 1 /* for the ' ' split char */;
if (terms.some(term => { return word.startsWith(term); })) {
return { idx : idx, end : idx + word.length };
} else {
return null;
}
}).filter(match => { return match !== null; });
// Format preview, by making each term bold and adding a short slice
// following after the term.
let preview = [];
matches.forEach(match => {
let end = Math.min(match.end + 40, body.length - 1);
preview.push(`<b>${body.substring(match.idx, match.end)}</b>`
+ body.substring(match.end, end)
+ "...");
});
return preview.join(" ");
}
const formatResult = (result, terms) => {
return "<div>"
+ `<a href="${result.ref}">${result.doc.title}</a>`
+ `<div>${formatResultPreview(result.doc.body, terms)}</div>`
+ "</div>";
}
const evaluteSearch = (ctx) => {
let term = ctx.input.value.trim();
// Nothing todo if current search term is the same as last one.
if (term === ctx.last_search) {
return;
}
// Empty search term, show list of blog pages again.
if (term === "") {
ctx.pages.style.display = "block";
ctx.results.style.display = "none";
return;
}
// Search term entered, show result list and hide list with blog posts.
ctx.pages.style.display = "none";
ctx.results.style.display = "block";
// Perform actual search.
let results = ctx.search_index.search(term, {
bool: "AND",
expand: true, // Also match if words starts with search query.
fields: {
title: {boost: 2},
body : {boost: 1},
}
});
if (results.length !== 0) {
// Update last search term.
ctx.last_search = term;
// Reset HTML of results items (previous search results).
ctx.results_items.innerHTML = "";
// Generate HTML for search results.
results.forEach(result => {
let item = document.createElement("li");
item.innerHTML = formatResult(result, term.split(" "));
ctx.results_items.appendChild(item);
});
}
};
const debounce = (func, wait) => {
let timeout = null;
return () => {
clearTimeout(timeout);
timeout = setTimeout(func , wait);
};
}
window.onload = () => {
let ctx = new class {
constructor() {
// Get HTML DOM elements.
this.input = document.getElementById("search");
this.results = document.getElementById("search-results");
this.results_items = document.getElementById("search-results-items");
this.pages = document.getElementById("pages");
// Load search index.
this.search_index = elasticlunr.Index.load(window.searchIndex);
// Last search term.
this.last_search = "";
}
};
if (ctx.search_index) {
ctx.input.onkeyup = debounce(() => { evaluteSearch(ctx); }, 200);
}
}
|