added optional js powered search

This commit is contained in:
Gabriele Musco 2021-04-13 15:21:40 +02:00
parent cfad046bb8
commit f51db60b35
No known key found for this signature in database
GPG Key ID: 8539FD3454380B83
8 changed files with 159 additions and 43 deletions

View File

@ -44,6 +44,8 @@ summaryLength = 70 # number of words for article summaries
gridView = false # show post list as a grid. goes well with discreteCards gridView = false # show post list as a grid. goes well with discreteCards
highlightBgColor = "#34363b"; # card and circle navigation background color for discrete card mode highlightBgColor = "#34363b"; # card and circle navigation background color for discrete card mode
enableSearch = true # enable search page
# enable comments support with commento using the script from your server # enable comments support with commento using the script from your server
commento = "https://example.com/js/commento.js" commento = "https://example.com/js/commento.js"

View File

@ -33,7 +33,7 @@ a {
color: $fg_color; color: $fg_color;
text-decoration: none; text-decoration: none;
border-bottom: 2px solid $fg_color; border-bottom: 2px solid $fg_color;
&:hover { &:hover, &:focus {
border-color: $accent_color !important; border-color: $accent_color !important;
} }
} }
@ -335,3 +335,27 @@ ul.list {
} }
} }
} }
.search {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
.nerdlink {
display: inline-block;
cursor: pointer;
}
#searchbar {
@extend a;
width: 100%;
display: inline-block;
background-color: transparent;
border-top: none;
border-left: none;
border-right: none;
padding: 6px 0;
font-family: $main_font;
font-size: 1.3em;
outline: none;
}
}

7
content/search.md Normal file
View File

@ -0,0 +1,7 @@
---
title: Search
layout: search
outputs:
- "html"
- "json"
---

View File

@ -0,0 +1,51 @@
{{ define "main" }}
<noscript>
You need to enable JavaScript to be able to search.
</noscript>
<div class="search">
<input id="searchbar" type="text" placeholder="{{ .Title }}" />
<a class="nerdlink" onclick="newSearch();">&#xf002;</a>
</div>
<div class="postlist {{ if .Site.Params.gridView }}gridView{{ end }}" id="postlist">
</div>
{{- partial "js_paginator.html" . -}}
<script>
function matchTags(page, term) {
for (let tag of page.tags) {
if (tag.includes(term)) return true;
}
return false;
}
function performSearch(term) {
document.getElementById('postlist').innerHTML = '';
term = term.toLowerCase();
fetch('/search/index.json').then(res => res.json())
.then(res => {
let articles = res.pages.filter(page => (
page.title.toLowerCase().includes(term) ||
matchTags(page, term) ||
page.text.includes(term)
));
if (articles.length > 0) renderArticles(articles);
else document.getElementById('postlist').innerHTML = `
<h3>No results found</h3>
`;
});
}
var url = location.href;
var baseUrl = url.split('?')[0];
var searchbar = document.getElementById('searchbar');
if (url.includes('?')) {
var urlParams = new URLSearchParams(url.split('?')[1]);
if (urlParams.has('q')) {
let searchTerm = urlParams.get('q');
searchbar.value = searchTerm;
performSearch(searchTerm);
}
}
function newSearch() {
location.href = baseUrl+`?q=${searchbar.value}`;
}
searchbar.onkeyup = (ev) => {if (ev.keyCode == 13) newSearch()};
</script>
{{ end }}

View File

@ -0,0 +1,26 @@
{{ define "main" }}
{
"pages": [
{{ range $i, $e := .Site.RegularPages }}
{{ if $i }},{{ end }}
{
"title": {{ .Title | jsonify }},
"date": {{ .Date.Format "2006-01-02" | jsonify }},
"tags": {{ if .Params.tags }}
{{ .Params.tags | jsonify }}
{{ else }}
[]
{{ end }},
"summary": {{ .Summary | jsonify }},
"text": {{ lower .Plain | jsonify }},
"link": "{{ .Permalink }}"
{{ if .Site.Params.imageInArticlePreview }}
, "image": {{ if .Params.Image }}
{{ .Params.Image | jsonify }}{{ else }}""
{{ end }}
{{ end }}
}
{{ end }}
]
}
{{ end }}

View File

@ -16,6 +16,9 @@
{{ if (.Site.Params.showTags | default true) }} {{ if (.Site.Params.showTags | default true) }}
<li><a href="/tags">Tags</a></li> <li><a href="/tags">Tags</a></li>
{{ end }} {{ end }}
{{ if (.Site.Params.enableSearch | default true) }}
<li><a href="/search">Search</a></li>
{{ end }}
</ul> </ul>
</nav> </nav>
</div> </div>

View File

@ -0,0 +1,44 @@
<script>
function renderTags(tags) {
if (tags.length <= 0) return '';
let res = '| <span title="tags"></span>';
for (let tag of tags) {
res += `<a href="/tags/${tag}">#${tag}</a>
`;
}
return res;
}
function renderImage(image) {
if (!image) return '';
return `<img src="${image}" />`
}
function renderSingleArticle(article) {
if (article.ignore) return '';
return `
<article class="card postlistitem {{ if .Site.Params.discreteCards }}discrete{{ end }}">
<div>
<h2>
<a href="${article.link}">${article.title}</a>
</h2>
<p class="date">
<span title="Date"></span>
${article.date}
${renderTags(article.tags)}
</p>
{{ if .Site.Params.imageInArticlePreview }}
${renderImage(article.image)}
{{end}}
<div class="articlePreview">
<p>${article.summary}</p>
<p><a href="${article.link}">Continue reading </a></p>
</div>
</div>
<hr />
</article>
`;
}
function renderArticles(articles) {
let rendered = articles.map(a => renderSingleArticle(a)).join('\n');
document.getElementById('postlist').innerHTML += rendered;
}
</script>

View File

@ -1,48 +1,7 @@
{{ if .Site.Params.infiniteScrolling }} {{ if .Site.Params.infiniteScrolling }}
{{- partial "js_paginator.html" . -}}
<script> <script>
var nextPage = '/index.json'; var nextPage = '/index.json';
function renderTags(tags) {
if (tags.length <= 0) return '';
let res = '| <span title="tags"></span>';
for (let tag of tags) {
res += `<a href="/tags/${tag}">#${tag}</a>
`;
}
return res;
}
function renderImage(image) {
if (!image) return '';
return `<img src="${image}" />`
}
function renderSingleArticle(article) {
if (article.ignore) return '';
return `
<article class="card postlistitem {{ if .Site.Params.discreteCards }}discrete{{ end }}">
<div>
<h2>
<a href="${article.link}">${article.title}</a>
</h2>
<p class="date">
<span title="Date"></span>
${article.date}
${renderTags(article.tags)}
</p>
{{ if .Site.Params.imageInArticlePreview }}
${renderImage(article.image)}
{{end}}
<div class="articlePreview">
<p>${article.summary}</p>
<p><a href="${article.link}">Continue reading </a></p>
</div>
</div>
<hr />
</article>
`;
}
function renderArticles(articles) {
let rendered = articles.map(a => renderSingleArticle(a)).join('\n');
document.getElementById('postlist').innerHTML += rendered;
}
function getNext(first=false) { function getNext(first=false) {
if (!nextPage) return; if (!nextPage) return;
fetch(nextPage).then(res => res.json()) fetch(nextPage).then(res => res.json())