added search to site - adapted from blog.kemonine.info code

This commit is contained in:
KemoNine 2022-12-03 13:18:42 -05:00
parent ba738b0d12
commit 7bc6e6e656
8 changed files with 199 additions and 0 deletions

View file

@ -14,6 +14,9 @@ pygmentsOptions = "linenos=table"
[permalinks] [permalinks]
posts = "/posts/:year/:month/:title" posts = "/posts/:year/:month/:title"
[outputs]
home = ["HTML", "RSS", "JSON"]
[params] [params]
author = "KemoNine" author = "KemoNine"
description = "a confused culinary notebook" description = "a confused culinary notebook"

10
content/search.md Normal file
View file

@ -0,0 +1,10 @@
+++
title = "Search"
author = ["KemoNine"]
draft = false
layout = "search"
[sitemap]
priority = 0.1
+++
This page was adapted from <https://makewithhugo.com/add-search-to-a-hugo-site/>

View file

@ -0,0 +1,5 @@
{{- $.Scratch.Add "index" slice -}}
{{- range .Site.RegularPages -}}
{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}

View file

@ -0,0 +1,19 @@
{{ define "content" }}
<div id="search-results"></div>
<template id="search-result-template"><div class="search_summary">
<h2 class="post-title no-text-decoration"><a class="search_link search_title" href=""></a></h2>
<p><em class="search_snippet"></em></p>
<small>
<table>
<tr class="search_iftags"><td><strong>Tags</strong></td><td class="search_tags"></td></tr>
</table>
</small>
</div></template>
<script src="{{ "js/fuse.min.js" | absURL }}"></script>
<script src="{{ "js/mark.min.js" | absURL }}"></script>
<script src="{{ "js/search.js" | absURL }}"></script>
</div>
{{ end }}

View file

@ -21,4 +21,10 @@
{{ end }} {{ end }}
</section> </section>
{{ end }} {{ end }}
<section class="pure-u-1 nav-center">
<form class="form" action="/search" method="GET">
<input class="title" type="search" name="q" id="search-query" placeholder="Search....">
<button class="button" type="submit">Search</button>
</form>
</section>
</nav> </nav>

File diff suppressed because one or more lines are too long

13
themes/gochowdown/static/js/mark.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,134 @@
// @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