Browse Source

implement the search function

main
Nicolas Massé 5 years ago
parent
commit
ce7caad5b3
  1. 12
      i18n/en.yaml
  2. 12
      i18n/fr.yaml
  3. 198
      layouts/_default/search.html
  4. 9
      layouts/partials/head.html
  5. 3
      layouts/partials/toolbar.html
  6. 23
      static/css/itix.css
  7. 9
      static/js/fuse.js

12
i18n/en.yaml

@ -22,3 +22,15 @@
translation: This page does not exist!
- id: go_home
translation: Go Home
- id: search
translation: Search
- id: search_here
translation: Search something...
- id: type_blog
translation: Article
- id: type_writing
translation: Writing
- id: type_speaking
translation: Speaking
- id: type_page
translation: Misc.

12
i18n/fr.yaml

@ -22,3 +22,15 @@
translation: Cette page n'existe pas !
- id: go_home
translation: Retour à la page d'accueil
- id: search
translation: Recherche
- id: search_here
translation: Rechercher quelque chose...
- id: type_blog
translation: Article
- id: type_writing
translation: Publication
- id: type_speaking
translation: Conférence
- id: type_page
translation: Divers

198
layouts/_default/search.html

@ -0,0 +1,198 @@
{{ define "main" }}
<!-- The "search/index.html" layout renders the search engine -->
<article class="search article-list">
<header>
<h1>{{ .Title }}</h1>
{{- with .Description }}
<h4>{{ . }}</h4>
{{- end }}
</header>
{{ .Content }}
<input id="searchInput" tabindex="0" placeholder="{{ i18n "search_here" }}" autofocus>
<div id="searchResults">
</div>
<script type="text/javascript">
var searchResults = document.getElementById('searchResults'); // targets the <div> above
var searchInput = document.getElementById('searchInput'); // input box for search
var lastSearch = "";
// Helper function to create an element with attributes
function tag(name, attrs) {
var el = document.createElement(name.toString());
!!attrs && Object.keys(attrs).forEach(function(key) {
el.setAttribute(key, attrs[key]);
});
return el;
}
function printSearchResult(result) {
var section = tag("section");
//
// Article headline
//
var a = tag("a", { href: result.item.permalink });
a.textContent = result.item.title;
if (result.item.hasEnFlag) {
var img = tag("img", { src: "/icons/en.svg", 'class': "tiny-flag", alt: "en flag" });
a.appendChild(img);
}
var h4 = tag("h4");
h4.textContent = result.item.type_label + " >> ";
h4.appendChild(a);
section.appendChild(h4);
//
// Metadata
//
if (result.item.type != "page") {
var div = tag("div", { 'class': 'post-metadata'});
var span_date = tag("span", { 'class': 'publication-date' });
span_date.textContent = {{ i18n "published_on" }} + " " + result.item.date;
div.appendChild(span_date);
if (result.item.topics != null) {
result.item.topics.forEach(function (v) {
var topic_detail = taxonomies.topics[v];
if (topic_detail == null) {
return;
}
var span = tag("span", { 'class': 'topic' });
var i = tag("i", { 'class': 'material-icons', 'aria-hidden': 'true'});
i.textContent = "link";
span.appendChild(i);
var a = tag("a", { href: topic_detail.href });
a.textContent = topic_detail.title;
span.appendChild(a);
div.appendChild(span);
});
}
if (result.item.opensource != null) {
result.item.opensource.forEach(function (v) {
var opensource_detail = taxonomies.opensource[v];
if (opensource_detail == null) {
return;
}
var span = tag("span", { 'class': 'opensource' });
var i = tag("i", { 'class': 'material-icons', 'aria-hidden': 'true'});
i.textContent = "label";
span.appendChild(i);
var a = tag("a", { href: opensource_detail.href });
a.textContent = opensource_detail.title;
span.appendChild(a);
div.appendChild(span);
});
}
if (result.item.reading_time != null) {
var span = tag("span", { 'class': 'reading-time' });
var i = tag("i", { 'class': "material-icons", 'aria-hidden': "true" });
i.textContent = "schedule";
span.appendChild(i);
var readingTime = document.createTextNode(result.item.reading_time);
span.appendChild(readingTime);
div.appendChild(span);
}
section.appendChild(div);
}
var p = tag("p", {});
p.textContent = result.item.summary;
var a = tag("a", { href: result.item.permalink, 'class': 'continue-reading' });
a.textContent = {{ i18n "continue_reading" }};
p.appendChild(a);
section.appendChild(p);
searchResults.appendChild(section);
}
function clearSearchResults() {
var child = searchResults.lastElementChild;
while (child) {
searchResults.removeChild(child);
child = searchResults.lastElementChild;
}
}
//
// load the search index
//
function fuseInit(searchDb) {
var options = { // fuse.js options; check fuse.js website for details
shouldSort: true,
ignoreLocation: true,
threshold: 0.4,
minMatchCharLength: 3,
keys: [
'title',
'permalink',
'summary',
'topics',
'opensource'
]
};
fuse = new Fuse(searchDb, options); // build the index from the json file
}
//
// using the index loaded previously, run a search query (for "term")
// every time a letter is typed in the search box
//
function executeSearch(term) {
if (lastSearch == term) {
// Don't query Fuse if the search terms have not changed
return;
}
clearSearchResults();
let results = fuse.search(term);
results.slice(0, 20).forEach(printSearchResult);
lastSearch = term;
}
// Fill-in details about taxonomies
{{- $.Scratch.Set "taxonomies" dict -}}
{{- range $name, $taxonomy := .Site.Taxonomies -}}
{{- $.Scratch.Set "taxonomy" dict -}}
{{- range (index $.Site.Taxonomies $name) -}}
{{- $.Scratch.SetInMap "taxonomy" .Page.Title (dict "href" .Page.RelPermalink "title" .Page.Title) -}}
{{- end -}}
{{- $.Scratch.SetInMap "taxonomies" $name ($.Scratch.Get "taxonomy") -}}
{{- end }}
var taxonomies = {{- $.Scratch.Get "taxonomies" | jsonify | safeJS -}};
// Fill-in the Search DB with site's content
{{- $.Scratch.Add "index" slice -}}
{{- range .Site.RegularPages -}}
{{- $.Scratch.Set "reading_time" nil -}}
{{- if eq .Type "blog" -}}
{{- $minutes := .ReadingTime -}}
{{- if gt $minutes 0 -}}
{{- $.Scratch.Set "reading_time" (i18n "minutes" $minutes) -}}
{{- end -}}
{{- end -}}
{{- $.Scratch.Set "date" nil -}}
{{- if ne .Type "page" -}}
{{- $.Scratch.Set "date" (.Date.Format (i18n "date_format")) -}}
{{- end -}}
{{ .Scratch.Set "en_article_in_fr_site" false }}
{{- partial "lang-detection.html" . -}}
{{- $.Scratch.Add "index" (dict "title" .Title "opensource" .Params.opensource "topics" .Params.topics "permalink" .Permalink "hasEnFlag" (.Scratch.Get "en_article_in_fr_site") "type" .Type "type_label" (i18n (print "type_" .Type)) "date" ($.Scratch.Get "date") "reading_time" ($.Scratch.Get "reading_time") "summary" (.Summary | plainify | htmlUnescape)) -}}
{{- end }}
var searchDb = {{- $.Scratch.Get "index" | jsonify | safeJS -}};
// Initialize the Fuse.io framework with the Search DB
fuseInit(searchDb);
//
// execute search as each character is typed
//
searchInput.onkeyup = function(e) {
executeSearch(this.value);
}
</script>
</article>
{{ end }}

9
layouts/partials/head.html

@ -34,6 +34,15 @@
<link rel="stylesheet" href="/css/chroma.css">
<link rel="stylesheet" href="/css/itix.css">
<!-- The search page is the only one to require Javascript -->
{{ if eq .Type "search" }}
<!--
-- Download and copy over fuse.js file from fusejs.io.
-- I used the UMD build, minified, version 6.4.3
-->
<script src="/js/fuse.js"></script>
{{ end }}
{{- if .Params.enableMathJax -}}
{{- partial "mathjax.html" . -}}
{{- end -}}

3
layouts/partials/toolbar.html

@ -11,5 +11,8 @@
<a href="#top" class="action" title="{{ i18n "back_to_top" }}">
<i class="material-icons" aria-hidden="true">vertical_align_top</i>
</a>
<a href="{{ relref . "/search/index.md" }}" class="action" title="{{ i18n "search" }}">
<i class="material-icons" aria-hidden="true">search</i>
</a>
</div>
</aside>

23
static/css/itix.css

@ -590,29 +590,38 @@ article header h4 {
position: fixed;
bottom: 0;
display: flex;
flex-direction: column;
flex-direction: row;
margin-bottom: 50px;
margin-left: 30px;
}
.actions .action {
display: flex;
flex-direction: column;
align-content: center;
justify-content: center;
align-items: center;
justify-items: center;
width: 64px;
cursor: pointer;
padding: 15px 0;
margin-right: 10px;
}
.actions a {
text-decoration: none;
color: var(--main-text-color);
}
.actions a:visited {
color: var(--main-text-color);
}
.actions .material-icons {
font-size: 2em;
}
/*
* Search page
*/
.search input {
width: 100%;
}
@media screen {
/*
* Main part of the page (center)

9
static/js/fuse.js

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save