135 lines
4.3 KiB
JavaScript
135 lines
4.3 KiB
JavaScript
|
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
|
||
|
// How many characters to include on either side of match keyword
|
||
|
const summaryInclude=60;
|
||
|
|
||
|
// Options for fuse.js
|
||
|
let fuseOptions = {
|
||
|
shouldSort: true,
|
||
|
includeMatches: true,
|
||
|
tokenize: true,
|
||
|
matchAllTokens: true,
|
||
|
threshold: 0.0,
|
||
|
location: 0,
|
||
|
distance: 100,
|
||
|
maxPatternLength: 64,
|
||
|
minMatchCharLength: 3,
|
||
|
keys: [
|
||
|
{name:"title",weight:0.8},
|
||
|
{name:"tags",weight:0.5},
|
||
|
{name:"categories",weight:0.5},
|
||
|
{name:"contents",weight:0.4}
|
||
|
]
|
||
|
};
|
||
|
|
||
|
function getUrlParameter(name) {
|
||
|
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||
|
let regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
||
|
let results = regex.exec(location.search);
|
||
|
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||
|
}
|
||
|
|
||
|
let searchQuery = getUrlParameter('q');
|
||
|
|
||
|
if(searchQuery){
|
||
|
document.getElementById("search-query").value = searchQuery;
|
||
|
executeSearch(searchQuery);
|
||
|
} else {
|
||
|
document.getElementById('search-results').innerHTML = "<p class=\"no-results\">Please enter a word or phrase above</p>";
|
||
|
}
|
||
|
|
||
|
function executeSearch(searchQuery) {
|
||
|
// Look for "index.json" in the same directory where this script is called.
|
||
|
fetch("/index.json").
|
||
|
then(function (response) {
|
||
|
return response.json()
|
||
|
}).
|
||
|
then(function (data) {
|
||
|
let fuse = new Fuse(data, fuseOptions);
|
||
|
let result = fuse.search(searchQuery);
|
||
|
if (result.length > 0) {
|
||
|
populateResults(result);
|
||
|
} else {
|
||
|
document.getElementById('search-results').innerHTML = "<p class=\"no-results\">No matches found</p>";
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function populateResults(result){
|
||
|
result.forEach( function (value, key) {
|
||
|
let contents= value.item.contents;
|
||
|
let snippet = "";
|
||
|
let snippetHighlights=[];
|
||
|
snippetHighlights.push(searchQuery);
|
||
|
if(snippet.length<1){
|
||
|
var getSentenceByWordRegex = new RegExp(
|
||
|
`[^.?!]*(?<=[.?\\s!])${searchQuery}(?=[\\s.?!])[^.?!]*[.?!]`,
|
||
|
'i'
|
||
|
);
|
||
|
var maxTextLength = summaryInclude*2
|
||
|
// Index of the matched search term
|
||
|
var indexOfMatch = contents.toLowerCase().indexOf(
|
||
|
searchQuery.toLowerCase()
|
||
|
);
|
||
|
// Index of the first word of the sentence with the search term in it
|
||
|
var indexOfSentence = contents.indexOf(
|
||
|
getSentenceByWordRegex.exec(contents)
|
||
|
);
|
||
|
|
||
|
var start
|
||
|
var cutStart = false
|
||
|
// Is the match in the result?
|
||
|
if(indexOfSentence+maxTextLength < indexOfMatch){
|
||
|
// Make sure that the match is in the result
|
||
|
start = indexOfMatch
|
||
|
// This bool is used to replace the first part with '...'
|
||
|
cutStart = true
|
||
|
} else {
|
||
|
// Match is in view, even if we show the whole sentence
|
||
|
start = indexOfSentence
|
||
|
}
|
||
|
|
||
|
// Change end length to the text length if it is longer than
|
||
|
// the text length to prevent problems
|
||
|
var end = start + maxTextLength
|
||
|
if (end > contents.length){
|
||
|
end = contents.length
|
||
|
}
|
||
|
|
||
|
if(cutStart){
|
||
|
// Replace first three characters with '...'
|
||
|
end -= 3;
|
||
|
snippet += "…" + contents.substring(start, end).trim();
|
||
|
}
|
||
|
else{
|
||
|
snippet += contents.substring(start, end).trim();
|
||
|
}
|
||
|
}
|
||
|
snippet += "…";
|
||
|
|
||
|
// Lifted from https://stackoverflow.com/posts/3700369/revisions
|
||
|
var elem = document.createElement('textarea');
|
||
|
elem.innerHTML = snippet;
|
||
|
var decoded = elem.value;
|
||
|
|
||
|
// Pull template from hugo template definition
|
||
|
let frag = document.getElementById('search-result-template').content.cloneNode(true);
|
||
|
// Replace values
|
||
|
frag.querySelector(".search_summary").setAttribute("id", "summary-" + key);
|
||
|
frag.querySelector(".search_link").setAttribute("href", value.item.permalink);
|
||
|
frag.querySelector(".search_title").textContent = value.item.title;
|
||
|
frag.querySelector(".search_snippet").textContent = decoded;
|
||
|
let tags = value.item.tags;
|
||
|
if (tags) {
|
||
|
frag.querySelector(".search_tags").textContent = tags;
|
||
|
} else {
|
||
|
frag.querySelector(".search_iftags").remove();
|
||
|
}
|
||
|
snippetHighlights.forEach( function (snipvalue, snipkey) {
|
||
|
let markjs = new Mark(frag);
|
||
|
markjs.mark(snipvalue);
|
||
|
});
|
||
|
document.getElementById("search-results").appendChild(frag);
|
||
|
});
|
||
|
}
|
||
|
// @license-end
|