added search to site - adapted from blog.kemonine.info code
This commit is contained in:
parent
ba738b0d12
commit
7bc6e6e656
|
@ -14,6 +14,9 @@ pygmentsOptions = "linenos=table"
|
|||
[permalinks]
|
||||
posts = "/posts/:year/:month/:title"
|
||||
|
||||
[outputs]
|
||||
home = ["HTML", "RSS", "JSON"]
|
||||
|
||||
[params]
|
||||
author = "KemoNine"
|
||||
description = "a confused culinary notebook"
|
||||
|
|
10
content/search.md
Normal file
10
content/search.md
Normal 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/>
|
5
themes/gochowdown/layouts/_default/index.json
Normal file
5
themes/gochowdown/layouts/_default/index.json
Normal 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 -}}
|
19
themes/gochowdown/layouts/_default/search.html
Normal file
19
themes/gochowdown/layouts/_default/search.html
Normal 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 }}
|
|
@ -21,4 +21,10 @@
|
|||
{{ end }}
|
||||
</section>
|
||||
{{ 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>
|
||||
|
|
9
themes/gochowdown/static/js/fuse.min.js
vendored
Normal file
9
themes/gochowdown/static/js/fuse.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
themes/gochowdown/static/js/mark.min.js
vendored
Normal file
13
themes/gochowdown/static/js/mark.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
134
themes/gochowdown/static/js/search.js
Normal file
134
themes/gochowdown/static/js/search.js
Normal 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
|
Loading…
Reference in a new issue