cleanup / fix search
This commit is contained in:
parent
ddabab99aa
commit
e353d2f97f
|
@ -9,21 +9,17 @@
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div id="search-results"></div>
|
<div id="search-results"></div>
|
||||||
<div class="search-loading"></div>
|
|
||||||
|
|
||||||
<script id="search-result-template" type="text/x-js-template">
|
<template id="search-result-template"><div class="search_summary">
|
||||||
<div id="summary-${key}">
|
<h2 class="post-title no-text-decoration"><a class="search_link search_title" href=""></a></h2>
|
||||||
<hr />
|
<p><em class="search_snippet"></em></p>
|
||||||
<h3><a href="${link}">${title}</a></h3>
|
|
||||||
<p>${snippet}</p>
|
|
||||||
<p>
|
|
||||||
<small>
|
<small>
|
||||||
${ isset tags }Tags: ${tags}<br>${ end }
|
<table>
|
||||||
|
<tr class="search_iftags"><td><strong>Tags</strong></td><td class="search_tags"></td></tr>
|
||||||
|
</table>
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</div></template>
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="{{ "js/fuse.min.js" | absURL }}"></script>
|
<script src="{{ "js/fuse.min.js" | absURL }}"></script>
|
||||||
|
|
8
themes/hugo-xmin/static/js/fuse.min.js
vendored
8
themes/hugo-xmin/static/js/fuse.min.js
vendored
File diff suppressed because one or more lines are too long
10
themes/hugo-xmin/static/js/mark.min.js
vendored
10
themes/hugo-xmin/static/js/mark.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,136 +1,134 @@
|
||||||
var summaryInclude = 180;
|
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
|
||||||
var fuseOptions = {
|
// How many characters to include on either side of match keyword
|
||||||
|
const summaryInclude=60;
|
||||||
|
|
||||||
|
// Options for fuse.js
|
||||||
|
let fuseOptions = {
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
includeMatches: true,
|
includeMatches: true,
|
||||||
includeScore: true,
|
|
||||||
tokenize: true,
|
tokenize: true,
|
||||||
|
matchAllTokens: true,
|
||||||
|
threshold: 0.0,
|
||||||
location: 0,
|
location: 0,
|
||||||
distance: 100,
|
distance: 100,
|
||||||
minMatchCharLength: 1,
|
maxPatternLength: 64,
|
||||||
|
minMatchCharLength: 3,
|
||||||
keys: [
|
keys: [
|
||||||
{name: "title", weight: 0.45},
|
{name:"title",weight:0.8},
|
||||||
{name: "contents", weight: 0.4},
|
{name:"tags",weight:0.5},
|
||||||
{name: "tags", weight: 0.1},
|
{name:"categories",weight:0.5},
|
||||||
{name: "categories", weight: 0.05}
|
{name:"contents",weight:0.4}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// =============================
|
function getUrlParameter(name) {
|
||||||
// Search
|
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||||
// =============================
|
let regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
||||||
|
let results = regex.exec(location.search);
|
||||||
|
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||||||
|
}
|
||||||
|
|
||||||
var inputBox = document.getElementById('search-query');
|
let searchQuery = getUrlParameter('q');
|
||||||
if (inputBox !== null) {
|
|
||||||
var searchQuery = param("q");
|
if(searchQuery){
|
||||||
if (searchQuery) {
|
document.getElementById("search-query").value = searchQuery;
|
||||||
inputBox.value = searchQuery || "";
|
executeSearch(searchQuery);
|
||||||
executeSearch(searchQuery, false);
|
} else {
|
||||||
} else {
|
document.getElementById('search-results').innerHTML = "<p class=\"no-results\">Please enter a word or phrase above</p>";
|
||||||
document.getElementById('search-results').innerHTML = '<p class="search-results-empty">Please enter a word or phrase above to search</p>';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeSearch(searchQuery) {
|
function executeSearch(searchQuery) {
|
||||||
|
// Look for "index.json" in the same directory where this script is called.
|
||||||
show(document.querySelector('.search-loading'));
|
fetch("/index.json").
|
||||||
|
then(function (response) {
|
||||||
fetch('/index.json').then(function (response) {
|
return response.json()
|
||||||
if (response.status !== 200) {
|
}).
|
||||||
console.log('Looks like there was a problem. Status Code: ' + response.status);
|
then(function (data) {
|
||||||
return;
|
let fuse = new Fuse(data, fuseOptions);
|
||||||
}
|
let result = fuse.search(searchQuery);
|
||||||
// Examine the text in the response
|
|
||||||
response.json().then(function (pages) {
|
|
||||||
var fuse = new Fuse(pages, fuseOptions);
|
|
||||||
var result = fuse.search(searchQuery);
|
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
populateResults(result);
|
populateResults(result);
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('search-results').innerHTML = '<p class=\"search-results-empty\">No matches found</p>';
|
document.getElementById('search-results').innerHTML = "<p class=\"no-results\">No matches found</p>";
|
||||||
}
|
}
|
||||||
hide(document.querySelector('.search-loading'));
|
|
||||||
})
|
|
||||||
.catch(function (err) {
|
|
||||||
console.log('Fetch Error :-S', err);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateResults(results) {
|
function populateResults(result){
|
||||||
|
result.forEach( function (value, key) {
|
||||||
var searchQuery = document.getElementById("search-query").value;
|
let contents= value.item.contents;
|
||||||
var searchResults = document.getElementById("search-results");
|
let snippet = "";
|
||||||
|
let snippetHighlights=[];
|
||||||
// pull template from hugo template definition
|
|
||||||
var templateDefinition = document.getElementById("search-result-template").innerHTML;
|
|
||||||
|
|
||||||
results.forEach(function (value, key) {
|
|
||||||
|
|
||||||
var contents = value.item.contents;
|
|
||||||
var snippet = "";
|
|
||||||
var snippetHighlights = [];
|
|
||||||
|
|
||||||
snippetHighlights.push(searchQuery);
|
snippetHighlights.push(searchQuery);
|
||||||
snippet = contents.substring(0, summaryInclude * 2) + '…';
|
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)
|
||||||
|
);
|
||||||
|
|
||||||
//replace values
|
var start
|
||||||
var tags = ""
|
var cutStart = false
|
||||||
if (value.item.tags) {
|
// Is the match in the result?
|
||||||
value.item.tags.forEach(function (element) {
|
if(indexOfSentence+maxTextLength < indexOfMatch){
|
||||||
tags = tags + "<a href='/tags/" + element + "'>" + "#" + element + "</a> "
|
// Make sure that the match is in the result
|
||||||
});
|
start = indexOfMatch
|
||||||
}
|
// This bool is used to replace the first part with '...'
|
||||||
|
cutStart = true
|
||||||
var output = render(templateDefinition, {
|
|
||||||
key: key,
|
|
||||||
title: value.item.title,
|
|
||||||
link: value.item.permalink,
|
|
||||||
tags: tags,
|
|
||||||
categories: value.item.categories,
|
|
||||||
snippet: snippet
|
|
||||||
});
|
|
||||||
searchResults.innerHTML += output;
|
|
||||||
|
|
||||||
snippetHighlights.forEach(function (snipvalue, snipkey) {
|
|
||||||
var instance = new Mark(document.getElementById('summary-' + key));
|
|
||||||
instance.mark(snipvalue);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(templateString, data) {
|
|
||||||
var conditionalMatches, conditionalPattern, copy;
|
|
||||||
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 contents.
|
|
||||||
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
|
|
||||||
} else {
|
} else {
|
||||||
//not valid, remove entire section
|
// Match is in view, even if we show the whole sentence
|
||||||
copy = copy.replace(conditionalMatches[0], '');
|
start = indexOfSentence
|
||||||
}
|
}
|
||||||
}
|
|
||||||
templateString = copy;
|
|
||||||
//now any conditionals removed we can do simple substitution
|
|
||||||
var key, find, re;
|
|
||||||
for (key in data) {
|
|
||||||
find = '\\$\\{\\s*' + key + '\\s*\\}';
|
|
||||||
re = new RegExp(find, 'g');
|
|
||||||
templateString = templateString.replace(re, data[key]);
|
|
||||||
}
|
|
||||||
return templateString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper Functions
|
// Change end length to the text length if it is longer than
|
||||||
function show(elem) {
|
// the text length to prevent problems
|
||||||
elem.style.display = 'block';
|
var end = start + maxTextLength
|
||||||
}
|
if (end > contents.length){
|
||||||
function hide(elem) {
|
end = contents.length
|
||||||
elem.style.display = 'none';
|
}
|
||||||
}
|
|
||||||
function param(name) {
|
if(cutStart){
|
||||||
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
|
// 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