chartjs-plugin-trendline 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,151 +1,194 @@
1
1
  /*!
2
2
  * chartjs-plugin-trendline.js
3
- * Version: 1.0.0
3
+ * Version: 1.0.2
4
4
  *
5
- * Copyright 2021 Marcus Alsterfjord
5
+ * Copyright 2022 Marcus Alsterfjord
6
6
  * Released under the MIT license
7
7
  * https://github.com/Makanz/chartjs-plugin-trendline/blob/master/README.md
8
8
  *
9
9
  * Mod by: vesal: accept also xy-data so works with scatter
10
10
  */
11
11
  var pluginTrendlineLinear = {
12
- id: "chartjs-plugin-trendline",
13
- afterDraw: function(chartInstance) {
14
- var yScale;
15
- var xScale;
12
+ id: 'chartjs-plugin-trendline',
13
+ afterDatasetsDraw: function (chartInstance) {
14
+ var yScale
15
+ var xScale
16
16
  for (var axis in chartInstance.scales) {
17
- if ( axis[0] == 'x')
18
- xScale = chartInstance.scales[axis];
19
- else
20
- yScale = chartInstance.scales[axis];
21
- if ( xScale && yScale ) break;
17
+ if (axis[0] == 'x') xScale = chartInstance.scales[axis]
18
+ else yScale = chartInstance.scales[axis]
19
+ if (xScale && yScale) break
22
20
  }
23
- var ctx = chartInstance.ctx;
24
-
25
- chartInstance.data.datasets.forEach(function(dataset, index) {
26
- if (dataset.trendlineLinear && chartInstance.isDatasetVisible(index) && dataset.data.length != 0) {
27
- var datasetMeta = chartInstance.getDatasetMeta(index);
28
- addFitter(datasetMeta, ctx, dataset, xScale, chartInstance.scales[datasetMeta.yAxisID]);
21
+ var ctx = chartInstance.ctx
22
+
23
+ chartInstance.data.datasets.forEach(function (dataset, index) {
24
+ if (
25
+ dataset.trendlineLinear &&
26
+ chartInstance.isDatasetVisible(index) &&
27
+ dataset.data.length != 0
28
+ ) {
29
+ var datasetMeta = chartInstance.getDatasetMeta(index)
30
+ addFitter(
31
+ datasetMeta,
32
+ ctx,
33
+ dataset,
34
+ xScale,
35
+ chartInstance.scales[datasetMeta.yAxisID]
36
+ )
29
37
  }
30
- });
38
+ })
31
39
 
32
- ctx.setLineDash([]);
33
- }
34
- };
40
+ ctx.setLineDash([])
41
+ },
42
+ }
35
43
 
36
44
  function addFitter(datasetMeta, ctx, dataset, xScale, yScale) {
37
- var style = dataset.trendlineLinear.style || dataset.borderColor;
38
- var lineWidth = dataset.trendlineLinear.width || dataset.borderWidth;
39
- var lineStyle = dataset.trendlineLinear.lineStyle || "solid";
45
+ var style = dataset.trendlineLinear.style || dataset.borderColor
46
+ var lineWidth = dataset.trendlineLinear.width || dataset.borderWidth
47
+ var lineStyle = dataset.trendlineLinear.lineStyle || 'solid'
48
+
49
+ style = style !== undefined ? style : 'rgba(169,169,169, .6)'
50
+ lineWidth = lineWidth !== undefined ? lineWidth : 3
51
+
52
+ var fitter = new LineFitter()
53
+ var lastIndex = dataset.data.length - 1
54
+ var startPos = datasetMeta.data[0].x
55
+ var endPos = datasetMeta.data[lastIndex].x
56
+
57
+ var xy = false
58
+ if (dataset.data && typeof dataset.data[0] === 'object') xy = true
59
+
60
+ dataset.data.forEach(function (data, index) {
61
+ if (data == null) return
62
+
63
+ if (xScale.options.type === 'time') {
64
+ var x = data.x != null ? data.x : data.t
65
+ fitter.add(new Date(x).getTime(), data.y)
66
+ } else if (xy) {
67
+ fitter.add(data.x, data.y)
68
+ } else {
69
+ fitter.add(index, data)
70
+ }
71
+ })
40
72
 
41
- style = (style !== undefined) ? style : "rgba(169,169,169, .6)";
42
- lineWidth = (lineWidth !== undefined) ? lineWidth : 3;
73
+ var x1 = xScale.getPixelForValue(fitter.minx)
74
+ var y1 = yScale.getPixelForValue(fitter.f(fitter.minx))
43
75
 
44
- var fitter = new LineFitter();
45
- var lastIndex = dataset.data.length - 1;
46
- var startPos = datasetMeta.data[0].x;
47
- var endPos = datasetMeta.data[lastIndex].x;
76
+ var x2
77
+ var y2
48
78
 
49
- var xy = false;
50
- if ( dataset.data && typeof dataset.data[0] === 'object') xy = true;
79
+ // Project only on x axes, do not project if trendline will never hit x axes
80
+ if (dataset.trendlineLinear.projection && fitter.scale() < 0) {
81
+ // X
82
+ var x2value = fitter.fo()
83
+ if (x2value < fitter.minx) x2value = fitter.maxx
84
+ x2 = xScale.getPixelForValue(x2value)
51
85
 
52
- dataset.data.forEach(function(data, index) {
86
+ // Y
87
+ y2 = yScale.getPixelForValue(fitter.f(x2value))
88
+ } else {
89
+ x2 = xScale.getPixelForValue(fitter.maxx)
90
+ y2 = yScale.getPixelForValue(fitter.f(fitter.maxx))
91
+ }
53
92
 
54
- if(data == null)
55
- return;
93
+ if (!xy) {
94
+ x1 = startPos
95
+ x2 = endPos
96
+ }
56
97
 
57
- if (xScale.options.type === "time") {
58
- var x = data.x != null ? data.x : data.t;
59
- fitter.add(new Date(x).getTime(), data.y);
60
- }
61
- else if (xy) {
62
- fitter.add(data.x, data.y);
63
- }
64
- else {
65
- fitter.add(index, data);
66
- }
67
-
68
- });
69
-
70
- var x1 = xScale.getPixelForValue(fitter.minx);
71
- var x2 = xScale.getPixelForValue(fitter.maxx);
72
- var y1 = yScale.getPixelForValue(fitter.f(fitter.minx));
73
- var y2 = yScale.getPixelForValue(fitter.f(fitter.maxx));
74
- if ( !xy ) { x1 = startPos; x2 = endPos; }
75
-
76
- var drawBottom = datasetMeta.controller.chart.chartArea.bottom;
77
- var chartWidth = datasetMeta.controller.chart.width;
78
-
79
- if(y1 > drawBottom) { // Left side is below zero
80
- var diff = y1 - drawBottom;
81
- var lineHeight = y1 - y2;
82
- var overlapPercentage = diff / lineHeight;
83
- var addition = chartWidth * overlapPercentage;
84
-
85
- y1 = drawBottom;
86
- x1 = (x1 + addition);
87
- } else if(y2 > drawBottom) { // right side is below zero
88
- var diff = y2 - drawBottom;
89
- var lineHeight = y2 - y1;
90
- var overlapPercentage = diff / lineHeight;
91
- var subtraction = chartWidth - (chartWidth * overlapPercentage);
92
-
93
- y2 = drawBottom;
94
- x2 = chartWidth - (x2 - subtraction);
98
+ var drawBottom = datasetMeta.controller.chart.chartArea.bottom
99
+ var chartWidth = datasetMeta.controller.chart.width
100
+
101
+ if (y1 > drawBottom) {
102
+ // Left side is below zero
103
+ var diff = y1 - drawBottom
104
+ var lineHeight = y1 - y2
105
+ var overlapPercentage = diff / lineHeight
106
+ var addition = chartWidth * overlapPercentage
107
+
108
+ y1 = drawBottom
109
+ x1 = x1 + addition
110
+ } else if (y2 > drawBottom) {
111
+ // right side is below zero
112
+ var diff = y2 - drawBottom
113
+ var lineHeight = y2 - y1
114
+ var overlapPercentage = diff / lineHeight
115
+ var subtraction = chartWidth - chartWidth * overlapPercentage
116
+
117
+ y2 = drawBottom
118
+ x2 = chartWidth - (x2 - subtraction)
95
119
  }
96
120
 
97
- ctx.lineWidth = lineWidth;
98
- if (lineStyle === "dotted") { ctx.setLineDash([2, 3]); }
99
- ctx.beginPath();
100
- ctx.moveTo(x1, y1);
101
- ctx.lineTo(x2, y2);
102
- ctx.strokeStyle = style;
103
- ctx.stroke();
121
+ ctx.lineWidth = lineWidth
122
+ if (lineStyle === 'dotted') {
123
+ ctx.setLineDash([2, 3])
124
+ }
125
+ ctx.beginPath()
126
+ ctx.moveTo(x1, y1)
127
+ ctx.lineTo(x2, y2)
128
+ ctx.strokeStyle = style
129
+ ctx.stroke()
104
130
  }
105
131
 
106
132
  function LineFitter() {
107
- this.count = 0;
108
- this.sumX = 0;
109
- this.sumX2 = 0;
110
- this.sumXY = 0;
111
- this.sumY = 0;
112
- this.minx = 1e100;
113
- this.maxx = -1e100;
133
+ this.count = 0
134
+ this.sumX = 0
135
+ this.sumX2 = 0
136
+ this.sumXY = 0
137
+ this.sumY = 0
138
+ this.minx = 1e100
139
+ this.maxx = -1e100
140
+ this.maxy = -1e100
114
141
  }
115
142
 
116
143
  LineFitter.prototype = {
117
- 'add': function (x, y) {
118
- x = parseFloat(x);
119
- y = parseFloat(y);
120
-
121
- this.count++;
122
- this.sumX += x;
123
- this.sumX2 += x * x;
124
- this.sumXY += x * y;
125
- this.sumY += y;
126
- if ( x < this.minx ) this.minx = x;
127
- if ( x > this.maxx ) this.maxx = x;
144
+ add: function (x, y) {
145
+ x = parseFloat(x)
146
+ y = parseFloat(y)
147
+
148
+ this.count++
149
+ this.sumX += x
150
+ this.sumX2 += x * x
151
+ this.sumXY += x * y
152
+ this.sumY += y
153
+ if (x < this.minx) this.minx = x
154
+ if (x > this.maxx) this.maxx = x
155
+ if (y > this.maxy) this.maxy = y
128
156
  },
129
- 'f': function (x) {
130
- x = parseFloat(x);
131
-
132
- var det = this.count * this.sumX2 - this.sumX * this.sumX;
133
- var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
134
- var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
135
- return offset + x * scale;
136
- }
137
- };
157
+ f: function (x) {
158
+ x = parseFloat(x)
159
+
160
+ var det = this.count * this.sumX2 - this.sumX * this.sumX
161
+ var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det
162
+ var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det
163
+ return offset + x * scale
164
+ },
165
+ fo: function () {
166
+ var det = this.count * this.sumX2 - this.sumX * this.sumX
167
+ var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det
168
+ var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det
169
+
170
+ // Get x when y = 0
171
+ var xo = -offset / scale
172
+ return xo
173
+ },
174
+ scale: function () {
175
+ var det = this.count * this.sumX2 - this.sumX * this.sumX
176
+ var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det
177
+
178
+ return scale
179
+ },
180
+ }
138
181
 
139
182
  // If we're in the browser and have access to the global Chart obj, register plugin automatically
140
- if (typeof window !== undefined && window.Chart) {
183
+ if (typeof window !== 'undefined' && window.Chart) {
141
184
  if (window.Chart.hasOwnProperty('register')) {
142
- window.Chart.register(pluginTrendlineLinear);
185
+ window.Chart.register(pluginTrendlineLinear)
143
186
  } else {
144
- window.Chart.plugins.register(pluginTrendlineLinear);
187
+ window.Chart.plugins.register(pluginTrendlineLinear)
145
188
  }
146
189
  }
147
190
 
148
191
  // Otherwise, try to export the plugin
149
192
  try {
150
- module.exports = exports = pluginTrendlineLinear;
193
+ module.exports = exports = pluginTrendlineLinear
151
194
  } catch (e) {}
@@ -0,0 +1,9 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ entry: './src/chartjs-plugin-trendline.js',
5
+ output: {
6
+ filename: 'chartjs-plugin-trendline.min.js',
7
+ path: path.resolve(__dirname, 'dist'),
8
+ },
9
+ };
package/gulpfile.js DELETED
@@ -1,37 +0,0 @@
1
- var gulp = require('gulp');
2
- var uglify = require('gulp-uglify');
3
- var pump = require('pump');
4
- var rename = require('gulp-rename');
5
- var del = require('del');
6
- var bump = require('gulp-bump');
7
-
8
- gulp.task('clean', function () {
9
- return del([
10
- 'dist/',
11
- ]);
12
- });
13
-
14
- gulp.task('compress', function (cb) {
15
- pump([
16
- gulp.src('src/*.js'),
17
- uglify(),
18
- rename('chartjs-plugin-trendline.min.js'),
19
- gulp.dest('dist/')
20
- ],
21
- cb
22
- );
23
- });
24
-
25
- gulp.task('bump-minor', function(){
26
- return gulp.src(['./package.json', './src/chartjs-plugin-trendline.js'], {base: './'})
27
- .pipe(bump({type:'minor'}))
28
- .pipe(gulp.dest('./'));
29
- });
30
-
31
- gulp.task('bump-patch', function(){
32
- return gulp.src(['./package.json', './src/chartjs-plugin-trendline.js'], {base: './'})
33
- .pipe(bump({type:'patch'}))
34
- .pipe(gulp.dest('./'));
35
- });
36
-
37
- gulp.task('default', gulp.series('clean', 'compress'));