From b57eb930977fd443453cfadafa210a8beb58816b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Wed, 8 Apr 2026 15:31:03 +0200 Subject: [PATCH] feat(04-01): add click navigation, project filter dropdown, and hover affordance --- Resources/public/heatmap.css | 30 + Resources/public/heatmap.js | 2282 +++++++++++++++++++++++++++++++++- assets/src/heatmap.ts | 96 +- assets/src/types.ts | 5 + 4 files changed, 2402 insertions(+), 11 deletions(-) diff --git a/Resources/public/heatmap.css b/Resources/public/heatmap.css index 53d0542..be4a1ce 100644 --- a/Resources/public/heatmap.css +++ b/Resources/public/heatmap.css @@ -15,6 +15,13 @@ .heatmap-cell { rx: 2; ry: 2; + cursor: pointer; + transition: opacity 0.1s ease; + pointer-events: fill; +} + +.heatmap-cell:hover { + opacity: 0.75; } .heatmap-empty { @@ -26,3 +33,26 @@ font-size: 10px; font-family: var(--tblr-font-sans-serif); } + +/* Layout: heatmap + filter side by side */ +.heatmap-wrapper { + display: flex; + align-items: flex-start; + gap: 16px; +} + +.heatmap-wrapper .heatmap-svg-area { + flex: 1; + min-width: 0; + overflow-x: auto; +} + +.heatmap-wrapper .heatmap-filter { + flex-shrink: 0; + padding-top: 20px; +} + +.heatmap-filter select { + min-width: 140px; + max-width: 200px; +} diff --git a/Resources/public/heatmap.js b/Resources/public/heatmap.js index 9e0b4b4..f40be03 100644 --- a/Resources/public/heatmap.js +++ b/Resources/public/heatmap.js @@ -1 +1,2281 @@ -"use strict";var KimaiHeatmap=(()=>{var Pt=Object.defineProperty;var Qr=Object.getOwnPropertyDescriptor;var Xr=Object.getOwnPropertyNames;var Gr=Object.prototype.hasOwnProperty;var Jr=(t,e)=>{for(var r in e)Pt(t,r,{get:e[r],enumerable:!0})},Kr=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Xr(e))!Gr.call(t,o)&&o!==r&&Pt(t,o,{get:()=>e[o],enumerable:!(n=Qr(e,o))||n.enumerable});return t};var jr=t=>Kr(Pt({},"__esModule",{value:!0}),t);var Pa={};Jr(Pa,{init:()=>Wa,renderHeatmap:()=>Ir});var St="http://www.w3.org/1999/xhtml",Et={svg:"http://www.w3.org/2000/svg",xhtml:St,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Tt(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),Et.hasOwnProperty(e)?{space:Et[e],local:t}:t}function tn(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===St&&e.documentElement.namespaceURI===St?e.createElement(t):e.createElementNS(r,t)}}function en(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Ct(t){var e=Tt(t);return(e.local?en:tn)(e)}function rn(){}function Dt(t){return t==null?rn:function(){return this.querySelector(t)}}function le(t){typeof t!="function"&&(t=Dt(t));for(var e=this._groups,r=e.length,n=new Array(r),o=0;o=D&&(D=b+1);!(L=E[D])&&++D=0;)(i=n[o])&&(a&&i.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(i,a),a=i);return this}function Te(t){t||(t=yn);function e(m,M){return m&&M?t(m.__data__,M.__data__):!m-!M}for(var r=this._groups,n=r.length,o=new Array(n),a=0;ae?1:t>=e?0:NaN}function Ce(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function De(){return Array.from(this)}function be(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?Sn:typeof e=="function"?Cn:Tn)(t,e,r??"")):Dn(this.node(),t)}function Dn(t,e){return t.style.getPropertyValue(e)||Ft(t).getComputedStyle(t,null).getPropertyValue(e)}function bn(t){return function(){delete this[t]}}function Un(t,e){return function(){this[t]=e}}function Fn(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function Ne(t,e){return arguments.length>1?this.each((e==null?bn:typeof e=="function"?Fn:Un)(t,e)):this.node()[t]}function Ye(t){return t.trim().split(/^|\s+/)}function Rt(t){return t.classList||new He(t)}function He(t){this._node=t,this._names=Ye(t.getAttribute("class")||"")}He.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function We(t,e){for(var r=Rt(t),n=-1,o=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function $n(t){return function(){var e=this.__on;if(e){for(var r=0,n=-1,o=e.length,a;re?1:t>=e?0:NaN}function zt(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function kt(t){let e,r,n;t.length!==2?(e=j,r=(u,f)=>j(t(u),f),n=(u,f)=>t(u)-f):(e=t===j||t===zt?t:jn,r=t,n=t);function o(u,f,s=0,p=u.length){if(s>>1;r(u[m],f)<0?s=m+1:p=m}while(s>>1;r(u[m],f)<=0?s=m+1:p=m}while(ss&&n(u[m-1],f)>-n(u[m],f)?m-1:m}return{left:o,center:i,right:a}}function jn(){return 0}function Vt(t){return t===null?NaN:+t}var Ke=kt(j),je=Ke.right,to=Ke.left,eo=kt(Vt).center,Bt=je;var ro=Math.sqrt(50),no=Math.sqrt(10),oo=Math.sqrt(2);function At(t,e,r){let n=(e-t)/Math.max(0,r),o=Math.floor(Math.log10(n)),a=n/Math.pow(10,o),i=a>=ro?10:a>=no?5:a>=oo?2:1,u,f,s;return o<0?(s=Math.pow(10,-o)/i,u=Math.round(t*s),f=Math.round(e*s),u/se&&--f,s=-s):(s=Math.pow(10,o)*i,u=Math.round(t/s),f=Math.round(e/s),u*se&&--f),f0))return[];if(t===e)return[t];let n=e=o))return[];let u=a-o+1,f=new Array(u);if(n)if(i<0)for(let s=0;s=n)&&(r=n);else{let n=-1;for(let o of t)(o=e(o,++n,t))!=null&&(r=o)&&(r=o)}return r}function tr(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}function er(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function tt(t,e){if(!isFinite(t)||t===0)return null;var r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"),n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function V(t){return t=tt(Math.abs(t)),t?t[1]:NaN}function rr(t,e){return function(r,n){for(var o=r.length,a=[],i=0,u=t[0],f=0;o>0&&u>0&&(f+u+1>n&&(u=Math.max(1,n-f)),a.push(r.substring(o-=u,o+u)),!((f+=u+1)>n));)u=t[i=(i+1)%t.length];return a.reverse().join(e)}}function nr(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var ao=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Q(t){if(!(e=ao.exec(t)))throw new Error("invalid format: "+t);var e;return new Nt({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Q.prototype=Nt.prototype;function Nt(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}Nt.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function or(t){t:for(var e=t.length,r=1,n=-1,o;r0&&(n=0);break}return n>0?t.slice(0,n)+t.slice(o+1):t}var pt;function ar(t,e){var r=tt(t,e);if(!r)return pt=void 0,t.toPrecision(e);var n=r[0],o=r[1],a=o-(pt=Math.max(-8,Math.min(8,Math.floor(o/3)))*3)+1,i=n.length;return a===i?n:a>i?n+new Array(a-i+1).join("0"):a>0?n.slice(0,a)+"."+n.slice(a):"0."+new Array(1-a).join("0")+tt(t,Math.max(0,e+a-1))[0]}function $t(t,e){var r=tt(t,e);if(!r)return t+"";var n=r[0],o=r[1];return o<0?"0."+new Array(-o).join("0")+n:n.length>o+1?n.slice(0,o+1)+"."+n.slice(o+1):n+new Array(o-n.length+2).join("0")}var Qt={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:er,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>$t(t*100,e),r:$t,s:ar,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Xt(t){return t}var ir=Array.prototype.map,ur=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function sr(t){var e=t.grouping===void 0||t.thousands===void 0?Xt:rr(ir.call(t.grouping,Number),t.thousands+""),r=t.currency===void 0?"":t.currency[0]+"",n=t.currency===void 0?"":t.currency[1]+"",o=t.decimal===void 0?".":t.decimal+"",a=t.numerals===void 0?Xt:nr(ir.call(t.numerals,String)),i=t.percent===void 0?"%":t.percent+"",u=t.minus===void 0?"\u2212":t.minus+"",f=t.nan===void 0?"NaN":t.nan+"";function s(m,M){m=Q(m);var _=m.fill,T=m.align,k=m.sign,E=m.symbol,N=m.zero,b=m.width,D=m.comma,C=m.precision,L=m.trim,w=m.type;w==="n"?(D=!0,w="g"):Qt[w]||(C===void 0&&(C=12),L=!0,w="g"),(N||_==="0"&&T==="=")&&(N=!0,_="0",T="=");var J=(M&&M.prefix!==void 0?M.prefix:"")+(E==="$"?r:E==="#"&&/[boxX]/.test(w)?"0"+w.toLowerCase():""),$=(E==="$"?n:/[%p]/.test(w)?i:"")+(M&&M.suffix!==void 0?M.suffix:""),I=Qt[w],y=/[defgprs%]/.test(w);C=C===void 0?6:/[gprs]/.test(w)?Math.max(1,Math.min(21,C)):Math.max(0,Math.min(20,C));function P(h){var R=J,F=$,q,_t,ot;if(w==="c")F=I(h)+F,h="";else{h=+h;var at=h<0||1/h<0;if(h=isNaN(h)?f:I(Math.abs(h),C),L&&(h=or(h)),at&&+h==0&&k!=="+"&&(at=!1),R=(at?k==="("?k:u:k==="-"||k==="("?"":k)+R,F=(w==="s"&&!isNaN(h)&&pt!==void 0?ur[8+pt/3]:"")+F+(at&&k==="("?")":""),y){for(q=-1,_t=h.length;++q<_t;)if(ot=h.charCodeAt(q),48>ot||ot>57){F=(ot===46?o+h.slice(q+1):h.slice(q))+F,h=h.slice(0,q);break}}}D&&!N&&(h=e(h,1/0));var it=R.length+h.length+F.length,O=it>1)+R+h+F+O.slice(it);break;default:h=O+R+h+F;break}return a(h)}return P.toString=function(){return m+""},P}function p(m,M){var _=Math.max(-8,Math.min(8,Math.floor(V(M)/3)))*3,T=Math.pow(10,-_),k=s((m=Q(m),m.type="f",m),{suffix:ur[8+_/3]});return function(E){return k(T*E)}}return{format:s,formatPrefix:p}}var Yt,Ht,Wt;Gt({thousands:",",grouping:[3],currency:["$",""]});function Gt(t){return Yt=sr(t),Ht=Yt.format,Wt=Yt.formatPrefix,Yt}function Jt(t){return Math.max(0,-V(Math.abs(t)))}function Kt(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(V(e)/3)))*3-V(Math.abs(t)))}function jt(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,V(e)-V(t))+1}function te(t,e,r,n){var o=Zt(t,e,r),a;switch(n=Q(n??",f"),n.type){case"s":{var i=Math.max(Math.abs(t),Math.abs(e));return n.precision==null&&!isNaN(a=Kt(o,i))&&(n.precision=a),Wt(n,i)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(a=jt(o,Math.max(Math.abs(t),Math.abs(e))))&&(n.precision=a-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(a=Jt(o))&&(n.precision=a-(n.type==="%")*2);break}}return Ht(n)}function fr(t){var e=t.domain;return t.ticks=function(r){var n=e();return Lt(n[0],n[n.length-1],r??10)},t.tickFormat=function(r,n){var o=e();return te(o[0],o[o.length-1],r??10,n)},t.nice=function(r){r==null&&(r=10);var n=e(),o=0,a=n.length-1,i=n[o],u=n[a],f,s,p=10;for(u0;){if(s=ct(i,u,r),s===f)return n[o]=i,n[a]=u,e(n);if(s>0)i=Math.floor(i/s)*s,u=Math.ceil(u/s)*s;else if(s<0)i=Math.ceil(i*s)/s,u=Math.floor(u*s)/s;else break;f=s}return t},t}function ht(){var t=0,e=1,r=1,n=[.5],o=[0,1],a;function i(f){return f!=null&&f<=f?o[Bt(n,f,0,r)]:a}function u(){var f=-1;for(n=new Array(r);++f=r?[n[r-1],e]:[n[s-1],n[s]]},i.unknown=function(f){return arguments.length&&(a=f),i},i.thresholds=function(){return n.slice()},i.copy=function(){return ht().domain([t,e]).range(o).unknown(a)},tr.apply(fr(i),arguments)}var ee=new Date,re=new Date;function A(t,e,r,n){function o(a){return t(a=arguments.length===0?new Date:new Date(+a)),a}return o.floor=a=>(t(a=new Date(+a)),a),o.ceil=a=>(t(a=new Date(a-1)),e(a,1),t(a),a),o.round=a=>{let i=o(a),u=o.ceil(a);return a-i(e(a=new Date(+a),i==null?1:Math.floor(i)),a),o.range=(a,i,u)=>{let f=[];if(a=o.ceil(a),u=u==null?1:Math.floor(u),!(a0))return f;let s;do f.push(s=new Date(+a)),e(a,u),t(a);while(sA(i=>{if(i>=i)for(;t(i),!a(i);)i.setTime(i-1)},(i,u)=>{if(i>=i)if(u<0)for(;++u<=0;)for(;e(i,-1),!a(i););else for(;--u>=0;)for(;e(i,1),!a(i););}),r&&(o.count=(a,i)=>(ee.setTime(+a),re.setTime(+i),t(ee),t(re),Math.floor(r(ee,re))),o.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?o.filter(n?i=>n(i)%a===0:i=>o.count(0,i)%a===0):o)),o}var et=A(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1),uo=et.range,dt=A(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>t.getUTCDate()-1),so=dt.range,lr=A(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>Math.floor(t/864e5)),fo=lr.range;function rt(t){return A(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,r)=>{e.setDate(e.getDate()+r*7)},(e,r)=>(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/6048e5)}var yt=rt(0),z=rt(1),cr=rt(2),mr=rt(3),X=rt(4),pr=rt(5),hr=rt(6),dr=yt.range,co=z.range,mo=cr.range,po=mr.range,ho=X.range,yo=pr.range,go=hr.range;function nt(t){return A(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCDate(e.getUTCDate()+r*7)},(e,r)=>(r-e)/6048e5)}var gt=nt(0),ut=nt(1),yr=nt(2),gr=nt(3),G=nt(4),xr=nt(5),Mr=nt(6),vr=gt.range,xo=ut.range,Mo=yr.range,vo=gr.range,wo=G.range,_o=xr.range,So=Mr.range;var xt=A(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),To=xt.range,wr=A(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),Co=wr.range;var B=A(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());B.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:A(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,r)=>{e.setFullYear(e.getFullYear()+r*t)});var Do=B.range,Z=A(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());Z.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:A(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCFullYear(e.getUTCFullYear()+r*t)});var bo=Z.range;function oe(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ae(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Mt(t,e,r){return{y:t,m:e,d:r,H:0,M:0,S:0,L:0}}function ie(t){var e=t.dateTime,r=t.date,n=t.time,o=t.periods,a=t.days,i=t.shortDays,u=t.months,f=t.shortMonths,s=vt(o),p=wt(o),m=vt(a),M=wt(a),_=vt(i),T=wt(i),k=vt(u),E=wt(u),N=vt(f),b=wt(f),D={a:ot,A:at,b:it,B:O,c:null,d:br,e:br,f:Go,g:ia,G:sa,H:$o,I:Qo,j:Xo,L:Lr,m:Jo,M:Ko,p:Rr,q:qr,Q:kr,s:Ar,S:jo,u:ta,U:ea,V:ra,w:na,W:oa,x:null,X:null,y:aa,Y:ua,Z:fa,"%":Fr},C={a:Or,A:zr,b:Vr,B:Br,c:null,d:Ur,e:Ur,f:pa,g:Sa,G:Ca,H:la,I:ca,j:ma,L:Yr,m:ha,M:da,p:Zr,q:$r,Q:kr,s:Ar,S:ya,u:ga,U:xa,V:Ma,w:va,W:wa,x:null,X:null,y:_a,Y:Ta,Z:Da,"%":Fr},L={a:y,A:P,b:h,B:R,c:F,d:Cr,e:Cr,f:zo,g:Tr,G:Sr,H:Dr,I:Dr,j:Io,L:Oo,m:Eo,M:Ro,p:I,q:Po,Q:Bo,s:Zo,S:qo,u:Lo,U:No,V:Yo,w:Ao,W:Ho,x:q,X:_t,y:Tr,Y:Sr,Z:Wo,"%":Vo};D.x=w(r,D),D.X=w(n,D),D.c=w(e,D),C.x=w(r,C),C.X=w(n,C),C.c=w(e,C);function w(c,d){return function(g){var l=[],Y=-1,v=0,H=c.length,W,K,fe;for(g instanceof Date||(g=new Date(+g));++Y53)return null;"w"in l||(l.w=1),"Z"in l?(v=ae(Mt(l.y,0,1)),H=v.getUTCDay(),v=H>4||H===0?ut.ceil(v):ut(v),v=dt.offset(v,(l.V-1)*7),l.y=v.getUTCFullYear(),l.m=v.getUTCMonth(),l.d=v.getUTCDate()+(l.w+6)%7):(v=oe(Mt(l.y,0,1)),H=v.getDay(),v=H>4||H===0?z.ceil(v):z(v),v=et.offset(v,(l.V-1)*7),l.y=v.getFullYear(),l.m=v.getMonth(),l.d=v.getDate()+(l.w+6)%7)}else("W"in l||"U"in l)&&("w"in l||(l.w="u"in l?l.u%7:"W"in l?1:0),H="Z"in l?ae(Mt(l.y,0,1)).getUTCDay():oe(Mt(l.y,0,1)).getDay(),l.m=0,l.d="W"in l?(l.w+6)%7+l.W*7-(H+5)%7:l.w+l.U*7-(H+6)%7);return"Z"in l?(l.H+=l.Z/100|0,l.M+=l.Z%100,ae(l)):oe(l)}}function $(c,d,g,l){for(var Y=0,v=d.length,H=g.length,W,K;Y=H)return-1;if(W=d.charCodeAt(Y++),W===37){if(W=d.charAt(Y++),K=L[W in _r?d.charAt(Y++):W],!K||(l=K(c,g,l))<0)return-1}else if(W!=g.charCodeAt(l++))return-1}return l}function I(c,d,g){var l=s.exec(d.slice(g));return l?(c.p=p.get(l[0].toLowerCase()),g+l[0].length):-1}function y(c,d,g){var l=_.exec(d.slice(g));return l?(c.w=T.get(l[0].toLowerCase()),g+l[0].length):-1}function P(c,d,g){var l=m.exec(d.slice(g));return l?(c.w=M.get(l[0].toLowerCase()),g+l[0].length):-1}function h(c,d,g){var l=N.exec(d.slice(g));return l?(c.m=b.get(l[0].toLowerCase()),g+l[0].length):-1}function R(c,d,g){var l=k.exec(d.slice(g));return l?(c.m=E.get(l[0].toLowerCase()),g+l[0].length):-1}function F(c,d,g){return $(c,e,d,g)}function q(c,d,g){return $(c,r,d,g)}function _t(c,d,g){return $(c,n,d,g)}function ot(c){return i[c.getDay()]}function at(c){return a[c.getDay()]}function it(c){return f[c.getMonth()]}function O(c){return u[c.getMonth()]}function Rr(c){return o[+(c.getHours()>=12)]}function qr(c){return 1+~~(c.getMonth()/3)}function Or(c){return i[c.getUTCDay()]}function zr(c){return a[c.getUTCDay()]}function Vr(c){return f[c.getUTCMonth()]}function Br(c){return u[c.getUTCMonth()]}function Zr(c){return o[+(c.getUTCHours()>=12)]}function $r(c){return 1+~~(c.getUTCMonth()/3)}return{format:function(c){var d=w(c+="",D);return d.toString=function(){return c},d},parse:function(c){var d=J(c+="",!1);return d.toString=function(){return c},d},utcFormat:function(c){var d=w(c+="",C);return d.toString=function(){return c},d},utcParse:function(c){var d=J(c+="",!0);return d.toString=function(){return c},d}}}var _r={"-":"",_:" ",0:"0"},U=/^\s*\d+/,Uo=/^%/,Fo=/[\\^$*+?|[\]().{}]/g;function x(t,e,r){var n=t<0?"-":"",o=(n?-t:t)+"",a=o.length;return n+(a[e.toLowerCase(),r]))}function Ao(t,e,r){var n=U.exec(e.slice(r,r+1));return n?(t.w=+n[0],r+n[0].length):-1}function Lo(t,e,r){var n=U.exec(e.slice(r,r+1));return n?(t.u=+n[0],r+n[0].length):-1}function No(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.U=+n[0],r+n[0].length):-1}function Yo(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.V=+n[0],r+n[0].length):-1}function Ho(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.W=+n[0],r+n[0].length):-1}function Sr(t,e,r){var n=U.exec(e.slice(r,r+4));return n?(t.y=+n[0],r+n[0].length):-1}function Tr(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function Wo(t,e,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(r,r+6));return n?(t.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function Po(t,e,r){var n=U.exec(e.slice(r,r+1));return n?(t.q=n[0]*3-3,r+n[0].length):-1}function Eo(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function Cr(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function Io(t,e,r){var n=U.exec(e.slice(r,r+3));return n?(t.m=0,t.d=+n[0],r+n[0].length):-1}function Dr(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function Ro(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function qo(t,e,r){var n=U.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function Oo(t,e,r){var n=U.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function zo(t,e,r){var n=U.exec(e.slice(r,r+6));return n?(t.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function Vo(t,e,r){var n=Uo.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function Bo(t,e,r){var n=U.exec(e.slice(r));return n?(t.Q=+n[0],r+n[0].length):-1}function Zo(t,e,r){var n=U.exec(e.slice(r));return n?(t.s=+n[0],r+n[0].length):-1}function br(t,e){return x(t.getDate(),e,2)}function $o(t,e){return x(t.getHours(),e,2)}function Qo(t,e){return x(t.getHours()%12||12,e,2)}function Xo(t,e){return x(1+et.count(B(t),t),e,3)}function Lr(t,e){return x(t.getMilliseconds(),e,3)}function Go(t,e){return Lr(t,e)+"000"}function Jo(t,e){return x(t.getMonth()+1,e,2)}function Ko(t,e){return x(t.getMinutes(),e,2)}function jo(t,e){return x(t.getSeconds(),e,2)}function ta(t){var e=t.getDay();return e===0?7:e}function ea(t,e){return x(yt.count(B(t)-1,t),e,2)}function Nr(t){var e=t.getDay();return e>=4||e===0?X(t):X.ceil(t)}function ra(t,e){return t=Nr(t),x(X.count(B(t),t)+(B(t).getDay()===4),e,2)}function na(t){return t.getDay()}function oa(t,e){return x(z.count(B(t)-1,t),e,2)}function aa(t,e){return x(t.getFullYear()%100,e,2)}function ia(t,e){return t=Nr(t),x(t.getFullYear()%100,e,2)}function ua(t,e){return x(t.getFullYear()%1e4,e,4)}function sa(t,e){var r=t.getDay();return t=r>=4||r===0?X(t):X.ceil(t),x(t.getFullYear()%1e4,e,4)}function fa(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+x(e/60|0,"0",2)+x(e%60,"0",2)}function Ur(t,e){return x(t.getUTCDate(),e,2)}function la(t,e){return x(t.getUTCHours(),e,2)}function ca(t,e){return x(t.getUTCHours()%12||12,e,2)}function ma(t,e){return x(1+dt.count(Z(t),t),e,3)}function Yr(t,e){return x(t.getUTCMilliseconds(),e,3)}function pa(t,e){return Yr(t,e)+"000"}function ha(t,e){return x(t.getUTCMonth()+1,e,2)}function da(t,e){return x(t.getUTCMinutes(),e,2)}function ya(t,e){return x(t.getUTCSeconds(),e,2)}function ga(t){var e=t.getUTCDay();return e===0?7:e}function xa(t,e){return x(gt.count(Z(t)-1,t),e,2)}function Hr(t){var e=t.getUTCDay();return e>=4||e===0?G(t):G.ceil(t)}function Ma(t,e){return t=Hr(t),x(G.count(Z(t),t)+(Z(t).getUTCDay()===4),e,2)}function va(t){return t.getUTCDay()}function wa(t,e){return x(ut.count(Z(t)-1,t),e,2)}function _a(t,e){return x(t.getUTCFullYear()%100,e,2)}function Sa(t,e){return t=Hr(t),x(t.getUTCFullYear()%100,e,2)}function Ta(t,e){return x(t.getUTCFullYear()%1e4,e,4)}function Ca(t,e){var r=t.getUTCDay();return t=r>=4||r===0?G(t):G.ceil(t),x(t.getUTCFullYear()%1e4,e,4)}function Da(){return"+0000"}function Fr(){return"%"}function kr(t){return+t}function Ar(t){return Math.floor(+t/1e3)}var st,ft,Wr,Pr,Er;ue({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function ue(t){return st=ie(t),ft=st.format,Wr=st.parse,Pr=st.utcFormat,Er=st.utcParse,st}var ba={cellSize:13,cellGap:2,marginTop:20,marginLeft:30,marginBottom:4},se=["#9be9a8","#40c463","#30a14e","#216e39"],Ua=["Mon","","Wed","","Fri","",""],Fa=ft("%b"),ka=ft("%Y-%m-%d"),Aa=ft("%a, %b %-d, %Y");function La(t){try{return getComputedStyle(t).getPropertyValue("--tblr-bg-surface"),se}catch{return se}}function Na(t){let e=new Map;for(let r of t)e.set(r.date,r);return e}function Ya(t,e,r){let n=z.floor(t),o=[],a=new Date(t);for(;a<=e;){let i=ka(a),u=z.count(n,a),f=(a.getDay()+6)%7;o.push({date:new Date(a),dateStr:i,entry:r.get(i)||null,week:u,day:f}),a=et.offset(a,1)}return o}function Ha(){let t=document.createElement("div");return t.className="heatmap-tooltip",t.style.display="none",t}function Ir(t,e,r=ba){if(t.innerHTML="",!e.days||e.days.length===0){let y=document.createElement("div");y.textContent="No tracking data available",y.style.padding="1rem",y.style.color="var(--tblr-secondary, #6c757d)",t.appendChild(y);return}let n=Na(e.days),o=new Date(e.range.begin),a=new Date(e.range.end),i=Ya(o,a,n),u=mt(e.days,y=>y.hours)||1,f=La(t),s=ht().domain([0,u]).range(f),{cellGap:p,marginTop:m,marginLeft:M,marginBottom:_}=r,T=(mt(i,y=>y.week)??0)+1,k=t.clientWidth||800,N=Math.min(18,Math.max(2,Math.floor((k-M)/T)-p)),b=N+p,D=M+T*b,C=m+7*b+_,L=document.createElement("div");L.style.maxWidth=`${D}px`,L.style.margin="0 auto",t.appendChild(L);let w=Ot(L).append("svg").attr("width",D).attr("height",C).attr("class","heatmap-svg"),J=[],$=z.floor(o);xt.range(xt.ceil(o),a).forEach(y=>{J.push({date:y,week:z.count($,y)})}),w.selectAll(".month-label").data(J).join("text").attr("class","heatmap-label month-label").attr("x",y=>M+y.week*b).attr("y",m-6).text(y=>Fa(y.date)),w.selectAll(".day-label").data(Ua).join("text").attr("class","heatmap-label day-label").attr("x",M-6).attr("y",(y,P)=>m+P*b+N-2).attr("text-anchor","end").text(y=>y);let I=Ha();L.style.position="relative",L.appendChild(I),w.selectAll(".heatmap-cell").data(i).join("rect").attr("class",y=>y.entry?"heatmap-cell":"heatmap-cell heatmap-empty").attr("x",y=>M+y.week*b).attr("y",y=>m+y.day*b).attr("width",N).attr("height",N).attr("fill",y=>y.entry?s(y.entry.hours):"").on("mouseenter",function(y,P){let h=P.entry?P.entry.hours.toFixed(1):"0.0",R=P.entry?P.entry.count:0;I.innerHTML=`${Aa(P.date)}
${h}h (${R} entries)`,I.style.display="block";let F=y.target.getBoundingClientRect(),q=L.getBoundingClientRect();I.style.left=`${F.left-q.left+N/2}px`,I.style.top=`${F.top-q.top-40}px`}).on("mouseleave",function(){I.style.display="none"})}function Wa(t){let e=t.getAttribute("data-url");if(!e){console.error("KimaiHeatmap: missing data-url attribute");return}fetch(e).then(r=>{if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json()}).then(r=>{Ir(t,r)}).catch(r=>{console.error("KimaiHeatmap: failed to load data",r),t.textContent="Failed to load heatmap data"})}return jr(Pa);})(); +"use strict"; +var KimaiHeatmap = (() => { + var __defProp = Object.defineProperty; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); + }; + var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; + }; + var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + + // assets/src/heatmap.ts + var heatmap_exports = {}; + __export(heatmap_exports, { + init: () => init, + renderHeatmap: () => renderHeatmap + }); + + // node_modules/d3-selection/src/namespaces.js + var xhtml = "http://www.w3.org/1999/xhtml"; + var namespaces_default = { + svg: "http://www.w3.org/2000/svg", + xhtml, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + + // node_modules/d3-selection/src/namespace.js + function namespace_default(name) { + var prefix = name += "", i = prefix.indexOf(":"); + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return namespaces_default.hasOwnProperty(prefix) ? { space: namespaces_default[prefix], local: name } : name; + } + + // node_modules/d3-selection/src/creator.js + function creatorInherit(name) { + return function() { + var document2 = this.ownerDocument, uri = this.namespaceURI; + return uri === xhtml && document2.documentElement.namespaceURI === xhtml ? document2.createElement(name) : document2.createElementNS(uri, name); + }; + } + function creatorFixed(fullname) { + return function() { + return this.ownerDocument.createElementNS(fullname.space, fullname.local); + }; + } + function creator_default(name) { + var fullname = namespace_default(name); + return (fullname.local ? creatorFixed : creatorInherit)(fullname); + } + + // node_modules/d3-selection/src/selector.js + function none() { + } + function selector_default(selector) { + return selector == null ? none : function() { + return this.querySelector(selector); + }; + } + + // node_modules/d3-selection/src/selection/select.js + function select_default(select) { + if (typeof select !== "function") select = selector_default(select); + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + } + } + } + return new Selection(subgroups, this._parents); + } + + // node_modules/d3-selection/src/array.js + function array(x) { + return x == null ? [] : Array.isArray(x) ? x : Array.from(x); + } + + // node_modules/d3-selection/src/selectorAll.js + function empty() { + return []; + } + function selectorAll_default(selector) { + return selector == null ? empty : function() { + return this.querySelectorAll(selector); + }; + } + + // node_modules/d3-selection/src/selection/selectAll.js + function arrayAll(select) { + return function() { + return array(select.apply(this, arguments)); + }; + } + function selectAll_default(select) { + if (typeof select === "function") select = arrayAll(select); + else select = selectorAll_default(select); + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + subgroups.push(select.call(node, node.__data__, i, group)); + parents.push(node); + } + } + } + return new Selection(subgroups, parents); + } + + // node_modules/d3-selection/src/matcher.js + function matcher_default(selector) { + return function() { + return this.matches(selector); + }; + } + function childMatcher(selector) { + return function(node) { + return node.matches(selector); + }; + } + + // node_modules/d3-selection/src/selection/selectChild.js + var find = Array.prototype.find; + function childFind(match) { + return function() { + return find.call(this.children, match); + }; + } + function childFirst() { + return this.firstElementChild; + } + function selectChild_default(match) { + return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match))); + } + + // node_modules/d3-selection/src/selection/selectChildren.js + var filter = Array.prototype.filter; + function children() { + return Array.from(this.children); + } + function childrenFilter(match) { + return function() { + return filter.call(this.children, match); + }; + } + function selectChildren_default(match) { + return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match))); + } + + // node_modules/d3-selection/src/selection/filter.js + function filter_default(match) { + if (typeof match !== "function") match = matcher_default(match); + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + return new Selection(subgroups, this._parents); + } + + // node_modules/d3-selection/src/selection/sparse.js + function sparse_default(update) { + return new Array(update.length); + } + + // node_modules/d3-selection/src/selection/enter.js + function enter_default() { + return new Selection(this._enter || this._groups.map(sparse_default), this._parents); + } + function EnterNode(parent, datum2) { + this.ownerDocument = parent.ownerDocument; + this.namespaceURI = parent.namespaceURI; + this._next = null; + this._parent = parent; + this.__data__ = datum2; + } + EnterNode.prototype = { + constructor: EnterNode, + appendChild: function(child) { + return this._parent.insertBefore(child, this._next); + }, + insertBefore: function(child, next) { + return this._parent.insertBefore(child, next); + }, + querySelector: function(selector) { + return this._parent.querySelector(selector); + }, + querySelectorAll: function(selector) { + return this._parent.querySelectorAll(selector); + } + }; + + // node_modules/d3-selection/src/constant.js + function constant_default(x) { + return function() { + return x; + }; + } + + // node_modules/d3-selection/src/selection/data.js + function bindIndex(parent, group, enter, update, exit, data) { + var i = 0, node, groupLength = group.length, dataLength = data.length; + for (; i < dataLength; ++i) { + if (node = group[i]) { + node.__data__ = data[i]; + update[i] = node; + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } + for (; i < groupLength; ++i) { + if (node = group[i]) { + exit[i] = node; + } + } + } + function bindKey(parent, group, enter, update, exit, data, key) { + var i, node, nodeByKeyValue = /* @__PURE__ */ new Map(), groupLength = group.length, dataLength = data.length, keyValues = new Array(groupLength), keyValue; + for (i = 0; i < groupLength; ++i) { + if (node = group[i]) { + keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + ""; + if (nodeByKeyValue.has(keyValue)) { + exit[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + } + } + for (i = 0; i < dataLength; ++i) { + keyValue = key.call(parent, data[i], i, data) + ""; + if (node = nodeByKeyValue.get(keyValue)) { + update[i] = node; + node.__data__ = data[i]; + nodeByKeyValue.delete(keyValue); + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } + for (i = 0; i < groupLength; ++i) { + if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) { + exit[i] = node; + } + } + } + function datum(node) { + return node.__data__; + } + function data_default(value, key) { + if (!arguments.length) return Array.from(this, datum); + var bind = key ? bindKey : bindIndex, parents = this._parents, groups = this._groups; + if (typeof value !== "function") value = constant_default(value); + for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { + var parent = parents[j], group = groups[j], groupLength = group.length, data = arraylike(value.call(parent, parent && parent.__data__, j, parents)), dataLength = data.length, enterGroup = enter[j] = new Array(dataLength), updateGroup = update[j] = new Array(dataLength), exitGroup = exit[j] = new Array(groupLength); + bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); + for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { + if (previous = enterGroup[i0]) { + if (i0 >= i1) i1 = i0 + 1; + while (!(next = updateGroup[i1]) && ++i1 < dataLength) ; + previous._next = next || null; + } + } + } + update = new Selection(update, parents); + update._enter = enter; + update._exit = exit; + return update; + } + function arraylike(data) { + return typeof data === "object" && "length" in data ? data : Array.from(data); + } + + // node_modules/d3-selection/src/selection/exit.js + function exit_default() { + return new Selection(this._exit || this._groups.map(sparse_default), this._parents); + } + + // node_modules/d3-selection/src/selection/join.js + function join_default(onenter, onupdate, onexit) { + var enter = this.enter(), update = this, exit = this.exit(); + if (typeof onenter === "function") { + enter = onenter(enter); + if (enter) enter = enter.selection(); + } else { + enter = enter.append(onenter + ""); + } + if (onupdate != null) { + update = onupdate(update); + if (update) update = update.selection(); + } + if (onexit == null) exit.remove(); + else onexit(exit); + return enter && update ? enter.merge(update).order() : update; + } + + // node_modules/d3-selection/src/selection/merge.js + function merge_default(context) { + var selection2 = context.selection ? context.selection() : context; + for (var groups0 = this._groups, groups1 = selection2._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + return new Selection(merges, this._parents); + } + + // node_modules/d3-selection/src/selection/order.js + function order_default() { + for (var groups = this._groups, j = -1, m = groups.length; ++j < m; ) { + for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + } + + // node_modules/d3-selection/src/selection/sort.js + function sort_default(compare) { + if (!compare) compare = ascending; + function compareNode(a, b) { + return a && b ? compare(a.__data__, b.__data__) : !a - !b; + } + for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group[i]) { + sortgroup[i] = node; + } + } + sortgroup.sort(compareNode); + } + return new Selection(sortgroups, this._parents).order(); + } + function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + + // node_modules/d3-selection/src/selection/call.js + function call_default() { + var callback = arguments[0]; + arguments[0] = this; + callback.apply(null, arguments); + return this; + } + + // node_modules/d3-selection/src/selection/nodes.js + function nodes_default() { + return Array.from(this); + } + + // node_modules/d3-selection/src/selection/node.js + function node_default() { + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { + var node = group[i]; + if (node) return node; + } + } + return null; + } + + // node_modules/d3-selection/src/selection/size.js + function size_default() { + let size = 0; + for (const node of this) ++size; + return size; + } + + // node_modules/d3-selection/src/selection/empty.js + function empty_default() { + return !this.node(); + } + + // node_modules/d3-selection/src/selection/each.js + function each_default(callback) { + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { + if (node = group[i]) callback.call(node, node.__data__, i, group); + } + } + return this; + } + + // node_modules/d3-selection/src/selection/attr.js + function attrRemove(name) { + return function() { + this.removeAttribute(name); + }; + } + function attrRemoveNS(fullname) { + return function() { + this.removeAttributeNS(fullname.space, fullname.local); + }; + } + function attrConstant(name, value) { + return function() { + this.setAttribute(name, value); + }; + } + function attrConstantNS(fullname, value) { + return function() { + this.setAttributeNS(fullname.space, fullname.local, value); + }; + } + function attrFunction(name, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.removeAttribute(name); + else this.setAttribute(name, v); + }; + } + function attrFunctionNS(fullname, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.removeAttributeNS(fullname.space, fullname.local); + else this.setAttributeNS(fullname.space, fullname.local, v); + }; + } + function attr_default(name, value) { + var fullname = namespace_default(name); + if (arguments.length < 2) { + var node = this.node(); + return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname); + } + return this.each((value == null ? fullname.local ? attrRemoveNS : attrRemove : typeof value === "function" ? fullname.local ? attrFunctionNS : attrFunction : fullname.local ? attrConstantNS : attrConstant)(fullname, value)); + } + + // node_modules/d3-selection/src/window.js + function window_default(node) { + return node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView; + } + + // node_modules/d3-selection/src/selection/style.js + function styleRemove(name) { + return function() { + this.style.removeProperty(name); + }; + } + function styleConstant(name, value, priority) { + return function() { + this.style.setProperty(name, value, priority); + }; + } + function styleFunction(name, value, priority) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.style.removeProperty(name); + else this.style.setProperty(name, v, priority); + }; + } + function style_default(name, value, priority) { + return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name); + } + function styleValue(node, name) { + return node.style.getPropertyValue(name) || window_default(node).getComputedStyle(node, null).getPropertyValue(name); + } + + // node_modules/d3-selection/src/selection/property.js + function propertyRemove(name) { + return function() { + delete this[name]; + }; + } + function propertyConstant(name, value) { + return function() { + this[name] = value; + }; + } + function propertyFunction(name, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) delete this[name]; + else this[name] = v; + }; + } + function property_default(name, value) { + return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name]; + } + + // node_modules/d3-selection/src/selection/classed.js + function classArray(string) { + return string.trim().split(/^|\s+/); + } + function classList(node) { + return node.classList || new ClassList(node); + } + function ClassList(node) { + this._node = node; + this._names = classArray(node.getAttribute("class") || ""); + } + ClassList.prototype = { + add: function(name) { + var i = this._names.indexOf(name); + if (i < 0) { + this._names.push(name); + this._node.setAttribute("class", this._names.join(" ")); + } + }, + remove: function(name) { + var i = this._names.indexOf(name); + if (i >= 0) { + this._names.splice(i, 1); + this._node.setAttribute("class", this._names.join(" ")); + } + }, + contains: function(name) { + return this._names.indexOf(name) >= 0; + } + }; + function classedAdd(node, names) { + var list = classList(node), i = -1, n = names.length; + while (++i < n) list.add(names[i]); + } + function classedRemove(node, names) { + var list = classList(node), i = -1, n = names.length; + while (++i < n) list.remove(names[i]); + } + function classedTrue(names) { + return function() { + classedAdd(this, names); + }; + } + function classedFalse(names) { + return function() { + classedRemove(this, names); + }; + } + function classedFunction(names, value) { + return function() { + (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); + }; + } + function classed_default(name, value) { + var names = classArray(name + ""); + if (arguments.length < 2) { + var list = classList(this.node()), i = -1, n = names.length; + while (++i < n) if (!list.contains(names[i])) return false; + return true; + } + return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value)); + } + + // node_modules/d3-selection/src/selection/text.js + function textRemove() { + this.textContent = ""; + } + function textConstant(value) { + return function() { + this.textContent = value; + }; + } + function textFunction(value) { + return function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + }; + } + function text_default(value) { + return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent; + } + + // node_modules/d3-selection/src/selection/html.js + function htmlRemove() { + this.innerHTML = ""; + } + function htmlConstant(value) { + return function() { + this.innerHTML = value; + }; + } + function htmlFunction(value) { + return function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + }; + } + function html_default(value) { + return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML; + } + + // node_modules/d3-selection/src/selection/raise.js + function raise() { + if (this.nextSibling) this.parentNode.appendChild(this); + } + function raise_default() { + return this.each(raise); + } + + // node_modules/d3-selection/src/selection/lower.js + function lower() { + if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); + } + function lower_default() { + return this.each(lower); + } + + // node_modules/d3-selection/src/selection/append.js + function append_default(name) { + var create = typeof name === "function" ? name : creator_default(name); + return this.select(function() { + return this.appendChild(create.apply(this, arguments)); + }); + } + + // node_modules/d3-selection/src/selection/insert.js + function constantNull() { + return null; + } + function insert_default(name, before) { + var create = typeof name === "function" ? name : creator_default(name), select = before == null ? constantNull : typeof before === "function" ? before : selector_default(before); + return this.select(function() { + return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); + }); + } + + // node_modules/d3-selection/src/selection/remove.js + function remove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + } + function remove_default() { + return this.each(remove); + } + + // node_modules/d3-selection/src/selection/clone.js + function selection_cloneShallow() { + var clone = this.cloneNode(false), parent = this.parentNode; + return parent ? parent.insertBefore(clone, this.nextSibling) : clone; + } + function selection_cloneDeep() { + var clone = this.cloneNode(true), parent = this.parentNode; + return parent ? parent.insertBefore(clone, this.nextSibling) : clone; + } + function clone_default(deep) { + return this.select(deep ? selection_cloneDeep : selection_cloneShallow); + } + + // node_modules/d3-selection/src/selection/datum.js + function datum_default(value) { + return arguments.length ? this.property("__data__", value) : this.node().__data__; + } + + // node_modules/d3-selection/src/selection/on.js + function contextListener(listener) { + return function(event) { + listener.call(this, event, this.__data__); + }; + } + function parseTypenames(typenames) { + return typenames.trim().split(/^|\s+/).map(function(t) { + var name = "", i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + return { type: t, name }; + }); + } + function onRemove(typename) { + return function() { + var on = this.__on; + if (!on) return; + for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { + if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.options); + } else { + on[++i] = o; + } + } + if (++i) on.length = i; + else delete this.__on; + }; + } + function onAdd(typename, value, options) { + return function() { + var on = this.__on, o, listener = contextListener(value); + if (on) for (var j = 0, m = on.length; j < m; ++j) { + if ((o = on[j]).type === typename.type && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.options); + this.addEventListener(o.type, o.listener = listener, o.options = options); + o.value = value; + return; + } + } + this.addEventListener(typename.type, listener, options); + o = { type: typename.type, name: typename.name, value, listener, options }; + if (!on) this.__on = [o]; + else on.push(o); + }; + } + function on_default(typename, value, options) { + var typenames = parseTypenames(typename + ""), i, n = typenames.length, t; + if (arguments.length < 2) { + var on = this.node().__on; + if (on) for (var j = 0, m = on.length, o; j < m; ++j) { + for (i = 0, o = on[j]; i < n; ++i) { + if ((t = typenames[i]).type === o.type && t.name === o.name) { + return o.value; + } + } + } + return; + } + on = value ? onAdd : onRemove; + for (i = 0; i < n; ++i) this.each(on(typenames[i], value, options)); + return this; + } + + // node_modules/d3-selection/src/selection/dispatch.js + function dispatchEvent(node, type, params) { + var window2 = window_default(node), event = window2.CustomEvent; + if (typeof event === "function") { + event = new event(type, params); + } else { + event = window2.document.createEvent("Event"); + if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; + else event.initEvent(type, false, false); + } + node.dispatchEvent(event); + } + function dispatchConstant(type, params) { + return function() { + return dispatchEvent(this, type, params); + }; + } + function dispatchFunction(type, params) { + return function() { + return dispatchEvent(this, type, params.apply(this, arguments)); + }; + } + function dispatch_default(type, params) { + return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params)); + } + + // node_modules/d3-selection/src/selection/iterator.js + function* iterator_default() { + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { + if (node = group[i]) yield node; + } + } + } + + // node_modules/d3-selection/src/selection/index.js + var root = [null]; + function Selection(groups, parents) { + this._groups = groups; + this._parents = parents; + } + function selection() { + return new Selection([[document.documentElement]], root); + } + function selection_selection() { + return this; + } + Selection.prototype = selection.prototype = { + constructor: Selection, + select: select_default, + selectAll: selectAll_default, + selectChild: selectChild_default, + selectChildren: selectChildren_default, + filter: filter_default, + data: data_default, + enter: enter_default, + exit: exit_default, + join: join_default, + merge: merge_default, + selection: selection_selection, + order: order_default, + sort: sort_default, + call: call_default, + nodes: nodes_default, + node: node_default, + size: size_default, + empty: empty_default, + each: each_default, + attr: attr_default, + style: style_default, + property: property_default, + classed: classed_default, + text: text_default, + html: html_default, + raise: raise_default, + lower: lower_default, + append: append_default, + insert: insert_default, + remove: remove_default, + clone: clone_default, + datum: datum_default, + on: on_default, + dispatch: dispatch_default, + [Symbol.iterator]: iterator_default + }; + + // node_modules/d3-selection/src/select.js + function select_default2(selector) { + return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root); + } + + // node_modules/d3-array/src/ascending.js + function ascending2(a, b) { + return a == null || b == null ? NaN : a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + + // node_modules/d3-array/src/descending.js + function descending(a, b) { + return a == null || b == null ? NaN : b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + } + + // node_modules/d3-array/src/bisector.js + function bisector(f) { + let compare1, compare2, delta; + if (f.length !== 2) { + compare1 = ascending2; + compare2 = (d, x) => ascending2(f(d), x); + delta = (d, x) => f(d) - x; + } else { + compare1 = f === ascending2 || f === descending ? f : zero; + compare2 = f; + delta = f; + } + function left(a, x, lo = 0, hi = a.length) { + if (lo < hi) { + if (compare1(x, x) !== 0) return hi; + do { + const mid = lo + hi >>> 1; + if (compare2(a[mid], x) < 0) lo = mid + 1; + else hi = mid; + } while (lo < hi); + } + return lo; + } + function right(a, x, lo = 0, hi = a.length) { + if (lo < hi) { + if (compare1(x, x) !== 0) return hi; + do { + const mid = lo + hi >>> 1; + if (compare2(a[mid], x) <= 0) lo = mid + 1; + else hi = mid; + } while (lo < hi); + } + return lo; + } + function center(a, x, lo = 0, hi = a.length) { + const i = left(a, x, lo, hi - 1); + return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i; + } + return { left, center, right }; + } + function zero() { + return 0; + } + + // node_modules/d3-array/src/number.js + function number(x) { + return x === null ? NaN : +x; + } + + // node_modules/d3-array/src/bisect.js + var ascendingBisect = bisector(ascending2); + var bisectRight = ascendingBisect.right; + var bisectLeft = ascendingBisect.left; + var bisectCenter = bisector(number).center; + var bisect_default = bisectRight; + + // node_modules/d3-array/src/ticks.js + var e10 = Math.sqrt(50); + var e5 = Math.sqrt(10); + var e2 = Math.sqrt(2); + function tickSpec(start, stop, count) { + const step = (stop - start) / Math.max(0, count), power = Math.floor(Math.log10(step)), error = step / Math.pow(10, power), factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1; + let i1, i2, inc; + if (power < 0) { + inc = Math.pow(10, -power) / factor; + i1 = Math.round(start * inc); + i2 = Math.round(stop * inc); + if (i1 / inc < start) ++i1; + if (i2 / inc > stop) --i2; + inc = -inc; + } else { + inc = Math.pow(10, power) * factor; + i1 = Math.round(start / inc); + i2 = Math.round(stop / inc); + if (i1 * inc < start) ++i1; + if (i2 * inc > stop) --i2; + } + if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2); + return [i1, i2, inc]; + } + function ticks(start, stop, count) { + stop = +stop, start = +start, count = +count; + if (!(count > 0)) return []; + if (start === stop) return [start]; + const reverse = stop < start, [i1, i2, inc] = reverse ? tickSpec(stop, start, count) : tickSpec(start, stop, count); + if (!(i2 >= i1)) return []; + const n = i2 - i1 + 1, ticks2 = new Array(n); + if (reverse) { + if (inc < 0) for (let i = 0; i < n; ++i) ticks2[i] = (i2 - i) / -inc; + else for (let i = 0; i < n; ++i) ticks2[i] = (i2 - i) * inc; + } else { + if (inc < 0) for (let i = 0; i < n; ++i) ticks2[i] = (i1 + i) / -inc; + else for (let i = 0; i < n; ++i) ticks2[i] = (i1 + i) * inc; + } + return ticks2; + } + function tickIncrement(start, stop, count) { + stop = +stop, start = +start, count = +count; + return tickSpec(start, stop, count)[2]; + } + function tickStep(start, stop, count) { + stop = +stop, start = +start, count = +count; + const reverse = stop < start, inc = reverse ? tickIncrement(stop, start, count) : tickIncrement(start, stop, count); + return (reverse ? -1 : 1) * (inc < 0 ? 1 / -inc : inc); + } + + // node_modules/d3-array/src/max.js + function max(values, valueof) { + let max2; + if (valueof === void 0) { + for (const value of values) { + if (value != null && (max2 < value || max2 === void 0 && value >= value)) { + max2 = value; + } + } + } else { + let index = -1; + for (let value of values) { + if ((value = valueof(value, ++index, values)) != null && (max2 < value || max2 === void 0 && value >= value)) { + max2 = value; + } + } + } + return max2; + } + + // node_modules/d3-scale/src/init.js + function initRange(domain, range) { + switch (arguments.length) { + case 0: + break; + case 1: + this.range(domain); + break; + default: + this.range(range).domain(domain); + break; + } + return this; + } + + // node_modules/d3-format/src/formatDecimal.js + function formatDecimal_default(x) { + return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10); + } + function formatDecimalParts(x, p) { + if (!isFinite(x) || x === 0) return null; + var i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e"), coefficient = x.slice(0, i); + return [ + coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, + +x.slice(i + 1) + ]; + } + + // node_modules/d3-format/src/exponent.js + function exponent_default(x) { + return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN; + } + + // node_modules/d3-format/src/formatGroup.js + function formatGroup_default(grouping, thousands) { + return function(value, width) { + var i = value.length, t = [], j = 0, g = grouping[0], length = 0; + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = grouping[j = (j + 1) % grouping.length]; + } + return t.reverse().join(thousands); + }; + } + + // node_modules/d3-format/src/formatNumerals.js + function formatNumerals_default(numerals) { + return function(value) { + return value.replace(/[0-9]/g, function(i) { + return numerals[+i]; + }); + }; + } + + // node_modules/d3-format/src/formatSpecifier.js + var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i; + function formatSpecifier(specifier) { + if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); + var match; + return new FormatSpecifier({ + fill: match[1], + align: match[2], + sign: match[3], + symbol: match[4], + zero: match[5], + width: match[6], + comma: match[7], + precision: match[8] && match[8].slice(1), + trim: match[9], + type: match[10] + }); + } + formatSpecifier.prototype = FormatSpecifier.prototype; + function FormatSpecifier(specifier) { + this.fill = specifier.fill === void 0 ? " " : specifier.fill + ""; + this.align = specifier.align === void 0 ? ">" : specifier.align + ""; + this.sign = specifier.sign === void 0 ? "-" : specifier.sign + ""; + this.symbol = specifier.symbol === void 0 ? "" : specifier.symbol + ""; + this.zero = !!specifier.zero; + this.width = specifier.width === void 0 ? void 0 : +specifier.width; + this.comma = !!specifier.comma; + this.precision = specifier.precision === void 0 ? void 0 : +specifier.precision; + this.trim = !!specifier.trim; + this.type = specifier.type === void 0 ? "" : specifier.type + ""; + } + FormatSpecifier.prototype.toString = function() { + return this.fill + this.align + this.sign + this.symbol + (this.zero ? "0" : "") + (this.width === void 0 ? "" : Math.max(1, this.width | 0)) + (this.comma ? "," : "") + (this.precision === void 0 ? "" : "." + Math.max(0, this.precision | 0)) + (this.trim ? "~" : "") + this.type; + }; + + // node_modules/d3-format/src/formatTrim.js + function formatTrim_default(s) { + out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) { + switch (s[i]) { + case ".": + i0 = i1 = i; + break; + case "0": + if (i0 === 0) i0 = i; + i1 = i; + break; + default: + if (!+s[i]) break out; + if (i0 > 0) i0 = 0; + break; + } + } + return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s; + } + + // node_modules/d3-format/src/formatPrefixAuto.js + var prefixExponent; + function formatPrefixAuto_default(x, p) { + var d = formatDecimalParts(x, p); + if (!d) return prefixExponent = void 0, x.toPrecision(p); + var coefficient = d[0], exponent = d[1], i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, n = coefficient.length; + return i === n ? coefficient : i > n ? coefficient + new Array(i - n + 1).join("0") : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; + } + + // node_modules/d3-format/src/formatRounded.js + function formatRounded_default(x, p) { + var d = formatDecimalParts(x, p); + if (!d) return x + ""; + var coefficient = d[0], exponent = d[1]; + return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) : coefficient + new Array(exponent - coefficient.length + 2).join("0"); + } + + // node_modules/d3-format/src/formatTypes.js + var formatTypes_default = { + "%": (x, p) => (x * 100).toFixed(p), + "b": (x) => Math.round(x).toString(2), + "c": (x) => x + "", + "d": formatDecimal_default, + "e": (x, p) => x.toExponential(p), + "f": (x, p) => x.toFixed(p), + "g": (x, p) => x.toPrecision(p), + "o": (x) => Math.round(x).toString(8), + "p": (x, p) => formatRounded_default(x * 100, p), + "r": formatRounded_default, + "s": formatPrefixAuto_default, + "X": (x) => Math.round(x).toString(16).toUpperCase(), + "x": (x) => Math.round(x).toString(16) + }; + + // node_modules/d3-format/src/identity.js + function identity_default(x) { + return x; + } + + // node_modules/d3-format/src/locale.js + var map = Array.prototype.map; + var prefixes = ["y", "z", "a", "f", "p", "n", "\xB5", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"]; + function locale_default(locale3) { + var group = locale3.grouping === void 0 || locale3.thousands === void 0 ? identity_default : formatGroup_default(map.call(locale3.grouping, Number), locale3.thousands + ""), currencyPrefix = locale3.currency === void 0 ? "" : locale3.currency[0] + "", currencySuffix = locale3.currency === void 0 ? "" : locale3.currency[1] + "", decimal = locale3.decimal === void 0 ? "." : locale3.decimal + "", numerals = locale3.numerals === void 0 ? identity_default : formatNumerals_default(map.call(locale3.numerals, String)), percent = locale3.percent === void 0 ? "%" : locale3.percent + "", minus = locale3.minus === void 0 ? "\u2212" : locale3.minus + "", nan = locale3.nan === void 0 ? "NaN" : locale3.nan + ""; + function newFormat(specifier, options) { + specifier = formatSpecifier(specifier); + var fill = specifier.fill, align = specifier.align, sign = specifier.sign, symbol = specifier.symbol, zero2 = specifier.zero, width = specifier.width, comma = specifier.comma, precision = specifier.precision, trim = specifier.trim, type = specifier.type; + if (type === "n") comma = true, type = "g"; + else if (!formatTypes_default[type]) precision === void 0 && (precision = 12), trim = true, type = "g"; + if (zero2 || fill === "0" && align === "=") zero2 = true, fill = "0", align = "="; + var prefix = (options && options.prefix !== void 0 ? options.prefix : "") + (symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : ""), suffix = (symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "") + (options && options.suffix !== void 0 ? options.suffix : ""); + var formatType = formatTypes_default[type], maybeSuffix = /[defgprs%]/.test(type); + precision = precision === void 0 ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision)); + function format2(value) { + var valuePrefix = prefix, valueSuffix = suffix, i, n, c; + if (type === "c") { + valueSuffix = formatType(value) + valueSuffix; + value = ""; + } else { + value = +value; + var valueNegative = value < 0 || 1 / value < 0; + value = isNaN(value) ? nan : formatType(Math.abs(value), precision); + if (trim) value = formatTrim_default(value); + if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; + valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; + valueSuffix = (type === "s" && !isNaN(value) && prefixExponent !== void 0 ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); + if (maybeSuffix) { + i = -1, n = value.length; + while (++i < n) { + if (c = value.charCodeAt(i), 48 > c || c > 57) { + valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; + value = value.slice(0, i); + break; + } + } + } + } + if (comma && !zero2) value = group(value, Infinity); + var length = valuePrefix.length + value.length + valueSuffix.length, padding = length < width ? new Array(width - length + 1).join(fill) : ""; + if (comma && zero2) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; + switch (align) { + case "<": + value = valuePrefix + value + valueSuffix + padding; + break; + case "=": + value = valuePrefix + padding + value + valueSuffix; + break; + case "^": + value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); + break; + default: + value = padding + valuePrefix + value + valueSuffix; + break; + } + return numerals(value); + } + format2.toString = function() { + return specifier + ""; + }; + return format2; + } + function formatPrefix2(specifier, value) { + var e = Math.max(-8, Math.min(8, Math.floor(exponent_default(value) / 3))) * 3, k = Math.pow(10, -e), f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), { suffix: prefixes[8 + e / 3] }); + return function(value2) { + return f(k * value2); + }; + } + return { + format: newFormat, + formatPrefix: formatPrefix2 + }; + } + + // node_modules/d3-format/src/defaultLocale.js + var locale; + var format; + var formatPrefix; + defaultLocale({ + thousands: ",", + grouping: [3], + currency: ["$", ""] + }); + function defaultLocale(definition) { + locale = locale_default(definition); + format = locale.format; + formatPrefix = locale.formatPrefix; + return locale; + } + + // node_modules/d3-format/src/precisionFixed.js + function precisionFixed_default(step) { + return Math.max(0, -exponent_default(Math.abs(step))); + } + + // node_modules/d3-format/src/precisionPrefix.js + function precisionPrefix_default(step, value) { + return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent_default(value) / 3))) * 3 - exponent_default(Math.abs(step))); + } + + // node_modules/d3-format/src/precisionRound.js + function precisionRound_default(step, max2) { + step = Math.abs(step), max2 = Math.abs(max2) - step; + return Math.max(0, exponent_default(max2) - exponent_default(step)) + 1; + } + + // node_modules/d3-scale/src/tickFormat.js + function tickFormat(start, stop, count, specifier) { + var step = tickStep(start, stop, count), precision; + specifier = formatSpecifier(specifier == null ? ",f" : specifier); + switch (specifier.type) { + case "s": { + var value = Math.max(Math.abs(start), Math.abs(stop)); + if (specifier.precision == null && !isNaN(precision = precisionPrefix_default(step, value))) specifier.precision = precision; + return formatPrefix(specifier, value); + } + case "": + case "e": + case "g": + case "p": + case "r": { + if (specifier.precision == null && !isNaN(precision = precisionRound_default(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); + break; + } + case "f": + case "%": { + if (specifier.precision == null && !isNaN(precision = precisionFixed_default(step))) specifier.precision = precision - (specifier.type === "%") * 2; + break; + } + } + return format(specifier); + } + + // node_modules/d3-scale/src/linear.js + function linearish(scale) { + var domain = scale.domain; + scale.ticks = function(count) { + var d = domain(); + return ticks(d[0], d[d.length - 1], count == null ? 10 : count); + }; + scale.tickFormat = function(count, specifier) { + var d = domain(); + return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier); + }; + scale.nice = function(count) { + if (count == null) count = 10; + var d = domain(); + var i0 = 0; + var i1 = d.length - 1; + var start = d[i0]; + var stop = d[i1]; + var prestep; + var step; + var maxIter = 10; + if (stop < start) { + step = start, start = stop, stop = step; + step = i0, i0 = i1, i1 = step; + } + while (maxIter-- > 0) { + step = tickIncrement(start, stop, count); + if (step === prestep) { + d[i0] = start; + d[i1] = stop; + return domain(d); + } else if (step > 0) { + start = Math.floor(start / step) * step; + stop = Math.ceil(stop / step) * step; + } else if (step < 0) { + start = Math.ceil(start * step) / step; + stop = Math.floor(stop * step) / step; + } else { + break; + } + prestep = step; + } + return scale; + }; + return scale; + } + + // node_modules/d3-scale/src/quantize.js + function quantize() { + var x0 = 0, x1 = 1, n = 1, domain = [0.5], range = [0, 1], unknown; + function scale(x) { + return x != null && x <= x ? range[bisect_default(domain, x, 0, n)] : unknown; + } + function rescale() { + var i = -1; + domain = new Array(n); + while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); + return scale; + } + scale.domain = function(_) { + return arguments.length ? ([x0, x1] = _, x0 = +x0, x1 = +x1, rescale()) : [x0, x1]; + }; + scale.range = function(_) { + return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice(); + }; + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]]; + }; + scale.unknown = function(_) { + return arguments.length ? (unknown = _, scale) : scale; + }; + scale.thresholds = function() { + return domain.slice(); + }; + scale.copy = function() { + return quantize().domain([x0, x1]).range(range).unknown(unknown); + }; + return initRange.apply(linearish(scale), arguments); + } + + // node_modules/d3-time/src/interval.js + var t0 = /* @__PURE__ */ new Date(); + var t1 = /* @__PURE__ */ new Date(); + function timeInterval(floori, offseti, count, field) { + function interval(date) { + return floori(date = arguments.length === 0 ? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date(+date)), date; + } + interval.floor = (date) => { + return floori(date = /* @__PURE__ */ new Date(+date)), date; + }; + interval.ceil = (date) => { + return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; + }; + interval.round = (date) => { + const d0 = interval(date), d1 = interval.ceil(date); + return date - d0 < d1 - date ? d0 : d1; + }; + interval.offset = (date, step) => { + return offseti(date = /* @__PURE__ */ new Date(+date), step == null ? 1 : Math.floor(step)), date; + }; + interval.range = (start, stop, step) => { + const range = []; + start = interval.ceil(start); + step = step == null ? 1 : Math.floor(step); + if (!(start < stop) || !(step > 0)) return range; + let previous; + do + range.push(previous = /* @__PURE__ */ new Date(+start)), offseti(start, step), floori(start); + while (previous < start && start < stop); + return range; + }; + interval.filter = (test) => { + return timeInterval((date) => { + if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); + }, (date, step) => { + if (date >= date) { + if (step < 0) while (++step <= 0) { + while (offseti(date, -1), !test(date)) { + } + } + else while (--step >= 0) { + while (offseti(date, 1), !test(date)) { + } + } + } + }); + }; + if (count) { + interval.count = (start, end) => { + t0.setTime(+start), t1.setTime(+end); + floori(t0), floori(t1); + return Math.floor(count(t0, t1)); + }; + interval.every = (step) => { + step = Math.floor(step); + return !isFinite(step) || !(step > 0) ? null : !(step > 1) ? interval : interval.filter(field ? (d) => field(d) % step === 0 : (d) => interval.count(0, d) % step === 0); + }; + } + return interval; + } + + // node_modules/d3-time/src/duration.js + var durationSecond = 1e3; + var durationMinute = durationSecond * 60; + var durationHour = durationMinute * 60; + var durationDay = durationHour * 24; + var durationWeek = durationDay * 7; + var durationMonth = durationDay * 30; + var durationYear = durationDay * 365; + + // node_modules/d3-time/src/day.js + var timeDay = timeInterval( + (date) => date.setHours(0, 0, 0, 0), + (date, step) => date.setDate(date.getDate() + step), + (start, end) => (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay, + (date) => date.getDate() - 1 + ); + var timeDays = timeDay.range; + var utcDay = timeInterval((date) => { + date.setUTCHours(0, 0, 0, 0); + }, (date, step) => { + date.setUTCDate(date.getUTCDate() + step); + }, (start, end) => { + return (end - start) / durationDay; + }, (date) => { + return date.getUTCDate() - 1; + }); + var utcDays = utcDay.range; + var unixDay = timeInterval((date) => { + date.setUTCHours(0, 0, 0, 0); + }, (date, step) => { + date.setUTCDate(date.getUTCDate() + step); + }, (start, end) => { + return (end - start) / durationDay; + }, (date) => { + return Math.floor(date / durationDay); + }); + var unixDays = unixDay.range; + + // node_modules/d3-time/src/week.js + function timeWeekday(i) { + return timeInterval((date) => { + date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); + date.setHours(0, 0, 0, 0); + }, (date, step) => { + date.setDate(date.getDate() + step * 7); + }, (start, end) => { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; + }); + } + var timeSunday = timeWeekday(0); + var timeMonday = timeWeekday(1); + var timeTuesday = timeWeekday(2); + var timeWednesday = timeWeekday(3); + var timeThursday = timeWeekday(4); + var timeFriday = timeWeekday(5); + var timeSaturday = timeWeekday(6); + var timeSundays = timeSunday.range; + var timeMondays = timeMonday.range; + var timeTuesdays = timeTuesday.range; + var timeWednesdays = timeWednesday.range; + var timeThursdays = timeThursday.range; + var timeFridays = timeFriday.range; + var timeSaturdays = timeSaturday.range; + function utcWeekday(i) { + return timeInterval((date) => { + date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); + date.setUTCHours(0, 0, 0, 0); + }, (date, step) => { + date.setUTCDate(date.getUTCDate() + step * 7); + }, (start, end) => { + return (end - start) / durationWeek; + }); + } + var utcSunday = utcWeekday(0); + var utcMonday = utcWeekday(1); + var utcTuesday = utcWeekday(2); + var utcWednesday = utcWeekday(3); + var utcThursday = utcWeekday(4); + var utcFriday = utcWeekday(5); + var utcSaturday = utcWeekday(6); + var utcSundays = utcSunday.range; + var utcMondays = utcMonday.range; + var utcTuesdays = utcTuesday.range; + var utcWednesdays = utcWednesday.range; + var utcThursdays = utcThursday.range; + var utcFridays = utcFriday.range; + var utcSaturdays = utcSaturday.range; + + // node_modules/d3-time/src/month.js + var timeMonth = timeInterval((date) => { + date.setDate(1); + date.setHours(0, 0, 0, 0); + }, (date, step) => { + date.setMonth(date.getMonth() + step); + }, (start, end) => { + return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; + }, (date) => { + return date.getMonth(); + }); + var timeMonths = timeMonth.range; + var utcMonth = timeInterval((date) => { + date.setUTCDate(1); + date.setUTCHours(0, 0, 0, 0); + }, (date, step) => { + date.setUTCMonth(date.getUTCMonth() + step); + }, (start, end) => { + return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; + }, (date) => { + return date.getUTCMonth(); + }); + var utcMonths = utcMonth.range; + + // node_modules/d3-time/src/year.js + var timeYear = timeInterval((date) => { + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); + }, (date, step) => { + date.setFullYear(date.getFullYear() + step); + }, (start, end) => { + return end.getFullYear() - start.getFullYear(); + }, (date) => { + return date.getFullYear(); + }); + timeYear.every = (k) => { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval((date) => { + date.setFullYear(Math.floor(date.getFullYear() / k) * k); + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); + }, (date, step) => { + date.setFullYear(date.getFullYear() + step * k); + }); + }; + var timeYears = timeYear.range; + var utcYear = timeInterval((date) => { + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); + }, (date, step) => { + date.setUTCFullYear(date.getUTCFullYear() + step); + }, (start, end) => { + return end.getUTCFullYear() - start.getUTCFullYear(); + }, (date) => { + return date.getUTCFullYear(); + }); + utcYear.every = (k) => { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval((date) => { + date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); + }, (date, step) => { + date.setUTCFullYear(date.getUTCFullYear() + step * k); + }); + }; + var utcYears = utcYear.range; + + // node_modules/d3-time-format/src/locale.js + function localDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); + date.setFullYear(d.y); + return date; + } + return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); + } + function utcDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); + date.setUTCFullYear(d.y); + return date; + } + return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); + } + function newDate(y, m, d) { + return { y, m, d, H: 0, M: 0, S: 0, L: 0 }; + } + function formatLocale(locale3) { + var locale_dateTime = locale3.dateTime, locale_date = locale3.date, locale_time = locale3.time, locale_periods = locale3.periods, locale_weekdays = locale3.days, locale_shortWeekdays = locale3.shortDays, locale_months = locale3.months, locale_shortMonths = locale3.shortMonths; + var periodRe = formatRe(locale_periods), periodLookup = formatLookup(locale_periods), weekdayRe = formatRe(locale_weekdays), weekdayLookup = formatLookup(locale_weekdays), shortWeekdayRe = formatRe(locale_shortWeekdays), shortWeekdayLookup = formatLookup(locale_shortWeekdays), monthRe = formatRe(locale_months), monthLookup = formatLookup(locale_months), shortMonthRe = formatRe(locale_shortMonths), shortMonthLookup = formatLookup(locale_shortMonths); + var formats = { + "a": formatShortWeekday, + "A": formatWeekday, + "b": formatShortMonth, + "B": formatMonth, + "c": null, + "d": formatDayOfMonth, + "e": formatDayOfMonth, + "f": formatMicroseconds, + "g": formatYearISO, + "G": formatFullYearISO, + "H": formatHour24, + "I": formatHour12, + "j": formatDayOfYear, + "L": formatMilliseconds, + "m": formatMonthNumber, + "M": formatMinutes, + "p": formatPeriod, + "q": formatQuarter, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatSeconds, + "u": formatWeekdayNumberMonday, + "U": formatWeekNumberSunday, + "V": formatWeekNumberISO, + "w": formatWeekdayNumberSunday, + "W": formatWeekNumberMonday, + "x": null, + "X": null, + "y": formatYear, + "Y": formatFullYear, + "Z": formatZone, + "%": formatLiteralPercent + }; + var utcFormats = { + "a": formatUTCShortWeekday, + "A": formatUTCWeekday, + "b": formatUTCShortMonth, + "B": formatUTCMonth, + "c": null, + "d": formatUTCDayOfMonth, + "e": formatUTCDayOfMonth, + "f": formatUTCMicroseconds, + "g": formatUTCYearISO, + "G": formatUTCFullYearISO, + "H": formatUTCHour24, + "I": formatUTCHour12, + "j": formatUTCDayOfYear, + "L": formatUTCMilliseconds, + "m": formatUTCMonthNumber, + "M": formatUTCMinutes, + "p": formatUTCPeriod, + "q": formatUTCQuarter, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatUTCSeconds, + "u": formatUTCWeekdayNumberMonday, + "U": formatUTCWeekNumberSunday, + "V": formatUTCWeekNumberISO, + "w": formatUTCWeekdayNumberSunday, + "W": formatUTCWeekNumberMonday, + "x": null, + "X": null, + "y": formatUTCYear, + "Y": formatUTCFullYear, + "Z": formatUTCZone, + "%": formatLiteralPercent + }; + var parses = { + "a": parseShortWeekday, + "A": parseWeekday, + "b": parseShortMonth, + "B": parseMonth, + "c": parseLocaleDateTime, + "d": parseDayOfMonth, + "e": parseDayOfMonth, + "f": parseMicroseconds, + "g": parseYear, + "G": parseFullYear, + "H": parseHour24, + "I": parseHour24, + "j": parseDayOfYear, + "L": parseMilliseconds, + "m": parseMonthNumber, + "M": parseMinutes, + "p": parsePeriod, + "q": parseQuarter, + "Q": parseUnixTimestamp, + "s": parseUnixTimestampSeconds, + "S": parseSeconds, + "u": parseWeekdayNumberMonday, + "U": parseWeekNumberSunday, + "V": parseWeekNumberISO, + "w": parseWeekdayNumberSunday, + "W": parseWeekNumberMonday, + "x": parseLocaleDate, + "X": parseLocaleTime, + "y": parseYear, + "Y": parseFullYear, + "Z": parseZone, + "%": parseLiteralPercent + }; + formats.x = newFormat(locale_date, formats); + formats.X = newFormat(locale_time, formats); + formats.c = newFormat(locale_dateTime, formats); + utcFormats.x = newFormat(locale_date, utcFormats); + utcFormats.X = newFormat(locale_time, utcFormats); + utcFormats.c = newFormat(locale_dateTime, utcFormats); + function newFormat(specifier, formats2) { + return function(date) { + var string = [], i = -1, j = 0, n = specifier.length, c, pad2, format2; + if (!(date instanceof Date)) date = /* @__PURE__ */ new Date(+date); + while (++i < n) { + if (specifier.charCodeAt(i) === 37) { + string.push(specifier.slice(j, i)); + if ((pad2 = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); + else pad2 = c === "e" ? " " : "0"; + if (format2 = formats2[c]) c = format2(date, pad2); + string.push(c); + j = i + 1; + } + } + string.push(specifier.slice(j, i)); + return string.join(""); + }; + } + function newParse(specifier, Z) { + return function(string) { + var d = newDate(1900, void 0, 1), i = parseSpecifier(d, specifier, string += "", 0), week, day; + if (i != string.length) return null; + if ("Q" in d) return new Date(d.Q); + if ("s" in d) return new Date(d.s * 1e3 + ("L" in d ? d.L : 0)); + if (Z && !("Z" in d)) d.Z = 0; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + if (d.m === void 0) d.m = "q" in d ? d.q : 0; + if ("V" in d) { + if (d.V < 1 || d.V > 53) return null; + if (!("w" in d)) d.w = 1; + if ("Z" in d) { + week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay(); + week = day > 4 || day === 0 ? utcMonday.ceil(week) : utcMonday(week); + week = utcDay.offset(week, (d.V - 1) * 7); + d.y = week.getUTCFullYear(); + d.m = week.getUTCMonth(); + d.d = week.getUTCDate() + (d.w + 6) % 7; + } else { + week = localDate(newDate(d.y, 0, 1)), day = week.getDay(); + week = day > 4 || day === 0 ? timeMonday.ceil(week) : timeMonday(week); + week = timeDay.offset(week, (d.V - 1) * 7); + d.y = week.getFullYear(); + d.m = week.getMonth(); + d.d = week.getDate() + (d.w + 6) % 7; + } + } else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; + day = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay(); + d.m = 0; + d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7; + } + if ("Z" in d) { + d.H += d.Z / 100 | 0; + d.M += d.Z % 100; + return utcDate(d); + } + return localDate(d); + }; + } + function parseSpecifier(d, specifier, string, j) { + var i = 0, n = specifier.length, m = string.length, c, parse; + while (i < n) { + if (j >= m) return -1; + c = specifier.charCodeAt(i++); + if (c === 37) { + c = specifier.charAt(i++); + parse = parses[c in pads ? specifier.charAt(i++) : c]; + if (!parse || (j = parse(d, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + function parsePeriod(d, string, i) { + var n = periodRe.exec(string.slice(i)); + return n ? (d.p = periodLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function parseShortWeekday(d, string, i) { + var n = shortWeekdayRe.exec(string.slice(i)); + return n ? (d.w = shortWeekdayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function parseWeekday(d, string, i) { + var n = weekdayRe.exec(string.slice(i)); + return n ? (d.w = weekdayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function parseShortMonth(d, string, i) { + var n = shortMonthRe.exec(string.slice(i)); + return n ? (d.m = shortMonthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function parseMonth(d, string, i) { + var n = monthRe.exec(string.slice(i)); + return n ? (d.m = monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function parseLocaleDateTime(d, string, i) { + return parseSpecifier(d, locale_dateTime, string, i); + } + function parseLocaleDate(d, string, i) { + return parseSpecifier(d, locale_date, string, i); + } + function parseLocaleTime(d, string, i) { + return parseSpecifier(d, locale_time, string, i); + } + function formatShortWeekday(d) { + return locale_shortWeekdays[d.getDay()]; + } + function formatWeekday(d) { + return locale_weekdays[d.getDay()]; + } + function formatShortMonth(d) { + return locale_shortMonths[d.getMonth()]; + } + function formatMonth(d) { + return locale_months[d.getMonth()]; + } + function formatPeriod(d) { + return locale_periods[+(d.getHours() >= 12)]; + } + function formatQuarter(d) { + return 1 + ~~(d.getMonth() / 3); + } + function formatUTCShortWeekday(d) { + return locale_shortWeekdays[d.getUTCDay()]; + } + function formatUTCWeekday(d) { + return locale_weekdays[d.getUTCDay()]; + } + function formatUTCShortMonth(d) { + return locale_shortMonths[d.getUTCMonth()]; + } + function formatUTCMonth(d) { + return locale_months[d.getUTCMonth()]; + } + function formatUTCPeriod(d) { + return locale_periods[+(d.getUTCHours() >= 12)]; + } + function formatUTCQuarter(d) { + return 1 + ~~(d.getUTCMonth() / 3); + } + return { + format: function(specifier) { + var f = newFormat(specifier += "", formats); + f.toString = function() { + return specifier; + }; + return f; + }, + parse: function(specifier) { + var p = newParse(specifier += "", false); + p.toString = function() { + return specifier; + }; + return p; + }, + utcFormat: function(specifier) { + var f = newFormat(specifier += "", utcFormats); + f.toString = function() { + return specifier; + }; + return f; + }, + utcParse: function(specifier) { + var p = newParse(specifier += "", true); + p.toString = function() { + return specifier; + }; + return p; + } + }; + } + var pads = { "-": "", "_": " ", "0": "0" }; + var numberRe = /^\s*\d+/; + var percentRe = /^%/; + var requoteRe = /[\\^$*+?|[\]().{}]/g; + function pad(value, fill, width) { + var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); + } + function requote(s) { + return s.replace(requoteRe, "\\$&"); + } + function formatRe(names) { + return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); + } + function formatLookup(names) { + return new Map(names.map((name, i) => [name.toLowerCase(), i])); + } + function parseWeekdayNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.w = +n[0], i + n[0].length) : -1; + } + function parseWeekdayNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.u = +n[0], i + n[0].length) : -1; + } + function parseWeekNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.U = +n[0], i + n[0].length) : -1; + } + function parseWeekNumberISO(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.V = +n[0], i + n[0].length) : -1; + } + function parseWeekNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.W = +n[0], i + n[0].length) : -1; + } + function parseFullYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 4)); + return n ? (d.y = +n[0], i + n[0].length) : -1; + } + function parseYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2e3), i + n[0].length) : -1; + } + function parseZone(d, string, i) { + var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); + return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; + } + function parseQuarter(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1; + } + function parseMonthNumber(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.m = n[0] - 1, i + n[0].length) : -1; + } + function parseDayOfMonth(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.d = +n[0], i + n[0].length) : -1; + } + function parseDayOfYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; + } + function parseHour24(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.H = +n[0], i + n[0].length) : -1; + } + function parseMinutes(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.M = +n[0], i + n[0].length) : -1; + } + function parseSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.S = +n[0], i + n[0].length) : -1; + } + function parseMilliseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.L = +n[0], i + n[0].length) : -1; + } + function parseMicroseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 6)); + return n ? (d.L = Math.floor(n[0] / 1e3), i + n[0].length) : -1; + } + function parseLiteralPercent(d, string, i) { + var n = percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; + } + function parseUnixTimestamp(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.Q = +n[0], i + n[0].length) : -1; + } + function parseUnixTimestampSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.s = +n[0], i + n[0].length) : -1; + } + function formatDayOfMonth(d, p) { + return pad(d.getDate(), p, 2); + } + function formatHour24(d, p) { + return pad(d.getHours(), p, 2); + } + function formatHour12(d, p) { + return pad(d.getHours() % 12 || 12, p, 2); + } + function formatDayOfYear(d, p) { + return pad(1 + timeDay.count(timeYear(d), d), p, 3); + } + function formatMilliseconds(d, p) { + return pad(d.getMilliseconds(), p, 3); + } + function formatMicroseconds(d, p) { + return formatMilliseconds(d, p) + "000"; + } + function formatMonthNumber(d, p) { + return pad(d.getMonth() + 1, p, 2); + } + function formatMinutes(d, p) { + return pad(d.getMinutes(), p, 2); + } + function formatSeconds(d, p) { + return pad(d.getSeconds(), p, 2); + } + function formatWeekdayNumberMonday(d) { + var day = d.getDay(); + return day === 0 ? 7 : day; + } + function formatWeekNumberSunday(d, p) { + return pad(timeSunday.count(timeYear(d) - 1, d), p, 2); + } + function dISO(d) { + var day = d.getDay(); + return day >= 4 || day === 0 ? timeThursday(d) : timeThursday.ceil(d); + } + function formatWeekNumberISO(d, p) { + d = dISO(d); + return pad(timeThursday.count(timeYear(d), d) + (timeYear(d).getDay() === 4), p, 2); + } + function formatWeekdayNumberSunday(d) { + return d.getDay(); + } + function formatWeekNumberMonday(d, p) { + return pad(timeMonday.count(timeYear(d) - 1, d), p, 2); + } + function formatYear(d, p) { + return pad(d.getFullYear() % 100, p, 2); + } + function formatYearISO(d, p) { + d = dISO(d); + return pad(d.getFullYear() % 100, p, 2); + } + function formatFullYear(d, p) { + return pad(d.getFullYear() % 1e4, p, 4); + } + function formatFullYearISO(d, p) { + var day = d.getDay(); + d = day >= 4 || day === 0 ? timeThursday(d) : timeThursday.ceil(d); + return pad(d.getFullYear() % 1e4, p, 4); + } + function formatZone(d) { + var z = d.getTimezoneOffset(); + return (z > 0 ? "-" : (z *= -1, "+")) + pad(z / 60 | 0, "0", 2) + pad(z % 60, "0", 2); + } + function formatUTCDayOfMonth(d, p) { + return pad(d.getUTCDate(), p, 2); + } + function formatUTCHour24(d, p) { + return pad(d.getUTCHours(), p, 2); + } + function formatUTCHour12(d, p) { + return pad(d.getUTCHours() % 12 || 12, p, 2); + } + function formatUTCDayOfYear(d, p) { + return pad(1 + utcDay.count(utcYear(d), d), p, 3); + } + function formatUTCMilliseconds(d, p) { + return pad(d.getUTCMilliseconds(), p, 3); + } + function formatUTCMicroseconds(d, p) { + return formatUTCMilliseconds(d, p) + "000"; + } + function formatUTCMonthNumber(d, p) { + return pad(d.getUTCMonth() + 1, p, 2); + } + function formatUTCMinutes(d, p) { + return pad(d.getUTCMinutes(), p, 2); + } + function formatUTCSeconds(d, p) { + return pad(d.getUTCSeconds(), p, 2); + } + function formatUTCWeekdayNumberMonday(d) { + var dow = d.getUTCDay(); + return dow === 0 ? 7 : dow; + } + function formatUTCWeekNumberSunday(d, p) { + return pad(utcSunday.count(utcYear(d) - 1, d), p, 2); + } + function UTCdISO(d) { + var day = d.getUTCDay(); + return day >= 4 || day === 0 ? utcThursday(d) : utcThursday.ceil(d); + } + function formatUTCWeekNumberISO(d, p) { + d = UTCdISO(d); + return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2); + } + function formatUTCWeekdayNumberSunday(d) { + return d.getUTCDay(); + } + function formatUTCWeekNumberMonday(d, p) { + return pad(utcMonday.count(utcYear(d) - 1, d), p, 2); + } + function formatUTCYear(d, p) { + return pad(d.getUTCFullYear() % 100, p, 2); + } + function formatUTCYearISO(d, p) { + d = UTCdISO(d); + return pad(d.getUTCFullYear() % 100, p, 2); + } + function formatUTCFullYear(d, p) { + return pad(d.getUTCFullYear() % 1e4, p, 4); + } + function formatUTCFullYearISO(d, p) { + var day = d.getUTCDay(); + d = day >= 4 || day === 0 ? utcThursday(d) : utcThursday.ceil(d); + return pad(d.getUTCFullYear() % 1e4, p, 4); + } + function formatUTCZone() { + return "+0000"; + } + function formatLiteralPercent() { + return "%"; + } + function formatUnixTimestamp(d) { + return +d; + } + function formatUnixTimestampSeconds(d) { + return Math.floor(+d / 1e3); + } + + // node_modules/d3-time-format/src/defaultLocale.js + var locale2; + var timeFormat; + var timeParse; + var utcFormat; + var utcParse; + defaultLocale2({ + dateTime: "%x, %X", + date: "%-m/%-d/%Y", + time: "%-I:%M:%S %p", + periods: ["AM", "PM"], + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + }); + function defaultLocale2(definition) { + locale2 = formatLocale(definition); + timeFormat = locale2.format; + timeParse = locale2.parse; + utcFormat = locale2.utcFormat; + utcParse = locale2.utcParse; + return locale2; + } + + // assets/src/heatmap.ts + var DEFAULT_CONFIG = { + cellSize: 13, + cellGap: 2, + marginTop: 20, + marginLeft: 30, + marginBottom: 4 + }; + var FALLBACK_COLORS = ["#9be9a8", "#40c463", "#30a14e", "#216e39"]; + var DAY_LABELS = ["Mon", "", "Wed", "", "Fri", "", ""]; + var MONTH_FORMAT = timeFormat("%b"); + var DATE_FORMAT = timeFormat("%Y-%m-%d"); + var DISPLAY_FORMAT = timeFormat("%a, %b %-d, %Y"); + function resolveColors(container) { + try { + const style = getComputedStyle(container); + const test = style.getPropertyValue("--tblr-bg-surface"); + if (!test) return FALLBACK_COLORS; + return FALLBACK_COLORS; + } catch { + return FALLBACK_COLORS; + } + } + function buildDateMap(days) { + const map2 = /* @__PURE__ */ new Map(); + for (const d of days) { + map2.set(d.date, d); + } + return map2; + } + function generateCells(begin, end, dateMap) { + const firstMonday = timeMonday.floor(begin); + const cells = []; + let current = new Date(begin); + while (current <= end) { + const dateStr = DATE_FORMAT(current); + const weeksSinceStart = timeMonday.count(firstMonday, current); + const dayOfWeek = (current.getDay() + 6) % 7; + cells.push({ + date: new Date(current), + dateStr, + entry: dateMap.get(dateStr) || null, + week: weeksSinceStart, + day: dayOfWeek + }); + current = timeDay.offset(current, 1); + } + return cells; + } + function createTooltip() { + const tip = document.createElement("div"); + tip.className = "heatmap-tooltip"; + tip.style.display = "none"; + return tip; + } + function renderHeatmap(container, data, config = DEFAULT_CONFIG, onCellClick, emptyMessage) { + container.innerHTML = ""; + if (!data.days || data.days.length === 0) { + const msg = document.createElement("div"); + msg.textContent = emptyMessage || "No tracking data available"; + msg.style.padding = "1rem"; + msg.style.color = "var(--tblr-secondary, #6c757d)"; + container.appendChild(msg); + return; + } + const dateMap = buildDateMap(data.days); + const begin = new Date(data.range.begin); + const end = new Date(data.range.end); + const cells = generateCells(begin, end, dateMap); + const maxHours = max(data.days, (d) => d.hours) || 1; + const colors = resolveColors(container); + const colorScale = quantize().domain([0, maxHours]).range(colors); + const { cellGap, marginTop, marginLeft, marginBottom } = config; + const numWeeks = (max(cells, (c) => c.week) ?? 0) + 1; + const containerWidth = container.clientWidth || 800; + const maxCellSize = 18; + const cellSize = Math.min(maxCellSize, Math.max(2, Math.floor((containerWidth - marginLeft) / numWeeks) - cellGap)); + const step = cellSize + cellGap; + const svgWidth = marginLeft + numWeeks * step; + const svgHeight = marginTop + 7 * step + marginBottom; + const wrapper = document.createElement("div"); + wrapper.style.maxWidth = `${svgWidth}px`; + wrapper.style.margin = "0 auto"; + container.appendChild(wrapper); + const svg = select_default2(wrapper).append("svg").attr("width", svgWidth).attr("height", svgHeight).attr("class", "heatmap-svg"); + const months = []; + const firstMonday = timeMonday.floor(begin); + timeMonth.range(timeMonth.ceil(begin), end).forEach((m) => { + months.push({ + date: m, + week: timeMonday.count(firstMonday, m) + }); + }); + svg.selectAll(".month-label").data(months).join("text").attr("class", "heatmap-label month-label").attr("x", (d) => marginLeft + d.week * step).attr("y", marginTop - 6).text((d) => MONTH_FORMAT(d.date)); + svg.selectAll(".day-label").data(DAY_LABELS).join("text").attr("class", "heatmap-label day-label").attr("x", marginLeft - 6).attr("y", (_d, i) => marginTop + i * step + cellSize - 2).attr("text-anchor", "end").text((d) => d); + const tooltip = createTooltip(); + wrapper.style.position = "relative"; + wrapper.appendChild(tooltip); + svg.selectAll(".heatmap-cell").data(cells).join("rect").attr( + "class", + (d) => d.entry ? "heatmap-cell" : "heatmap-cell heatmap-empty" + ).attr("x", (d) => marginLeft + d.week * step).attr("y", (d) => marginTop + d.day * step).attr("width", cellSize).attr("height", cellSize).attr("fill", (d) => d.entry ? colorScale(d.entry.hours) : "").on("mouseenter", function(event, d) { + const hours = d.entry ? d.entry.hours.toFixed(1) : "0.0"; + const count = d.entry ? d.entry.count : 0; + tooltip.innerHTML = `${DISPLAY_FORMAT(d.date)}
${hours}h (${count} entries)`; + tooltip.style.display = "block"; + const rect = event.target.getBoundingClientRect(); + const wrapperRect = wrapper.getBoundingClientRect(); + tooltip.style.left = `${rect.left - wrapperRect.left + cellSize / 2}px`; + tooltip.style.top = `${rect.top - wrapperRect.top - 40}px`; + }).on("mouseleave", function() { + tooltip.style.display = "none"; + }).on("click", function(_event, d) { + if (!onCellClick) return; + onCellClick(d.dateStr); + }); + } + function init(container) { + const baseUrl = container.getAttribute("data-url"); + if (!baseUrl) { + console.error("KimaiHeatmap: missing data-url attribute"); + return; + } + const timesheetUrl = container.getAttribute("data-timesheet-url") || "/en/timesheet/"; + const projectsJson = container.getAttribute("data-projects"); + const projects = projectsJson ? JSON.parse(projectsJson) : []; + let activeProjectId = null; + const onCellClick = (dateStr) => { + const daterange = `${dateStr} - ${dateStr}`; + let url = `${timesheetUrl}?daterange=${encodeURIComponent(daterange)}`; + if (activeProjectId) { + url += `&projects[]=${activeProjectId}`; + } + window.location.href = url; + }; + container.innerHTML = ""; + const wrapper = document.createElement("div"); + wrapper.className = "heatmap-wrapper"; + const svgArea = document.createElement("div"); + svgArea.className = "heatmap-svg-area"; + wrapper.appendChild(svgArea); + if (projects.length > 0) { + const filterDiv = document.createElement("div"); + filterDiv.className = "heatmap-filter"; + const select = document.createElement("select"); + select.className = "form-select form-select-sm"; + select.setAttribute("aria-label", "Filter by project"); + const defaultOpt = document.createElement("option"); + defaultOpt.value = ""; + defaultOpt.textContent = "All Projects"; + select.appendChild(defaultOpt); + for (const p of projects) { + const opt = document.createElement("option"); + opt.value = String(p.id); + opt.textContent = p.name; + select.appendChild(opt); + } + select.addEventListener("change", () => { + const val = select.value; + activeProjectId = val ? parseInt(val, 10) : null; + const fetchUrl = val ? `${baseUrl}?project=${val}` : baseUrl; + fetch(fetchUrl).then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.json(); + }).then((data) => { + renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick, "No tracking data for this project"); + }).catch((err) => { + console.error("KimaiHeatmap: failed to load filtered data", err); + }); + }); + filterDiv.appendChild(select); + wrapper.appendChild(filterDiv); + } + container.appendChild(wrapper); + fetch(baseUrl).then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.json(); + }).then((data) => { + renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick); + }).catch((err) => { + console.error("KimaiHeatmap: failed to load data", err); + svgArea.textContent = "Failed to load heatmap data"; + }); + } + return __toCommonJS(heatmap_exports); +})(); diff --git a/assets/src/heatmap.ts b/assets/src/heatmap.ts index f06fb48..5a6fb83 100644 --- a/assets/src/heatmap.ts +++ b/assets/src/heatmap.ts @@ -3,7 +3,7 @@ import { scaleQuantize } from 'd3-scale'; import { timeMonday, timeDay, timeMonth } from 'd3-time'; import { timeFormat } from 'd3-time-format'; import { max } from 'd3-array'; -import type { DayEntry, HeatmapData, HeatmapConfig } from './types'; +import type { DayEntry, HeatmapData, HeatmapConfig, ProjectOption } from './types'; const DEFAULT_CONFIG: HeatmapConfig = { cellSize: 13, @@ -85,12 +85,14 @@ export function renderHeatmap( container: HTMLElement, data: HeatmapData, config: HeatmapConfig = DEFAULT_CONFIG, + onCellClick?: (dateStr: string) => void, + emptyMessage?: string, ): void { container.innerHTML = ''; if (!data.days || data.days.length === 0) { const msg = document.createElement('div'); - msg.textContent = 'No tracking data available'; + msg.textContent = emptyMessage || 'No tracking data available'; msg.style.padding = '1rem'; msg.style.color = 'var(--tblr-secondary, #6c757d)'; container.appendChild(msg); @@ -192,26 +194,100 @@ export function renderHeatmap( }) .on('mouseleave', function () { tooltip.style.display = 'none'; + }) + .on('click', function (_event: MouseEvent, d: DayCell) { + if (!onCellClick) return; + onCellClick(d.dateStr); }); } export function init(container: HTMLElement): void { - const url = container.getAttribute('data-url'); - if (!url) { + const baseUrl = container.getAttribute('data-url'); + if (!baseUrl) { console.error('KimaiHeatmap: missing data-url attribute'); return; } - fetch(url) - .then((res) => { + const timesheetUrl = container.getAttribute('data-timesheet-url') || '/en/timesheet/'; + const projectsJson = container.getAttribute('data-projects'); + const projects: ProjectOption[] = projectsJson ? JSON.parse(projectsJson) : []; + + let activeProjectId: number | null = null; + + const onCellClick = (dateStr: string): void => { + const daterange = `${dateStr} - ${dateStr}`; + let url = `${timesheetUrl}?daterange=${encodeURIComponent(daterange)}`; + if (activeProjectId) { + url += `&projects[]=${activeProjectId}`; + } + window.location.href = url; + }; + + // Build wrapper layout + container.innerHTML = ''; + const wrapper = document.createElement('div'); + wrapper.className = 'heatmap-wrapper'; + + const svgArea = document.createElement('div'); + svgArea.className = 'heatmap-svg-area'; + wrapper.appendChild(svgArea); + + // Build filter dropdown (only if projects exist) + if (projects.length > 0) { + const filterDiv = document.createElement('div'); + filterDiv.className = 'heatmap-filter'; + + const select = document.createElement('select'); + select.className = 'form-select form-select-sm'; + select.setAttribute('aria-label', 'Filter by project'); + + const defaultOpt = document.createElement('option'); + defaultOpt.value = ''; + defaultOpt.textContent = 'All Projects'; + select.appendChild(defaultOpt); + + for (const p of projects) { + const opt = document.createElement('option'); + opt.value = String(p.id); + opt.textContent = p.name; + select.appendChild(opt); + } + + select.addEventListener('change', () => { + const val = select.value; + activeProjectId = val ? parseInt(val, 10) : null; + const fetchUrl = val ? `${baseUrl}?project=${val}` : baseUrl; + + fetch(fetchUrl) + .then(res => { + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.json() as Promise; + }) + .then(data => { + renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick, 'No tracking data for this project'); + }) + .catch(err => { + console.error('KimaiHeatmap: failed to load filtered data', err); + }); + }); + + filterDiv.appendChild(select); + wrapper.appendChild(filterDiv); + } + + container.appendChild(wrapper); + + // Initial data fetch + fetch(baseUrl) + .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json() as Promise; }) - .then((data) => { - renderHeatmap(container, data); + .then(data => { + renderHeatmap(svgArea, data, DEFAULT_CONFIG, onCellClick); }) - .catch((err) => { + .catch(err => { console.error('KimaiHeatmap: failed to load data', err); - container.textContent = 'Failed to load heatmap data'; + svgArea.textContent = 'Failed to load heatmap data'; }); } diff --git a/assets/src/types.ts b/assets/src/types.ts index c8e563a..e7f90dd 100644 --- a/assets/src/types.ts +++ b/assets/src/types.ts @@ -19,3 +19,8 @@ export interface HeatmapConfig { marginLeft: number; marginBottom: number; } + +export interface ProjectOption { + id: number; + name: string; +}