1const Mt=Object.freeze({RESAMPLE_M:8,R_REP:20,R_NEW:30,BEARING_TOL:45,BEARING_BASELINE_SAMPLES:2,GUARD_ALONG:50,L_MIN:24,GAP_SPLIT_M:60,GAP_SPLIT_S:60,STATION_S:90,STATION_D:24,ALT_GATE:10,CELL_MERC:64,FOLDBACK_RATIO:.4,uTurnDedup:!0,offsetDetector:!0,OFFSET_MIN_RUN:200,OFFSET_SEARCH:50,OFFSET_MIN_FRAC:.8,OFFSET_MAX_IQR:8,OFFSET_DIR_TOL:30});function At(t){const e=Object.keys(t).sort();return JSON.stringify(e.map(s=>[s,t[s]]))}function St(t){const e=At(t);let s=2166136261;for(let o=0;o<e.length;o++)s^=e.charCodeAt(o),s=Math.imul(s,16777619)>>>0;return`fnv1a:${s.toString(16).padStart(8,"0")}`}const wt=St(Mt),lt=2,kt=1,xt=2,$=6378137,K=Math.PI/180;function It(t){return t*K*$}function Et(t){return Math.log(Math.tan(Math.PI/4+t*K/2))*$}function B(t){return t/$/K}function C(t){return(2*Math.atan(Math.exp(t/$))-Math.PI/2)/K}const ft={dark:{exploration:255,heatmap:90},light:{exploration:255,heatmap:190}},ut={dark:{heatmap:["#256abf","#3987e5","#6da7ec","#9ec5f4","#cde2fb"],frontier:"#eda100",oneWay:["#be4b1d","#d15421","#e15a24","#f76730","#f88d68","#faac90"],bothWays:["#256abf","#3579cd","#4a86cf","#5598e7","#79b0ef","#9ec5f4"]},light:{heatmap:["#5b52e8","#4a34c9","#3822a0","#261577","#170a4d"],frontier:"#c07a00",oneWay:["#b5471b","#a44017","#8f3713","#7d2f0f","#6e280c","#581e07"],bothWays:["#5b52e8","#5346d9","#4a34c9","#4029b4","#3822a0","#2b1a80"]}},_t="unique-miles",Ot=1,dt="activities",R="artifacts",mt="syncState",ht="credentials";function Nt(){return new Promise((t,e)=>{const s=indexedDB.open(_t,Ot);s.onupgradeneeded=()=>{const o=s.result;o.objectStoreNames.contains(dt)||o.createObjectStore(dt,{keyPath:"id"}).createIndex("byStart","startTs"),o.objectStoreNames.contains(R)||o.createObjectStore(R,{keyPath:"name"}),o.objectStoreNames.contains(mt)||o.createObjectStore(mt,{keyPath:"k"}),o.objectStoreNames.contains(ht)||o.createObjectStore(ht,{keyPath:"k"})},s.onsuccess=()=>t(s.result),s.onerror=()=>e(s.error)})}let q=null;function vt(){return q||(q=Nt()),q}function Ct(t){return new Promise((e,s)=>{t.onsuccess=()=>e(t.result),t.onerror=()=>s(t.error)})}async function tt(t,e){const s=await vt();return Ct(s.transaction(t,"readonly").objectStore(t).get(e))}const yt={kind:"idb",async manifest(){const t=await tt(R,"manifest");return(t==null?void 0:t.data)??null},async block(t){const e=await tt(R,t);if(!e)throw new Error(`artifact block "${t}" missing from storage`);return e.data},async activities(){const t=await tt(R,"activities");return(t==null?void 0:t.data)??[]}};async function Rt(t){return await yt.manifest().catch(()=>null)?yt:null}const M=(t,e)=>self.postMessage(t,e??[]),et=t=>[parseInt(t.slice(1,3),16),parseInt(t.slice(3,5),16),parseInt(t.slice(5,7),16)];function Lt(t,e){if(t.length===1)return t[0];const s=Math.min(1,Math.max(0,e))*(t.length-1),o=Math.min(t.length-2,Math.floor(s)),a=s-o,r=t[o],c=t[o+1];return[r[0]+(c[0]-r[0])*a,r[1]+(c[1]-r[1])*a,r[2]+(c[2]-r[2])*a]}const Pt=t=>{const e=ut[t]??ut.dark;return{oneWay:e.oneWay.map(et),bothWays:e.bothWays.map(et),heat:e.heatmap.map(et)}};let P=null,w=[],g=0,G,W,N,U,D,st,L,it=[],_,J,ct,gt,v,Q,z;const ot=1024,nt=new Uint32Array(ot),j=[null,null],at=[!0,!0];let Y=NaN,E=NaN,V="";function T(t,e){switch(e.type){case"Int32":return new Int32Array(t,e.byteOffset,e.length);case"Uint32":return new Uint32Array(t,e.byteOffset,e.length);case"Uint16":return new Uint16Array(t,e.byteOffset,e.length);default:return new Uint8Array(t,e.byteOffset,e.length)}}let I=null;async function Ft(){if(I=await Rt(),!I){M({type:"error",kind:"no-artifacts",message:"No history imported yet."});return}const t=await I.manifest();if(!t){M({type:"error",kind:"no-artifacts",message:"No history imported yet."});return}if(t.formatVersion!==lt){M({type:"error",kind:"format-mismatch",message:`Artifacts use format ${t.formatVersion}, this build expects ${lt}.`});return}const[e,s,o]=await Promise.all([I.block("sites"),I.block("touches"),I.activities()]);for(const[f,u]of[["sites",e],["touches",s]]){const n=t.files[f].byteLength;if(u.byteLength!==n){M({type:"error",kind:"format-mismatch",message:`Your stored map is incomplete: ${f} is ${u.byteLength} bytes where it should be ${n}. Rebuilding fixes this and costs no Strava requests.`});return}}P=t,w=o,g=t.counts.sites;const a=t.files.sites.blocks;G=T(e,a.x),W=T(e,a.y),N=T(e,a.creditCm),L=T(e,a.mintTs),U=T(e,a.mintAct),it=a.firstTsByGroup.map(f=>T(e,f)),_=T(s,t.files.touches.blocks.actOffsets),J=T(s,t.files.touches.blocks.siteIds),ct=T(s,t.files.touches.blocks.dirs),D=new Float64Array(w.length).fill(1/0),st=new Float64Array(w.length).fill(-1/0);for(let f=0;f<g;f++){const u=U[f];
1if(u>=D.length)continue;const n=L[f];n<D[u]&&(D[u]=n),n>st[u]&&(st[u]=n)}v=new Uint16Array(g),Q=new Uint16Array(g),z=new Uint16Array(g),j[0]=new Uint8Array(4*g),j[1]=new Uint8Array(4*g);const r=T(e,a.bearing);gt=r;const c=(t.params.RESAMPLE_M??8)/2,p=new Float32Array(2*g),y=new Float32Array(2*g);for(let f=0;f<g;f++){const u=G[f]/100,n=W[f]/100,l=C(n),i=Math.cos(l*Math.PI/180),h=r[f]*2*Math.PI/180,b=Math.sin(h)*c/i,m=Math.cos(h)*c/i;p[2*f]=B(u-b),p[2*f+1]=C(n-m),y[2*f]=B(u+b),y[2*f+1]=C(n+m)}const S=L.slice(),A=U.slice();M({type:"ready",manifest:t,activities:w,sourcePositions:p,targetPositions:y,nSites:g,siteMintTs:S,siteMintAct:A},[p.buffer,y.buffer,S.buffer,A.buffer]),t.paramsHash!==wt&&M({type:"error",kind:"params-mismatch",message:"Artifacts were built with different algorithm parameters. Run `npm run build:ledger`."})}function Dt(t,e){const s=G[t],o=W[t];return s>=e.minX&&s<=e.maxX&&o>=e.minY&&o<=e.maxY}function bt(t,e){const s=_[t],o=_[t+1];for(let a=s;a<o;a++){const r=J[a],c=ct[a];v[r]+=e,Q[r]+=(c&kt)*e,z[r]+=((c&xt)>>1)*e}}function H(t,e,s,o){return t.startTs>=e&&t.startTs<=s&&o.has(t.group)}function X(t,e){const s=[...e].sort().join(",");if(t.incremental===!0&&s===V&&Y===t.t0&&Number.isFinite(E)&&t.t1>=E){let r=0;for(const c of w)e.has(c.group)&&c.startTs>=t.t0&&c.startTs<=t.t1&&r++,e.has(c.group)&&c.startTs>
1E&&c.startTs<=t.t1&&bt(c.idx,1);return E=t.t1,r}v.fill(0),Q.fill(0),z.fill(0);let a=0;for(const r of w)H(r,t.t0,t.t1,e)&&(bt(r.idx,1),a++);return Y=t.t0,E=t.t1,V=s,a}function Ut(t,e,s,o,a,r){const c=s==="light"?"light":"dark",p=Pt(c),y=e==="heatmap",S=y?ft[c].heatmap:ft[c].exploration,A=1,f=Math.max(1,r-A);for(let u=0;u<g;u++){const n=v[u],l=4*u;if(n===0||(a?L[u]<o:L[u]>o)){t[l+3]=0;continue}const i=y?p.heat:Q[u]>0&&z[u]>0?p.bothWays:p.oneWay,h=Lt(i,(n-A)/f);t[l]=h[0],t[l+1]=h[1],t[l+2]=h[2],t[l+3]=S}}function Tt(t,e){let s=4294967295;for(let o=0;o<e.length;o++){const a=it[e[o]][t];a<s&&(s=a)}return s}function Bt(t,e){const s=w.filter(n=>H(n,t.t0,t.t1,e)),o=[];for(const n of s){let l=0;const i=_[n.idx],h=_[n.idx+1];for(let b=i;b<h;b++){const m=J[b];Tt(m,t.groups)===n.startTs&&U[m]===n.idx&&(l+=N[m]/100)}o.push({idx:n.idx,newM:l})}o.sort((n,l)=>l.newM-n.newM);const r=(t.t1-t.t0)/(365.25*86400)<3,c=new Map,p=new Map(o.map(n=>[n.idx,n.newM]));for(const n of s){const l=new Date(n.startDateLocal),i=r?Date.UTC(l.getUTCFullYear(),l.getUTCMonth()):Date.UTC(l.getUTCFullYear(),0),h=c.get(i)??{newM:0,totalM:0};h.newM+=p.get(n.idx)??0,h.totalM+=n.distanceM,c.set(i,h)}const y=[...c.entries()].sort((n,l)=>n[0]-l[0]).map(([n,l])=>({bucketStart:n/1e3,...l})),S=[],A=Y,f=E,u=V;for(const n of t.groups){const l=new Set([n]);X({...t,incremental:!1},l);let i=0,h=0;for(let m=0;m<g;m++){v[m]>0&&(i+=N[m]/100);const d=it[n][m];d!==4294967295&&d>=t.t0&&d<=t.t1&&(h+=N[m]/100)}let b=0;for(const m of w)m.group===n&&m.startTs>=t.t0&&m.startTs<=t.t1&&(b+=m.distanceM);(b>0||i>0)&&S.push({group:n,distinctM:i,newM:h,totalM:b})}return X({...t,incremental:!1},e),Y=A,E=f,V=u,{perActivityNewM:o.slice(0,20),byBucket:y,byGroup:S}}function Gt(t){if(!P)return;const e=new Set(t.groups),s=X(t,e),o=t.drawer?Bt(t,e):void 0;t.drawer&&X({...t,incremental:!1},e);const a=at[0]?0:1;at[a]=!1;const r=j[a];let c=0,p=0;const y=t.viewport;nt.fill(0);let S=0;for(let i=0;i<g;i++){const h=v[i],b=!y||Dt(i,y);if(h>0&&b&&(c+=N[i]/100,nt[Math.min(h,ot-1)]++,S++),b){const m=Tt(i,t.groups);m!==4294967295&&m>=t.t0&&m<=t.t1&&(p+=N[i]/100)}}let A=1;{const i=S*.98;let h=0;for(let b=1;b<ot;b++)if(h+=nt[b],h>=i){A=b;break}}const f=t.scaleMax&&t.scaleMax>1?t.scaleMax:A;Ut(r,t.mode,t.theme??"dark",t.playing?t.reverse?t.t0:t.t1:t.reverse?-1/0:1/0,t.reverse===!0,f);let u=0,n=s;if(y)u=null,n=w.filter(i=>H(i,t.t0,t.t1,e)&&i.bbox[0]<=B(y.maxX/100)&&i.bbox[2]>=B(y.minX/100)&&i.bbox[1]<=C(y.maxY/100)&&i.bbox[3]>=C(y.minY/100)).length;else for(const i of w)H(i,t.t0,t.t1,e)&&(u+=i.distanceM);const l=r.buffer;M({type:"result",slot:a,colors:l,distinctM:c,newM:p,totalM:u,activityCount:n,maxVisit:f,extras:o},[l])}const Wt=["N","NE","E","SE","S","SW","W","NW"];function pt(t){const e=t*2%360;return Wt[Math.round(e/45)%8]}function jt(t){if(!P)return;const e=It(t.lng)*100,s=Et(t.lat)*100,o=Math.cos(t.lat*Math.PI/180),a=t.radiusM/o*100,r=a*a;let c=-1,p=r;for(let d=0;d<g;d++){const k=G[d]-e;if(k>a||k<-a)continue;const x=W[d]-s;if(x>a||x<-a)continue;const O=k*k+x*x;O<p&&(p=O,c=d)}if(c<0){M({type:"siteInfoResult",seq:t.seq,siteIndex:-1,visits:0,alongCount:0,againstCount:0,alongLabel:"",againstLabel:"",firstTs:0,lastTs:0,firstActivityName:"",visitsAllTime:0});return}const y=c,S=new Set(t.groups);let A=0,f=0,u=0,n=0,l=0,i=0,h="";const b=[];for(const d of w){let k=_[d.idx],x=_[d.idx+1]-1,O=-1;for(;k<=x;){const F=k+x>>1,rt=J[F];if(rt===y){O=F;break}rt<y?k=F+1:x=F-1}if(O<0||(f++,d.startTs<t.t0||d.startTs>t.t1||!S.has(d.group)))continue;A++;const Z=ct[O];Z&1&&u++,Z&2&&n++,t.detail&&b.push({idx:d.idx,stravaId:d.stravaId,name:d.name,startTs:d.startTs,startDateLocal:d.startDateLocal,sportType:d.sportType,dir:Z}),(l===0||d.startTs<l)&&(l=d.startTs,h=d.name),d.startTs>i&&(i=d.startTs)}const m=gt[y];M({type:"siteInfoResult",seq:t.seq,siteIndex:y,visits:A,alongCount:u,againstCount:n,alongLabel:pt(m),againstLabel:pt((m+90)%180),firstTs:l,lastTs:i,firstActivityName:h,visitsAllTime:f,activities:t.detail?b.sort((d,k)=>k.startTs-d.startTs):void 0})}async function Yt(){if(!P||!I)return;const t=await I.block("tracks"),e=P.files.tracks.blocks,s=T(t,e.trackOffsets).slice(),o=T(t,e.px).slice(),a=T(t,e.py).slice(),r=T(t,e.flag).slice();M({type:"tracks",trackOffsets:s,px:o,py:a,flag:r},[s.buffer,o.buffer,a.buffer,r.buffer])}self.onmessage=t=>{const e=t.data;switch(e.type){case"init":Ft().catch(s=>M({type:"error",kind:"failed",message:String(s)}));break;case"query":try{Gt(e)}catch(s){M({type:"error",kind:"failed",message:String(s)})}break;case"release":j[e.slot]=new Uint8Array(e.colors),at[e.slot]=!0;break;case"siteAt":try{jt(e)}catch(s){M({type:"error",kind:"failed",message:String(s)})}break;case"loadTracks":Yt().catch(s=>M({type:"error",kind:"failed",message:String(s)}));break}};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.