staticsearch
Advanced tools
@@ -1,1 +0,1 @@ | ||
| :host{--fsize: var(--staticsearch-fontsize, 1em);--dmax: var(--staticsearch-maxwidth, 60ch);--dmargin: var(--staticsearch-margin, 3vmin);--dpadding: var(--staticsearch-padding, 2vmin);--sheight: var(--staticsearch-fieldset-height, calc(3em + (2 * var(--dpadding))) );--colback0: var(--staticsearch-color-back, Canvas);--colbord0: var(--staticsearch-color-border, ButtonFace);--colfore0: var(--staticsearch-color-fore, CanvasText);--colfore1: var(--staticsearch-color-dim, color-mix(in oklab, CanvasText 70%, Canvas));--collinkd: var(--staticsearch-color-link, color-mix(in oklab, LinkText 70%, CanvasText));--collinkv: var(--staticsearch-color-visited, color-mix(in oklab, VisitedText 70%, CanvasText));--colshad0: var(--staticsearch-color-shadow, #000);--colshad1: var(--staticsearch-color-backdrop, color-mix(in srgb, var(--colshad0), transparent 30%));--colblur0: var(--staticsearch-backdrop-blur, 3px)}template{display:none}*{box-sizing:border-box;font-size:inherit;padding:0;margin:0}@media (prefers-reduced-motion: reduce){*,*:before,*:after,::backdrop{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;transition-behavior:normal!important;scroll-behavior:auto!important}}dialog{position:fixed;font-size:var(--fsize);inline-size:var(--dmax);block-size:100%;max-inline-size:calc(100dvw - (2 * var(--dmargin)));max-block-size:calc(100dvh - (2 * var(--dmargin)));margin:auto;padding:0;color:var(--colfore0);background-color:var(--colback0);border:1px solid var(--colbord0);border-radius:5px;box-shadow:0 2px 4px var(--colshad0);overflow:hidden;transform-origin:50% 0;&,&::backdrop{transition:all .3s ease-in-out;transition-behavior:allow-discrete}&[open]{opacity:1;transform:scale(1);@starting-style{display:block;opacity:.5;transform:scaleX(.5) scaleY(0)}&::backdrop{background-color:var(--colshad1);-webkit-backdrop-filter:blur(var(--colblur0));backdrop-filter:blur(var(--colblur0));@starting-style{background-color:#0000;-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0)}}}&:not([open]){display:none;opacity:0;transform:scaleX(.5) scaleY(0)}}[part=close]{position:absolute;inset-inline-end:0;inset-block-start:0;block-size:1.4em;inline-size:1.4em;min-block-size:unset;min-inline-size:unset;padding:0;margin:0;background-color:transparent;border:0 none;cursor:pointer;z-index:3;&:is(:hover,:focus,:active){color:var(--collinkd)}}[part=search]{position:absolute;block-size:var(--sheight);inset-inline:0;inset-block-start:0;padding-inline:var(--dpadding);padding-block-start:var(--dpadding);box-shadow:0 1px 5px var(--colshad0);overflow:hidden;z-index:2;&:focus-within{label,input{color:var(--colfore0)}input{border-color:var(--colfore1)}}}[part=searchlabel]{display:block;font-size:.8em;padding-inline-start:.625em;padding-block-end:.2em;color:var(--colfore1);-webkit-user-select:none;user-select:none}input[part=searchinput]{display:block;width:100%;line-height:1.5;padding-block:.2em;padding-inline:.5em;color:var(--colfore1);background-color:var(--colback0);border:1px solid transparent;border-radius:3px;outline-style:none}[part=results]{position:absolute;inset-inline:0;inset-block:0;inset-block-start:var(--sheight);padding:var(--dpadding);overflow-x:hidden;overflow-y:auto;&:after{position:fixed;content:"";block-size:5vh;inset-inline:0;inset-block-end:0;background:linear-gradient(transparent,var(--colback0));pointer-events:none}}[part=searchterm]{font-style:italic;&:before,&:after{content:'"'}}[part=item]{margin-block:1.5em;margin-inline-start:1.5em}[part=title]{font-weight:bolder;line-height:1.2;text-wrap:balance;text-underline-offset:.2em;margin-block-end:.1em}[part=description]{font-size:.8em;line-height:1.4;text-wrap:pretty;color:var(--colfore1)}a:focus-visible{outline-color:var(--collinkd);outline-style:solid;outline-offset:.2em;outline-width:.2rem}a:link{display:block;text-decoration:none;color:var(--collinkd)}a:visited{color:var(--collinkv)}a:hover,a:focus,a:active{color:var(--collinkd);[part=title]{text-decoration:underline}[part=description]{color:var(--colfore0)}} | ||
| :host{--fsize: var(--staticsearch-fontsize, 1em);--dmax: var(--staticsearch-maxwidth, 60ch);--dmargin: var(--staticsearch-margin, 3vmin);--dpadding: var(--staticsearch-padding, 2vmin);--sheight: var(--staticsearch-fieldset-height, calc(3em + (2 * var(--dpadding))) );--colback0: var(--staticsearch-color-back, Canvas);--colbord0: var(--staticsearch-color-border, ButtonFace);--colfore0: var(--staticsearch-color-fore0, CanvasText);--colfore1: var(--staticsearch-color-fore1, color-mix(in oklab, CanvasText 80%, Canvas));--colfore2: var(--staticsearch-color-fore2, color-mix(in oklab, CanvasText 60%, Canvas));--collinkd: var(--staticsearch-color-link, color-mix(in oklab, LinkText 80%, CanvasText));--collinkv: var(--staticsearch-color-visited, color-mix(in oklab, VisitedText 80%, CanvasText));--colshad0: var(--staticsearch-color-shadow, #000);--colshad1: var(--staticsearch-color-backdrop, color-mix(in srgb, var(--colshad0), transparent 25%));--colblur0: var(--staticsearch-backdrop-blur, 3px)}template{display:none}*{box-sizing:border-box;font-size:inherit;padding:0;margin:0}@media (prefers-reduced-motion: reduce){*,*:before,*:after,::backdrop{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;transition-behavior:normal!important;scroll-behavior:auto!important}}dialog{position:fixed;font-size:var(--fsize);inline-size:var(--dmax);block-size:100%;max-inline-size:calc(100dvw - (2 * var(--dmargin)));max-block-size:calc(100dvh - (2 * var(--dmargin)));margin:auto;padding:0;color:var(--colfore0);background-color:var(--colback0);border:1px solid var(--colbord0);border-radius:5px;box-shadow:0 2px 4px var(--colshad0);overflow:hidden;transform-origin:50% 0;&,&::backdrop{transition:all .3s ease-in-out;transition-behavior:allow-discrete}&[open]{opacity:1;transform:scale(1);@starting-style{display:block;opacity:.5;transform:scaleX(.5) scaleY(0)}&::backdrop{background-color:var(--colshad1);-webkit-backdrop-filter:blur(var(--colblur0));backdrop-filter:blur(var(--colblur0));@starting-style{background-color:#0000;-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0)}}}&:not([open]){display:none;opacity:0;transform:scaleX(.5) scaleY(0)}}[part=close]{position:absolute;inset-inline-end:0;inset-block-start:0;block-size:1.4em;inline-size:1.4em;min-block-size:unset;min-inline-size:unset;padding:0;margin:0;background-color:transparent;border:0 none;cursor:pointer;z-index:3;&:is(:hover,:focus,:active){color:var(--collinkd)}}[part=search]{position:absolute;block-size:var(--sheight);inset-inline:0;inset-block-start:0;padding-inline:var(--dpadding);padding-block-start:var(--dpadding);box-shadow:0 1px 5px var(--colshad0);overflow:hidden;z-index:2;&:focus-within{label,input{color:var(--colfore0)}input{border-color:var(--colfore1)}}}[part=searchlabel]{display:block;font-size:.8em;padding-inline-start:.625em;padding-block-end:.2em;color:var(--colfore1);-webkit-user-select:none;user-select:none}input[part=searchinput]{display:block;inline-size:100%;line-height:1.5;padding-block:.2em;padding-inline:.5em;color:var(--colfore1);background-color:transparent;border:1px solid transparent;border-radius:3px;outline-style:none}[part=results]{position:absolute;inset-inline:0;inset-block:0;inset-block-start:var(--sheight);padding:var(--dpadding);overflow-x:hidden;overflow-y:auto;&:after{position:fixed;content:"";block-size:5vh;inset-inline:0;inset-block-end:0;background:linear-gradient(transparent,var(--colback0));pointer-events:none}}[part=searchterm]{font-style:italic;&:before,&:after{content:'"'}}[part=item]{margin-block:1.5em;margin-inline-start:1.5em}[part=title]{font-weight:bolder;line-height:1.2;text-wrap:balance;text-underline-offset:.2em;margin-block-end:.1em}[part=meta]{font-size:.75em;line-height:1.4;color:var(--colfore2)}[part=description]{font-size:.85em;line-height:1.4;text-wrap:pretty;color:var(--colfore1)}a:focus-visible{outline-color:var(--collinkd);outline-style:solid;outline-offset:.2em;outline-width:.2rem}a:link{display:block;text-decoration:none;color:var(--collinkd)}a:visited{color:var(--collinkv)}a:hover,a:focus,a:active{color:var(--collinkd);[part=title]{text-decoration:underline}} |
@@ -1,1 +0,1 @@ | ||
| import{staticsearch as m}from"./__SSDIR__/staticsearch.js";var f="q",S=0,y="__AGENT__",E=Symbol(y),b=500;(()=>{let t=document.getElementById("staticsearch_search"),r=document.getElementById("staticsearch_result");r&&L(r,r.getAttribute("minscore"),r.getAttribute("maxresults")),t&&t.tagName==="INPUT"&&C(t)})();function v(){return new URLSearchParams(location.search).get(f)||""}function I(t,r){let n=new URL(location);t?n.searchParams.set(f,t):n.searchParams.delete(f),n.hash=r||"",history.replaceState({},"",n.href)}function C(t){if(t[E])return;t[E]=!0,t.name&&(f=t.name);let r=v()||sessionStorage.getItem(y)||"";r&&(t.value=r);let n=t.value;n&&a(n);let p;t.addEventListener("input",s=>{let c=s.target.value;clearTimeout(p),p=setTimeout(()=>a(c),b)},!1);function a(s){sessionStorage.setItem(y,s),!(s.length<2)&&m.find(s).then(c=>{})}}function L(t,r,n,p,a,s){if(t[E])return;t[E]=!0;let c="";r=parseFloat(r)||0,n=parseFloat(n)||0,a=a||document.getElementById("staticsearch_resultmessage"),a||(a=document.createElement("template"),a.innerHTML='<p part="resultmessage"><span part="resultcount">0</span> found for <span part="searchterm"></span>…</p>'),s=s||document.getElementById("staticsearch_item"),s||(s=document.createElement("template"),s.innerHTML='<li part="item"><a part="link"><h2 part="title"></h2><p part="description"></p></a></li>'),t.addEventListener("click",i=>{let l=i.target.closest("a");l&&c&&I(c,l.id)}),document.addEventListener("staticsearch:result",i=>{c=i.detail.search;let l=i.detail.result;t.innerHTML="";let o=document.createElement(p||"ol");o.setAttribute("part","searchresult"),l.forEach((e,g)=>{if(r&&e.relevancy<r||n&&g>=n)return;let u=s.content.cloneNode(!0);h(u,"link",null,{href:e.url,id:`staticsearchresult-${e.id}`}),h(u,"title",e.title),h(u,"description",e.description),o.appendChild(u)});let d=a.content.cloneNode(!0);if(h(d,"resultcount",o.childElementCount),h(d,"searchterm",c),t.appendChild(d),t.appendChild(o),!S&&location.hash){let e=t.querySelector(location.hash);e&&(e.focus(),e.scrollIntoView({behavior:"smooth",block:"nearest"}))}else t.scrollTop=0;S++},!1);function h(i,l,o,d={}){i.querySelectorAll(`[part="${l}"]`).forEach(e=>{for(let[g,u]of Object.entries(d))e.setAttribute(g,u);if(o){for(;e.firstElementChild;)e=e.firstElementChild;e.textContent=o}})}}export{C as staticSearchInput,v as staticSearchQuery,L as staticSearchResult,I as staticSearchSetQuery}; | ||
| import{staticsearch as b}from"./__SSDIR__/staticsearch.js";var f="q",T=0,S="__AGENT__",g=Symbol(S),w=500,y={date:new Intl.DateTimeFormat([],{dateStyle:"long"}).format,number:new Intl.NumberFormat([],{maximumFractionDigits:0}).format};(()=>{let e=document.getElementById("staticsearch_search"),a=document.getElementById("staticsearch_result");a&&C(a,{minScore:a.getAttribute("minscore"),maxResults:a.getAttribute("maxresults")}),e&&e.tagName==="INPUT"&&x(e)})();function I(){return new URLSearchParams(location.search).get(f)||""}function v(e,a){let r=new URL(location);e?r.searchParams.set(f,e):r.searchParams.delete(f),r.hash=a||"",history.replaceState({},"",r.href)}function x(e){if(e[g])return;e[g]=!0,e.name&&(f=e.name);let a=I()||sessionStorage.getItem(S)||"";a&&(e.value=a);let r=e.value;r&&p(r);let m;e.addEventListener("input",n=>{let o=n.target.value;clearTimeout(m),m=setTimeout(()=>p(o),w)},!1);function p(n){sessionStorage.setItem(S,n),!(n.length<2)&&b.find(n).then(o=>{})}}function C(e,a={}){if(e[g])return;e[g]=!0;let r="",m=parseFloat(a.minScore)||0,p=parseFloat(a.maxResults)||0,n=a.resultElement||"ol",o=a.messageTemplate||document.getElementById("staticsearch_resultmessage");o||(o=document.createElement("template"),o.innerHTML='<p part="resultmessage"><span part="resultcount">0</span> found for <span part="searchterm"></span>…</p>');let h=a.itemTemplate||document.getElementById("staticsearch_item");h||(h=document.createElement("template"),h.innerHTML='<li part="item"><a part="link"><h2 part="title"></h2><p part="meta"><time part="date"></time> – <span part="words">0</span> words</p><p part="description"></p></a></li>'),e.addEventListener("click",i=>{let u=i.target.closest("a");u&&r&&v(r,u.id)}),document.addEventListener("staticsearch:result",i=>{r=i.detail.search;let u=i.detail.result;e.innerHTML="";let l=document.createElement(n);l.setAttribute("part","searchresult"),u.forEach((t,E)=>{if(m&&t.relevancy<m||p&&E>=p)return;let s=h.content.cloneNode(!0);c(s,"link",null,{href:t.url,id:`staticsearchresult-${t.id}`}),c(s,"title",t.title),c(s,"description",t.description),t.date&&c(s,"date",y.date(new Date(t.date)),{datetime:t.date}),t.words&&c(s,"words",y.number(t.words)),l.appendChild(s)});let d=o.content.cloneNode(!0);if(c(d,"resultcount",l.childElementCount),c(d,"searchterm",r),e.appendChild(d),e.appendChild(l),!T&&location.hash){let t=e.querySelector(location.hash);t&&(t.focus(),t.scrollIntoView({behavior:"smooth",block:"nearest"}))}else e.scrollTop=0;T++},!1);function c(i,u,l,d={}){i.querySelectorAll(`[part="${u}"]`).forEach(t=>{for(let[E,s]of Object.entries(d))t.setAttribute(E,s);if(l){for(;t.firstElementChild;)t=t.firstElementChild;t.textContent=l}})}}export{x as staticSearchInput,I as staticSearchQuery,C as staticSearchResult,v as staticSearchSetQuery}; |
@@ -1,2 +0,2 @@ | ||
| import{staticSearchQuery as h,staticSearchSetQuery as l,staticSearchInput as o,staticSearchResult as n}from"./__SSDIR__/staticsearch-bind.js";var a=class r extends HTMLElement{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");#t=null;#e=null;#i=null;constructor(){super()}connectedCallback(){let t=this.firstElementChild;if(this.attachShadow({mode:"open"}),!t)return;t.setAttribute("part","startsearch");let s=document.createElement("link");s.rel="stylesheet",s.href=`${r.path}css/component.css`,this.shadowRoot.appendChild(s);let i=this.shadowRoot.appendChild(t);this.#r(),i.style.cursor="pointer",i.addEventListener("click",e=>{e.preventDefault(),this.#s()}),window.addEventListener("keydown",e=>{e.key==="k"&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.#s())}),h()&&this.#s()}#s(){this.#t.open?this.#a():(this.#t.showModal(),this.setAttribute("aria-expanded","true"),this.#e.focus())}#a(){this.#t.close(),this.removeAttribute("aria-expanded"),l()}#r(){let t=document.createElement("dialog");t.setAttribute("closedby","any"),t.setAttribute("part","dialog"),t.innerHTML=` | ||
| import{staticSearchQuery as l,staticSearchSetQuery as h,staticSearchInput as o,staticSearchResult as n}from"./__SSDIR__/staticsearch-bind.js";var a=class r extends HTMLElement{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");#t=null;#e=null;#i=null;constructor(){super()}connectedCallback(){let t=this.firstElementChild;if(this.attachShadow({mode:"open"}),!t)return;t.setAttribute("part","startsearch");let s=document.createElement("link");s.rel="stylesheet",s.href=`${r.path}css/component.css`,this.shadowRoot.appendChild(s);let i=this.shadowRoot.appendChild(t);this.#r(),i.style.cursor="pointer",i.addEventListener("click",e=>{e.preventDefault(),this.#s()}),window.addEventListener("keydown",e=>{e.key==="k"&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.#s())}),l()&&this.#s()}#s(){this.#t.open?this.#a():(this.#t.showModal(),this.setAttribute("aria-expanded","true"),this.#e.focus())}#a(){this.#t.close(),this.removeAttribute("aria-expanded"),h()}#r(){let t=document.createElement("dialog");t.setAttribute("closedby","any"),t.setAttribute("part","dialog"),t.innerHTML=` | ||
| <form method="dialog"><button part="close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path xmlns="http://www.w3.org/2000/svg" fill="currentColor" d="M5.3 5.3a1 1 0 0 1 1.4 0l5.3 5.3 5.3-5.3a1 1 0 1 1 1.4 1.4L13.4 12l5.3 5.3a1 1 0 0 1-1.4 1.4L12 13.4l-5.3 5.3a1 1 0 0 1-1.4-1.4l5.3-5.3-5.3-5.3a1 1 0 0 1 0-1.4Z"/></svg></button></form> | ||
@@ -8,2 +8,2 @@ <search part="search"> | ||
| <div part="results"></div> | ||
| `,this.#t=this.shadowRoot.appendChild(t),this.#e=this.shadowRoot.querySelector("#search"),this.#i=this.shadowRoot.querySelector('[part="results"]'),n(this.#i,this.getAttribute("minscore"),this.getAttribute("maxresults")),o(this.#e),this.#t.addEventListener("close",()=>this.#a()),this.#t.addEventListener("click",s=>{this.#t.open&&s.target===this.#t&&this.#a()})}};customElements.define("static-search",a); | ||
| `,this.#t=this.shadowRoot.appendChild(t),this.#e=this.shadowRoot.querySelector("#search"),this.#i=this.shadowRoot.querySelector('[part="results"]'),n(this.#i,{minScore:this.getAttribute("minscore"),maxResults:this.getAttribute("maxresults")}),o(this.#e),this.#t.addEventListener("close",()=>this.#a()),this.#t.addEventListener("click",s=>{this.#t.open&&s.target===this.#t&&this.#a()})}};customElements.define("static-search",a); |
@@ -1,1 +0,1 @@ | ||
| import{stem as p}from"./stem/__STEMFILE__";function d(o,e,t=7,r=new Set){return new Set(o.normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[\u2018\u2019\u201A\u201B\u0027]/g,"").toLowerCase().replace(/[^a-z\s]/g," ").split(/\s+/g).map(s=>e(s.trim()).slice(0,t)).filter(s=>s.length>1&&!r.has(s)).sort())}var h=class{#t=null;#e=null;#r=null;constructor(o,e,t){return this.#e=o||"db",this.#r=e||1,this.#s(t)}connect(){return this.#s().then(()=>!0).catch(()=>!1)}close(){this.#t.close(),this.#t=null}#s(o){return new Promise((e,t)=>{if(!("indexedDB"in window)){t(new Error("No indexedDB support"));return}let r=indexedDB.open(this.#e,this.#r);r.onsuccess=()=>{this.#t=r.result,e(this)},r.onerror=s=>{t(new Error(`IndexedDB error ${s.target.errorCode}: ${s.target.message}`,{cause:s}))},o&&(r.onupgradeneeded=s=>{o(r.result,s.oldVersion,s.newVersion)})})}get isConnected(){return!!this.#t}get name(){return this.#e}get version(){return this.#r}#i(o,e,t){return new Promise((r,s)=>{let{transaction:a,store:i}=this.#a(o,null,!0);a.oncomplete=()=>r(),a.onerror=n=>{s(new Error(n.target.error.message,{cause:n}))},e=Array.isArray(e)?e:[e],e.forEach(n=>{t?i.put(n):i.add(n)}),a.commit()})}add({store:o,item:e=[]}={}){return this.#i(o,e,!1)}put({store:o,item:e=[]}={}){return this.#i(o,e,!0)}#o(o,e,t,r){return new Promise((s,a)=>{r=Array.isArray(r)?r:[r];let i=t==="delete"||t==="clear",n=this.#a(o,e,i).store[t](...r);n.onsuccess=()=>s(n.result),n.onerror=()=>a(n.error??!1)})}count({store:o,index:e,lowerBound:t,upperBound:r}={}){return this.#o(o,e,"count",this.#n(t,r))}get({store:o,index:e,key:t}={}){return this.#o(o,e,"get",t)}getAll({store:o,index:e,lowerBound:t,upperBound:r,count:s}={}){return this.#o(o,e,"getAll",[this.#n(t,r),s])}getAllKeys({store:o,index:e,lowerBound:t,upperBound:r,count:s}={}){return this.#o(o,e,"getAllKeys",[this.#n(t,r),s])}delete({store:o,key:e}={}){return this.#o(o,null,"delete",[e])}deleteAll({store:o,index:e,lowerBound:t,upperBound:r}={}){return this.#o(o,e,"delete",[this.#n(t,r)])}clear({store:o}={}){return this.#o(o,null,"clear")}getCursor({store:o,index:e,lowerBound:t,upperBound:r,direction:s="next",callback:a}={}){return new Promise((i,n)=>{let l=this.#a(o,e).store.openCursor(this.#n(t,r),s);l.onsuccess=()=>{let u=l.result;u?u.advance(a&&a(u)||1):i(!0)},l.onerror=()=>n(l.error)})}drop(){return new Promise((o,e)=>{this.close();let t=indexedDB.deleteDatabase(this.#e);t.onsuccess=()=>{this.#e=null,this.#r=null,o(!0)},t.onerror=()=>e(!1)})}#a(o,e,t){let r=this.#t.transaction(o,t?"readwrite":"readonly",{durability:t?"strict":"default"}),s=r.objectStore(o);return{transaction:r,store:e&&!t?s.index(e):s}}#n(o,e){let t;return o&&e?t=IDBKeyRange.bound(o,e):o?t=IDBKeyRange.lowerBound(o):e&&(t=IDBKeyRange.upperBound(e)),t}};var c=class o{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");static dbName="__AGENT__";static version="__VERSION__";static wordCrop=__WORDCROP__;#t=!1;#e=null;#r=null;fetchTimeout=5e3;constructor(){}async init(){if(this.#t)return!0;if(this.#e=this.#e||await new h(o.dbName,1,(r,s,a)=>{switch(s){case 0:r.createObjectStore("cfg",{keyPath:"name"}),r.createObjectStore("page",{keyPath:"id"}),r.createObjectStore("file",{keyPath:"id"}),r.createObjectStore("index",{keyPath:"word"})}}),!this.#e?.isConnected)return!1;if((await this.#e.get({store:"cfg",key:"version"}))?.value===o.version)return this.#r=new Set((await this.#e.get({store:"cfg",key:"stopword"}))?.value),this.#t=!0,!0;await Promise.allSettled([this.#e.put({store:"cfg",item:{name:"version",value:o.version}}),this.#e.clear({store:"page"}),this.#e.clear({store:"file"}),this.#e.clear({store:"index"})]);let t=await this.#i("index.json");return t===null?!1:(this.#r=new Set(t.stopword),await this.#e.put({store:"cfg",item:{name:"stopword",value:t.stopword}}),await this.#e.add({store:"page",item:t.page.map((r,s)=>({id:s,url:r.u,title:r.t,description:r.d}))}),await this.#e.add({store:"file",item:t.file.map(r=>({id:r,loaded:!1}))}),this.#t=!0,!0)}async find(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");e=e.trim(),this.#s("find",{search:e});let t=[],r=[...this.wordList(e)];if(!r.length)return this.#s("result",{search:e,result:t}),t;await Promise.allSettled(r.map(i=>this.loadIndex(i)));let s={};(await Promise.allSettled(r.map(i=>this.#e.get({store:"index",key:i})))).forEach(i=>{if(i.value?.page)for(let n in i.value.page)s[n]=s[n]||0,s[n]+=i.value.page[n]});let a=[];for(let i in s)a.push({id:i,relevancy:s[i]});return a.sort((i,n)=>n.relevancy-i.relevancy),(await Promise.allSettled(a.map(i=>this.#e.get({store:"page",key:parseFloat(i.id)})))).forEach((i,n)=>{i.value&&(t[n]=i.value,t[n].relevancy=a[n].relevancy)}),this.#s("result",{search:e,result:t}),t}wordList(e){return d(e,p,o.wordCrop,this.#r)}async loadIndex(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");if(e=e.toLowerCase().replace(/[^a-z]/g,"").slice(0,2),!e)return;let t=e.slice(0,e.length-1)+String.fromCharCode(e.charCodeAt(e.length-1)+1),r=(await this.#e.getAll({store:"file",lowerBound:e,upperBound:t})).filter(i=>!i.loaded);if(!r.length)return;let s=[],a=[];(await Promise.allSettled(r.map(i=>this.#i(`data/${i.id}.json`)))).map((i,n)=>{i.value&&(s.push({id:r[n].id,loaded:!0}),a.push(...Object.getOwnPropertyNames(i.value).map(l=>({word:l,page:i.value[l]}))))}),await Promise.allSettled([this.#e.put({store:"file",item:s}),this.#e.put({store:"index",item:a})])}#s(e="unknown",t={}){document.dispatchEvent(new CustomEvent("staticsearch:"+e,{detail:t}))}async#i(e){let t=o.path+e;try{let r=new AbortController,s=setTimeout(()=>r.abort(),this.fetchTimeout),a=await fetch(t,{signal:r.signal});if(clearTimeout(s),!a.ok)throw new Error(`Fetch failed ${t}: ${a.status}`);return await a.json()}catch{return null}}},g=new c;await g.init();export{g as staticsearch}; | ||
| import{stem as p}from"./stem/__STEMFILE__";function d(o,e,t=7,r=new Set){return new Set(o.normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[\u2018\u2019\u201A\u201B\u0027]/g,"").toLowerCase().replace(/[^a-z\s]/g," ").split(/\s+/g).map(s=>e(s.trim()).slice(0,t)).filter(s=>s.length>1&&!r.has(s)).sort())}var h=class{#t=null;#e=null;#r=null;constructor(o,e,t){return this.#e=o||"db",this.#r=e||1,this.#s(t)}connect(){return this.#s().then(()=>!0).catch(()=>!1)}close(){this.#t.close(),this.#t=null}#s(o){return new Promise((e,t)=>{if(!("indexedDB"in window)){t(new Error("No indexedDB support"));return}let r=indexedDB.open(this.#e,this.#r);r.onsuccess=()=>{this.#t=r.result,e(this)},r.onerror=s=>{t(new Error(`IndexedDB error ${s.target.errorCode}: ${s.target.message}`,{cause:s}))},o&&(r.onupgradeneeded=s=>{o(r.result,s.oldVersion,s.newVersion)})})}get isConnected(){return!!this.#t}get name(){return this.#e}get version(){return this.#r}#i(o,e,t){return new Promise((r,s)=>{let{transaction:a,store:i}=this.#a(o,null,!0);a.oncomplete=()=>r(),a.onerror=n=>{s(new Error(n.target.error.message,{cause:n}))},e=Array.isArray(e)?e:[e],e.forEach(n=>{t?i.put(n):i.add(n)}),a.commit()})}add({store:o,item:e=[]}={}){return this.#i(o,e,!1)}put({store:o,item:e=[]}={}){return this.#i(o,e,!0)}#o(o,e,t,r){return new Promise((s,a)=>{r=Array.isArray(r)?r:[r];let i=t==="delete"||t==="clear",n=this.#a(o,e,i).store[t](...r);n.onsuccess=()=>s(n.result),n.onerror=()=>a(n.error??!1)})}count({store:o,index:e,lowerBound:t,upperBound:r}={}){return this.#o(o,e,"count",this.#n(t,r))}get({store:o,index:e,key:t}={}){return this.#o(o,e,"get",t)}getAll({store:o,index:e,lowerBound:t,upperBound:r,count:s}={}){return this.#o(o,e,"getAll",[this.#n(t,r),s])}getAllKeys({store:o,index:e,lowerBound:t,upperBound:r,count:s}={}){return this.#o(o,e,"getAllKeys",[this.#n(t,r),s])}delete({store:o,key:e}={}){return this.#o(o,null,"delete",[e])}deleteAll({store:o,index:e,lowerBound:t,upperBound:r}={}){return this.#o(o,e,"delete",[this.#n(t,r)])}clear({store:o}={}){return this.#o(o,null,"clear")}getCursor({store:o,index:e,lowerBound:t,upperBound:r,direction:s="next",callback:a}={}){return new Promise((i,n)=>{let l=this.#a(o,e).store.openCursor(this.#n(t,r),s);l.onsuccess=()=>{let u=l.result;u?u.advance(a&&a(u)||1):i(!0)},l.onerror=()=>n(l.error)})}drop(){return new Promise((o,e)=>{this.close();let t=indexedDB.deleteDatabase(this.#e);t.onsuccess=()=>{this.#e=null,this.#r=null,o(!0)},t.onerror=()=>e(!1)})}#a(o,e,t){let r=this.#t.transaction(o,t?"readwrite":"readonly",{durability:t?"strict":"default"}),s=r.objectStore(o);return{transaction:r,store:e&&!t?s.index(e):s}}#n(o,e){let t;return o&&e?t=IDBKeyRange.bound(o,e):o?t=IDBKeyRange.lowerBound(o):e&&(t=IDBKeyRange.upperBound(e)),t}};var c=class o{static path=new URL(import.meta.url).pathname.replace("__FILENAME__","");static dbName="__AGENT__";static version="__VERSION__";static wordCrop=__WORDCROP__;#t=!1;#e=null;#r=null;fetchTimeout=5e3;constructor(){}async init(){if(this.#t)return!0;if(this.#e=this.#e||await new h(o.dbName,1,(r,s,a)=>{switch(s){case 0:r.createObjectStore("cfg",{keyPath:"name"}),r.createObjectStore("page",{keyPath:"id"}),r.createObjectStore("file",{keyPath:"id"}),r.createObjectStore("index",{keyPath:"word"})}}),!this.#e?.isConnected)return!1;if((await this.#e.get({store:"cfg",key:"version"}))?.value===o.version)return this.#r=new Set((await this.#e.get({store:"cfg",key:"stopword"}))?.value),this.#t=!0,!0;await Promise.allSettled([this.#e.put({store:"cfg",item:{name:"version",value:o.version}}),this.#e.clear({store:"page"}),this.#e.clear({store:"file"}),this.#e.clear({store:"index"})]);let t=await this.#i("index.json");return t===null?!1:(this.#r=new Set(t.stopword),await this.#e.put({store:"cfg",item:{name:"stopword",value:t.stopword}}),await this.#e.add({store:"page",item:t.page.map((r,s)=>({id:s,url:r.u,title:r.t||r.u,description:r.d||"",date:r.p||"",words:r.w||0}))}),await this.#e.add({store:"file",item:t.file.map(r=>({id:r,loaded:!1}))}),this.#t=!0,!0)}async find(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");e=e.trim(),this.#s("find",{search:e});let t=[],r=[...this.wordList(e)];if(!r.length)return this.#s("result",{search:e,result:t}),t;await Promise.allSettled(r.map(i=>this.loadIndex(i)));let s={};(await Promise.allSettled(r.map(i=>this.#e.get({store:"index",key:i})))).forEach(i=>{if(i.value?.page)for(let n in i.value.page)s[n]=s[n]||0,s[n]+=i.value.page[n]});let a=[];for(let i in s)a.push({id:i,relevancy:s[i]});return a.sort((i,n)=>n.relevancy-i.relevancy),(await Promise.allSettled(a.map(i=>this.#e.get({store:"page",key:parseFloat(i.id)})))).forEach((i,n)=>{i.value&&(t[n]=i.value,t[n].relevancy=a[n].relevancy)}),this.#s("result",{search:e,result:t}),t}wordList(e){return d(e,p,o.wordCrop,this.#r)}async loadIndex(e){if(!this.#t)throw new Error("StaticSearch failed to initialize");if(e=e.toLowerCase().replace(/[^a-z]/g,"").slice(0,2),!e)return;let t=e.slice(0,e.length-1)+String.fromCharCode(e.charCodeAt(e.length-1)+1),r=(await this.#e.getAll({store:"file",lowerBound:e,upperBound:t})).filter(i=>!i.loaded);if(!r.length)return;let s=[],a=[];(await Promise.allSettled(r.map(i=>this.#i(`data/${i.id}.json`)))).map((i,n)=>{i.value&&(s.push({id:r[n].id,loaded:!0}),a.push(...Object.getOwnPropertyNames(i.value).map(l=>({word:l,page:i.value[l]}))))}),await Promise.allSettled([this.#e.put({store:"file",item:s}),this.#e.put({store:"index",item:a})])}#s(e="unknown",t={}){document.dispatchEvent(new CustomEvent("staticsearch:"+e,{detail:t}))}async#i(e){let t=o.path+e;try{let r=new AbortController,s=setTimeout(()=>r.abort(),this.fetchTimeout),a=await fetch(t,{signal:r.signal});if(clearTimeout(s),!a.ok)throw new Error(`Fetch failed ${t}: ${a.status}`);return await a.json()}catch{return null}}},g=new c;await g.init();export{g as staticsearch}; |
@@ -1,1 +0,1 @@ | ||
| var g={skis:"ski",skies:"sky",dying:"die",lying:"lie",tying:"tie",idly:"idl",gently:"gentl",ugly:"ugli",early:"earli",only:"onli",singly:"singl"},h=["sky","news","howe","atlas","cosmos","bias","andes"],r=["inning","outing","canning","herring","earring","proceed","exceed","succeed"],b=["bb","dd","ff","gg","mm","nn","pp","rr","tt"];function s(i,e){for(var n=i.length,t=0,f=e.length;t<f;t++)if(e[t][0].test(i))return n-e[t][3]<e[t][2]?i:i.replace(e[t][0],e[t][1]);return i}function x(i,e){return i[e]?!1:/(^[aeiouy][^aeiouy])|([aeiouy][^aeiouywxY]$)/.test(i)}function m(i){var e,n;if(i.length<=2||h.indexOf(i)!==-1)return i;if(Object.hasOwn(g,i))return g[i];i=i.replace(/^y|([aeiouy])y/,"$1Y"),i=i.replace(/^'/,"");let t=/^(gener|commun|arsen)(\w*?[aeiouy][^aeiouy])?(\w*)$/,f=/^(\w*?[aeiouy][^aeiouy])(\w*?[aeiouy][^aeiouy])?(\w*)$/;var a=t.exec(i)||f.exec(i)||[];if(a[1]&&a[1].length<i.length?e=a[1].length:e=1/0,a[2]&&a[1].length+a[2].length<i.length?n=a[1].length+a[2].length:n=1/0,i=i.replace(/'s'$|'s$|'$/,""),i=s(i,[[/sses$/,"ss"],[/(\w{2,})(ie[d|s])$/,"$1i"],[/(ie[d|s])$/,"ie"],[/(us|ss)$/,"$1"],[/([aeiouy]\w+)(s)$/,"$1"]]),r.indexOf(i)!==-1)return i;return i=s(i,[[/eedly$/,"ee",e,5],[/(\w*[aeiouy]\w*)ingly$/,u],[/(\w*[aeiouy]\w*)edly$/,u],[/eed$/,"ee",e,3],[/(\w*[aeiouy]\w*)ing$/,u],[/(\w*[aeiouy]\w*)ed$/,u]]),i=i.replace(/(\w+[^aeiouy])([yY])$/,"$1i"),e!==1/0&&(i=s(i,[[/ational$/,"ate",e,7],[/fulness$/,"ful",e,7],[/iveness$/,"ive",e,7],[/ization$/,"ize",e,7],[/ousness$/,"ous",e,7],[/biliti$/,"ble",e,6],[/lessli$/,"less",e,6],[/tional$/,"tion",e,6],[/(?:alism|aliti)$/,"al",e,5],[/ation$/,"ate",e,5],[/entli$/,"ent",e,5],[/fulli$/,"ful",e,5],[/iviti$/,"ive",e,5],[/ousli$/,"ous",e,5],[/abli$/,"able",e,4],[/alli$/,"al",e,4],[/anci$/,"ance",e,4],[/ator$/,"ate",e,4],[/enci$/,"ence",e,4],[/izer$/,"ize",e,4],[/bli$/,"ble",e,3],[/(l)ogi$/,"$1og",e,3],[/([cdeghkmnrt])li$/,"$1",e,2]]),i=s(i,[[/ational$/,"ate",e,7],[/tional$/,"tion",e,6],[/alize$/,"al",e,5],[/ative$/,"",n,5],[/(?:icate|iciti)$/,"ic",e,5],[/ical$/,"ic",e,4],[/ness$/,"",e,4],[/ful$/,"",e,3]]),n!==1/0&&(i=s(i,[[/ement$/,"",n,5],[/(?:able|ance|ence|ible|ment)$/,"",n,4],[/(?:ant|ate|ent|ism|iti|ive|ize|ous)$/,"",n,3],[/([s|t])ion$/,"$1",n,3],[/(?:al|er|ic)$/,"",n,2]])),i=s(i,[[/(^[aeiouy][^aeiouy]|[^aeiouy][aeiouy][^aeiouywxY])?e$/,o],[/(l)l$/,"$1",n,1]])),i=i.replace(/Y/,"y"),i;function u(c,y){var l=y,$=l.substring(l.length-2);return["at","bl","iz"].indexOf($)!==-1?l=l+"e":b.indexOf($)!==-1?l=l.substring(0,l.length-1):x(l,e)&&(l=l+"e"),l}function o(c,y,l,$){return $.length-1>=n?y||"":!y&&$.length-1>=e?"":c}}export{m as stem}; | ||
| var g=new Map([["skis","ski"],["skies","sky"],["dying","die"],["lying","lie"],["tying","tie"],["idly","idl"],["gently","gentl"],["ugly","ugli"],["early","earli"],["only","onli"],["singly","singl"]]),f=new Set(["sky","news","howe","atlas","cosmos","bias","andes"]),r=new Set(["inning","outing","canning","herring","earring","proceed","exceed","succeed"]),h=new Set(["bb","dd","ff","gg","mm","nn","pp","rr","tt"]),o=new Set(["at","bl","iz"]),b=/^y|([aeiouy])y/,m=/Y/,p=/^'/,x=/(^[aeiouy][^aeiouy])|([aeiouy][^aeiouywxY]$)/,v=/^(gener|commun|arsen)(\w*?[aeiouy][^aeiouy])?(\w*)$/,S=/^(\w*?[aeiouy][^aeiouy])(\w*?[aeiouy][^aeiouy])?(\w*)$/,z=/'s'$|'s$|'$/,Y=/(\w+[^aeiouy])([yY])$/;function s(i,e){for(var l=i.length,n=0,a=e.length;n<a;n++)if(e[n][0].test(i))return l-e[n][3]<e[n][2]?i:i.replace(e[n][0],e[n][1]);return i}function k(i,e){return i[e]?!1:x.test(i)}function R(i){var e,l;if(i.length<=2||f.has(i))return i;if(g.has(i))return g.get(i);i=i.replace(b,"$1Y"),i=i.replace(p,"");var n=v.exec(i)||S.exec(i)||[];if(n[1]&&n[1].length<i.length?e=n[1].length:e=1/0,n[2]&&n[1].length+n[2].length<i.length?l=n[1].length+n[2].length:l=1/0,i=i.replace(z,""),i=s(i,[[/sses$/,"ss"],[/(\w{2,})(ie[d|s])$/,"$1i"],[/(ie[d|s])$/,"ie"],[/(us|ss)$/,"$1"],[/([aeiouy]\w+)(s)$/,"$1"]]),r.has(i))return i;return i=s(i,[[/eedly$/,"ee",e,5],[/(\w*[aeiouy]\w*)ingly$/,a],[/(\w*[aeiouy]\w*)edly$/,a],[/eed$/,"ee",e,3],[/(\w*[aeiouy]\w*)ing$/,a],[/(\w*[aeiouy]\w*)ed$/,a]]),i=i.replace(Y,"$1i"),e!==1/0&&(i=s(i,[[/ational$/,"ate",e,7],[/fulness$/,"ful",e,7],[/iveness$/,"ive",e,7],[/ization$/,"ize",e,7],[/ousness$/,"ous",e,7],[/biliti$/,"ble",e,6],[/lessli$/,"less",e,6],[/tional$/,"tion",e,6],[/(?:alism|aliti)$/,"al",e,5],[/ation$/,"ate",e,5],[/entli$/,"ent",e,5],[/fulli$/,"ful",e,5],[/iviti$/,"ive",e,5],[/ousli$/,"ous",e,5],[/abli$/,"able",e,4],[/alli$/,"al",e,4],[/anci$/,"ance",e,4],[/ator$/,"ate",e,4],[/enci$/,"ence",e,4],[/izer$/,"ize",e,4],[/bli$/,"ble",e,3],[/(l)ogi$/,"$1og",e,3],[/([cdeghkmnrt])li$/,"$1",e,2]]),i=s(i,[[/ational$/,"ate",e,7],[/tional$/,"tion",e,6],[/alize$/,"al",e,5],[/ative$/,"",l,5],[/(?:icate|iciti)$/,"ic",e,5],[/ical$/,"ic",e,4],[/ness$/,"",e,4],[/ful$/,"",e,3]]),l!==1/0&&(i=s(i,[[/ement$/,"",l,5],[/(?:able|ance|ence|ible|ment)$/,"",l,4],[/(?:ant|ate|ent|ism|iti|ive|ize|ous)$/,"",l,3],[/([s|t])ion$/,"$1",l,3],[/(?:al|er|ic)$/,"",l,2]])),i=s(i,[[/(^[aeiouy][^aeiouy]|[^aeiouy][aeiouy][^aeiouywxY])?e$/,c],[/(l)l$/,"$1",l,1]])),i=i.replace(m,"y"),i;function a(y,u){var t=u,$=t.substring(t.length-2);return o.has($)?t=t+"e":h.has($)?t=t.substring(0,t.length-1):k(t,e)&&(t=t+"e"),t}function c(y,u,t,$){return $.length-1>=l?u||"":!u&&$.length-1>=e?"":y}}export{R as stem}; |
+8
-2
@@ -17,3 +17,3 @@ import process from 'node:process'; | ||
| // console logger | ||
| export const concol = new ConCol('StaticSearch', 'cyan'); | ||
| export const concol = new ConCol('StaticSearch', 'magentaBright'); | ||
@@ -184,3 +184,7 @@ | ||
| // page data | ||
| pageIndex[idx] = { u: page.slug, t: page.html.title, d: page.html.description }; | ||
| pageIndex[idx] = { u: page.slug }; | ||
| if (page.html.title) pageIndex[idx].t = page.html.title; | ||
| if (page.html.description) pageIndex[idx].d = page.html.description; | ||
| if (page.html.date) pageIndex[idx].p = page.html.date; | ||
| if (page.html.wordcount) pageIndex[idx].w = page.html.wordcount; | ||
@@ -337,2 +341,4 @@ // title scores | ||
| '', | ||
| ] | ||
@@ -339,0 +345,0 @@ |
+83
-3
@@ -86,8 +86,42 @@ // data parsing functions | ||
| // get schema.org data | ||
| const schemaOrg = getSchemaOrg(html); | ||
| // get title | ||
| res.title = (dom.querySelector('title')?.structuredText || dom.querySelector('h1')?.structuredText || 'Untitled').trim(); | ||
| res.title = ( | ||
| schemaOrg?.headline || | ||
| dom.querySelector('title')?.structuredText || | ||
| dom.querySelector('h1')?.structuredText || | ||
| '' | ||
| ).trim(); | ||
| let fullContent = res.title + ' '; | ||
| // get description | ||
| res.description = (dom.querySelector('meta[name="description"]')?.getAttribute('content') || '').trim(); | ||
| res.description = ( | ||
| schemaOrg?.description || | ||
| dom.querySelector('meta[name="description"]')?.getAttribute('content') || | ||
| '' | ||
| ).trim(); | ||
| // get modified/publication date | ||
| if (schemaOrg) { | ||
| res.date = ( | ||
| isDate( schemaOrg.dateModified ) || | ||
| isDate( schemaOrg.datePublished ) || | ||
| isDate( schemaOrg.published ) || | ||
| isDate( schemaOrg.datePosted ) || | ||
| isDate( schemaOrg.generatedAt ) || | ||
| isDate( schemaOrg.uploadDate ) | ||
| ); | ||
| } | ||
| res.date = res.date || ( | ||
| isDate( dom.querySelector('meta[property="article:modified_time"]')?.getAttribute('content') ) || | ||
| isDate( dom.querySelector('meta[property="article:published_time"]')?.getAttribute('content') ) || | ||
| isDate( dom.querySelector('time[itemProp="dateModified"]')?.getAttribute('datetime') ) || | ||
| isDate( dom.querySelector('time[itemProp="datePublished"]')?.getAttribute('datetime') ) || | ||
| isDate( dom.querySelector('time[itemProp="dateCreated"]')?.getAttribute('datetime') ) | ||
| ); | ||
| // extract words | ||
@@ -125,3 +159,5 @@ res.word = { | ||
| // all content | ||
| res.word.content = res.word.content.union( wSet( node.structuredText ) ); | ||
| const content = node.structuredText.replace(/<[^<]+?>/g, ''); | ||
| fullContent += content + ' '; | ||
| res.word.content = res.word.content.union( wSet( content ) ); | ||
@@ -173,2 +209,5 @@ // strong, b, em, i | ||
| // get word count | ||
| res.wordcount = parseFloat(schemaOrg?.wordCount) || wordCount(fullContent); | ||
| return res; | ||
@@ -189,1 +228,42 @@ | ||
| } | ||
| // check value is a date and return YYYY-MM-DD string or undefined | ||
| function isDate(d) { | ||
| d = new Date(d); | ||
| if (!isNaN(d)) return d.toISOString().slice(0, 10); | ||
| } | ||
| // extract schema | ||
| function getSchemaOrg(html) { | ||
| try { | ||
| const | ||
| match = html.match(/application\/ld\+json.*?>(.*?)<\/script/s), | ||
| data = match?.[1], | ||
| schema = data && JSON.parse(data); | ||
| return schema; | ||
| } | ||
| catch (e) {} | ||
| } | ||
| // count words in a string | ||
| function wordCount(str) { | ||
| return str | ||
| .replace(/<[^<]+?>/g, '') | ||
| .replace(/\W/g, ' ') | ||
| .replace(/\s+/g, ' ') | ||
| .trim() | ||
| .replace(/\S/g, '') | ||
| .length + 1; | ||
| } |
+1
-1
| { | ||
| "name": "staticsearch", | ||
| "version": "0.2.1", | ||
| "version": "0.3.0", | ||
| "description": "Simple static site search system.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+32
-12
@@ -324,2 +324,6 @@ # StaticSearch | ||
| <h2 part="title"></h2> | ||
| <p part="meta"> | ||
| <time part="date"></time> – | ||
| <span part="words">0</span> words | ||
| </p> | ||
| <p part="description"></p> | ||
@@ -330,4 +334,6 @@ </a> | ||
| You can override this using a `<template>` with an ID of `staticsearch_item` in your HTML page (it can be within `<static-search>` or anywhere else). It can set the `part` attributes `"item"`, `"link"`, `"title"`, and `"description"` as necessary, e.g. show the title but no description in an `<article>`: | ||
| Note that the date and word count number are formatted for the user's locale. | ||
| You can override this using a `<template>` with an ID of `staticsearch_item` in your HTML page (it can be within `<static-search>` or anywhere else). Set the `part` attributes `"item"`, `"link"`, `"title"`, `meta`, `date`, `words`, and `"description"` as necessary, e.g. show the title but no meta values or description in an `<article>`: | ||
| ```html | ||
@@ -365,4 +371,5 @@ <template id="staticsearch_item"> | ||
| --staticsearch-color-fore: CanvasText; | ||
| --staticsearch-color-dim: color-mix(in oklab, CanvasText 70%, Canvas); | ||
| --staticsearch-color-fore0: CanvasText; | ||
| --staticsearch-color-fore1: color-mix(in oklab, CanvasText 80%, Canvas); | ||
| --staticsearch-color-fore2: color-mix(in oklab, CanvasText 60%, Canvas); | ||
@@ -440,2 +447,7 @@ --staticsearch-color-link: color-mix(in oklab, LinkText 70%, CanvasText); | ||
| The `staticsearch_result` element can set optional attributes: | ||
| * `minscore="<num>"` - only show pages with total relevancy scores of this or above on results | ||
| * `maxresults="<num>"` - show up to this number of pages on the results | ||
| As [shown above](#overriding-html-templates), you can set your own HTML `<template>`s and style anything as you like: | ||
@@ -462,3 +474,6 @@ | ||
| <article> | ||
| <h2 part="title"><a part="link"></a></h2> | ||
| <h2 part="title"> | ||
| <a part="link"></a>, | ||
| <time part="date"></time> | ||
| </h2> | ||
| </article> | ||
@@ -491,12 +506,11 @@ </li> | ||
| `staticSearchInput()` has a single parameter with the input element. | ||
| `staticSearchInput(element)` is passed the input element. | ||
| `staticSearchResult()` has the following parameters: | ||
| `staticSearchResult(element, options)` is passed the result element and an optional options object with the following properties: | ||
| * `element` - the result element (required) | ||
| * `minScore` - only show pages with total word scores of this or above on results | ||
| * `maxResults` - show up to this number of pages on the results | ||
| * `resultElement` - the outer list element (defaults to `ol`) | ||
| * `messageTemplate` - a DOM `<template>` configuring the results message | ||
| * `itemTemplate` - a DOM `<template>` configuring a result item | ||
| * `.minScore` - only show pages with total word scores of this or above on results | ||
| * `.maxResults` - show up to this number of pages on the results | ||
| * `.resultElement` - the outer list element (defaults to `ol`) | ||
| * `.messageTemplate` - a DOM `<template>` configuring the results message | ||
| * `.itemTemplate` - a DOM `<template>` configuring a result item | ||
@@ -520,2 +534,4 @@ | ||
| "description": "Do static sites rank better in search engines?", | ||
| "date": "2025-01-31", | ||
| "words": 1234 | ||
| "relevancy": 21 | ||
@@ -528,2 +544,4 @@ }, | ||
| "description": "How to output a list of all pages for search engines.", | ||
| "date": "2025-02-01", | ||
| "words": 954 | ||
| "relevancy": 12 | ||
@@ -536,2 +554,4 @@ }, | ||
| "description": "Static sites typically perform better than others.", | ||
| "date": "2025-02-22", | ||
| "words": 222 | ||
| "relevancy": 2 | ||
@@ -538,0 +558,0 @@ } |
+44
-32
@@ -5,26 +5,39 @@ // English Porter2 stemming function | ||
| const | ||
| special = { | ||
| skis: 'ski', | ||
| skies: 'sky', | ||
| dying: 'die', | ||
| lying: 'lie', | ||
| tying: 'tie', | ||
| idly: 'idl', | ||
| gently: 'gentl', | ||
| ugly: 'ugli', | ||
| early: 'earli', | ||
| only: 'onli', | ||
| singly: 'singl' | ||
| }, | ||
| special = new Map([ | ||
| [ 'skis', 'ski' ], | ||
| [ 'skies', 'sky' ], | ||
| [ 'dying', 'die' ], | ||
| [ 'lying', 'lie' ], | ||
| [ 'tying', 'tie' ], | ||
| [ 'idly', 'idl' ], | ||
| [ 'gently', 'gentl' ], | ||
| [ 'ugly', 'ugli' ], | ||
| [ 'early', 'earli' ], | ||
| [ 'only', 'onli' ], | ||
| [ 'singly', 'singl' ], | ||
| ]), | ||
| exceptions = [ | ||
| exceptions = new Set([ | ||
| 'sky', 'news', 'howe', 'atlas', 'cosmos', 'bias', 'andes' | ||
| ], | ||
| ]), | ||
| exceptions1a = [ | ||
| exceptions1a = new Set([ | ||
| 'inning', 'outing', 'canning', 'herring', 'earring', 'proceed', 'exceed', 'succeed' | ||
| ], | ||
| ]), | ||
| doubles = ['bb', 'dd', 'ff', 'gg', 'mm', 'nn', 'pp', 'rr', 'tt']; | ||
| doubles = new Set(['bb', 'dd', 'ff', 'gg', 'mm', 'nn', 'pp', 'rr', 'tt']), | ||
| sb1Suffix = new Set(['at', 'bl', 'iz']), | ||
| reReplaceY = /^y|([aeiouy])y/, | ||
| reResetY = /Y/, | ||
| reReplaceApos = /^'/, | ||
| reShort = /(^[aeiouy][^aeiouy])|([aeiouy][^aeiouywxY]$)/, | ||
| regionRegex1 = /^(gener|commun|arsen)(\w*?[aeiouy][^aeiouy])?(\w*)$/, | ||
| regionRegex2 = /^(\w*?[aeiouy][^aeiouy])(\w*?[aeiouy][^aeiouy])?(\w*)$/, | ||
| reStep0 = /'s'$|'s$|'$/, | ||
| reStep1c = /(\w+[^aeiouy])([yY])$/; | ||
| // suffix handler | ||
| function handleSuffix(word, patterns) { | ||
@@ -46,5 +59,6 @@ var wordLen = word.length; | ||
| // short handler | ||
| function isShort(word, r1) { | ||
| if (!word[r1]) { | ||
| return /(^[aeiouy][^aeiouy])|([aeiouy][^aeiouywxY]$)/.test(word); | ||
| return reShort.test(word); | ||
| } | ||
@@ -61,3 +75,3 @@ return false; | ||
| // 1 or 2 letter words (and some other exceptions) are returned unchanged | ||
| if (word.length <= 2 || exceptions.indexOf(word) !== -1) { | ||
| if (word.length <= 2 || exceptions.has(word)) { | ||
| return word; | ||
@@ -67,15 +81,13 @@ } | ||
| // some words have predefined stem forms | ||
| if (Object.hasOwn(special, word)) { | ||
| return special[word]; | ||
| if (special.has(word)) { | ||
| return special.get(word); | ||
| } | ||
| // replace y | ||
| word = word.replace(/^y|([aeiouy])y/, '$1Y'); | ||
| word = word.replace(reReplaceY, '$1Y'); | ||
| // remove initial ' | ||
| word = word.replace(/^'/, ''); | ||
| word = word.replace(reReplaceApos, ''); | ||
| // find regions | ||
| const regionRegex1 = /^(gener|commun|arsen)(\w*?[aeiouy][^aeiouy])?(\w*)$/; | ||
| const regionRegex2 = /^(\w*?[aeiouy][^aeiouy])(\w*?[aeiouy][^aeiouy])?(\w*)$/; | ||
| var match = regionRegex1.exec(word) || regionRegex2.exec(word) || []; | ||
@@ -98,3 +110,3 @@ | ||
| // step 0 | ||
| word = word.replace(/'s'$|'s$|'$/, ''); | ||
| word = word.replace(reStep0, ''); | ||
@@ -111,3 +123,3 @@ // step 1a | ||
| // recheck exceptions | ||
| if (exceptions1a.indexOf(word) !== -1) { | ||
| if (exceptions1a.has(word)) { | ||
| return word; | ||
@@ -127,3 +139,3 @@ } | ||
| // step 1c | ||
| word = word.replace(/(\w+[^aeiouy])([yY])$/, '$1i'); | ||
| word = word.replace(reStep1c, '$1i'); | ||
@@ -189,3 +201,3 @@ if (r1 !== Infinity) { | ||
| // reset Y | ||
| word = word.replace(/Y/, 'y'); | ||
| word = word.replace(reResetY, 'y'); | ||
@@ -200,5 +212,5 @@ return word; | ||
| if (['at', 'bl', 'iz'].indexOf(suffix) !== -1) { | ||
| if (sb1Suffix.has(suffix)) { | ||
| result = result + 'e'; | ||
| } else if (doubles.indexOf(suffix) !== -1) { | ||
| } else if (doubles.has(suffix)) { | ||
| result = result.substring(0, result.length - 1); | ||
@@ -205,0 +217,0 @@ } else if (isShort(result, r1)) { |
@@ -88,4 +88,2 @@ a | ||
| are | ||
| area | ||
| areas | ||
| aren | ||
@@ -97,4 +95,2 @@ arent | ||
| as | ||
| aside | ||
| asides | ||
| ask | ||
@@ -111,4 +107,2 @@ asked | ||
| atop | ||
| atrule | ||
| attr | ||
| aug | ||
@@ -118,3 +112,2 @@ aught | ||
| august | ||
| auth | ||
| available | ||
@@ -125,3 +118,2 @@ aw | ||
| b | ||
| back | ||
| backed | ||
@@ -176,7 +168,3 @@ backing | ||
| bo | ||
| bold | ||
| bolder | ||
| boldest | ||
| boo | ||
| boolean | ||
| both | ||
@@ -190,3 +178,2 @@ bottom | ||
| bs | ||
| builtin | ||
| but | ||
@@ -211,3 +198,2 @@ buts | ||
| causes | ||
| cdata | ||
| certain | ||
@@ -218,6 +204,4 @@ certainer | ||
| changes | ||
| char | ||
| cheers | ||
| circa | ||
| class | ||
| clear | ||
@@ -237,3 +221,2 @@ clearly | ||
| considering | ||
| constant | ||
| contain | ||
@@ -261,3 +244,2 @@ containing | ||
| darn | ||
| date | ||
| day | ||
@@ -268,4 +250,2 @@ dear | ||
| definitely | ||
| delete | ||
| deleted | ||
| describe | ||
@@ -286,3 +266,2 @@ described | ||
| do | ||
| doctype | ||
| doe | ||
@@ -338,4 +317,2 @@ does | ||
| entirely | ||
| entities | ||
| entity | ||
| equal | ||
@@ -397,3 +374,2 @@ equals | ||
| five | ||
| fix | ||
| followed | ||
@@ -423,3 +399,2 @@ following | ||
| from | ||
| front | ||
| full | ||
@@ -544,7 +519,4 @@ fully | ||
| immediately | ||
| importance | ||
| important | ||
| in | ||
| indeed | ||
| index | ||
| indicate | ||
@@ -557,4 +529,2 @@ indicated | ||
| inner | ||
| insert | ||
| inserted | ||
| inside | ||
@@ -575,3 +545,2 @@ insofar | ||
| it | ||
| italic | ||
| itd | ||
@@ -599,3 +568,2 @@ itll | ||
| keys | ||
| keyword | ||
| kg | ||
@@ -610,4 +578,2 @@ kind | ||
| la | ||
| lang | ||
| language | ||
| large | ||
@@ -757,4 +723,2 @@ largely | ||
| nowheres | ||
| number | ||
| numbers | ||
| o | ||
@@ -790,4 +754,2 @@ obtain | ||
| opens | ||
| operate | ||
| operator | ||
| opposite | ||
@@ -870,8 +832,3 @@ or | ||
| probably | ||
| problem | ||
| problems | ||
| prolog | ||
| promptly | ||
| properties | ||
| property | ||
| proud | ||
@@ -882,4 +839,2 @@ provide | ||
| providing | ||
| punctuate | ||
| punctuation | ||
| put | ||
@@ -943,3 +898,2 @@ puts | ||
| seconds | ||
| section | ||
| see | ||
@@ -953,4 +907,2 @@ seeing | ||
| sees | ||
| select | ||
| selector | ||
| self | ||
@@ -1031,3 +983,2 @@ selves | ||
| stopped | ||
| string | ||
| strongly | ||
@@ -1049,3 +1000,2 @@ style | ||
| sure | ||
| symbol | ||
| syn | ||
@@ -1132,4 +1082,2 @@ system | ||
| tills | ||
| time | ||
| times | ||
| tip | ||
@@ -1140,3 +1088,2 @@ tis | ||
| together | ||
| token | ||
| too | ||
@@ -1200,4 +1147,2 @@ took | ||
| v | ||
| value | ||
| variable | ||
| various | ||
@@ -1365,2 +1310,2 @@ versus | ||
| zero | ||
| zillion | ||
| zillion |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
86718
4%1040
7.88%576
3.6%