Skip to content

Commit

Permalink
[Add] search (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiennm99 committed Nov 30, 2024
1 parent c297097 commit f730e36
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 0 deletions.
6 changes: 6 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,9 @@ languages:
url: https://tiennm99.github.io/webcv
- name: PDF CV
url: https://tiennm99.github.io/cv/miti99.pdf

outputs:
home:
- html
- rss
- json
6 changes: 6 additions & 0 deletions content/search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "Search"
sitemap:
priority : 0.1
layout: "search"
---
5 changes: 5 additions & 0 deletions layouts/_default/index.json
Original file line number Diff line number Diff line change
@@ -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 -}}
24 changes: 24 additions & 0 deletions layouts/_default/search.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{ define "main" }}

<main>
<div id="search-results"></div>
<div class="search-loading">Loading...</div>

<script id="search-result-template" type="text/x-js-template">
<div id="summary-${key}">
<h3><a href="${link}">${title}</a></h3>
<p>${snippet}</p>
<p>
<small>
${ isset tags }Tags: ${tags}<br>${ end }
</small>
</p>
</div>
</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/6.6.2/fuse.min.js" integrity="sha512-Nqw1tH3mpavka9cQCc5zWWEZNfIPdOYyQFjlV1NvflEtQ0/XI6ZQ+H/D3YgJdqSUJlMLAPRj/oXlaHCFbFCjoQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js" integrity="sha512-5CYOlHXGh6QpOFA/TeTylKLWfB3ftPsde7AnmhuitiTX4K5SqCLBeKro6sPS8ilsz1Q4NRx3v8Ko2IBiszzdww==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</main>

{{ end }}
136 changes: 136 additions & 0 deletions static/js/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
var summaryInclude = 180;
var fuseOptions = {
shouldSort: true,
includeMatches: true,
includeScore: true,
tokenize: true,
location: 0,
distance: 100,
minMatchCharLength: 1,
keys: [
{name: "title", weight: 0.45},
{name: "contents", weight: 0.4},
{name: "tags", weight: 0.1},
{name: "categories", weight: 0.05}
]
};

// =============================
// Search
// =============================

var inputBox = document.getElementById('search-query');
if (inputBox !== null) {
var searchQuery = param("q");
if (searchQuery) {
inputBox.value = searchQuery || "";
executeSearch(searchQuery, false);
} else {
document.getElementById('search-results').innerHTML = '<p class="search-results-empty">Please enter a word or phrase above, or see <a href="/tags/">all tags</a>.</p>';
}
}

function executeSearch(searchQuery) {

show(document.querySelector('.search-loading'));

fetch('/index.json').then(function (response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' + response.status);
return;
}
// 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) {
populateResults(result);
} else {
document.getElementById('search-results').innerHTML = '<p class=\"search-results-empty\">No matches found</p>';
}
hide(document.querySelector('.search-loading'));
})
.catch(function (err) {
console.log('Fetch Error :-S', err);
});
});
}

function populateResults(results) {

var searchQuery = document.getElementById("search-query").value;
var searchResults = document.getElementById("search-results");

// 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);
snippet = contents.substring(0, summaryInclude * 2) + '&hellip;';

//replace values
var tags = ""
if (value.item.tags) {
value.item.tags.forEach(function (element) {
tags = tags + "<a href='/tags/" + element + "'>" + "#" + element + "</a> "
});
}

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 {
//not valid, remove entire section
copy = copy.replace(conditionalMatches[0], '');
}
}
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
function show(elem) {
elem.style.display = 'block';
}
function hide(elem) {
elem.style.display = 'none';
}
function param(name) {
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
}

0 comments on commit f730e36

Please sign in to comment.