Finished search and moved components to setup functions

This commit is contained in:
squidfunk 2019-12-20 18:03:31 +01:00
parent c7dfcb87cc
commit 6863aeb4a4
36 changed files with 1184 additions and 354 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@
* lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.6
* Copyright (C) 2019 Oliver Nightingale
* @license MIT
*/!function(){var s,o,a,u,l,c,h,d,f,p,y,m,g,x,v,w,Q,S,b,E,k,P,L,T,O,I,R=function(e){var t=new R.Builder;return t.pipeline.add(R.trimmer,R.stopWordFilter,R.stemmer),t.searchPipeline.add(R.stemmer),e.call(t,t),t.build()};R.version="2.3.6"
*/!function(){var s,o,a,u,l,c,h,d,f,p,y,m,g,x,v,w,Q,b,S,E,k,P,L,T,O,I,R=function(e){var t=new R.Builder;return t.pipeline.add(R.trimmer,R.stopWordFilter,R.stemmer),t.searchPipeline.add(R.stemmer),e.call(t,t),t.build()};R.version="2.3.6"
/*!
* lunr.utils
* Copyright (C) 2019 Oliver Nightingale
@ -28,7 +28,7 @@
* lunr.stemmer
* Copyright (C) 2019 Oliver Nightingale
* Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
*/,R.stemmer=(o={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},a={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},u="[aeiouy]",l="[^aeiou][^aeiouy]*",c=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*"),h=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*"),d=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*([aeiouy][aeiou]*)?$"),f=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy]"),p=/^(.+?)(ss|i)es$/,y=/^(.+?)([^s])s$/,m=/^(.+?)eed$/,g=/^(.+?)(ed|ing)$/,x=/.$/,v=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),Q=new RegExp("^"+l+u+"[^aeiouwxy]$"),S=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,k=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,P=/^(.+?)(s|t)(ion)$/,L=/^(.+?)e$/,T=/ll$/,O=new RegExp("^"+l+u+"[^aeiouwxy]$"),I=function(e){var t,r,n,i,s,u,l;if(e.length<3)return e;if("y"==(n=e.substr(0,1))&&(e=n.toUpperCase()+e.substr(1)),s=y,(i=p).test(e)?e=e.replace(i,"$1$2"):s.test(e)&&(e=e.replace(s,"$1$2")),s=g,(i=m).test(e)){var I=i.exec(e);(i=c).test(I[1])&&(i=x,e=e.replace(i,""))}else s.test(e)&&(t=(I=s.exec(e))[1],(s=f).test(t)&&(u=w,l=Q,(s=v).test(e=t)?e+="e":u.test(e)?(i=x,e=e.replace(i,"")):l.test(e)&&(e+="e")));return(i=S).test(e)&&(e=(t=(I=i.exec(e))[1])+"i"),(i=b).test(e)&&(t=(I=i.exec(e))[1],r=I[2],(i=c).test(t)&&(e=t+o[r])),(i=E).test(e)&&(t=(I=i.exec(e))[1],r=I[2],(i=c).test(t)&&(e=t+a[r])),s=P,(i=k).test(e)?(t=(I=i.exec(e))[1],(i=h).test(t)&&(e=t)):s.test(e)&&(t=(I=s.exec(e))[1]+I[2],(s=h).test(t)&&(e=t)),(i=L).test(e)&&(t=(I=i.exec(e))[1],s=d,u=O,((i=h).test(t)||s.test(t)&&!u.test(t))&&(e=t)),s=h,(i=T).test(e)&&s.test(e)&&(i=x,e=e.replace(i,"")),"y"==n&&(e=n.toLowerCase()+e.substr(1)),e},function(e){return e.update(I)}),R.Pipeline.registerFunction(R.stemmer,"stemmer")
*/,R.stemmer=(o={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},a={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},u="[aeiouy]",l="[^aeiou][^aeiouy]*",c=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*"),h=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*"),d=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*([aeiouy][aeiou]*)?$"),f=new RegExp("^([^aeiou][^aeiouy]*)?[aeiouy]"),p=/^(.+?)(ss|i)es$/,y=/^(.+?)([^s])s$/,m=/^(.+?)eed$/,g=/^(.+?)(ed|ing)$/,x=/.$/,v=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),Q=new RegExp("^"+l+u+"[^aeiouwxy]$"),b=/^(.+?[^aeiou])y$/,S=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,k=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,P=/^(.+?)(s|t)(ion)$/,L=/^(.+?)e$/,T=/ll$/,O=new RegExp("^"+l+u+"[^aeiouwxy]$"),I=function(e){var t,r,n,i,s,u,l;if(e.length<3)return e;if("y"==(n=e.substr(0,1))&&(e=n.toUpperCase()+e.substr(1)),s=y,(i=p).test(e)?e=e.replace(i,"$1$2"):s.test(e)&&(e=e.replace(s,"$1$2")),s=g,(i=m).test(e)){var I=i.exec(e);(i=c).test(I[1])&&(i=x,e=e.replace(i,""))}else s.test(e)&&(t=(I=s.exec(e))[1],(s=f).test(t)&&(u=w,l=Q,(s=v).test(e=t)?e+="e":u.test(e)?(i=x,e=e.replace(i,"")):l.test(e)&&(e+="e")));return(i=b).test(e)&&(e=(t=(I=i.exec(e))[1])+"i"),(i=S).test(e)&&(t=(I=i.exec(e))[1],r=I[2],(i=c).test(t)&&(e=t+o[r])),(i=E).test(e)&&(t=(I=i.exec(e))[1],r=I[2],(i=c).test(t)&&(e=t+a[r])),s=P,(i=k).test(e)?(t=(I=i.exec(e))[1],(i=h).test(t)&&(e=t)):s.test(e)&&(t=(I=s.exec(e))[1]+I[2],(s=h).test(t)&&(e=t)),(i=L).test(e)&&(t=(I=i.exec(e))[1],s=d,u=O,((i=h).test(t)||s.test(t)&&!u.test(t))&&(e=t)),s=h,(i=T).test(e)&&s.test(e)&&(i=x,e=e.replace(i,"")),"y"==n&&(e=n.toLowerCase()+e.substr(1)),e},function(e){return e.update(I)}),R.Pipeline.registerFunction(R.stemmer,"stemmer")
/*!
* lunr.stopWordFilter
* Copyright (C) 2019 Oliver Nightingale
@ -44,7 +44,7 @@
/*!
* lunr.Index
* Copyright (C) 2019 Oliver Nightingale
*/,R.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},R.Index.prototype.search=function(e){return this.query((function(t){new R.QueryParser(e,t).parse()}))},R.Index.prototype.query=function(e){for(var t=new R.Query(this.fields),r=Object.create(null),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=0;a<this.fields.length;a++)n[this.fields[a]]=new R.Vector;e.call(t,t);for(a=0;a<t.clauses.length;a++){var u=t.clauses[a],l=null,c=R.Set.complete;l=u.usePipeline?this.pipeline.runString(u.term,{fields:u.fields}):[u.term];for(var h=0;h<l.length;h++){var d=l[h];u.term=d;var f=R.TokenSet.fromClause(u),p=this.tokenSet.intersect(f).toArray();if(0===p.length&&u.presence===R.Query.presence.REQUIRED){for(var y=0;y<u.fields.length;y++){s[F=u.fields[y]]=R.Set.empty}break}for(var m=0;m<p.length;m++){var g=p[m],x=this.invertedIndex[g],v=x._index;for(y=0;y<u.fields.length;y++){var w=x[F=u.fields[y]],Q=Object.keys(w),S=g+"/"+F,b=new R.Set(Q);if(u.presence==R.Query.presence.REQUIRED&&(c=c.union(b),void 0===s[F]&&(s[F]=R.Set.complete)),u.presence!=R.Query.presence.PROHIBITED){if(n[F].upsert(v,u.boost,(function(e,t){return e+t})),!i[S]){for(var E=0;E<Q.length;E++){var k,P=Q[E],L=new R.FieldRef(P,F),T=w[P];void 0===(k=r[L])?r[L]=new R.MatchData(g,F,T):k.add(g,F,T)}i[S]=!0}}else void 0===o[F]&&(o[F]=R.Set.empty),o[F]=o[F].union(b)}}}if(u.presence===R.Query.presence.REQUIRED)for(y=0;y<u.fields.length;y++){s[F=u.fields[y]]=s[F].intersect(c)}}var O=R.Set.complete,I=R.Set.empty;for(a=0;a<this.fields.length;a++){var F;s[F=this.fields[a]]&&(O=O.intersect(s[F])),o[F]&&(I=I.union(o[F]))}var j=Object.keys(r),_=[],C=Object.create(null);if(t.isNegated()){j=Object.keys(this.fieldVectors);for(a=0;a<j.length;a++){L=j[a];var N=R.FieldRef.fromString(L);r[L]=new R.MatchData}}for(a=0;a<j.length;a++){var D=(N=R.FieldRef.fromString(j[a])).docRef;if(O.contains(D)&&!I.contains(D)){var A,B=this.fieldVectors[N],M=n[N.fieldName].similarity(B);if(void 0!==(A=C[D]))A.score+=M,A.matchData.combine(r[N]);else{var V={ref:D,score:M,matchData:r[N]};C[D]=V,_.push(V)}}}return _.sort((function(e,t){return t.score-e.score}))},R.Index.prototype.toJSON=function(){var e=Object.keys(this.invertedIndex).sort().map((function(e){return[e,this.invertedIndex[e]]}),this),t=Object.keys(this.fieldVectors).map((function(e){return[e,this.fieldVectors[e].toJSON()]}),this);return{version:R.version,fields:this.fields,fieldVectors:t,invertedIndex:e,pipeline:this.pipeline.toJSON()}},R.Index.load=function(e){var t={},r={},n=e.fieldVectors,i=Object.create(null),s=e.invertedIndex,o=new R.TokenSet.Builder,a=R.Pipeline.load(e.pipeline);e.version!=R.version&&R.utils.warn("Version mismatch when loading serialised index. Current version of lunr '"+R.version+"' does not match serialized index '"+e.version+"'");for(var u=0;u<n.length;u++){var l=(h=n[u])[0],c=h[1];r[l]=new R.Vector(c)}for(u=0;u<s.length;u++){var h,d=(h=s[u])[0],f=h[1];o.insert(d),i[d]=f}return o.finish(),t.fields=e.fields,t.fieldVectors=r,t.invertedIndex=i,t.tokenSet=o.root,t.pipeline=a,new R.Index(t)}
*/,R.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},R.Index.prototype.search=function(e){return this.query((function(t){new R.QueryParser(e,t).parse()}))},R.Index.prototype.query=function(e){for(var t=new R.Query(this.fields),r=Object.create(null),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=0;a<this.fields.length;a++)n[this.fields[a]]=new R.Vector;e.call(t,t);for(a=0;a<t.clauses.length;a++){var u=t.clauses[a],l=null,c=R.Set.complete;l=u.usePipeline?this.pipeline.runString(u.term,{fields:u.fields}):[u.term];for(var h=0;h<l.length;h++){var d=l[h];u.term=d;var f=R.TokenSet.fromClause(u),p=this.tokenSet.intersect(f).toArray();if(0===p.length&&u.presence===R.Query.presence.REQUIRED){for(var y=0;y<u.fields.length;y++){s[F=u.fields[y]]=R.Set.empty}break}for(var m=0;m<p.length;m++){var g=p[m],x=this.invertedIndex[g],v=x._index;for(y=0;y<u.fields.length;y++){var w=x[F=u.fields[y]],Q=Object.keys(w),b=g+"/"+F,S=new R.Set(Q);if(u.presence==R.Query.presence.REQUIRED&&(c=c.union(S),void 0===s[F]&&(s[F]=R.Set.complete)),u.presence!=R.Query.presence.PROHIBITED){if(n[F].upsert(v,u.boost,(function(e,t){return e+t})),!i[b]){for(var E=0;E<Q.length;E++){var k,P=Q[E],L=new R.FieldRef(P,F),T=w[P];void 0===(k=r[L])?r[L]=new R.MatchData(g,F,T):k.add(g,F,T)}i[b]=!0}}else void 0===o[F]&&(o[F]=R.Set.empty),o[F]=o[F].union(S)}}}if(u.presence===R.Query.presence.REQUIRED)for(y=0;y<u.fields.length;y++){s[F=u.fields[y]]=s[F].intersect(c)}}var O=R.Set.complete,I=R.Set.empty;for(a=0;a<this.fields.length;a++){var F;s[F=this.fields[a]]&&(O=O.intersect(s[F])),o[F]&&(I=I.union(o[F]))}var j=Object.keys(r),C=[],_=Object.create(null);if(t.isNegated()){j=Object.keys(this.fieldVectors);for(a=0;a<j.length;a++){L=j[a];var N=R.FieldRef.fromString(L);r[L]=new R.MatchData}}for(a=0;a<j.length;a++){var D=(N=R.FieldRef.fromString(j[a])).docRef;if(O.contains(D)&&!I.contains(D)){var A,B=this.fieldVectors[N],M=n[N.fieldName].similarity(B);if(void 0!==(A=_[D]))A.score+=M,A.matchData.combine(r[N]);else{var V={ref:D,score:M,matchData:r[N]};_[D]=V,C.push(V)}}}return C.sort((function(e,t){return t.score-e.score}))},R.Index.prototype.toJSON=function(){var e=Object.keys(this.invertedIndex).sort().map((function(e){return[e,this.invertedIndex[e]]}),this),t=Object.keys(this.fieldVectors).map((function(e){return[e,this.fieldVectors[e].toJSON()]}),this);return{version:R.version,fields:this.fields,fieldVectors:t,invertedIndex:e,pipeline:this.pipeline.toJSON()}},R.Index.load=function(e){var t={},r={},n=e.fieldVectors,i=Object.create(null),s=e.invertedIndex,o=new R.TokenSet.Builder,a=R.Pipeline.load(e.pipeline);e.version!=R.version&&R.utils.warn("Version mismatch when loading serialised index. Current version of lunr '"+R.version+"' does not match serialized index '"+e.version+"'");for(var u=0;u<n.length;u++){var l=(h=n[u])[0],c=h[1];r[l]=new R.Vector(c)}for(u=0;u<s.length;u++){var h,d=(h=s[u])[0],f=h[1];o.insert(d),i[d]=f}return o.finish(),t.fields=e.fields,t.fieldVectors=r,t.invertedIndex=i,t.tokenSet=o.root,t.pipeline=a,new R.Index(t)}
/*!
* lunr.Builder
* Copyright (C) 2019 Oliver Nightingale
@ -55,5 +55,5 @@
* Copyright(c) 2015 Andreas Lubbe
* Copyright(c) 2015 Tiancheng "Timothy" Gu
* MIT Licensed
*/var n=/["'&<>]/;e.exports=function(e){var t,r=""+e,i=n.exec(r);if(!i)return r;var s="",o=0,a=0;for(o=i.index;o<r.length;o++){switch(r.charCodeAt(o)){case 34:t="&quot;";break;case 38:t="&amp;";break;case 39:t="&#39;";break;case 60:t="&lt;";break;case 62:t="&gt;";break;default:continue}a!==o&&(s+=r.substring(a,o)),a=o+1,s+=t}return a!==o?s+r.substring(a,o):s}},function(e,t,r){"use strict";const n=/[|\\{}()[\]^$+*?.-]/g;e.exports=e=>{if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(n,"\\$&")}},function(e,t,r){"use strict";r.r(t);var n=r(0),i=r(1),s=function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,i,s=r.call(e),o=[];try{for(;(void 0===t||t-- >0)&&!(n=s.next()).done;)o.push(n.value)}catch(e){i={error:e}}finally{try{n&&!n.done&&(r=s.return)&&r.call(s)}finally{if(i)throw i.error}}return o};var a=r(2),u=function(){return(u=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};var l,c,h=function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},d=function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,i,s=r.call(e),o=[];try{for(;(void 0===t||t-- >0)&&!(n=s.next()).done;)o.push(n.value)}catch(e){i={error:e}}finally{try{n&&!n.done&&(r=s.return)&&r.call(s)}finally{if(i)throw i.error}}return o},f=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(d(arguments[t]));return e},p={pipeline:{trimmer:!0,stopwords:!0}},y=function(){function e(e){var t=e.config,r=e.docs,l=e.options,c=e.index;this.documents=function(e){var t,r,n=new Map;try{for(var a=s(e),u=a.next();!u.done;u=a.next()){var l=u.value,c=o(l.location.split("#"),2),h=c[0],d=c[1],f=l.location,p=l.title,y=i(l.text).replace(/\s+(?=[,.:;!?])/g,"").replace(/\s+/g," ");if(d){var m=n.get(h);m.section?n.set(f,{location:f,title:p,text:y,article:m}):(m.title=l.title,m.text=y,m.section=!0)}else n.set(f,{location:f,title:p,text:y,section:!1})}}catch(e){t={error:e}}finally{try{u&&!u.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}return n}(r),this.highlight=function(e){var t=new RegExp(e.separator,"img"),r=function(e,t,r){return t+"<em>"+r+"</em>"};return function(n){n=n.replace(/[\s*+-:~^]+/g," ").trim();var i=new RegExp("(^|"+e.separator+")("+a(n).replace(t,"|")+")","img");return function(e){return u(u({},e),{title:e.title.replace(i,r),text:e.text.replace(i,r)})}}}(t),this.index=void 0===c?n((function(){var e,t,i=(l||p).pipeline;this.pipeline.reset(),i.trimmer&&this.pipeline.add(n.trimmer),i.stopwords&&this.pipeline.add(n.stopWordFilter),this.field("title",{boost:10}),this.field("text"),this.ref("location");try{for(var s=h(r),o=s.next();!o.done;o=s.next()){var a=o.value;this.add(a)}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}})):n.Index.load("string"==typeof c?JSON.parse(c):c)}return e.prototype.search=function(e){var t=this;if(!(e=e.replace(/(?:^|\s+)[*+-:^~]+(?=\s+|$)/g,"").trim()))return[];var r=this.index.search(e).reduce((function(e,r){var n=t.documents.get(r.ref);if(void 0!==n)if("article"in n){var i=n.article.location;e.set(i,f(e.get(i)||[],[r]))}else{i=n.location;e.set(i,e.get(i)||[])}return e}),new Map),n=this.highlight(e);return f(r).map((function(e){var r=d(e,2),i=r[0],s=r[1];return{article:n(t.documents.get(i)),sections:s.map((function(e){return n(t.documents.get(e.ref))}))}}))},e.prototype.toString=function(){return JSON.stringify(this.index)},e}();function m(e){switch(e.type){case l.SETUP:return c=new y(e.data),{type:l.DUMP,data:c.toString()};case l.QUERY:return{type:l.RESULT,data:c.search(e.data)};default:throw new TypeError("Invalid message type")}}!function(e){e[e.SETUP=0]="SETUP",e[e.DUMP=1]="DUMP",e[e.QUERY=2]="QUERY",e[e.RESULT=3]="RESULT"}(l||(l={})),r.d(t,"handler",(function(){return m})),addEventListener("message",(function(e){postMessage(m(e.data))}))}]);
*/var n=/["'&<>]/;e.exports=function(e){var t,r=""+e,i=n.exec(r);if(!i)return r;var s="",o=0,a=0;for(o=i.index;o<r.length;o++){switch(r.charCodeAt(o)){case 34:t="&quot;";break;case 38:t="&amp;";break;case 39:t="&#39;";break;case 60:t="&lt;";break;case 62:t="&gt;";break;default:continue}a!==o&&(s+=r.substring(a,o)),a=o+1,s+=t}return a!==o?s+r.substring(a,o):s}},function(e,t,r){"use strict";const n=/[|\\{}()[\]^$+*?.-]/g;e.exports=e=>{if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(n,"\\$&")}},function(e,t,r){"use strict";r.r(t);var n=r(0),i=r(1),s=function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,i,s=r.call(e),o=[];try{for(;(void 0===t||t-- >0)&&!(n=s.next()).done;)o.push(n.value)}catch(e){i={error:e}}finally{try{n&&!n.done&&(r=s.return)&&r.call(s)}finally{if(i)throw i.error}}return o};var a=r(2),u=function(){return(u=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};var l,c,h=function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},d=function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,i,s=r.call(e),o=[];try{for(;(void 0===t||t-- >0)&&!(n=s.next()).done;)o.push(n.value)}catch(e){i={error:e}}finally{try{n&&!n.done&&(r=s.return)&&r.call(s)}finally{if(i)throw i.error}}return o},f=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(d(arguments[t]));return e},p={pipeline:{trimmer:!0,stopwords:!0}},y=function(){function e(e){var t=e.config,r=e.docs,l=e.options,c=e.index;this.documents=function(e){var t,r,n=new Map;try{for(var a=s(e),u=a.next();!u.done;u=a.next()){var l=u.value,c=o(l.location.split("#"),2),h=c[0],d=c[1],f=l.location,p=l.title,y=i(l.text).replace(/\s+(?=[,.:;!?])/g,"").replace(/\s+/g," ");if(d){var m=n.get(h);m.section?n.set(f,{location:f,title:p,text:y,article:m}):(m.title=l.title,m.text=y,m.section=!0)}else n.set(f,{location:f,title:p,text:y,section:!1})}}catch(e){t={error:e}}finally{try{u&&!u.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}return n}(r),this.highlight=function(e){var t=new RegExp(e.separator,"img"),r=function(e,t,r){return t+"<em>"+r+"</em>"};return function(n){n=n.replace(/[\s*+-:~^]+/g," ").trim();var i=new RegExp("(^|"+e.separator+")("+a(n).replace(t,"|")+")","img");return function(e){return u(u({},e),{title:e.title.replace(i,r),text:e.text.replace(i,r)})}}}(t),this.index=void 0===c?n((function(){var e,t,i=(l||p).pipeline;this.pipeline.reset(),i.trimmer&&this.pipeline.add(n.trimmer),i.stopwords&&this.pipeline.add(n.stopWordFilter),this.field("title",{boost:10}),this.field("text"),this.ref("location");try{for(var s=h(r),o=s.next();!o.done;o=s.next()){var a=o.value;this.add(a)}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}})):n.Index.load("string"==typeof c?JSON.parse(c):c)}return e.prototype.search=function(e){var t=this;if(e)try{var r=this.index.search(e).reduce((function(e,r){var n=t.documents.get(r.ref);if(void 0!==n)if("article"in n){var i=n.article.location;e.set(i,f(e.get(i)||[],[r]))}else{i=n.location;e.set(i,e.get(i)||[])}return e}),new Map),n=this.highlight(e);return f(r).map((function(e){var r=d(e,2),i=r[0],s=r[1];return{article:n(t.documents.get(i)),sections:s.map((function(e){return n(t.documents.get(e.ref))}))}}))}catch(t){console.warn("Invalid query: "+e+" see https://bit.ly/2s3ChXG")}return[]},e.prototype.toString=function(){return JSON.stringify(this.index)},e}();function m(e){switch(e.type){case l.SETUP:return c=new y(e.data),{type:l.DUMP,data:c.toString()};case l.QUERY:return{type:l.RESULT,data:c?c.search(e.data):[]};default:throw new TypeError("Invalid message type")}}!function(e){e[e.SETUP=0]="SETUP",e[e.DUMP=1]="DUMP",e[e.QUERY=2]="QUERY",e[e.RESULT=3]="RESULT"}(l||(l={})),r.d(t,"handler",(function(){return m})),addEventListener("message",(function(e){postMessage(m(e.data))}))}]);
//# sourceMappingURL=search.js.map

File diff suppressed because one or more lines are too long

View File

@ -1936,6 +1936,7 @@ hr {
.md-typeset details:not([open]) > summary {
border-bottom: none; }
.md-typeset details summary {
position: relative;
padding-right: 2rem; }
[dir="rtl"] .md-typeset details summary {
padding-left: 2rem; }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -226,7 +226,7 @@
"search.language",
"search.pipeline.stopwords",
"search.pipeline.trimmer",
"search.placeholder",
"search.result.placeholder",
"search.result.none",
"search.result.one",
"search.result.other",

View File

@ -50,7 +50,7 @@ export function setSearchResultMeta(
export function resetSearchResultMeta(
el: HTMLElement
): void {
el.textContent = translate("search.placeholder")
el.textContent = translate("search.result.placeholder")
}
/* ------------------------------------------------------------------------- */

View File

@ -70,7 +70,7 @@ interface Options {
* ------------------------------------------------------------------------- */
/**
* Watch component map
* Watch component mapping
*
* This function returns an observable that will maintain bindings to the given
* components in-between document switches and update the document in-place.
@ -78,7 +78,7 @@ interface Options {
* @param names - Component names
* @param options - Options
*
* @return Component map observable
* @return Component mapping observable
*/
export function watchComponentMap(
names: Component[], { document$ }: Options

View File

@ -28,7 +28,8 @@ import {
switchMapTo
} from "rxjs/operators"
import { ViewportOffset, ViewportSize } from "../../../utilities"
import { Agent, ViewportOffset } from "utilities"
import { Header } from "../_"
/* ----------------------------------------------------------------------------
@ -39,8 +40,6 @@ import { Header } from "../_"
* Options
*/
interface Options {
size$: Observable<ViewportSize> /* Viewport size observable */
offset$: Observable<ViewportOffset> /* Viewport offset observable */
header$: Observable<Header> /* Header observable */
}
@ -55,16 +54,17 @@ interface Options {
* top of the given element based on the current viewport offset.
*
* @param el - HTML element
* @param agent - Agent
* @param options - Options
*
* @return Viewport offset observable
*/
export function watchHeaderOffsetToTopOf(
el: HTMLElement, { size$, offset$, header$ }: Options
el: HTMLElement, { viewport }: Agent, { header$ }: Options
): Observable<ViewportOffset> {
/* Compute necessary adjustment for offset */
const adjust$ = size$
const adjust$ = viewport.size$
.pipe(
switchMapTo(header$),
map(({ height }) => el.offsetTop - height),
@ -72,7 +72,7 @@ export function watchHeaderOffsetToTopOf(
)
/* Compute relative offset and return as hot observable */
return combineLatest([offset$, adjust$])
return combineLatest([viewport.offset$, adjust$])
.pipe(
map(([{ x, y }, adjust]) => ({ x, y: y - adjust })),
shareReplay(1)
@ -86,16 +86,17 @@ export function watchHeaderOffsetToTopOf(
* bottom of the given element based on the current viewport offset.
*
* @param el - HTML element
* @param agent - Agent
* @param options - Options
*
* @return Viewport offset observable
*/
export function watchHeaderOffsetToBottomOf(
el: HTMLElement, { size$, offset$, header$ }: Options
el: HTMLElement, { viewport }: Agent, { header$ }: Options
): Observable<ViewportOffset> {
/* Compute necessary adjustment for offset */
const adjust$ = size$
const adjust$ = viewport.size$
.pipe(
switchMapTo(header$),
map(({ height }) => el.offsetTop + el.offsetHeight - height),
@ -103,7 +104,7 @@ export function watchHeaderOffsetToBottomOf(
)
/* Compute relative offset and return as hot observable */
return combineLatest([offset$, adjust$])
return combineLatest([viewport.offset$, adjust$])
.pipe(
map(([{ x, y }, adjust]) => ({ x, y: y - adjust })),
shareReplay(1)

View File

@ -32,7 +32,8 @@ import {
tap
} from "rxjs/operators"
import { resetHeaderShadow, setHeaderShadow } from "../../../actions"
import { resetHeaderShadow, setHeaderShadow } from "actions"
import { Main } from "../../main"
/* ----------------------------------------------------------------------------

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, shareReplay } from "rxjs/operators"
import { switchMapIf } from "extensions"
import { Agent, paintHidden } from "utilities"
import { Header, watchHeaderOffsetToTopOf } from "../header"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Hero
*/
export interface Hero {
hidden: boolean /* Whether the hero is hidden */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Setup hero from source observable
*
* @param agent - Agent
* @param options - Options
*
* @return Operator function
*/
export function setupHero(
agent: Agent, { header$ }: Options
): OperatorFunction<HTMLElement, Hero> {
const { media } = agent
return pipe(
switchMapIf(media.screen$, el => {
/* Watch and paint visibility */
const hidden$ = watchHeaderOffsetToTopOf(el, agent, { header$ })
.pipe(
paintHidden(el, 20)
)
/* Combine into a single hot observable */
return hidden$
.pipe(
map(hidden => ({ hidden }))
)
}),
shareReplay(1)
)
}

View File

@ -21,9 +21,10 @@
*/
export * from "./_"
export * from "./anchor"
export * from "./header"
export * from "./hidden"
export * from "./hero"
export * from "./navigation"
export * from "./main"
export * from "./search"
export * from "./sidebar"
export * from "./tabs"
export * from "./toc"

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
import {
distinctUntilChanged,
map,
pluck,
shareReplay,
switchMap
} from "rxjs/operators"
import { Agent } from "utilities"
import { Header } from "../../header"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Main area
*/
export interface Main {
offset: number /* Main area top offset */
height: number /* Main area visible height */
active: boolean /* Scrolled past top offset */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Setup main area from source observable
*
* This function returns an observable that computes the visual parameters of
* the main area from the viewport height and vertical offset, as well as the
* height of the header element. The height of the main area is corrected by
* the height of the header (if fixed) and footer element.
*
* @param agent - Agent
* @param options - Options
*
* @return Operator function
*/
export function setupMain(
{ viewport }: Agent, { header$ }: Options
): OperatorFunction<HTMLElement, Main> {
return pipe(
switchMap(el => {
/* Compute necessary adjustment for header */
const adjust$ = header$
.pipe(
pluck("height")
)
/* Compute the main area's visible height */
const height$ = combineLatest([
viewport.offset$,
viewport.size$,
adjust$
])
.pipe(
map(([{ y }, { height }, adjust]) => {
const top = el.offsetTop
const bottom = el.offsetHeight + top
return height
- Math.max(0, top - y, adjust)
- Math.max(0, height + y - bottom)
}),
distinctUntilChanged()
)
/* Compute whether the viewport offset is past the main area's top */
const active$ = combineLatest([viewport.offset$, adjust$])
.pipe(
map(([{ y }, adjust]) => y >= el.offsetTop - adjust),
distinctUntilChanged()
)
/* Combine into a single hot observable */
return combineLatest([height$, adjust$, active$])
.pipe(
map(([height, adjust, active]) => ({
offset: el.offsetTop - adjust,
height,
active
}))
)
}),
shareReplay(1)
)
}

View File

@ -20,99 +20,5 @@
* IN THE SOFTWARE.
*/
import { Observable, combineLatest } from "rxjs"
import {
distinctUntilChanged,
map,
pluck,
shareReplay
} from "rxjs/operators"
import { ViewportOffset, ViewportSize } from "utilities"
import { Header } from "../header"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Main area
*/
export interface Main {
offset: number /* Main area top offset */
height: number /* Main area visible height */
active: boolean /* Scrolled past top offset */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
size$: Observable<ViewportSize> /* Viewport size observable */
offset$: Observable<ViewportOffset> /* Viewport offset observable */
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Watch main area
*
* This function returns an observable that computes the visual parameters of
* the main area from the viewport height and vertical offset, as well as the
* height of the header element. The height of the main area is corrected by
* the height of the header (if fixed) and footer element.
*
* @param el - Main area element
* @param options - Options
*
* @return Main area observable
*/
export function watchMain(
el: HTMLElement, { size$, offset$, header$ }: Options
): Observable<Main> {
/* Compute necessary adjustment for header */
const adjust$ = header$
.pipe(
pluck("height")
)
/* Compute the main area's visible height */
const height$ = combineLatest([offset$, size$, adjust$])
.pipe(
map(([{ y }, { height }, adjust]) => {
const top = el.offsetTop
const bottom = el.offsetHeight + top
return height
- Math.max(0, top - y, adjust)
- Math.max(0, height + y - bottom)
}),
distinctUntilChanged()
)
/* Compute whether the viewport offset is past the main area's top */
const active$ = combineLatest([offset$, adjust$])
.pipe(
map(([{ y }, adjust]) => y >= el.offsetTop - adjust),
distinctUntilChanged()
)
/* Combine into a single hot observable */
return combineLatest([height$, adjust$, active$])
.pipe(
map(([height, adjust, active]) => ({
offset: el.offsetTop - adjust,
height,
active
})),
shareReplay(1)
)
}
export * from "./_"
export * from "./sidebar"

View File

@ -43,7 +43,7 @@ import {
setSidebarHeight,
setSidebarLock
} from "actions"
import { ViewportOffset } from "utilities"
import { Agent } from "utilities"
import { Main } from "../main"
@ -67,7 +67,6 @@ export interface Sidebar {
* Options
*/
interface Options {
offset$: Observable<ViewportOffset> /* Viewport offset observable */
main$: Observable<Main> /* Main area observable */
}
@ -84,12 +83,13 @@ interface Options {
* the sidebar is locked and fills the remaining space.
*
* @param el - Sidebar element
* @param agent - Agent
* @param options - Options
*
* @return Sidebar observable
*/
export function watchSidebar(
el: HTMLElement, { offset$, main$ }: Options
el: HTMLElement, { viewport }: Agent, { main$ }: Options
): Observable<Sidebar> {
/* Adjust for internal main area offset */
@ -99,7 +99,7 @@ export function watchSidebar(
)
/* Compute the sidebar's available height */
const height$ = combineLatest([offset$, main$])
const height$ = combineLatest([viewport.offset$, main$])
.pipe(
map(([{ y }, { offset, height }]) => {
return height - adjust + Math.min(adjust, Math.max(0, y - offset))
@ -107,7 +107,7 @@ export function watchSidebar(
)
/* Compute whether the sidebar should be locked */
const lock$ = combineLatest([offset$, main$])
const lock$ = combineLatest([viewport.offset$, main$])
.pipe(
map(([{ y }, { offset }]) => y >= offset + adjust)
)

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, shareReplay } from "rxjs/operators"
import { switchMapIf } from "extensions"
import { Agent } from "utilities"
import {
Main,
Sidebar,
paintSidebar,
watchSidebar
} from "../../main"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Navigation
*/
export interface Navigation {
sidebar: Sidebar /* Sidebar */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
main$: Observable<Main> /* Main observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Setup navigation from source observable
*
* @param agent - Agent
* @param options - Options
*
* @return Operator function
*/
export function setupNavigation(
agent: Agent, { main$ }: Options
): OperatorFunction<HTMLElement, Navigation> {
const { media } = agent
return pipe(
switchMapIf(media.screen$, el => {
/* Watch and paint sidebar */
const sidebar$ = watchSidebar(el, agent, { main$ })
.pipe(
paintSidebar(el)
)
/* Combine into a single hot observable */
return sidebar$
.pipe(
map(sidebar => ({ sidebar }))
)
}),
shareReplay(1)
)
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
export * from "./_"

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { identity } from "ramda"
import { Observable, OperatorFunction, pipe } from "rxjs"
import {
distinctUntilChanged,
filter,
map,
switchMap,
} from "rxjs/operators"
import { SearchResult } from "modules"
import { Agent, watchElementOffset } from "utilities"
import { paintSearchResultList } from "../list"
import { paintSearchResultMeta } from "../meta"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
result$: Observable<SearchResult[]> /* Search result observable */
query$: Observable<string> /* Search query observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Setup search result from source observable
*
* @param el - Search result element
* @param agent - Agent
*
* @return Operator function
*/
export function setupSearchResult(
agent: Agent, { result$, query$ }: Options
): OperatorFunction<HTMLElement, SearchResult[]> {
return pipe(
switchMap(el => {
const parent = el.parentElement!
/* Compute whether more elements need to be rendered */
const render$ = watchElementOffset(parent, agent)
.pipe(
map(({ y }) => y >= parent.scrollHeight - parent.offsetHeight - 16),
distinctUntilChanged(),
filter(identity)
)
/* Paint search results */
return result$
.pipe(
paintSearchResultMeta(el, { query$ }),
paintSearchResultList(el, { render$ })
)
})
)
}

View File

@ -20,99 +20,6 @@
* IN THE SOFTWARE.
*/
import { identity } from "ramda"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
pipe
} from "rxjs"
import {
distinctUntilChanged,
filter,
finalize,
map,
mapTo,
observeOn,
scan,
switchMap,
tap
} from "rxjs/operators"
import {
addToSearchResultList,
resetSearchResultList,
setSearchResultMeta
} from "actions"
import { SearchResult } from "modules"
import { renderSearchResult } from "templates"
import { ViewportSize, watchElementOffset } from "utilities"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
size$: Observable<ViewportSize> /* Viewport size observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint search result from source observable
*
* @param el - Search result element
*
* @return Operator function
*/
export function paintSearchResult(
el: HTMLElement, { size$ }: Options
): MonoTypeOperatorFunction<SearchResult[]> {
const container = el.parentElement!
/* Compute whether the container is near the bottom offset */
const render$ = watchElementOffset(container, { size$ })
.pipe(
map(({ y }) => y >= container.scrollHeight - container.offsetHeight - 16),
distinctUntilChanged(),
filter(identity)
)
/* Paint search results lazily */
const [meta, list] = Array.from(el.children) as HTMLElement[]
return pipe(
/* Paint search result metadata */
tap(result => {
setSearchResultMeta(meta, result.length)
}),
/* Paint search result list */
switchMap(result => render$
.pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
scan(index => {
while (index < result.length) {
addToSearchResultList(list, renderSearchResult(result[index++]))
if (container.scrollHeight - container.offsetHeight > 16)
break
}
return index
}, 0),
mapTo(result),
/* Reset on complete or error */
finalize(() => {
resetSearchResultList(list)
})
)
)
)
}
export * from "./_"
export * from "./list"
export * from "./meta"

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
pipe
} from "rxjs"
import {
finalize,
mapTo,
observeOn,
scan,
switchMap
} from "rxjs/operators"
import {
addToSearchResultList,
resetSearchResultList
} from "actions"
import { SearchResult } from "modules"
import { renderSearchResult } from "templates"
import { getElement } from "utilities"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
render$: Observable<boolean> /* Render trigger observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint search result list from source observable
*
* @param el - Search result element
* @param options - Options
*
* @return Operator function
*/
export function paintSearchResultList(
el: HTMLElement, { render$ }: Options
): MonoTypeOperatorFunction<SearchResult[]> {
const parent = el.parentElement!
const list = getElement(".md-search-result__list", el)!
return pipe(
switchMap(result => render$
.pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
scan(index => {
while (index < result.length) {
addToSearchResultList(list, renderSearchResult(result[index++]))
if (parent.scrollHeight - parent.offsetHeight > 16)
break
}
return index
}, 0),
/* Re-map to search result */
mapTo(result),
/* Reset on complete or error */
finalize(() => {
resetSearchResultList(list)
})
)
)
)
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { MonoTypeOperatorFunction, Observable, pipe } from "rxjs"
import { map, withLatestFrom } from "rxjs/operators"
import {
resetSearchResultMeta,
setSearchResultMeta
} from "actions"
import { SearchResult } from "modules"
import { getElement } from "utilities"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
query$: Observable<string> /* Search query observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint search result metadata from source observable
*
* @param el - Search result metadata element
* @param options - Options
*
* @return Operator function
*/
export function paintSearchResultMeta(
el: HTMLElement, { query$ }: Options
): MonoTypeOperatorFunction<SearchResult[]> {
const meta = getElement(".md-search-result__meta", el)!
return pipe(
withLatestFrom(query$),
map(([result, query]) => {
if (query) {
setSearchResultMeta(meta, result.length)
} else {
resetSearchResultMeta(meta)
}
return result
})
)
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, shareReplay } from "rxjs/operators"
import { switchMapIf } from "extensions"
import { Agent, paintHidden } from "utilities"
import { Header, watchHeaderOffsetToTopOf } from "../header"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Tabs
*/
export interface Tabs {
hidden: boolean /* Whether the tabs are hidden */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Setup tabs from source observable
*
* @param agent - Agent
* @param options - Options
*
* @return Operator function
*/
export function setupTabs(
agent: Agent, { header$ }: Options
): OperatorFunction<HTMLElement, Tabs> {
const { media } = agent
return pipe(
switchMapIf(media.screen$, el => {
/* Watch and paint visibility */
const hidden$ = watchHeaderOffsetToTopOf(el, agent, { header$ })
.pipe(
paintHidden(el, 8)
)
/* Combine into a single hot observable */
return hidden$
.pipe(
map(hidden => ({ hidden }))
)
}),
shareReplay(1)
)
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
import { map, shareReplay } from "rxjs/operators"
import { switchMapIf } from "extensions"
import { Agent, getElements } from "utilities"
import { Header } from "../../header"
import {
Main,
Sidebar,
paintSidebar,
watchSidebar
} from "../../main"
import {
AnchorList,
paintAnchorList,
watchAnchorList
} from "../anchor"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Table of contents
*/
export interface TableOfContents {
sidebar: Sidebar /* Sidebar */
anchors: AnchorList /* Anchor list */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Options
*/
interface Options {
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Setup table of contents from source observable
*
* @param agent - Agent
* @param options - Options
*
* @return Operator function
*/
export function setupTableOfContents(
agent: Agent, { header$, main$ }: Options
): OperatorFunction<HTMLElement, TableOfContents> {
const { media } = agent
return pipe(
switchMapIf(media.tablet$, el => {
/* Watch and paint sidebar */
const sidebar$ = watchSidebar(el, agent, { main$ })
.pipe(
paintSidebar(el)
)
/* Watch and paint anchor list (scroll spy) */
const els = getElements<HTMLAnchorElement>(".md-nav__link", el)
const anchors$ = watchAnchorList(els, agent, { header$ })
.pipe(
paintAnchorList(els)
)
/* Combine into a single hot observable */
return combineLatest([sidebar$, anchors$])
.pipe(
map(([sidebar, anchors]) => ({ sidebar, anchors }))
)
}),
shareReplay(1)
)
}

View File

@ -45,13 +45,9 @@ import {
setAnchorActive,
setAnchorBlur
} from "actions"
import {
ViewportOffset,
ViewportSize,
getElement
} from "utilities"
import { Agent, getElement } from "utilities"
import { Header } from "../header"
import { Header } from "../../header"
/* ----------------------------------------------------------------------------
* Types
@ -73,8 +69,6 @@ export interface AnchorList {
* Options
*/
interface Options {
size$: Observable<ViewportSize> /* Viewport size observable */
offset$: Observable<ViewportOffset> /* Viewport offset observable */
header$: Observable<Header> /* Header observable */
}
@ -98,12 +92,13 @@ interface Options {
* Note that the current anchor is the last item of the `prev` anchor list.
*
* @param els - Anchor elements
* @param agent - Agent
* @param options - Options
*
* @return Anchor list observable
*/
export function watchAnchorList(
els: HTMLAnchorElement[], { size$, offset$, header$ }: Options
els: HTMLAnchorElement[], { viewport }: Agent, { header$ }: Options
): Observable<AnchorList> {
const table = new Map<HTMLAnchorElement, HTMLElement>()
for (const el of els) {
@ -120,7 +115,7 @@ export function watchAnchorList(
)
/* Compute partition of previous and next anchors */
const partition$ = size$
const partition$ = viewport.size$
.pipe(
/* Build index to map anchor paths to vertical offsets */
@ -143,7 +138,7 @@ export function watchAnchorList(
}),
/* Re-compute partition when viewport offset changes */
switchMap(index => combineLatest(offset$, adjust$)
switchMap(index => combineLatest(viewport.offset$, adjust$)
.pipe(
scan(([prev, next], [{ y }, adjust]) => {

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
export * from "./_"
export * from "./anchor"

View File

@ -20,16 +20,26 @@
* IN THE SOFTWARE.
*/
// TODO: remove this later on
// tslint:disable
import { identity } from "ramda"
import {
EMPTY,
MonoTypeOperatorFunction,
NEVER,
Observable,
Subject,
defer,
forkJoin,
fromEvent,
merge,
of,
pipe,
} from "rxjs"
import {
combineAll,
delay,
distinctUntilKeyChanged,
filter,
@ -38,42 +48,44 @@ import {
shareReplay,
switchMap,
switchMapTo,
take,
tap,
} from "rxjs/operators"
import { ajax } from "rxjs/ajax"
import {} from "components"
import { AjaxResponse, ajax } from "rxjs/ajax"
import {
Component,
paintHeaderShadow,
paintHidden,
paintSidebar,
setupHero,
setupMain,
setupNavigation,
setupSearchResult,
switchComponent,
watchBottomOffset,
watchComponentMap,
watchHeader,
watchMain,
watchSearchReset,
watchSidebar,
watchTopOffset
} from "./components"
import {
not,
switchMapIf
} from "./extensions"
import { SearchIndex } from "./modules/search"
import { SearchIndex, SearchResult } from "./modules/search"
import {
getElement,
setupAgent,
watchDocument,
watchDocumentSwitch,
watchLocation,
watchLocationFragment,
watchLocationHash,
watchMedia,
watchToggle,
watchViewportOffset,
watchViewportSize,
watchWorker
} from "./utilities"
import { SearchMessage, SearchMessageType } from "./workers"
import {
SearchMessage,
SearchMessageType,
SearchSetupMessage,
isSearchDumpMessage,
isSearchResultMessage
} from "./workers"
/**
* Configuration
@ -86,9 +98,13 @@ export interface Config {
}
}
import { PackerMessage, PackerMessageType } from "./workers/packer"
import {
PackerMessage,
PackerMessageType
} from "./workers/packer"
import { renderSearchResult } from "./templates"
import { setupTabs } from "components/tabs"
import { setupTableOfContents } from "components/toc/_"
/* ----------------------------------------------------------------------------
* Functions
@ -109,7 +125,7 @@ export function isConfig(config: any): config is Config {
// TBD
// TODO: put this somewhere else... (merge with config!) JSON schema!?
const names = [
const names: Component[] = [
"header", /* Header */
"title", /* Header title */
"search", /* Search */
@ -122,7 +138,7 @@ const names = [
"tabs", /* Tabs */
"navigation", /* Navigation */
"toc" /* Table of contents */
] as const
]
// modernizr for the poor
document.documentElement.classList.remove("no-js")
@ -132,6 +148,19 @@ document.documentElement.classList.add("js")
* Functions
* ------------------------------------------------------------------------- */
/**
*
* Rogue control characters must be filtered before handing the query to the
* search index, as lunr will throw otherwise.
*/
function prepareQuery(value: string): string {
const newvalue = value
.replace(/(?:^|\s+)[*+-:^~]+(?=\s+|$)/g, "")
.trim()
return newvalue ? newvalue.replace(/\s+|$/g, "* ") : ""
}
/**
* Initialize Material for MkDocs
*
@ -141,72 +170,113 @@ export function initialize(config: unknown) {
if (!isConfig(config))
throw new SyntaxError(`Invalid configuration: ${JSON.stringify(config)}`)
const agent = setupAgent()
const worker = new Worker(config.worker.search)
const packer = new Worker(config.worker.packer)
// const query = message.data.trim().replace(/\s+|$/g, "* ") // TODO: do this outside of the worker
const packerMessage$ = new Subject<PackerMessage>()
const packer$ = watchWorker(packer, { message$: packerMessage$ })
const packer$ = watchWorker(packer, { send$: packerMessage$ })
// send a message, then switchMapTo worker!
packer$.subscribe(message => {
console.log("PACKER.MSG", message)
console.log("PACKER.MSG", message.data.length)
// is always packed!
console.log(message.data.length)
localStorage.setItem("index", message.data)
if (message.type === PackerMessageType.BINARY && message.data[0] !== "{")
localStorage.setItem("index", message.data)
})
// storing = experimental feature
const searchMessage$ = new Subject<SearchMessage>()
const search$ = watchWorker(worker, { message$: searchMessage$ })
search$.subscribe(message => {
if (message.type === SearchMessageType.DUMP) {
console.log(message.data.length)
packerMessage$.next({
const search$ = watchWorker(worker, { send$: searchMessage$ })
// paintSearchResult <-- must paint META AND LIST!
// list must be painted based on scroll offset...
/* Render search results */
// search$
// .pipe(
// filter(isSearchResultMessage),
// pluck("data")
// )
// .subscribe(result => {
// const list = getElement(".md-search-result__list")!
// list.innerHTML = ""
// for (const el of result.map(renderSearchResult)) // TODO: perform entire lazy render!!!!
// list.appendChild(el)
// })
// scroll!
// watchSearchResult
/* Link search to packer */
search$
.pipe(
filter(isSearchDumpMessage),
map(message => ({
type: PackerMessageType.STRING,
data: message.data
})
} else if (message.type === SearchMessageType.RESULT) {
console.log("RESULT", message)
})),
tap(message => packerMessage$.next(message)) // send message and wait!
// switchMapTo(packer$)
)
.subscribe()
const list = document.querySelector(".md-search-result__list")!
list.innerHTML = ""
for (const el of message.data.map(renderSearchResult)) // TODO: perform entire lazy render!!!!
list.appendChild(el as any) // only render visibile stuff...!
// paint on next animation frame!?
// build a rendering pipeline for search results + scroll bottom!
}
// if (message.type === 0) {
// console.log("Packing...")
// packerMessage$.next(message.toString())
// } else {
// console.log((message as any).term, ":", (message as any).res)
// }
})
// filter singular "+" or "-",as it will result in a lunr.js error
ajax({
const data$ = ajax({
url: `${config.base}/search/search_index.json`,
responseType: "json",
withCredentials: true
})
.pipe(
pluck("response"),
map<SearchIndex, SearchMessage>(data => ({
.pipe<SearchIndex>(
pluck("response")
// take(1)
)
const fromLocal = localStorage.getItem("index")
;
(fromLocal ? of({
type: PackerMessageType.BINARY,
data: localStorage.getItem("index")!
}) : EMPTY)
.subscribe(x => {
// console.log("send message to packer")
packerMessage$.next(x)
})
const index$ = fromLocal ? packer$.pipe(pluck("data"), take(1)) : of(undefined) // of(localStorage.getItem("index"))
// index$.subscribe(xx => console.log("INDEX", xx))
forkJoin([data$, index$])
.pipe<SearchSetupMessage>(
map(([data, index]) => ({
type: SearchMessageType.SETUP,
data
data: { ...data, index }
}))
)
.subscribe(message => {
searchMessage$.next(message) // TODO: this shall not complete
})
// filter singular "+" or "-",as it will result in a lunr.js error
// data$
// .pipe(
// map<SearchIndex, SearchMessage>(data => ({
// type: SearchMessageType.SETUP,
// data
// }))
// )
// .subscribe(message => {
// searchMessage$.next(message) // TODO: this shall not complete
// })
/* ----------------------------------------------------------------------- */
/* Create viewport observables */
@ -219,10 +289,16 @@ export function initialize(config: unknown) {
/* Create location observables */
const location$ = watchLocation()
const fragment$ = watchLocationFragment()
const fragment$ = watchLocationHash()
/* Create document observables */
const load$ = watchDocument()
// Complete set of AgentObservables...
// component map!
//
// const switch$ = watchDocumentSwitch({ location$ })
/* ----------------------------------------------------------------------- */
@ -230,44 +306,95 @@ export function initialize(config: unknown) {
/* Create component map observable */
const components$ = watchComponentMap(names, { document$: load$ })
const component = (name: Component): Observable<HTMLElement> => {
const component = <T extends HTMLElement>(name: Component): Observable<T> => {
return components$
.pipe(
switchComponent(name)
switchComponent<T>(name)
)
}
/* Create header observable */
const header$ = component("header")
const header$ = component("header") // TODO:!
.pipe(
switchMap(watchHeader)
)
/* Create main area observable */
// DONE
const main$ = component("main")
.pipe(
switchMap(el => watchMain(el, { size$, offset$, header$ })),
shareReplay(1)
setupMain(agent, { header$ })
)
// setupHeader(agent) ??
// setupSearch
// ----------------------------------------------------------------------------
component("query")
/* Create header shadow toggle */
component("header")
.pipe(
switchMap(el => fromEvent(el, "keyup") // not super nice...
switchMap(el => main$
.pipe(
map<Event, SearchMessage>(() => ({
type: SearchMessageType.QUERY,
data: (el as HTMLInputElement).value
})), // TODO. ugly...
distinctUntilKeyChanged("data")
paintHeaderShadow(el)
)
)
)
.subscribe()
// watchSearchResult // emit, if at bottom...
// receive results as a second observable!? filter stuff, paint
const result$ = search$
.pipe(
filter(isSearchResultMessage),
pluck("data")
)
const query$ = component<HTMLInputElement>("query")
.pipe(
switchMap(el => fromEvent(el, "keyup")
.pipe(
map(() => prepareQuery(el.value))
)
)
)
// DONE
component("result")
.pipe(
setupSearchResult(agent, { result$, query$ })
)
.subscribe()
query$
.pipe(
map(data => ({ // put this into some function...
type: SearchMessageType.QUERY,
data
})), // TODO. ugly...
distinctUntilKeyChanged("data")
)
.subscribe(x => {
searchMessage$.next(x)
searchMessage$.next(x as any) // TODO
})
// Focus on search input
component("query")
.pipe(
switchMap(el => fromEvent(el, "focus")
.pipe(
tap(() => {
if (!search.checked)
search.click() // move this inside the search query stuff? not important...
})
)
) // not super nice...
)
.subscribe()
// // WIP: instant loading
// load$
// .pipe(
@ -310,62 +437,27 @@ export function initialize(config: unknown) {
/* ----------------------------------------------------------------------- */
/* Create header shadow toggle */
component("header")
.pipe(
switchMap(el => main$
.pipe(
paintHeaderShadow(el)
)
)
)
.subscribe()
/* Create sidebar with navigation */
component("navigation")
.pipe(
switchMapIf(screen$, el => watchSidebar(el, { offset$, main$ })
.pipe(
paintSidebar(el)
)
),
shareReplay(1)
setupNavigation(agent, { main$ })
)
.subscribe()
/* Create sidebar with table of contents */
component("toc")
.pipe(
switchMapIf(tablet$, el => watchSidebar(el, { offset$, main$ })
.pipe(
paintSidebar(el)
)
),
shareReplay(1)
setupTableOfContents(agent, { header$, main$ })
)
.subscribe()
/* Create tabs visibility toggle */
component("tabs")
.pipe(
switchMapIf(screen$, el => watchTopOffset(el, { size$, offset$, header$ })
.pipe(
paintHidden(el, 8)
)
),
shareReplay(1)
setupTabs(agent, { header$ })
)
.subscribe()
/* Create hero visibility toggle */
component("hero")
.pipe(
switchMap(el => watchTopOffset(el, { size$, offset$, header$ })
.pipe(
paintHidden(el, 20)
)
),
shareReplay(1)
setupHero(agent, { header$ })
)
.subscribe()
@ -429,6 +521,20 @@ export function initialize(config: unknown) {
)
.subscribe()
/* Wrap all data tables for better overflow scrolling */
// const tables = getElements<HTMLTableElement>("table:not([class])")
// tables.forEach(table => {
// console.log("x", table)
// table.parentNode!.insertBefore(renderTable(table), table)
// table.replaceWith(renderTable(table) as any)
// // table.parentElement!.replaceChild(, table)
// })
return {
// agent, // agent.viewport.offset$
// component, // component.toc$
}
/* Return observable factories */
return {

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, Subject } from "rxjs"
import {
ViewportOffset,
ViewportSize,
watchDocument,
watchLocation,
watchLocationHash,
watchMedia,
watchViewportOffset,
watchViewportSize
} from "utilities"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Agent document
*/
export interface AgentDocument {
load$: Observable<Document> /* Document observable */
}
/**
* Agent location
*/
export interface AgentLocation {
href$: Subject<string> /* Location subject */
hash$: Observable<string> /* Location hash observable */
}
/**
* Agent media
*/
export interface AgentMedia {
screen$: Observable<boolean> /* Media observable for screen */
tablet$: Observable<boolean> /* Media observable for tablet */
}
/**
* Agent viewport
*/
export interface AgentViewport {
offset$: Observable<ViewportOffset> /* Viewport offset observable */
size$: Observable<ViewportSize> /* Viewport size observable */
}
/* ------------------------------------------------------------------------- */
/**
* Agent
*/
export interface Agent {
document: AgentDocument /* Document observables */
location: AgentLocation /* Location observables */
media: AgentMedia /* Media observables */
viewport: AgentViewport /* Viewport observables */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Create the agent
*
* This function returns a data structure that contains all observables that
* are related to the browser and/or environment.
*
* @return Agent
*/
export function setupAgent(): Agent {
return {
document: {
load$: watchDocument()
},
location: {
href$: watchLocation(),
hash$: watchLocationHash()
},
media: {
screen$: watchMedia("(min-width: 1220px)"),
tablet$: watchMedia("(min-width: 960px)")
},
viewport: {
offset$: watchViewportOffset(),
size$: watchViewportSize()
}
}
}

View File

@ -23,6 +23,7 @@
import { Observable, fromEvent, merge } from "rxjs"
import { map, shareReplay, startWith } from "rxjs/operators"
import { Agent } from "../../_"
import { ViewportSize } from "../../viewport"
/* ----------------------------------------------------------------------------
@ -71,16 +72,16 @@ export function getElementOffset(el: HTMLElement): ElementOffset {
/**
* Watch element offset
*
* @paramel - Element
* @param options - Options
* @param el - Element
* @param agent - Agent
*
* @return Element offset observable
*/
export function watchElementOffset(
el: HTMLElement, { size$ }: Options
el: HTMLElement, { viewport }: Agent
): Observable<ElementOffset> {
const scroll$ = fromEvent(el, "scroll")
return merge(scroll$, size$)
return merge(scroll$, viewport.size$)
.pipe(
map(() => getElementOffset(el)),
startWith(getElementOffset(el)),

View File

@ -20,6 +20,7 @@
* IN THE SOFTWARE.
*/
export * from "./_"
export * from "./document"
export * from "./element"
export * from "./location"

View File

@ -60,11 +60,11 @@ export function watchLocation(): Subject<string> {
}
/**
* Watch location fragment
* Watch location hash
*
* @return Location fragment observable
* @return Location hash observable
*/
export function watchLocationFragment(): Observable<string> {
export function watchLocationHash(): Observable<string> {
return hashchange$
.pipe(
map(() => location.hash),

View File

@ -30,7 +30,8 @@ import {
} from "rxjs/operators"
import { resetHidden, setHidden } from "actions"
import { ViewportOffset } from "utilities"
import { ViewportOffset } from "../agent"
/* ----------------------------------------------------------------------------
* Functions

View File

@ -21,5 +21,6 @@
*/
export * from "./agent"
export * from "./hidden"
export * from "./string"
export * from "./toggle"

View File

@ -429,7 +429,7 @@
"search.language",
"search.pipeline.stopwords",
"search.pipeline.trimmer",
"search.placeholder",
"search.result.placeholder",
"search.result.none",
"search.result.one",
"search.result.other",