Files
website/themes/hugo-theme-monochrome/assets/js/search.js
Simon Gardling 032679b34a setup
2023-10-11 17:38:05 -04:00

144 lines
4.9 KiB
JavaScript

import * as params from '@params';
import Fuse from '../lib/js/fuse-v6.6.2.min.js';
async function init() {
const data = fetch(params.index_url);
const search_btn = document.getElementById("search_btn");
const search_menu_wrapper = document.getElementById("search_menu_wrapper");
const search_menu_close_btn = document.getElementById("search_menu_close_btn");
const search_menu_input = document.getElementById("search_menu_input");
const search_menu_results = document.getElementById("search_menu_results");
search_btn.addEventListener("click", function() {
search_menu_wrapper.classList.remove("hidden");
search_menu_input.focus();
});
search_menu_close_btn.addEventListener("click", function() {
search_menu_wrapper.classList.add("hidden");
});
const index_json = await (await data).json();
const options = {
distance: 100,
threshold: 0.3,
ignoreLocation: true,
minMatchCharLength: 2,
keys: [
'title',
'permalink',
'content',
],
includeMatches: true,
};
const fuse = new Fuse(index_json, options);
const createItem = (title, permalink, content) => {
return `<a href="${permalink}">
<div class="search-menu-result-item">
<div class="search-menu-result-item-title">${title}</div>
<div class="search-menu-result-item-content">${content}</div>
</div>
</a>`;
};
const hlItem = (item, matches) => {
const highlightTitle = (text, match) => {
let textHl = "", ptr = 0;
match.forEach(idx => {
if (ptr < idx[0]) {
textHl += text.substring(ptr, idx[0]);
}
textHl += "<mark>" + text.substring(idx[0], idx[1] + 1) + "</mark>";
ptr = idx[1] + 1;
})
textHl += text.substring(ptr, text.length);
return textHl;
};
const highlightContent = (text, match) => {
let textHl = "<mark>" + text.substring(match[0][0], match[0][1] + 1) + "</mark>";
let ptr = match[0][1] + 1;
let length = match[0][1] + 1 - match[0][0];
if (match[0][0] > 0) {
textHl = "..." + textHl;
}
for (let i = 1; i < match.length; i++) {
const idx = match[i];
if (ptr < idx[0] && (length + idx[0] - ptr) >= 100) {
textHl += text.substring(ptr, ptr + (100 - length + 1)) + "...";
length = 100;
break;
}
if (ptr < idx[0]) {
textHl += text.substring(ptr, idx[0]);
length += idx[0] - ptr;
}
textHl += "<mark>" + text.substring(idx[0], idx[1] + 1) + "</mark>";
length += idx[1] + 1 - idx[0];
ptr = idx[1] + 1;
if (length >= 100) {
break;
}
}
if (length < 100) {
textHl += text.substring(ptr, ptr + (100 - length));
}
if (ptr <= text.length) {
textHl += "...";
}
return textHl;
}
let itemHl = {
title: undefined,
permalink: item.permalink,
content: undefined,
};
matches.forEach(match => {
if (match.key == 'title') {
itemHl.title = highlightTitle(item.title, match.indices);
} else if (match.key == 'content') {
itemHl.content = highlightContent(item.content, match.indices);
}
});
if (itemHl.title === undefined) itemHl.title = item.title;
if (itemHl.content === undefined) itemHl.content = item.content;
return itemHl;
}
const buildAllItems = () => {
search_menu_results.innerHTML = index_json.reduce((acc, curr) => {
let content = (curr.content.length > 100) ? curr.content.substring(0, 100) + "..." : curr.content;
return acc + createItem(curr.title, curr.permalink, content);
}, "");
};
const search = (value) => {
const results = fuse.search(value);
if (results.length == 0) {
search_menu_results.innerHTML = '';
} else {
search_menu_results.innerHTML = results.reduce((acc, curr) => {
const item = hlItem(curr.item, curr.matches);
return acc + createItem(item.title, item.permalink, item.content);
}, "")
}
};
search_menu_input.addEventListener("input", function() {
if (this.value === '') {
buildAllItems();
} else {
search(this.value.trim());
}
});
buildAllItems();
}
window.addEventListener("DOMContentLoaded", init);