mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03:00
Finished search and moved components to setup functions
This commit is contained in:
parent
c7dfcb87cc
commit
6863aeb4a4
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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=""";break;case 38:t="&";break;case 39:t="'";break;case 60:t="<";break;case 62:t=">";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=""";break;case 38:t="&";break;case 39:t="'";break;case 60:t="<";break;case 62:t=">";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
@ -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
2
material/assets/stylesheets/app.min.css
vendored
2
material/assets/stylesheets/app.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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",
|
||||
|
@ -50,7 +50,7 @@ export function setSearchResultMeta(
|
||||
export function resetSearchResultMeta(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.textContent = translate("search.placeholder")
|
||||
el.textContent = translate("search.result.placeholder")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -32,7 +32,8 @@ import {
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { resetHeaderShadow, setHeaderShadow } from "../../../actions"
|
||||
import { resetHeaderShadow, setHeaderShadow } from "actions"
|
||||
|
||||
import { Main } from "../../main"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
86
src/assets/javascripts/components/hero/index.ts
Normal file
86
src/assets/javascripts/components/hero/index.ts
Normal 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)
|
||||
)
|
||||
}
|
@ -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"
|
||||
|
125
src/assets/javascripts/components/main/_/index.ts
Normal file
125
src/assets/javascripts/components/main/_/index.ts
Normal 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)
|
||||
)
|
||||
}
|
@ -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"
|
||||
|
@ -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)
|
||||
)
|
91
src/assets/javascripts/components/navigation/_/index.ts
Normal file
91
src/assets/javascripts/components/navigation/_/index.ts
Normal 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)
|
||||
)
|
||||
}
|
23
src/assets/javascripts/components/navigation/index.ts
Normal file
23
src/assets/javascripts/components/navigation/index.ts
Normal 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 "./_"
|
85
src/assets/javascripts/components/search/result/_/index.ts
Normal file
85
src/assets/javascripts/components/search/result/_/index.ts
Normal 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$ })
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
@ -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"
|
||||
|
@ -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)
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
@ -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
|
||||
})
|
||||
)
|
||||
}
|
86
src/assets/javascripts/components/tabs/index.ts
Normal file
86
src/assets/javascripts/components/tabs/index.ts
Normal 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)
|
||||
)
|
||||
}
|
106
src/assets/javascripts/components/toc/_/index.ts
Normal file
106
src/assets/javascripts/components/toc/_/index.ts
Normal 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)
|
||||
)
|
||||
}
|
@ -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]) => {
|
||||
|
24
src/assets/javascripts/components/toc/index.ts
Normal file
24
src/assets/javascripts/components/toc/index.ts
Normal 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"
|
@ -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 {
|
||||
|
||||
|
113
src/assets/javascripts/utilities/agent/_/index.ts
Normal file
113
src/assets/javascripts/utilities/agent/_/index.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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)),
|
||||
|
@ -20,6 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./_"
|
||||
export * from "./document"
|
||||
export * from "./element"
|
||||
export * from "./location"
|
||||
|
@ -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),
|
||||
|
@ -30,7 +30,8 @@ import {
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { resetHidden, setHidden } from "actions"
|
||||
import { ViewportOffset } from "utilities"
|
||||
|
||||
import { ViewportOffset } from "../agent"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
@ -21,5 +21,6 @@
|
||||
*/
|
||||
|
||||
export * from "./agent"
|
||||
export * from "./hidden"
|
||||
export * from "./string"
|
||||
export * from "./toggle"
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user