/*! * chartjs-plugin-trendline v3.3.1 * https://github.com/Makanz/chartjs-plugin-trendline * (c) 2026 Marcus Alsterfjord * Released under the MIT license */ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).ChartJSTrendline=e()}(this,function(){"use strict";class t{constructor(){this.count=0,this.sumx=0,this.sumx2=0,this.minx=Number.MAX_VALUE,this.maxx=Number.MIN_VALUE,this._cacheValid=!1}add(t){this.sumx+=t,this.sumx2+=t*t,tthis.maxx&&(this.maxx=t),this.count++,this._cacheValid=!1}scale(){return 0}}class e extends t{constructor(){super(),this.sumy=0,this.sumxy=0,this._meanx=0,this._meany=0,this._m2x=0,this._cxy=0,this._cachedSlope=null,this._cachedIntercept=null}add(t,e){super.add(t),this.sumy+=e,this.sumxy+=t*e;const i=this.count,n=t-this._meanx;this._meanx+=n/i;const a=e-this._meany;this._meany+=a/i,this._m2x+=n*(t-this._meanx),this._cxy+=n*(e-this._meany)}slope(){return this._cacheValid||this._computeCoefficients(),this._cachedSlope}intercept(){return this._cacheValid||this._computeCoefficients(),this._cachedIntercept}f(t){return this.slope()*t+this.intercept()}fo(){const t=this.slope();return isFinite(t)&&0!==t?-this.intercept()/t:NaN}scale(){return this.slope()}_computeCoefficients(){return this.count<2?(this._cachedSlope=NaN,this._cachedIntercept=NaN,void(this._cacheValid=!0)):this._m2x>0?(this._cachedSlope=this._cxy/this._m2x,this._cachedIntercept=this._meany-this._cachedSlope*this._meanx,void(this._cacheValid=!0)):(this._cachedSlope=0,this._cachedIntercept=this._meany,void(this._cacheValid=!0))}}class i extends t{constructor(){super(),this.sumlny=0,this.sumxlny=0,this.hasValidData=!0,this.sumlny2=0,this._meanx=0,this._meanlny=0,this._m2x=0,this._cxy=0,this._m2lny=0,this._cachedGrowthRate=null,this._cachedCoefficient=null,this._cachedCorrelation=null}add(t,e){if(e<=0)return void(this.hasValidData=!1);const i=Math.log(e);if(!isFinite(i))return void(this.hasValidData=!1);super.add(t),this.sumlny+=i,this.sumxlny+=t*i,this.sumlny2+=i*i;const n=this.count,a=t-this._meanx;this._meanx+=a/n;const s=i-this._meanlny;this._meanlny+=s/n,this._m2x+=a*(t-this._meanx),this._cxy+=a*(i-this._meanlny),this._m2lny+=s*(i-this._meanlny)}growthRate(){return!this.hasValidData||this.count<2?0:(this._cacheValid||this._computeCoefficients(),this._cachedGrowthRate)}coefficient(){return!this.hasValidData||this.count<2?1:(this._cacheValid||this._computeCoefficients(),this._cachedCoefficient)}f(t){if(!this.hasValidData||this.count<2)return 0;this._cacheValid||this._computeCoefficients();const e=this._cachedGrowthRate*(t-this._meanx);if(!isFinite(e)||Math.abs(e)>500)return 0;const i=Math.exp(this._meanlny+e);return isFinite(i)?i:0}correlation(){return!this.hasValidData||this.count<2?0:(this._cacheValid||this._computeCoefficients(),this._cachedCorrelation)}scale(){return this.growthRate()}_computeCoefficients(){if(!this.hasValidData||this.count<2)return this._cachedGrowthRate=0,this._cachedCoefficient=1,this._cachedCorrelation=0,void(this._cacheValid=!0);if(!(this._m2x>0))return this._cachedGrowthRate=0,this._cachedCoefficient=Math.exp(this._meanlny),this._cachedCorrelation=0,void(this._cacheValid=!0);this._cachedGrowthRate=this._cxy/this._m2x;const t=this._meanlny-this._cachedGrowthRate*this._meanx;this._cachedCoefficient=Math.exp(t);const e=this._m2lny,i=this._m2lny-this._cachedGrowthRate*this._cachedGrowthRate*this._m2x;this._cachedCorrelation=0===e?1:Math.max(0,1-i/e),this._cacheValid=!0}}const n=(t,e)=>{switch(e){case"dotted":t.setLineDash([2,2]);break;case"dashed":t.setLineDash([8,3]);break;case"dashdot":t.setLineDash([8,3,2,3]);break;default:t.setLineDash([])}},a=(t,e)=>{for(const i of Object.values(t))if(!isFinite(i))return console.warn(`Cannot ${e}: coordinates contain non-finite values`,t),!1;return!0},s=(t,e)=>{if(Array.isArray(t)&&t.length>0){if(t.every(t=>Array.isArray(t)&&t.length>=2&&t.every(t=>t&&isFinite(t.x)&&isFinite(t.y))))return!0}return console.warn(`Cannot ${e}: coordinates contain non-finite values`,t),!1},l=({ctx:t,fromX:e,fromY:i,toX:n,toY:a,colorMin:s,colorMax:l})=>{if(s===l)return s;const o=n-e,r=a-i,c=Math.sqrt(o*o+r*r);if(c<.01)return console.warn("Gradient vector too small, using solid color:",{x1:e,y1:i,x2:n,y2:a,length:c}),s;try{const o=t.createLinearGradient(e,i,n,a);return o.addColorStop(0,s),o.addColorStop(1,l),o}catch(t){return console.warn("Gradient creation failed, using solid color:",t),s}},o=(t,n)=>{const{isExponential:a,xScale:s,xAxisKey:l,yAxisKey:o,chartLabels:r}=n;let c=n.trendoffset||0;Math.abs(c)>=t.data.length&&(c=0);const h=((t,e)=>{const i=e>0?e:0,n=t.data;for(let t=i;t{const c=s?.options?.type??s?.type,h="time"===c||"timeseries"===c;e.data.forEach((s,c)=>{if(null!=s&&!(i>0&&c=e.data.length+i))if(h&&a){let e=null!=s[l]?s[l]:s.t;const i=s[o];null==e||null==i||isNaN(i)||t.add("number"==typeof e?e:new Date(e).getTime(),i)}else if(a){const e=s[l],i=s[o],n=null!=e&&!isNaN(e),a=null!=i&&!isNaN(i);n&&a&&t.add(e,i)}else if(h&&!a){if(r&&r[c]&&null!=s&&!isNaN(s)){const e=r[c],i="number"==typeof e?e:new Date(e).getTime();isNaN(i)||t.add(i,s)}}else null==s||isNaN(s)||t.add(c,s)})})(d,t,c,h,x,s,l,o,r),d},r=(t,e,i,r,h,x)=>{const d=t?.yScale||t?.controller?.chart?.scales?.[i.yAxisID||"y"]||h,u=!!i.trendlineExponential,f=i.trendlineExponential||i.trendlineLinear||{},y=i.borderColor||"rgba(169,169,169, .6)",{colorMin:p=y,colorMax:m=y,width:g=i.borderWidth||3,lineStyle:_="solid",fillColor:b=!1}=f,{color:M=y,text:V=(u?"Exponential Trendline":"Trendline"),display:F=!0,displayValue:w=!0,offset:C=10,percentage:S=!1}=f&&f.label||{},{family:N="'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:P=12}=f&&f.label&&f.label.font||{},A=t.controller.chart.options,D="object"==typeof A.parsing?A.parsing:void 0,v=f?.xAxisKey||D?.xAxisKey||"x",T=f?.yAxisKey||D?.yAxisKey||"y",E=t.controller.chart.data.labels,L={isExponential:u,trendoffset:f.trendoffset||0,xScale:r,xAxisKey:v,yAxisKey:T,chartLabels:E},$=x?((t,e,i)=>{const n=i.xScale?.options?.type??i.xScale?.type,a=[i.isExponential?"exp":"lin",i.xAxisKey,i.yAxisKey,i.trendoffset||0,n].join("|"),s=t.get(e);if(s&&s.signature===a&&s.data===e.data&&s.length===e.data.length)return s;const l={signature:a,data:e.data,length:e.data.length,fitter:o(e,i),curve:null};return t.set(e,l),l})(x,i,L):null,j=$?$.fitter:o(i,L);if(j.count<2)return;const G=t.controller.chart.chartArea;let R;if(u){const t=()=>((t,e)=>{const i=[];let n=[];const a=()=>{n.length>=2&&i.push(n),n=[]},s=t=>t&&isFinite(t.x)&&isFinite(t.y);for(let i=0;i1e-6||Math.abs(h.y-r.y1)>1e-6)&&(a(),n.push({x:r.x1,y:r.y1})),n.push({x:r.x2,y:r.y2})}return a(),i})(((t,e,i,n,a)=>{let s,l;if(e.projection?(s=i.getValueForPixel(a.left),l=i.getValueForPixel(a.right)):(s=t.minx,l=t.maxx),!isFinite(s)||!isFinite(l)||s===l)return[];const o=[];for(let e=0;e<=64;e++){const a=s+(l-s)*e/64,r=i.getPixelForValue(a),c=n.getPixelForValue(t.f(a));o.push(isFinite(r)&&isFinite(c)?{x:r,y:c}:null)}return o})(j,f,r,d,G),G),i=$?((t,e,i)=>{const{trendlineConfig:n,xScale:a,yScale:s,chartArea:l}=e,o=t.curve;return o&&o.projection===!!n.projection&&o.left===l.left&&o.right===l.right&&o.top===l.top&&o.bottom===l.bottom&&o.xMin===a.min&&o.xMax===a.max&&o.yMin===s.min&&o.yMax===s.max?o.polylines:(t.curve={projection:!!n.projection,left:l.left,right:l.right,top:l.top,bottom:l.bottom,xMin:a.min,xMax:a.max,yMin:s.min,yMax:s.max,polylines:i()},t.curve.polylines)})($,{trendlineConfig:f,xScale:r,yScale:d,chartArea:G},t):t();if(!i.length)return;e.lineWidth=g,n(e,_),(({ctx:t,polylines:e,colorMin:i,colorMax:n})=>{if(!s(e,"draw trendline"))return;t.beginPath(),e.forEach(e=>{t.moveTo(e[0].x,e[0].y);for(let i=1;i{s(e,"fill below trendline")&&a({drawBottom:i},"fill below trendline")&&(t.fillStyle=n,e.forEach(e=>{const n=e[0],a=e[e.length-1];t.beginPath(),t.moveTo(n.x,n.y);for(let i=1;i{const e=t.reduce((t,e)=>e.length>t.length?e:t),i=Math.floor(e.length/2),n=e[Math.max(0,i-1)],a=e[Math.min(e.length-1,i+1)],s=e[i];return{x:s.x,y:s.y,angle:Math.atan2(a.y-n.y,a.x-n.x)}})(i)}else{let{x1_px:t,y1_px:i,x2_px:s,y2_px:o}=((t,e,i,n,a)=>{let s,l,o,r;if(e.projection){const e=[],c=t.slope(),h=t.intercept(),x=t.maxx-t.minx,d=n.max-n.min||1;if(Math.abs(c*x)<1e-6*Math.abs(d))e.push({x:i.getValueForPixel(a.left),y:h}),e.push({x:i.getValueForPixel(a.right),y:h});else{const t=n.getValueForPixel(a.top),i=(t-h)/c;e.push({x:i,y:t});const s=n.getValueForPixel(a.bottom),l=(s-h)/c;e.push({x:l,y:s})}const u=i.getValueForPixel(a.left),f=t.f(u);e.push({x:u,y:f});const y=i.getValueForPixel(a.right),p=t.f(y);e.push({x:y,y:p});const m=i.getValueForPixel(a.left),g=i.getValueForPixel(a.right),_=[n.getValueForPixel(a.top),n.getValueForPixel(a.bottom)].filter(t=>isFinite(t)),b=_.length>0?Math.min(..._):-1/0,M=_.length>0?Math.max(..._):1/0;let V=e.filter(t=>isFinite(t.x)&&isFinite(t.y)&&t.x>=m&&t.x<=g&&t.y>=b&&t.y<=M);V=V.filter((t,e,i)=>e===i.findIndex(e=>Math.abs(e.x-t.x)<1e-4&&Math.abs(e.y-t.y)<1e-4)),V.length>=2?(V.sort((t,e)=>t.x-e.x||t.y-e.y),s=i.getPixelForValue(V[0].x),l=n.getPixelForValue(V[0].y),o=i.getPixelForValue(V[V.length-1].x),r=n.getPixelForValue(V[V.length-1].y)):(s=NaN,l=NaN,o=NaN,r=NaN)}else{const e=t.f(t.minx),a=t.f(t.maxx);s=i.getPixelForValue(t.minx),l=n.getPixelForValue(e),o=i.getPixelForValue(t.maxx),r=n.getPixelForValue(a)}return{x1_px:s,y1_px:l,x2_px:o,y2_px:r}})(j,f,r,d,G),h=null;if(isFinite(t)&&isFinite(i)&&isFinite(s)&&isFinite(o)&&(h=c(t,i,s,o,G)),!h)return;if(t=h.x1,i=h.y1,s=h.x2,o=h.y2,Math.abs(t-s)<.5&&Math.abs(i-o)<.5)return;e.lineWidth=g,n(e,_),(({ctx:t,x1:e,y1:i,x2:n,y2:s,colorMin:o,colorMax:r})=>{a({x1:e,y1:i,x2:n,y2:s},"draw trendline")&&(t.beginPath(),t.moveTo(e,i),t.lineTo(n,s),t.strokeStyle=l({ctx:t,fromX:e,fromY:i,toX:n,toY:s,colorMin:o,colorMax:r}),t.stroke(),t.closePath())})({ctx:e,x1:t,y1:i,x2:s,y2:o,colorMin:p,colorMax:m}),b&&((t,e,i,n,s,l,o)=>{a({x1:e,y1:i,x2:n,y2:s,drawBottom:l},"fill below trendline")&&(t.beginPath(),t.moveTo(e,i),t.lineTo(n,s),t.lineTo(n,l),t.lineTo(e,l),t.lineTo(e,i),t.closePath(),t.fillStyle=o,t.fill())})(e,t,i,s,o,G.bottom,b),R={x:(t+s)/2,y:(i+o)/2,angle:Math.atan2(o-i,s-t)}}if(f.label&&!1!==F){let t=V;if(w)if(u){const e=j.coefficient(),i=j.growthRate();t=`${V} (a=${e.toFixed(2)}, b=${i.toFixed(2)})`}else{const e=j.slope();t=`${V} (Slope: ${S?(100*e).toFixed(2)+"%":e.toFixed(2)})`}((t,e)=>{const{label:i,x:n,y:a,x1:s,y1:l,x2:o,y2:r,angle:c,labelColor:h,family:x,size:d,offset:u}=e;t.font=`${d}px ${x}`,t.fillStyle=h;const f=t.measureText(i).width,y=n??(s+o)/2,p=a??(l+r)/2;t.save(),t.translate(y,p),t.rotate(c);const m=-f/2,g=u;t.fillText(i,m,g),t.restore()})(e,{label:t,x:R.x,y:R.y,angle:R.angle,labelColor:M,family:N,size:P,offset:C})}};function c(t,e,i,n,a){let s=i-t,l=n-e,o=0,r=1;const c=[-s,s,-l,l],h=[t-a.left,a.right-t,e-a.top,a.bottom-e];for(let t=0;t<4;t++)if(0===c[t]){if(h[t]<0)return null}else{const e=h[t]/c[t];if(c[t]<0){if(e>r)return null;o=Math.max(o,e)}else{if(er)return null;return{x1:t+o*s,y1:e+o*l,x2:t+r*s,y2:e+r*l}}function h(t){const e=t.canvas;if(!e)return;e.setAttribute("role","img");const i=function(t){const e=[];return t.data.datasets.forEach(t=>{const i=!!t.trendlineExponential,n=t.trendlineExponential||t.trendlineLinear;if(!n)return;if(n.accessibility&&n.accessibility.description)return void e.push(n.accessibility.description);const a=t.label||"Dataset",s=i?"Exponential trendline":"Linear trendline";n.accessibility&&n.accessibility.label?e.push(`${s} for ${a}: ${n.accessibility.label}`):e.push(`${s} for ${a}.`)}),e.join(" ")}(t);if(i){e.hasAttribute("data-trendline-original-label")||e.setAttribute("data-trendline-original-label",e.getAttribute("aria-label")||"");const t=e.getAttribute("data-trendline-original-label")||"";e.setAttribute("aria-label",t?`${t}. ${i}`:i)}}function x(t,e){if(!e)return null;const i=e.legend;return i&&!1!==i.display?{text:i.text||t.label||"Trendline",strokeStyle:i.strokeStyle||i.color||t.borderColor||"rgba(169,169,169, .6)",fillStyle:i.fillStyle||"transparent",lineCap:i.lineCap||"butt",lineDash:i.lineDash||[],lineWidth:i.lineWidth??i.width??1}:null}const d=new WeakMap,u=t=>{d.delete(t)},f={id:"chartjs-plugin-trendline",afterDatasetsDraw:t=>{const e=t.ctx,{xScale:i,yScale:n}=(t=>{let e,i;for(const n of Object.values(t.scales))if(n.isHorizontal()?e=n:i=n,e&&i)break;return{xScale:e,yScale:i}})(t),a=(t=>{let e=d.get(t);return e||(e={fits:new Map,sortedDatasets:null},d.set(t,e)),e})(t);a.sortedDatasets||(a.sortedDatasets=t.data.datasets.map((t,e)=>({dataset:t,index:e})).filter(t=>t.dataset.trendlineLinear||t.dataset.trendlineExponential).sort((t,e)=>{const i=t.dataset.order??0,n=e.dataset.order??0;return 0===i&&0!==n?1:0===n&&0!==i?-1:i-n})),a.sortedDatasets.forEach(({dataset:s,index:l})=>{if(!(s.alwaysShowTrendline||t.isDatasetVisible(l))||s.data.length<=1)return;const o=t.getDatasetMeta(l),c=o.xScale||t.scales?.[s.xAxisID||"x"]||i;r(o,e,s,c,n,a.fits)}),e.setLineDash([])},afterInit:t=>{h(t)},afterUpdate:t=>{h(t)},beforeUpdate:t=>{u(t)},beforeDestroy:t=>{u(t)},beforeInit:t=>{if(!t.data.datasets.some(t=>null!==x(t,t.trendlineLinear||t.trendlineExponential)))return;const e=t.legend?.options?.labels;if(!e)return;const i=e.generateLabels;e.generateLabels=function(t){const e=i(t);return t.data.datasets.forEach(t=>{const i=x(t,t.trendlineLinear||t.trendlineExponential);i&&e.push(i)}),e}}};if("undefined"!=typeof window){const t=window.Chart;t&&("function"==typeof t.register?t.register(f):t.plugins.register(f))}return f}); //# sourceMappingURL=chartjs-plugin-trendline.min.js.map