diff --git a/assets/js/custom.js b/assets/js/custom.js
index bf4c405..7930eec 100644
--- a/assets/js/custom.js
+++ b/assets/js/custom.js
@@ -10,4 +10,76 @@ Array.from(myLabels).forEach(label => {
label.click();
};
});
-});
\ No newline at end of file
+});
+
+// Search helpers from https://github.com/onweru/compose
+const rootURL = window.location.protocol + "//" + window.location.host;
+const searchFieldClass = '.search_field';
+const searchClass = '.search';
+const quickLinks = '{{ T "quick_links" }}';
+const searchResultsLabel = '{{ T "search_results_label" }}';
+const shortSearchQuery = '{{ T "short_search_query" }}'
+const typeToSearch = '{{ T "type_to_search" }}';
+const noMatchesFound = '{{ T "no_matches" }}';
+
+function createEl(element = 'div') {
+ return document.createElement(element);
+}
+
+function emptyEl(el) {
+ while(el.firstChild)
+ el.removeChild(el.firstChild);
+}
+
+function wrapText(text, context, wrapper = 'mark') {
+ let open = `<${wrapper}>`;
+ let close = `${wrapper}>`;
+ let escapedOpen = `%3C${wrapper}%3E`;
+ let escapedClose = `%3C/${wrapper}%3E`;
+ function wrap(context) {
+ let c = context.innerHTML;
+ let pattern = new RegExp(text, "gi");
+ let matches = text.length ? c.match(pattern) : null;
+
+ if(matches) {
+ matches.forEach(function(matchStr){
+ c = c.replaceAll(matchStr, `${open}${matchStr}${close}`);
+ context.innerHTML = c;
+ });
+
+ const images = elems('img', context);
+
+ if(images) {
+ images.forEach(image => {
+ image.src = image.src.replaceAll(open, '').replaceAll(close, '').replaceAll(escapedOpen, '').replaceAll(escapedClose, '');
+ });
+ }
+ }
+ }
+
+ const contents = ["h1", "h2", "h3", "h4", "h5", "h6", "p", "code", "td"];
+
+ contents.forEach(function(c){
+ const cs = elems(c, context);
+ if(cs.length) {
+ cs.forEach(function(cx, index){
+ if(cx.children.length >= 1) {
+ Array.from(cx.children).forEach(function(child){
+ wrap(child);
+ })
+ wrap(cx);
+ } else {
+ wrap(cx);
+ }
+ // sanitize urls and ids
+ });
+ }
+ });
+
+ const hyperLinks = elems('a');
+ if(hyperLinks) {
+ hyperLinks.forEach(function(link){
+ link.href = link.href.replaceAll(encodeURI(open), "").replaceAll(encodeURI(close), "");
+ });
+ }
+}
diff --git a/assets/sass/_custom.sass b/assets/sass/_custom.sass
index 0129993..572113f 100644
--- a/assets/sass/_custom.sass
+++ b/assets/sass/_custom.sass
@@ -7,6 +7,14 @@ html, body
&_brand
img
max-height: 2rem
+ // search box
+ &-item
+ display: grid
+ align-items: center
+ .search
+ @media screen and (min-width: 992px)
+ margin-right: 1.5rem
+
// Magic for collapsing content, borrowed from https://www.digitalocean.com/community/tutorials/css-collapsible
.lbl-toggle
@@ -64,4 +72,59 @@ html, body
code
word-break: normal
&.noClass
- line-break: normal
\ No newline at end of file
+ line-break: normal
+
+
+// search from https://github.com/onweru/compose
+.search
+ flex: 1
+ display: flex
+ justify-content: flex-end
+ position: relative
+ &_field
+ padding: 0.5rem 1.5rem 0.5rem 2.5rem
+ border-radius: 1.5rem
+ width: 13.5rem
+ outline: none
+ border: none
+ background: transparent
+ color: var(--text)
+ box-shadow: 0 1rem 4rem rgba(0,0,0,0.17)
+ font-size: 1rem
+ &_label
+ width: 1rem
+ height: 1rem
+ position: absolute
+ left: 0.33rem
+ top: 0.25rem
+ opacity: 0.33
+ svg
+ width: 100%
+ height: 100%
+ fill: var(--text)
+ &_result
+ padding: 0.5rem 1rem
+ &:not(.passive):hover
+ background-color: var(--theme)
+ color: var(--light)
+ &.passive
+ display: grid
+ &s
+ width: 13.5rem
+ background-color: var(--bg)
+ border-radius: 0 0 0.25rem 0.25rem
+ box-shadow: 0 1rem 4rem rgba(0,0,0,0.17)
+ position: absolute
+ top: 125%
+ display: grid
+ overflow: hidden
+ z-index: 5
+ &:empty
+ display: none
+ &_title
+ padding: 0.5rem 1rem 0.5rem 1rem
+ background: var(--theme)
+ color: var(--light)
+ font-size: 0.9rem
+ opacity: 0.87
+ text-transform: uppercase
diff --git a/config/_default/config.toml b/config/_default/config.toml
index 5a4a95c..547c093 100644
--- a/config/_default/config.toml
+++ b/config/_default/config.toml
@@ -10,3 +10,6 @@ DefaultContentLanguage = "en"
[permalinks]
posts = ":filename"
+
+[outputs]
+ home = ["HTML", "RSS","JSON"]
\ No newline at end of file
diff --git a/content/search.md b/content/search.md
new file mode 100644
index 0000000..65759a0
--- /dev/null
+++ b/content/search.md
@@ -0,0 +1,5 @@
++++
+title = "Search"
+searchPage = true
+type = "search"
++++
\ No newline at end of file
diff --git a/i18n/en.toml b/i18n/en.toml
index 20eef55..5cfd87a 100644
--- a/i18n/en.toml
+++ b/i18n/en.toml
@@ -14,4 +14,22 @@ other = "Powered by"
other = "and"
[view_source]
-other = "View source"
\ No newline at end of file
+other = "View source"
+
+[no_matches]
+other = "No matches found"
+
+[quick_links]
+other = "Quick links"
+
+[search_field_placeholder]
+other = "Search"
+
+[search_results_label]
+other = "Search Results"
+
+[short_search_query]
+other = "Query is too short"
+
+[type_to_search]
+other = "Type to search"
\ No newline at end of file
diff --git a/layouts/_default/index.json b/layouts/_default/index.json
new file mode 100644
index 0000000..5642b61
--- /dev/null
+++ b/layouts/_default/index.json
@@ -0,0 +1,7 @@
+{{- $.Scratch.Add "index" slice -}}
+{{- range .Site.Pages -}}
+ {{- if ne .Type "search" -}}
+ {{- $.Scratch.Add "index" (dict "title" .Title "body" .Plain "link" .Permalink) -}}
+ {{- end -}}
+{{- end -}}
+{{- jsonify (uniq ($.Scratch.Get "index")) -}}
\ No newline at end of file
diff --git a/layouts/partials/icons.html b/layouts/partials/icons.html
index 852ef1a..cdadb9b 100644
--- a/layouts/partials/icons.html
+++ b/layouts/partials/icons.html
@@ -61,4 +61,7 @@
+
+
+
diff --git a/layouts/partials/nav.html b/layouts/partials/nav.html
new file mode 100644
index 0000000..e854819
--- /dev/null
+++ b/layouts/partials/nav.html
@@ -0,0 +1,39 @@
+{{- $menu := .menu }}
+{{ $menuData := .context.Site.Data.menu }}
+{{- $link := .context.Permalink }}
+{{- $url := "" }}
+{{- $name := "" }}
+{{- $forwardSlash := "/" }}
+{{- $children := false }}
+{{- range $menu }}
+ {{- if eq $menu $menuData }}
+ {{- $children = .submenu }}
+ {{- $name = .name }}
+ {{- $url = absURL .link }}
+ {{- else }}
+ {{- $children = .Children }}
+ {{- $name = .Name }}
+ {{- $url = absLangURL .URL }}
+ {{- end }}
+
+
{{ $name }} {{ with $children }} {{ end }}
+ {{- with $children }}
+
+
+ {{- range . }}
+ {{- if eq $menu $menuData }}
+ {{- $name = .name }}
+ {{- $url = absURL .link }}
+ {{- else }}
+ {{- $name = .Name }}
+ {{- $url = absLangURL .URL }}
+ {{- end }}
+
{{ $name }}
+ {{- end }}
+
+ {{- end }}
+
+{{- end }}
+
+ {{- partial "search" . }}
+
\ No newline at end of file
diff --git a/layouts/partials/search.html b/layouts/partials/search.html
new file mode 100644
index 0000000..9bd4b7e
--- /dev/null
+++ b/layouts/partials/search.html
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/layouts/search/single.html b/layouts/search/single.html
new file mode 100644
index 0000000..456a268
--- /dev/null
+++ b/layouts/search/single.html
@@ -0,0 +1,5 @@
+{{- define "main" }}
+
+
+
+{{- end }}
\ No newline at end of file
diff --git a/static/js/fuse.js b/static/js/fuse.js
new file mode 100644
index 0000000..c3ba584
--- /dev/null
+++ b/static/js/fuse.js
@@ -0,0 +1,9 @@
+/**
+ * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io)
+ *
+ * Copyright (c) 2021 Kiro Risk (http://kiro.me)
+ * All Rights Reserved. Apache Software License 2.0
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+ var e,t;e=this,t=function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:3,t=new Map,n=Math.pow(10,e);return{get:function(e){var r=e.match(I).length;if(t.has(r))return t.get(r);var i=1/Math.sqrt(r),o=parseFloat(Math.round(i*n)/n);return t.set(r,o),o},clear:function(){t.clear()}}}var E=function(){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=n.getFn,i=void 0===r?A.getFn:r;t(this,e),this.norm=C(3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return r(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,g(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();g(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?A.getFn:r,o=new E({getFn:i});return o.setKeys(e.map(_)),o.setSources(t),o.create(),o}function R(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,c=t.expectedLocation,a=void 0===c?0:c,s=t.distance,u=void 0===s?A.distance:s,h=t.ignoreLocation,f=void 0===h?A.ignoreLocation:h,l=r/e.length;if(f)return l;var d=Math.abs(a-o);return u?l+d/u:d?1:l}function F(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:A.minMatchCharLength,n=[],r=-1,i=-1,o=0,c=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}function P(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,c=void 0===o?A.location:o,a=i.threshold,s=void 0===a?A.threshold:a,u=i.distance,h=void 0===u?A.distance:u,f=i.includeMatches,l=void 0===f?A.includeMatches:f,d=i.findAllMatches,v=void 0===d?A.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?A.minMatchCharLength:g,p=i.isCaseSensitive,m=void 0===p?A.isCaseSensitive:p,k=i.ignoreLocation,M=void 0===k?A.ignoreLocation:k;if(t(this,e),this.options={location:c,threshold:s,distance:h,includeMatches:l,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:m,ignoreLocation:M},this.pattern=m?n:n.toLowerCase(),this.chunks=[],this.pattern.length){var b=function(e,t){r.chunks.push({pattern:e,alphabet:P(e),startIndex:t})},x=this.pattern.length;if(x>32){for(var L=0,S=x%32,w=x-S;L3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?A.location:i,c=r.distance,a=void 0===c?A.distance:c,s=r.threshold,u=void 0===s?A.threshold:s,h=r.findAllMatches,f=void 0===h?A.findAllMatches:h,l=r.minMatchCharLength,d=void 0===l?A.minMatchCharLength:l,v=r.includeMatches,g=void 0===v?A.includeMatches:v,y=r.ignoreLocation,p=void 0===y?A.ignoreLocation:y;if(t.length>32)throw new Error(L(32));for(var m,k=t.length,M=e.length,b=Math.max(0,Math.min(o,M)),x=u,S=b,w=d>1||g,_=w?Array(M):[];(m=e.indexOf(t,S))>-1;){var O=R(t,{currentLocation:m,expectedLocation:b,distance:a,ignoreLocation:p});if(x=Math.min(O,x),S=m+k,w)for(var j=0;j=K;J-=1){var T=J-1,U=n[e.charAt(T)];if(w&&(_[T]=+!!U),W[J]=(W[J+1]<<1|1)&U,P&&(W[J]|=(I[J+1]|I[J])<<1|1|I[J+1]),W[J]&$&&(C=R(t,{errors:P,currentLocation:T,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(S=T)<=b)break;K=Math.max(1,2*b-S)}}var V=R(t,{errors:P+1,currentLocation:b,expectedLocation:b,distance:a,ignoreLocation:p});if(V>x)break;I=W}var B={isMatch:S>=0,score:Math.max(.001,C)};if(w){var G=F(_,d);G.length?g&&(B.indices=G):B.isMatch=!1}return B}(e,n,i,{location:c+o,distance:a,threshold:s,findAllMatches:u,minMatchCharLength:h,includeMatches:r,ignoreLocation:f}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(l(d),l(k)))}));var y={isMatch:g,score:g?v/this.chunks.length:1};return g&&r&&(y.indices=d),y}}]),e}(),D=function(){function e(n){t(this,e),this.pattern=n}return r(e,[{key:"search",value:function(){}}],[{key:"isMultiMatch",value:function(e){return z(e,this.multiRegex)}},{key:"isSingleMatch",value:function(e){return z(e,this.singleRegex)}}]),e}();function z(e,t){var n=e.match(t);return n?n[1]:null}var K=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"exact"}},{key:"multiRegex",get:function(){return/^="(.*)"$/}},{key:"singleRegex",get:function(){return/^=(.*)$/}}]),i}(D),q=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"$/}},{key:"singleRegex",get:function(){return/^!(.*)$/}}]),i}(D),W=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"prefix-exact"}},{key:"multiRegex",get:function(){return/^\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^\^(.*)$/}}]),i}(D),J=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-prefix-exact"}},{key:"multiRegex",get:function(){return/^!\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^!\^(.*)$/}}]),i}(D),T=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}}],[{key:"type",get:function(){return"suffix-exact"}},{key:"multiRegex",get:function(){return/^"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^(.*)\$$/}}]),i}(D),U=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-suffix-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^!(.*)\$$/}}]),i}(D),V=function(e){a(i,e);var n=f(i);function i(e){var r,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=o.location,a=void 0===c?A.location:c,s=o.threshold,u=void 0===s?A.threshold:s,h=o.distance,f=void 0===h?A.distance:h,l=o.includeMatches,d=void 0===l?A.includeMatches:l,v=o.findAllMatches,g=void 0===v?A.findAllMatches:v,y=o.minMatchCharLength,p=void 0===y?A.minMatchCharLength:y,m=o.isCaseSensitive,k=void 0===m?A.isCaseSensitive:m,M=o.ignoreLocation,b=void 0===M?A.ignoreLocation:M;return t(this,i),(r=n.call(this,e))._bitapSearch=new N(e,{location:a,threshold:u,distance:f,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:k,ignoreLocation:b}),r}return r(i,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),i}(D),B=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var o=!!r.length;return{isMatch:o,score:o?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),i}(D),G=[K,B,W,J,U,T,q,V],H=G.length,Q=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;function X(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(Q).filter((function(e){return e&&!!e.trim()})),r=[],i=0,o=n.length;i1&&void 0!==arguments[1]?arguments[1]:{},i=r.isCaseSensitive,o=void 0===i?A.isCaseSensitive:i,c=r.includeMatches,a=void 0===c?A.includeMatches:c,s=r.minMatchCharLength,u=void 0===s?A.minMatchCharLength:s,h=r.ignoreLocation,f=void 0===h?A.ignoreLocation:h,l=r.findAllMatches,d=void 0===l?A.findAllMatches:l,v=r.location,g=void 0===v?A.location:v,y=r.threshold,p=void 0===y?A.threshold:y,m=r.distance,k=void 0===m?A.distance:m;t(this,e),this.query=null,this.options={isCaseSensitive:o,includeMatches:a,minMatchCharLength:u,findAllMatches:d,ignoreLocation:f,location:g,threshold:p,distance:k},this.pattern=o?n:n.toLowerCase(),this.query=X(this.pattern,this.options)}return r(e,[{key:"searchIn",value:function(e){var t=this.query;if(!t)return{isMatch:!1,score:1};var n=this.options,r=n.includeMatches;e=n.isCaseSensitive?e:e.toLowerCase();for(var i=0,o=[],c=0,a=0,s=t.length;a-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function le(e,t){t.score=e.score}function de(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?A.includeMatches:r,o=n.includeScore,c=void 0===o?A.includeScore:o,a=[];return i&&a.push(fe),c&&a.push(le),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}))}var ve=function(){function e(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2?arguments[2]:void 0;t(this,e),this.options=c({},A,{},r),this.options.useExtendedSearch,this._keyStore=new w(this.options.keys),this.setCollection(n,i)}return r(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof E))throw new Error("Incorrect 'index' type");this._myIndex=t||$(this.options.keys,this._docs,{getFn:this.options.getFn})}},{key:"add",value:function(e){k(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&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,u=i.ignoreFieldNorm,h=g(e)?g(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return he(h,{ignoreFieldNorm:u}),a&&h.sort(s),y(r)&&r>-1&&(h=h.slice(0,r)),de(h,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=te(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(k(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,u=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:u}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.auto,i=void 0===r||r,o=function e(n){var r=Object.keys(n),o=ae(n);if(!o&&r.length>1&&!ce(n))return e(ue(n));if(se(n)){var c=o?n[ie]:r[0],a=o?n[oe]:n[c];if(!g(a))throw new Error(x(c));var s={keyId:j(c),pattern:a};return i&&(s.searcher=te(a,t)),s}var u={children:[],operator:r[0]};return r.forEach((function(t){var r=n[t];v(r)&&r.forEach((function(t){u.children.push(e(t))}))})),u};return ce(e)||(e=ue(e)),o(e)}(e,this.options),r=this._myIndex.records,i={},o=[];return r.forEach((function(e){var r=e.$,c=e.i;if(k(r)){var a=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}switch(n.operator){case ne:for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?A.getFn:n,i=e.keys,o=e.records,c=new E({getFn:r});return c.setKeys(i),c.setIndexRecords(o),c},ve.config=A,function(){ee.push.apply(ee,arguments)}(Z),ve},"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
new file mode 100644
index 0000000..0c96d96
--- /dev/null
+++ b/static/js/search.js
@@ -0,0 +1,205 @@
+// search from https://github.com/onweru/compose
+
+function initializeSearch(index) {
+ const searchKeys = ['title', 'link', 'body', 'id'];
+
+ const searchPageElement = elem('#searchpage');
+
+ const searchOptions = {
+ ignoreLocation: true,
+ findAllMatches: true,
+ includeScore: true,
+ shouldSort: true,
+ keys: searchKeys,
+ threshold: 0.0
+ };
+
+ index = new Fuse(index, searchOptions);
+
+ function minQueryLen(query) {
+ query = query.trim();
+ const queryIsFloat = parseFloat(query);
+ const minimumQueryLength = queryIsFloat ? 1 : 2;
+ return minimumQueryLength;
+ }
+
+ function searchResults(results=[], query="", passive = false) {
+ let resultsFragment = new DocumentFragment();
+ let showResults = elem('.search_results');
+ if(passive) {
+ showResults = searchPageElement;
+ }
+ emptyEl(showResults);
+
+ const queryLen = query.length;
+ const requiredQueryLen = minQueryLen(query);
+
+ if(results.length && queryLen >= requiredQueryLen) {
+ let resultsTitle = createEl('h3');
+ resultsTitle.className = 'search_title';
+ resultsTitle.innerText = quickLinks;
+ if(passive) {
+ resultsTitle.innerText = searchResultsLabel;
+ }
+ resultsFragment.appendChild(resultsTitle);
+ if(!searchPageElement) {
+ results = results.slice(0,8);
+ } else {
+ results = results.slice(0,12);
+ }
+ results.forEach(function(result){
+ let item = createEl('a');
+ item.href = `${result.link}?query=${query}`;
+ item.className = 'search_result';
+ item.style.order = result.score;
+ if(passive) {
+ pushClass(item, 'passive');
+ let itemTitle = createEl('h3');
+ itemTitle.textContent = result.title;
+ item.appendChild(itemTitle);
+
+ let itemDescription = createEl('p');
+ // position of first search term instance
+ let queryInstance = result.body.indexOf(query);
+ itemDescription.textContent = `... ${result.body.substring(queryInstance, queryInstance + 200)} ...`;
+ item.appendChild(itemDescription);
+ } else {
+ item.textContent = result.title;
+ }
+ resultsFragment.appendChild(item);
+ });
+ }
+
+ if(queryLen >= requiredQueryLen) {
+ if (!results.length) {
+ showResults.innerHTML = `${noMatchesFound} `;
+ }
+ } else {
+ showResults.innerHTML = `${ queryLen > 1 ? shortSearchQuery : typeToSearch } `
+ }
+
+ showResults.appendChild(resultsFragment);
+ }
+
+ function search(searchTerm, passive = false) {
+ if(searchTerm.length) {
+ let rawResults = index.search(searchTerm);
+ rawResults = rawResults.map(function(result){
+ const score = result.score;
+ const resultItem = result.item;
+ resultItem.score = (parseFloat(score) * 50).toFixed(0);
+ return resultItem;
+ });
+
+ passive ? searchResults(rawResults, searchTerm, true) : searchResults(rawResults, searchTerm);
+
+ } else {
+ passive ? searchResults([], "", true) : searchResults();
+ }
+ }
+
+ function liveSearch() {
+ const searchField = elem(searchFieldClass);
+
+ if (searchField) {
+ searchField.addEventListener('input', function() {
+ const searchTerm = searchField.value.trim().toLowerCase();
+ search(searchTerm);
+ });
+
+ if(!searchPageElement) {
+ searchField.addEventListener('search', function(){
+ const searchTerm = searchField.value.trim().toLowerCase();
+ if(searchTerm.length) {
+ window.location.href = new URL(`search/?query=${searchTerm}`, rootURL).href;
+ }
+ });
+ }
+ }
+ }
+
+ function findQuery(query = 'query') {
+ const urlParams = new URLSearchParams(window.location.search);
+ if(urlParams.has(query)){
+ let c = urlParams.get(query);
+ return c;
+ }
+ return "";
+ }
+
+ function passiveSearch() {
+ if(searchPageElement) {
+ const searchTerm = findQuery();
+ search(searchTerm, true);
+
+ // search actively after search page has loaded
+ const searchField = elem(searchFieldClass);
+
+ if(searchField) {
+ searchField.addEventListener('input', function() {
+ const searchTerm = searchField.value.trim().toLowerCase();
+ search(searchTerm, true);
+ wrapText(searchTerm, main);
+ });
+ }
+ }
+ }
+
+ function hasSearchResults() {
+ const searchResults = elem('.results');
+ if(searchResults) {
+ const body = searchResults.innerHTML.length;
+ return [searchResults, body];
+ }
+ return false
+ }
+
+ function clearSearchResults() {
+ let searchResults = hasSearchResults();
+ if(searchResults) {
+ searchResults = searchResults[0];
+ searchResults.innerHTML = "";
+ // clear search field
+ const searchField = elem(searchFieldClass);
+ searchField.value = "";
+ }
+ }
+
+ function onEscape(fn){
+ window.addEventListener('keydown', function(event){
+ if(event.code === "Escape") {
+ fn();
+ }
+ });
+ }
+
+ let main = elem('main');
+ if(!main) {
+ main = elem('.main');
+ }
+
+ searchPageElement ? false : liveSearch();
+ passiveSearch();
+
+ wrapText(findQuery(), main);
+
+ onEscape(clearSearchResults);
+
+ window.addEventListener('click', function(event){
+ const target = event.target;
+ const isSearch = target.closest(searchClass) || target.matches(searchClass);
+ if(!isSearch && !searchPageElement) {
+ clearSearchResults();
+ }
+ });
+}
+
+window.addEventListener('load', function() {
+ fetch(new URL("index.json", rootURL).href)
+ .then(response => response.json())
+ .then(function(data) {
+ data = data.length ? data : [];
+ initializeSearch(data);
+ })
+ .catch((error) => console.error(error));
+});
\ No newline at end of file