InkSoul/themes/meme_cdn/assets/js/lunr-search.js

202 lines
6.9 KiB
JavaScript

window.addEventListener("DOMContentLoaded", event => {
let index = null;
let lookup = null;
let queuedTerm = null;
let queuedDoNotAddState = false;
let origContent = null;
const form = document.getElementById("search");
const input = document.getElementById("search-input");
form.addEventListener("submit", function(event) {
event.preventDefault();
let term = input.value.trim();
if (!term) {
return;
}
startSearch(term, false);
}, false);
if (history.state && history.state.type == "search") {
startSearch(history.state.term, true);
}
window.addEventListener("popstate", function(event) {
if (event.state && event.state.type == "search") {
startSearch(event.state.term, true);
}
else if (!event.state && origContent) {
let target = document.querySelector(".main-inner");
while (target.firstChild) {
target.removeChild(target.firstChild);
}
for (let node of origContent) {
target.appendChild(node);
}
origContent = null;
}
}, false);
function startSearch(term, doNotAddState) {
input.value = term;
form.setAttribute("data-running", "true");
if (index) {
search(term, doNotAddState);
}
else if (queuedTerm) {
queuedTerm = term;
queuedDoNotAddState = doNotAddState;
}
else {
queuedTerm = term;
queuedDoNotAddState = doNotAddState;
initIndex();
}
}
function searchDone() {
form.removeAttribute("data-running");
// A magic trick to make search field loses focus on mobile,
// which prevents the virtual keyboard from popping up.
const header = document.querySelector('.header');
if (header && header.classList.contains('fade')) {
input.blur();
}
queuedTerm = null;
queuedDoNotAddState = false;
}
function initIndex() {
let request = new XMLHttpRequest();
request.open("GET", "{{ partial "utils/relative-url.html" (dict "$" . "filename" (((.Site.GetPage "").OutputFormats.Get "SearchIndex").RelPermalink | strings.TrimPrefix "/")) }}");
request.responseType = "json";
request.addEventListener("load", function(event) {
let documents = request.response;
if (!documents)
{
console.error("Search index could not be downloaded, was it generated?");
searchDone();
return;
}
lookup = {};
index = lunr(function() {
const language = "{{ .Site.Language.Lang }}";
if (language != "en" && lunr.hasOwnProperty(language)) {
this.use(lunr[language]);
}
this.ref("uri");
this.field("title");
this.field("subtitle");
this.field("content");
this.field("description");
this.field("categories");
this.field("tags");
for (let document of documents) {
this.add(document);
lookup[document.uri] = document;
}
});
search(queuedTerm, queuedDoNotAddState);
}, false);
request.addEventListener("error", searchDone, false);
request.send(null);
}
function search(term, doNotAddState) {
try {
let results = index.search(term);
let target = document.querySelector(".main-inner") || document.querySelector("main.home");
let replaced = [];
while (target.firstChild) {
replaced.push(target.firstChild);
target.removeChild(target.firstChild);
}
if (!origContent) {
origContent = replaced;
}
let title = document.createElement("h1");
title.id = "search-results";
title.className = "list-title";
// This is an overly simple pluralization scheme, it will only work
// for some languages.
if (results.length == 0) {
title.textContent = "{{ i18n "searchResultsNone" (dict "Term" "{}") }}".replace("{}", term);
}
else if (results.length == 1) {
title.textContent = "{{ i18n "searchResultsTitle" (dict "Count" 1 "Term" "{}") }}".replace("{}", term);
}
else {
title.textContent = "{{ i18n "searchResultsTitle" (dict "Count" 13579 "Term" "{}") }}".replace("{}", term).replace("13579", results.length);
}
target.appendChild(title);
document.title = title.textContent;
let template = document.getElementById("search-result");
for (let result of results) {
let doc = lookup[result.ref];
let element = template.content.cloneNode(true);
element.querySelector(".summary-title-link").href = element.querySelector(".read-more-link").href = doc.uri;
element.querySelector(".summary-title-link").textContent = doc.title;
element.querySelector(".summary").textContent = truncateToEndOfSentence(doc.content, 70);
target.appendChild(element);
}
title.scrollIntoView(true);
if (!doNotAddState) {
history.pushState({type: "search", term: term}, title.textContent, "#search=" + encodeURIComponent(term));
}
{{ if .Site.Params.enableNavToggle }}
let navToggleLabel = document.querySelector('.nav-toggle');
if (navToggleLabel && navToggleLabel.classList.contains("open")) {
document.getElementById(navToggleLabel.getAttribute("for")).click();
}
{{ end }}
}
finally {
searchDone();
}
}
// This matches Hugo's own summary logic:
// https://github.com/gohugoio/hugo/blob/b5f39d23b86f9cb83c51da9fe4abb4c19c01c3b7/helpers/content.go#L543
function truncateToEndOfSentence(text, minWords)
{
let match;
let result = "";
let wordCount = 0;
let regexp = /(\S+)(\s*)/g;
while (match = regexp.exec(text)) {
wordCount++;
if (wordCount <= minWords) {
result += match[0];
}
else
{
let char1 = match[1][match[1].length - 1];
let char2 = match[2][0];
if (/[.?!"]/.test(char1) || char2 == "\n") {
result += match[1];
break;
}
else {
result += match[0];
}
}
}
return result;
}
}, {once: true});