1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,c=i.includeScore,a=i.shouldSort,s=i.sortFn,h=i.ignoreFieldNorm,d=u(e)?u(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return $(d,{ignoreFieldNorm:h}),a&&d.sort(s),l(r)&&r>-1&&(d=d.slice(0,r)),D(d,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=P(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(f(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,h=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:h}]})}})),r}},{key:"_searchLogical",value:function(e){throw new Error("Logical search is not available")}},{key:"_searchObjectList",value:function(e){var t=this,n=P(e,this.options),r=this._myIndex,i=r.keys,o=r.records,c=[];return o.forEach((function(e){var r=e.$,o=e.i;if(f(r)){var s=[];i.forEach((function(e,i){s.push.apply(s,a(t._findMatches({key:e,value:r[i],searcher:n})))})),s.length&&c.push({idx:o,item:r,matches:s})}})),c}},{key:"_findMatches",value:function(e){var t=e.key,n=e.value,r=e.searcher;if(!f(n))return[];var i=[];if(h(n))n.forEach((function(e){var n=e.v,o=e.i,c=e.n;if(f(n)){var a=r.searchIn(n),s=a.isMatch,h=a.score,u=a.indices;s&&i.push({score:h,key:t,value:n,idx:o,norm:c,indices:u})}}));else{var o=n.v,c=n.n,a=r.searchIn(o),s=a.isMatch,u=a.score,l=a.indices;s&&i.push({score:u,key:t,value:o,norm:c,indices:l})}return i}}]),e}();function $(e,t){var n=t.ignoreFieldNorm,r=void 0===n?x.ignoreFieldNorm:n;e.forEach((function(e){var t=1;e.matches.forEach((function(e){var n=e.key,i=e.norm,o=e.score,c=n?n.weight:null;t*=Math.pow(0===o&&c?Number.EPSILON:o,(c||1)*(r?1:i))})),e.score=t}))}function D(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?x.includeMatches:r,o=n.includeScore,c=void 0===o?x.includeScore:o,a=[];return i&&a.push(O),c&&a.push(A),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}return N.version="6.4.3",N.createIndex=S,N.parseIndex=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?x.getFn:n,i=e.keys,o=e.records,c=new _({getFn:r});return c.setKeys(i),c.setIndexRecords(o),c},N.config=x,N},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Fuse=t();
\ No newline at end of file
diff --git a/static/js/search.js b/static/js/search.js
index 3f706cc..3e2ad5b 100644
--- a/static/js/search.js
+++ b/static/js/search.js
@@ -1,13 +1,17 @@
//
-// Search engine of the "search" page.
+// Search engine of the "search" page (ES module).
//
-// The search index, the taxonomies and the translated strings are generated
-// by Hugo in a JSON data block (see layouts/search.html), so that no inline
+// The search index and the taxonomies are generated by Hugo as a separate
+// JSON file (see layouts/search.searchindex.json), loaded when the page is
+// displayed and cached by the browser. The URL of the index and the translated
+// strings are embedded in the page in a JSON data block, so that no inline
// script is needed (Content Security Policy).
//
-var searchData = JSON.parse(document.getElementById('search-data').textContent);
-var taxonomies = searchData.taxonomies;
-var searchDb = searchData.index;
+import Fuse from './fuse.basic.min.js';
+
+var searchConfig = JSON.parse(document.getElementById('search-config').textContent);
+var taxonomies = {};
+var fuse = null;
var searchResults = document.getElementById('searchResults'); // targets the above
var searchInput = document.getElementById('searchInput'); // input box for search
@@ -50,7 +54,7 @@ function printSearchResult(result) {
if (result.item.type != "page") {
var div = tag("div", { 'class': 'post-metadata'});
var span_date = tag("span", { 'class': 'publication-date' });
- span_date.textContent = searchData.i18n.published_on + " " + result.item.date;
+ span_date.textContent = searchConfig.i18n.published_on + " " + result.item.date;
div.appendChild(span_date);
if (result.item.topics != null) {
result.item.topics.forEach(function (v) {
@@ -105,7 +109,7 @@ function printSearchResult(result) {
var p = tag("p", {});
p.textContent = result.item.summary;
var a = tag("a", { href: result.item.permalink, 'class': 'continue-reading' });
- a.textContent = searchData.i18n.continue_reading;
+ a.textContent = searchConfig.i18n.continue_reading;
p.appendChild(a);
section.appendChild(p);
@@ -121,28 +125,40 @@ function clearSearchResults() {
}
//
-// load the search index
+// Fuse.js options; check fuse.js website for details
//
-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
-}
+var fuseOptions = {
+ shouldSort: true,
+ ignoreLocation: true,
+ threshold: 0.4,
+ minMatchCharLength: 3,
+ keys: [
+ 'title',
+ 'permalink',
+ 'summary',
+ 'topics',
+ 'opensource'
+ ]
+};
+
+//
+// Load the search index and initialize Fuse.js
+//
+var searchReady = fetch(searchConfig.indexUrl)
+ .then(function (response) {
+ if (!response.ok) {
+ throw new Error("Cannot load the search index: HTTP " + response.status);
+ }
+ return response.json();
+ })
+ .then(function (data) {
+ taxonomies = data.taxonomies;
+ fuse = new Fuse(data.index, fuseOptions);
+ });
-//
-// using the index loaded previously, run a search query (for "term")
-// every time a letter is typed in the search box
+//
+// Run a search query (for "term") every time a letter is typed in the
+// search box
//
function executeSearch(term) {
if (lastSearch == term) {
@@ -156,12 +172,12 @@ function executeSearch(term) {
lastSearch = term;
}
-// 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);
-}
+searchInput.addEventListener('input', function () {
+ var term = this.value;
+ searchReady.then(function () {
+ // Only display the results of the latest search terms
+ if (searchInput.value === term) {
+ executeSearch(term);
+ }
+ });
+});