add search functionality in /posts page

This commit is contained in:
diego 2020-09-14 17:40:32 +02:00
parent 6c2cca0127
commit 4e7f74bcf0
6 changed files with 250 additions and 5 deletions

View file

@ -77,3 +77,12 @@ params:
- I am a Developer
- I work with Go
- I love to work with some fun projects
outputs:
home:
- HTML
- JSON
- RSS
page:
- HTML
- RSS

View file

@ -0,0 +1,5 @@
{{- $.Scratch.Add "index" slice -}}
{{- range .Site.RegularPages -}}
{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "content" .Plain "relPermalink" .RelPermalink "summary" .Summary) -}}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}

View file

@ -29,18 +29,27 @@
<section class="content-section" id="content-section">
<div class="content container-fluid" id="content">
<div class="container-fluid post-card-holder" id="post-card-holder">
{{ $paginator := .Paginate .RegularPagesRecursive 12 }}
{{ range $paginator.Pages }}
{{ partial "cards/post.html" . }}
{{ end }}
{{ $paginator := .Paginate .RegularPagesRecursive 12 }}
{{ range $paginator.Pages }}
{{ partial "cards/post.html" . }}
{{ end }}
</div>
<div class="paginator">
{{ template "_internal/pagination.html" . }}
{{ template "_internal/pagination.html" . }}
</div>
<div class="container-fluid post-card-holder" id="search-results-holder">
</div>
<div class="search-results-template" id="search-results-template">
{{ partial "cards/search-result-post.html" . }}
</div>
<div class="search-results" id="search-results"></div>
</div>
</section>
{{ end }}
{{ define "scripts" }}
<script src="/assets/js/list.js"></script>
<script src="/assets/js/search.js"></script>
{{ end }}

View file

@ -0,0 +1,21 @@
<div class="post-card" id="${RelPermalink}">
<a href="${RelPermalink}" class="post-card-link">
<div class="card">
<div class="card-head">
<img class="card-img-top" src='{{ partial "helpers/get-hero.html" . }}'/>
</div>
<div class="card-body">
<h5 class="card-title">${Title}</h5>
<p class="card-text post-summary">${Summary}</p>
</div>
<div class="card-footer">
<span class="float-left">{{ .Date.Format "January 2, 2006" }}</span>
<a
href="${RelPermalink}"
class="float-right btn btn-outline-info btn-sm"
>Read</a
>
</div>
</div>
</a>
</div>

View file

@ -86,6 +86,22 @@
display: none;
}
#search-results-template {
display: none;
}
.search-results {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
margin: auto;
color: #248aaa;
background-color: white;
border-radius: 0.25em;
padding: 2px;
}
.pagination {
margin-left: auto;
margin-right: auto;

185
static/assets/js/search.js Normal file
View file

@ -0,0 +1,185 @@
"use strict";
var searchFn = function () {
var lastTerm = "You are likely to be eaten by a grue.";
var stopwords = ["i", "me", "my", "we", "our", "you", "it",
"its", "this", "that", "am", "is", "are", "was", "be",
"has", "had", "do", "a", "an", "the", "but", "if", "or", "as",
"of", "at", "by", "for", "with", "to", "then", "no", "not",
"so", "too", "can", "and", "but"];
var normalizer = document.createElement("textarea");
var normalize = function (input) {
normalizer.innerHTML = input;
var inputDecoded = normalizer.value;
return " " + inputDecoded.trim().toLowerCase().replace(/[^0-9a-z ]/gi, " ").replace(/\s+/g, " ") + " ";
}
var limit = 30;
var minChars = 2;
var searching = false;
var render = function (results) {
results.sort(function (a, b) { return b.weight - a.weight; });
for (var i = 0; i < results.length && i < limit; i += 1) {
var result = results[i].item;
var templateDefinition = $('#search-results-template').html();
var output = renderTemplate(templateDefinition,{"Title":result.showTitle,"Summary":result.summary,"RelPermalink":result.relPermalink});
$('#search-results-holder').append(output);
}
showSearchResultsAndHideCards();
};
var showSearchResultsAndHideCards = function() {
$("#search-results-holder").show();
$("#post-card-holder ").hide();
$("#search-results-holder .post-card").show();
$('.paginator').hide();
$('#search-results').show();
}
var showCardssAndHideSearchResults = function() {
$("#search-results-holder").hide();
$('#post-card-holder').show();
$('#search-results-holder').html("");
$('.paginator').show();
$('#search-results').hide();
}
var renderTemplate = function(templateString, data) {
var conditionalMatches,conditionalPattern,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;
}
var checkTerms = function (terms, weight, target) {
var weightResult = 0;
terms.forEach(function (term) {
if (~target.indexOf(term.term)) {
var idx = target.indexOf(term.term);
while (~idx) {
weightResult += term.weight * weight;
idx = target.indexOf(term.term, idx + 1);
}
}
});
return weightResult;
};
var search = function (terms) {
var results = [];
searchHost.index.forEach(function (item) {
if (item.tags) {
var weight_1 = 0;
terms.forEach(function (term) {
if (item.title.startsWith(term.term)) {
weight_1 += term.weight * 32;
}
});
weight_1 += checkTerms(terms, 1, item.content);
//weight_1 += checkTerms(terms, 2, item.description);
//weight_1 += checkTerms(terms, 2, item.subtitle);
item.tags.forEach(function (tag) {
weight_1 += checkTerms(terms, 4, tag);
});
weight_1 += checkTerms(terms, 16, item.title);
if (weight_1) {
results.push({
weight: weight_1,
item: item
});
}
}
});
if (results.length) {
var resultsMessage = results.length + " items found.";
if (results.length > limit) {
resultsMessage += " Showing first " + limit + " results.";
}
$("#search-results").html("<p>" + resultsMessage + "</p>");
render(results);
}
else {
showCardssAndHideSearchResults();
}
};
var runSearch = function () {
if (searching) {
return;
}
var term = normalize($("#search-box").val()).trim();
if (term === lastTerm) {
return;
}
lastTerm = term;
if (term.length < minChars) {
showCardssAndHideSearchResults();
return;
}
searching = true;
var startSearch = new Date();
$("#search-results").html('<p>Processing search...</p>');
var terms = term.split(" ");
var termsTree = [];
for (var i = 0; i < terms.length; i += 1) {
for (var j = i; j < terms.length; j += 1) {
var weight = Math.pow(2, j - i);
var str = "";
for (var k = i; k <= j; k += 1) {
str += (terms[k] + " ");
}
var newTerm = str.trim();
if (newTerm.length >= minChars && stopwords.indexOf(newTerm) < 0) {
termsTree.push({
weight: weight,
term: " " + str.trim() + " "
});
}
}
}
search(termsTree);
searching = false;
var endSearch = new Date();
$("#search-results").append("<p><small>Search took " + (endSearch - startSearch) + "ms.</small></p>");
};
var initSearch = function () {
$("#search-box").keyup(function () {
runSearch();
});
runSearch();
};
$("#search-box").hide();
var searchHost = {};
$.getJSON("/index.json", function (results) {
searchHost.index = [];
var dup = {};
results.forEach(function (result) {
if (result.tags) {
var res = {};
res.showTitle = result.title;
res.title = normalize(result.title);
res.subtitle = normalize(result.subtitle);
res.summary = result.summary;
res.content = normalize(result.content);
res.relPermalink = result.relPermalink;
var newTags_1 = [];
result.tags.forEach(function (tag) {
return newTags_1.push(normalize(tag));
});
res.tags = newTags_1;
searchHost.index.push(res);
dup[result.permalink] = true;
}
});
$("#loading").hide();
$("#search-box").show()
.removeAttr("disabled")
.focus();
initSearch();
});
};
window.addEventListener("DOMContentLoaded", searchFn);