diff --git a/README.md b/README.md index 25f5278..92e45d2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Hugo Book Theme + [![Hugo](https://img.shields.io/badge/hugo-0.68-blue.svg)](https://gohugo.io) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) ![Build with Hugo](https://github.com/alex-shpak/hugo-book/workflows/Build%20with%20Hugo/badge.svg) @@ -94,12 +95,12 @@ hugo server --minify --theme hugo-book ### File tree menu (default) -By default, the theme will render pages from the `content/docs` section as a menu in a tree structure. +By default, the theme will render pages from the `content/docs` section as a menu in a tree structure. You can set `title` and `weight` in the front matter of pages to adjust the order and titles in the menu. ### Leaf bundle menu (Deprecated, to be removed in June 2022) -You can also use leaf bundle and the content of its `index.md` file as menu. +You can also use leaf bundle and the content of its `index.md` file as menu. Given you have the following file structure: ``` @@ -133,14 +134,14 @@ And Enable it by setting `BookMenuBundle: /menu` in Site configuration. ## Blog -A simple blog is supported in the section `posts`. +A simple blog is supported in the section `posts`. A blog is not the primary usecase of this theme, so it has only minimal features. ## Configuration ### Site Configuration -There are a few configuration options that you can add to your `config.toml` file. +There are a few configuration options that you can add to your `config.toml` file. You can also see the `yaml` example [here](https://github.com/alex-shpak/hugo-book/blob/master/exampleSite/config.yaml). ```toml @@ -347,7 +348,7 @@ If you want lower maintenance, use one of the released versions. If you want to ### [Extra credits to contributors](https://github.com/alex-shpak/hugo-book/graphs/contributors) -Contributions are welcome and I will review and consider pull requests. +Contributions are welcome and I will review and consider pull requests. Primary goals are: - Keep it simple. diff --git a/assets/_main.scss b/assets/_main.scss index a9ba8ac..c88c78c 100644 --- a/assets/_main.scss +++ b/assets/_main.scss @@ -244,11 +244,65 @@ body[dir="rtl"] .book-menu { } .book-search { + z-index: 2; position: relative; margin: $padding-16 0; border-bottom: 1px solid transparent; + #book-search-input { + display: flex; + align-items: center; + img { + z-index: 5; + position: absolute; + left: 30px; + } + } + #search-overlay { + background-color: #000; + pointer-events: auto; + opacity: 0.1; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 3; + } + #book-search-results { + position: absolute; + z-index: 3; + #book-search-hits { + overflow-y: scroll; + max-height: calc(100vh - 130px); // 130px -> uitopbar + content topbar + background-color: var(--body-background); + border-radius: 10px; + box-shadow: 0 16px 32px 16px rgba(0, 0, 0, 0.16); + margin-top: 5px; + padding: 16px; + position: relative; + ul { + margin: 0; + padding: 0; + li { + box-sizing: border-box; + cursor: pointer; + list-style-type: none; + padding: 10px; + h4 { + font-weight: 700; + color: var(--body-font-color); + } + &:hover { + background-color: var(--gray-100); + } + } + } + } + } input { + z-index: 4; + position: relative; width: 100%; padding: 7px 20px 7px 48px; @@ -303,14 +357,6 @@ body[dir="rtl"] .book-menu { } } -#book-search-results { - position: absolute; - z-index: 9999; - background: cyan; - li { - list-style: none; - } -} @media screen and (min-width: $mobile-breakpoint) { .book-search { diff --git a/assets/search.js b/assets/search.js index 2d75fee..2a30cec 100644 --- a/assets/search.js +++ b/assets/search.js @@ -2,29 +2,29 @@ {{ $searchDataFile := printf "%s.search-data.json" .Language.Lang }} {{ $searchData := resources.Get "search-data.json" | resources.ExecuteAsTemplate $searchDataFile . | resources.Minify | resources.Fingerprint }} -{{ $searchConfig := i18n "bookSearchConfig" | default "{}" }} (function () { const searchDataURL = '{{ $searchData.RelPermalink }}'; - const indexConfig = Object.assign({{ $searchConfig }}, { - doc: { - id: 'id', - field: ['title', 'content'], - store: ['title', 'href', 'section'] - } - }); - - const input = document.querySelector('#book-search-input'); - const results = document.querySelector('#book-search-results'); + const input = document.querySelector('#book-search-input input'); + const searchOverlay = document.querySelector('#search-overlay') + const resultsContainer = document.querySelector('#book-search-hits'); + const results = document.querySelector('#book-search-results ul'); + const LIMIT_RESULTS = Infinity + const MIN_INPUT_SIZE = 2 // SDK ✅ + const documents = new Map() if (!input) { return } + // Listeners input.addEventListener('focus', init); input.addEventListener('keyup', search); - document.addEventListener('keypress', focusSearchFieldOnKeyPress); + searchOverlay.addEventListener('click', () => { + hideSearchBox() + }) + /** * @param {Event} event @@ -49,7 +49,7 @@ /** * @param {String} character - * @returns {Boolean} + * @returns {Boolean} */ function isHotkey(character) { const dataHotkeys = input.getAttribute('data-hotkeys') || ''; @@ -59,39 +59,114 @@ function init() { input.removeEventListener('focus', init); // init once input.required = true; - fetch(searchDataURL) .then(pages => pages.json()) .then(pages => { - window.bookSearchIndex = FlexSearch.create('balance', indexConfig); - window.bookSearchIndex.add(pages); + window.lunrIdx = lunr(function() { + this.ref('id') + this.field('id') + this.field('content') + this.field('href') + this.metadataWhitelist = ['position'] + for (const page of pages) { + documents.set(page.id, page) + this.add(page); + } + }) }) .then(() => input.required = false) .then(search); } function search() { + const value = input.value?.trim() + if (input.required) { + return + } + while (results.firstChild) { results.removeChild(results.firstChild); } - if (!input.value) { + if (!value || value.length <= MIN_INPUT_SIZE) { + hideSearchBox() return; } - const searchHits = window.bookSearchIndex.search(input.value, 10); - searchHits.forEach(function (page) { - const li = element('
  • '); - const a = li.querySelector('a'), small = li.querySelector('small'); + function searchValue(fuzzy) { + // Operators: + // +: means AND. i.e +sdk +metaverse will found words that contains sdk & metaverse + // ~n: looks for N fuzzy words. i.e. metaverse~1 => metavese ✅ + return value.split(' ').map(val => { + // Avoid blankspaces + if (!val) return + // if its a short word or fuzzy option is turned off, then return only the value with the +operator + if (val.length <= 4 || !fuzzy) return `+${val}` - a.href = page.href; - a.textContent = page.title; - small.textContent = page.section; + return `+${val}~1` + }).filter(a => !!a).join(' ') + } - results.appendChild(li); + function getSearchHits() { + // First search for the words without fuzzy, so we can have a more accurate result. + const hits = window.lunrIdx.search(searchValue()).slice(0, LIMIT_RESULTS); + if (hits.length) return hits + return window.lunrIdx.search(searchValue(true)).slice(0, LIMIT_RESULTS); + } + const searchHits = getSearchHits() + showSearchBox() + if (!searchHits.length) { + resultCard(`Not Found`, `Sorry, we couldn't find any matches. Try searching for a different keyword`) + return + } + searchHits.forEach((hit) => { + const document = documents.get(Number(hit.ref)) + if (!document) return + const highlightedContent = highlightContent(document.content, hit) + resultCard(document.title, highlightedContent, document.href) }); } + function resultCard(title, content, href) { + const li = element('
  • '); + if (href) li.querySelector('a').href = href; + li.querySelector('h4').textContent = title; + li.querySelector('span').innerHTML = content + results.appendChild(li); + } + + function highlightContent(content, hit) { + const amountLetters = 60 + const { metadata } = hit.matchData + let from = 0 + let to = 100 + const keys = Object.keys(metadata).sort() + for (const key of keys) { + const positions = metadata[key]?.content?.position + if (!positions) { + continue + } + for (const position of positions) { + const positionStart = position[0] + from = Math.max(0, content.length - positionStart <= amountLetters + ? positionStart - amountLetters * 2 + : positionStart - amountLetters) + to = positionStart + position[1] + amountLetters + } + break + } + let value = content.slice(from, to) + if (from !== 0) { + value = `...${value}` + } + for (const key of keys) { + value = value.replace(new RegExp(key, 'gi'), '$&') + } + + return value + '...' + } + + // HELPERS /** * @param {String} content * @returns {Node} @@ -101,4 +176,26 @@ div.innerHTML = content; return div.firstChild; } + + function hide(element) { + element.classList.add('hidden') + } + + function show(element) { + element.classList.remove('hidden') + } + + function showSearchBox() { + if (!resultsContainer.classList.contains('hidden')) { + return + } + resultsContainer.scrollTop = 0 + show(searchOverlay) + show(resultsContainer) + } + + function hideSearchBox() { + hide(searchOverlay) + hide(resultsContainer) + } })(); diff --git a/layouts/partials/docs/html-head.html b/layouts/partials/docs/html-head.html index b22c610..90fd79e 100644 --- a/layouts/partials/docs/html-head.html +++ b/layouts/partials/docs/html-head.html @@ -27,7 +27,7 @@ {{- if default true .Site.Params.BookSearch -}} {{- $searchJSFile := printf "%s.search.js" .Language.Lang }} {{- $searchJS := resources.Get "search.js" | resources.ExecuteAsTemplate $searchJSFile . | resources.Minify | resources.Fingerprint }} - + {{ end -}} diff --git a/layouts/partials/docs/search.html b/layouts/partials/docs/search.html index 5cc9dad..6eef719 100644 --- a/layouts/partials/docs/search.html +++ b/layouts/partials/docs/search.html @@ -1,7 +1,15 @@ {{ if default true .Site.Params.BookSearch }} {{ end }} diff --git a/static/flexsearch.min.js b/static/flexsearch.min.js deleted file mode 100644 index 984d8c6..0000000 --- a/static/flexsearch.min.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - FlexSearch v0.6.30 - Copyright 2019 Nextapps GmbH - Author: Thomas Wilkerling - Released under the Apache 2.0 Licence - https://github.com/nextapps-de/flexsearch -*/ -'use strict';(function(K,R,w){let L;(L=w.define)&&L.amd?L([],function(){return R}):(L=w.modules)?L[K.toLowerCase()]=R:"object"===typeof exports?module.exports=R:w[K]=R})("FlexSearch",function ma(K){function w(a,c){const b=c?c.id:a&&a.id;this.id=b||0===b?b:na++;this.init(a,c);fa(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].c):Object.keys(this.c)});fa(this,"length",function(){return this.index.length})}function L(a,c,b,d){this.u!==this.g&&(this.o=this.o.concat(b),this.u++, -d&&this.o.length>=d&&(this.u=this.g),this.u===this.g&&(this.cache&&this.j.set(c,this.o),this.F&&this.F(this.o)));return this}function S(a){const c=B();for(const b in a)if(a.hasOwnProperty(b)){const d=a[b];F(d)?c[b]=d.slice(0):G(d)?c[b]=S(d):c[b]=d}return c}function W(a,c){const b=a.length,d=O(c),e=[];for(let f=0,h=0;f=h&&(a=a[g-(e+.5>>0)],a=a[b]||(a[b]=[]), -a[a.length]=d);return e}function ba(a,c){if(a){const b=Object.keys(a);for(let d=0,e=b.length;da?1:a?-1:0}function pa(a,c){a=a[M];c=c[M];return ac?1:0}function oa(a,c){const b=M.length;for(let d=0;dc?1:0}function T(a,c,b){return a?{page:a,next:c?""+c:null,result:b}:b}function ha(a,c,b,d,e,f,h){let g,k=[];if(!0===b){b="0";var l=""}else l=b&&b.split(":");const p=a.length;if(1h&&(l=0),l=l||0,g=l+c,g=this.m.length&&(this.C=0),this.m[this.C].postMessage({add:!0,id:a, -content:c}),this.c[f]=""+this.C,b&&b(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let t=this;f=new Promise(function(v){setTimeout(function(){t.add(a,c,null,d,!0);t=null;v()})});if(b)f.then(b);else return f;return this}if(b)return this.add(a,c,null,d,!0),b(),this}c=this.encode(c);if(!c.length)return this;b=this.f;e=O(b)?b(c):c.split(this.split);this.filter&&(e=W(e,this.filter));const n=B();n._ctx=B();const m=e.length,u=this.threshold,q=this.depth,A=this.b,z=this.i,y=this.D;for(let t= -0;tp;x--)l=h.substring(p,x),V(z,n,l,a,v,k,u,A-1)}break;default:if(g=V(z,n,h,a,1,k,u,A-1),q&&1=u)for(g=n._ctx[h]||(n._ctx[h]=B()),h=this.h[h]||(this.h[h]=ia(A-(u||0))),k=t-q,l=t+q+1,0>k&&(k=0),l> -m&&(l=m);kh;d--)e=g[d-1],g[d]=e,f[e]=d;g[h]=c;f[c]=h}}}return b};return a}();return w}(function(){const K={},R="undefined"!==typeof Blob&& -"undefined"!==typeof URL&&URL.createObjectURL;return function(w,L,S,W,P){S=R?URL.createObjectURL(new Blob(["("+S.toString()+")()"],{type:"text/javascript"})):w+".min.js";w+="-"+L;K[w]||(K[w]=[]);K[w][P]=new Worker(S);K[w][P].onmessage=W;return K[w][P]}}()),this); diff --git a/static/lunr.min.js b/static/lunr.min.js new file mode 100644 index 0000000..ae2661c --- /dev/null +++ b/static/lunr.min.js @@ -0,0 +1,6 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + */ +!function(){var e=function(t){var r=new e.Builder;return r.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),r.searchPipeline.add(e.stemmer),t.call(r,r),r.build()};e.version="2.3.9",e.utils={},e.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),e.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},e.utils.clone=function(e){if(null===e||void 0===e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i0){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); \ No newline at end of file diff --git a/static/svg/search.svg b/static/svg/search.svg new file mode 100644 index 0000000..6a43842 --- /dev/null +++ b/static/svg/search.svg @@ -0,0 +1,6 @@ + + + + + +