Files
Atay-Makhzan/docs/assets/js/search.js
T

175 lines
5.2 KiB
JavaScript
Raw Normal View History

2020-03-09 01:41:52 +01:00
/* global Fuse, Mark */
2019-11-13 12:03:18 -06:00
function ready(fn) {
2020-03-09 01:41:52 +01:00
if (document.readyState !== 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
2019-11-13 12:03:18 -06:00
}
ready(doSearch);
const summaryInclude = 60;
const fuseOptions = {
2020-03-09 01:41:52 +01:00
shouldSort: true,
includeMatches: true,
matchAllTokens: true,
2021-11-22 09:19:01 +01:00
threshold: 0, // for parsing diacritics
2020-03-09 01:41:52 +01:00
tokenize: true,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [{
name: 'title',
weight: 0.8
},
{
name: 'contents',
weight: 0.5
},
{
name: 'tags',
weight: 0.3
},
{
name: 'categories',
weight: 0.3
}
]
2019-11-13 12:03:18 -06:00
};
function param(name) {
2020-03-09 01:41:52 +01:00
return decodeURIComponent((window.location.search.split(`${name}=`)[1] || '').split('&')[0]).replace(/\+/g, ' ');
2019-11-13 12:03:18 -06:00
}
2020-03-09 01:41:52 +01:00
const searchQuery = param('s');
2019-11-13 12:03:18 -06:00
function doSearch() {
2020-03-09 01:41:52 +01:00
if (searchQuery) {
document.getElementById('search-query').value = searchQuery;
executeSearch(searchQuery);
} else {
const para = document.createElement('P');
2021-11-22 09:19:01 +01:00
para.textContent = 'Please enter a word or phrase above';
2020-03-09 01:41:52 +01:00
document.getElementById('search-results').appendChild(para);
}
2019-11-13 12:03:18 -06:00
}
function getJSON(url, fn) {
2020-03-09 01:41:52 +01:00
const request = new XMLHttpRequest();
request.open('GET', url, true);
2021-11-22 09:19:01 +01:00
request.addEventListener('load', () => {
2020-03-09 01:41:52 +01:00
if (request.status >= 200 && request.status < 400) {
const data = JSON.parse(request.responseText);
fn(data);
} else {
console.error(`Target reached on ${url} with error ${request.status}`);
}
2021-11-22 09:19:01 +01:00
});
request.addEventListener('error', () => {
2020-03-09 01:41:52 +01:00
console.error(`Connection error ${request.status}`);
2021-11-22 09:19:01 +01:00
});
2020-03-09 01:41:52 +01:00
request.send();
2019-11-13 12:03:18 -06:00
}
function executeSearch(searchQuery) {
2020-03-09 01:41:52 +01:00
getJSON(`/${document.LANG}/index.json`, (data) => {
const pages = data;
const fuse = new Fuse(pages, fuseOptions);
const result = fuse.search(searchQuery);
document.getElementById('search-results').innerHTML = '';
if (result.length > 0) {
populateResults(result);
} else {
const para = document.createElement('P');
2021-11-22 09:19:01 +01:00
para.textContent = 'No matches found';
2020-03-09 01:41:52 +01:00
document.getElementById('search-results').appendChild(para);
}
});
2019-11-13 12:03:18 -06:00
}
function populateResults(result) {
2021-11-22 09:19:01 +01:00
for (const [key, value] of result.entries()) {
2020-03-09 01:41:52 +01:00
const content = value.item.contents;
let snippet = '';
const snippetHighlights = [];
if (fuseOptions.tokenize) {
snippetHighlights.push(searchQuery);
2021-11-22 09:19:01 +01:00
for (const mvalue of value.matches) {
2020-03-09 01:41:52 +01:00
if (mvalue.key === 'tags' || mvalue.key === 'categories') {
snippetHighlights.push(mvalue.value);
} else if (mvalue.key === 'contents') {
const ind = content.toLowerCase().indexOf(searchQuery.toLowerCase());
const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0;
const end = ind + searchQuery.length + summaryInclude < content.length ? ind + searchQuery.length + summaryInclude : content.length;
snippet += content.substring(start, end);
if (ind > -1) {
snippetHighlights.push(content.substring(ind, ind + searchQuery.length));
} else {
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
}
2019-11-13 12:03:18 -06:00
}
2021-11-22 09:19:01 +01:00
}
2020-03-09 01:41:52 +01:00
}
2019-11-13 12:03:18 -06:00
2020-03-09 01:41:52 +01:00
if (snippet.length < 1) {
snippet += content.substring(0, summaryInclude * 2);
}
// pull template from hugo template definition
const templateDefinition = document.getElementById('search-result-template').innerHTML;
// replace values
const output = render(templateDefinition, {
key,
title: value.item.title,
link: value.item.permalink,
tags: value.item.tags,
categories: value.item.categories,
snippet
});
document.getElementById('search-results').appendChild(htmlToElement(output));
2019-11-13 12:03:18 -06:00
2021-11-22 09:19:01 +01:00
for (const snipvalue of snippetHighlights) {
2020-03-09 01:41:52 +01:00
new Mark(document.getElementById(`summary-${key}`)).mark(snipvalue);
2021-11-22 09:19:01 +01:00
}
}
2019-11-13 12:03:18 -06:00
}
function render(templateString, data) {
2020-03-09 01:41:52 +01:00
let conditionalMatches, copy;
const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
// since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
copy = templateString;
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
if (data[conditionalMatches[1]]) {
// valid key, remove conditionals, leave content.
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
} else {
// not valid, remove entire section
copy = copy.replace(conditionalMatches[0], '');
2019-11-13 12:03:18 -06:00
}
2020-03-09 01:41:52 +01:00
}
templateString = copy;
// now any conditionals removed we can do simple substitution
let key, find, re;
for (key of Object.keys(data)) {
find = `\\$\\{\\s*${key}\\s*\\}`;
re = new RegExp(find, 'g');
templateString = templateString.replace(re, data[key]);
}
return templateString;
2019-11-13 12:03:18 -06:00
}
/**
* By Mark Amery: https://stackoverflow.com/a/35385518
* @param {String} HTML representing a single element
* @return {Element}
*/
function htmlToElement(html) {
2020-03-09 01:41:52 +01:00
const template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
2019-11-13 12:03:18 -06:00
}