highcharts-rails 4.2.7 → 5.0.0

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +34 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +53 -32
  5. data/app/assets/javascripts/highcharts.js +18775 -17176
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +1849 -1563
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +2162 -1988
  8. data/app/assets/javascripts/highcharts/modules/accessibility.js +1005 -0
  9. data/app/assets/javascripts/highcharts/modules/annotations.js +408 -401
  10. data/app/assets/javascripts/highcharts/modules/boost.js +561 -546
  11. data/app/assets/javascripts/highcharts/modules/broken-axis.js +330 -324
  12. data/app/assets/javascripts/highcharts/modules/data.js +973 -965
  13. data/app/assets/javascripts/highcharts/modules/drilldown.js +783 -723
  14. data/app/assets/javascripts/highcharts/modules/exporting.js +864 -785
  15. data/app/assets/javascripts/highcharts/modules/funnel.js +290 -306
  16. data/app/assets/javascripts/highcharts/modules/heatmap.js +701 -645
  17. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +150 -132
  18. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +414 -355
  19. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +164 -0
  20. data/app/assets/javascripts/highcharts/modules/series-label.js +473 -448
  21. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +279 -271
  22. data/app/assets/javascripts/highcharts/modules/treemap.js +921 -886
  23. data/app/assets/javascripts/highcharts/themes/dark-blue.js +307 -244
  24. data/app/assets/javascripts/highcharts/themes/dark-green.js +303 -244
  25. data/app/assets/javascripts/highcharts/themes/dark-unica.js +231 -201
  26. data/app/assets/javascripts/highcharts/themes/gray.js +314 -245
  27. data/app/assets/javascripts/highcharts/themes/grid-light.js +91 -66
  28. data/app/assets/javascripts/highcharts/themes/grid.js +124 -96
  29. data/app/assets/javascripts/highcharts/themes/sand-signika.js +119 -94
  30. data/app/assets/javascripts/highcharts/themes/skies.js +108 -85
  31. data/lib/highcharts/version.rb +1 -1
  32. metadata +13 -14
  33. data/app/assets/javascripts/highcharts/adapters/standalone-framework.js +0 -1
  34. data/app/assets/javascripts/highcharts/modules/canvas-tools.js +0 -3115
  35. data/app/assets/javascripts/highcharts/modules/map.js +0 -2117
@@ -1,621 +1,636 @@
1
1
  /**
2
- * This is an experimental Highcharts module that draws long data series on a canvas
3
- * in order to increase performance of the initial load time and tooltip responsiveness.
4
- *
5
- * Compatible with HTML5 canvas compatible browsers (not IE < 9).
2
+ * @license Highcharts JS v5.0.0 (2016-09-29)
3
+ * Boost module
6
4
  *
5
+ * (c) 2010-2016 Highsoft AS
7
6
  * Author: Torstein Honsi
8
7
  *
9
- *
10
- * Development plan
11
- * - Column range.
12
- * - Heatmap.
13
- * - Treemap.
14
- * - Check how it works with Highstock and data grouping. Currently it only works when navigator.adaptToUpdatedData
15
- * is false. It is also recommended to set scrollbar.liveRedraw to false.
16
- * - Check inverted charts.
17
- * - Check reversed axes.
18
- * - Chart callback should be async after last series is drawn. (But not necessarily, we don't do
19
- that with initial series animation).
20
- * - Cache full-size image so we don't have to redraw on hide/show and zoom up. But k-d-tree still
21
- * needs to be built.
22
- * - Test IE9 and IE10.
23
- * - Stacking is not perhaps not correct since it doesn't use the translation given in
24
- * the translate method. If this gets to complicated, a possible way out would be to
25
- * have a simplified renderCanvas method that simply draws the areaPath on a canvas.
26
- *
27
- * If this module is taken in as part of the core
28
- * - All the loading logic should be merged with core. Update styles in the core.
29
- * - Most of the method wraps should probably be added directly in parent methods.
30
- *
31
- * Notes for boost mode
32
- * - Area lines are not drawn
33
- * - Point markers are not drawn on line-type series
34
- * - Lines are not drawn on scatter charts
35
- * - Zones and negativeColor don't work
36
- * - Columns are always one pixel wide. Don't set the threshold too low.
37
- *
38
- * Optimizing tips for users
39
- * - For scatter plots, use a marker.radius of 1 or less. It results in a rectangle being drawn, which is
40
- * considerably faster than a circle.
41
- * - Set extremes (min, max) explicitly on the axes in order for Highcharts to avoid computing extremes.
42
- * - Set enableMouseTracking to false on the series to improve total rendering time.
43
- * - The default threshold is set based on one series. If you have multiple, dense series, the combined
44
- * number of points drawn gets higher, and you may want to set the threshold lower in order to
45
- * use optimizations.
8
+ * License: www.highcharts.com/license
46
9
  */
47
-
48
- /* eslint indent: [2, 4] */
49
- (function (factory) {
10
+ (function(factory) {
50
11
  if (typeof module === 'object' && module.exports) {
51
12
  module.exports = factory;
52
13
  } else {
53
14
  factory(Highcharts);
54
15
  }
55
- }(function (H) {
56
-
57
- 'use strict';
58
-
59
- var win = H.win,
60
- doc = win.document,
61
- noop = function () {},
62
- Color = H.Color,
63
- Series = H.Series,
64
- seriesTypes = H.seriesTypes,
65
- each = H.each,
66
- extend = H.extend,
67
- addEvent = H.addEvent,
68
- fireEvent = H.fireEvent,
69
- grep = H.grep,
70
- isNumber = H.isNumber,
71
- merge = H.merge,
72
- pick = H.pick,
73
- wrap = H.wrap,
74
- plotOptions = H.getOptions().plotOptions,
75
- CHUNK_SIZE = 50000,
76
- destroyLoadingDiv;
77
-
78
- function eachAsync(arr, fn, finalFunc, chunkSize, i) {
79
- i = i || 0;
80
- chunkSize = chunkSize || CHUNK_SIZE;
81
-
82
- var threshold = i + chunkSize,
83
- proceed = true;
84
-
85
- while (proceed && i < threshold && i < arr.length) {
86
- proceed = fn(arr[i], i);
87
- i = i + 1;
88
- }
89
- if (proceed) {
90
- if (i < arr.length) {
91
- setTimeout(function () {
92
- eachAsync(arr, fn, finalFunc, chunkSize, i);
93
- });
94
- } else if (finalFunc) {
95
- finalFunc();
96
- }
97
- }
98
- }
99
-
100
- // Set default options
101
- each(['area', 'arearange', 'column', 'line', 'scatter'], function (type) {
102
- if (plotOptions[type]) {
103
- plotOptions[type].boostThreshold = 5000;
104
- }
105
- });
106
-
107
- /**
108
- * Override a bunch of methods the same way. If the number of points is below the threshold,
109
- * run the original method. If not, check for a canvas version or do nothing.
110
- */
111
- each(['translate', 'generatePoints', 'drawTracker', 'drawPoints', 'render'], function (method) {
112
- function branch(proceed) {
113
- var letItPass = this.options.stacking && (method === 'translate' || method === 'generatePoints');
114
- if ((this.processedXData || this.options.data).length < (this.options.boostThreshold || Number.MAX_VALUE) ||
115
- letItPass) {
16
+ }(function(Highcharts) {
17
+ (function(H) {
18
+ /**
19
+ * License: www.highcharts.com/license
20
+ * Author: Torstein Honsi
21
+ *
22
+ * This is an experimental Highcharts module that draws long data series on a canvas
23
+ * in order to increase performance of the initial load time and tooltip responsiveness.
24
+ *
25
+ * Compatible with HTML5 canvas compatible browsers (not IE < 9).
26
+ *
27
+ *
28
+ *
29
+ * Development plan
30
+ * - Column range.
31
+ * - Heatmap.
32
+ * - Treemap.
33
+ * - Check how it works with Highstock and data grouping. Currently it only works when navigator.adaptToUpdatedData
34
+ * is false. It is also recommended to set scrollbar.liveRedraw to false.
35
+ * - Check inverted charts.
36
+ * - Check reversed axes.
37
+ * - Chart callback should be async after last series is drawn. (But not necessarily, we don't do
38
+ that with initial series animation).
39
+ * - Cache full-size image so we don't have to redraw on hide/show and zoom up. But k-d-tree still
40
+ * needs to be built.
41
+ * - Test IE9 and IE10.
42
+ * - Stacking is not perhaps not correct since it doesn't use the translation given in
43
+ * the translate method. If this gets to complicated, a possible way out would be to
44
+ * have a simplified renderCanvas method that simply draws the areaPath on a canvas.
45
+ *
46
+ * If this module is taken in as part of the core
47
+ * - All the loading logic should be merged with core. Update styles in the core.
48
+ * - Most of the method wraps should probably be added directly in parent methods.
49
+ *
50
+ * Notes for boost mode
51
+ * - Area lines are not drawn
52
+ * - Point markers are not drawn on line-type series
53
+ * - Lines are not drawn on scatter charts
54
+ * - Zones and negativeColor don't work
55
+ * - Columns are always one pixel wide. Don't set the threshold too low.
56
+ *
57
+ * Optimizing tips for users
58
+ * - For scatter plots, use a marker.radius of 1 or less. It results in a rectangle being drawn, which is
59
+ * considerably faster than a circle.
60
+ * - Set extremes (min, max) explicitly on the axes in order for Highcharts to avoid computing extremes.
61
+ * - Set enableMouseTracking to false on the series to improve total rendering time.
62
+ * - The default threshold is set based on one series. If you have multiple, dense series, the combined
63
+ * number of points drawn gets higher, and you may want to set the threshold lower in order to
64
+ * use optimizations.
65
+ */
116
66
 
117
- // Clear image
118
- if (method === 'render' && this.image) {
119
- this.image.attr({ href: '' });
120
- this.animate = null; // We're zooming in, don't run animation
67
+ 'use strict';
68
+
69
+ var win = H.win,
70
+ doc = win.document,
71
+ noop = function() {},
72
+ Color = H.Color,
73
+ Series = H.Series,
74
+ seriesTypes = H.seriesTypes,
75
+ each = H.each,
76
+ extend = H.extend,
77
+ addEvent = H.addEvent,
78
+ fireEvent = H.fireEvent,
79
+ grep = H.grep,
80
+ isNumber = H.isNumber,
81
+ merge = H.merge,
82
+ pick = H.pick,
83
+ wrap = H.wrap,
84
+ plotOptions = H.getOptions().plotOptions,
85
+ CHUNK_SIZE = 50000,
86
+ destroyLoadingDiv;
87
+
88
+ function eachAsync(arr, fn, finalFunc, chunkSize, i) {
89
+ i = i || 0;
90
+ chunkSize = chunkSize || CHUNK_SIZE;
91
+
92
+ var threshold = i + chunkSize,
93
+ proceed = true;
94
+
95
+ while (proceed && i < threshold && i < arr.length) {
96
+ proceed = fn(arr[i], i);
97
+ i = i + 1;
98
+ }
99
+ if (proceed) {
100
+ if (i < arr.length) {
101
+ setTimeout(function() {
102
+ eachAsync(arr, fn, finalFunc, chunkSize, i);
103
+ });
104
+ } else if (finalFunc) {
105
+ finalFunc();
121
106
  }
122
-
123
- proceed.call(this);
124
-
125
- // If a canvas version of the method exists, like renderCanvas(), run
126
- } else if (this[method + 'Canvas']) {
127
-
128
- this[method + 'Canvas']();
129
107
  }
130
108
  }
131
- wrap(Series.prototype, method, branch);
132
109
 
133
- // A special case for some types - its translate method is already wrapped
134
- if (method === 'translate') {
135
- if (seriesTypes.column) {
136
- wrap(seriesTypes.column.prototype, method, branch);
137
- }
138
- if (seriesTypes.arearange) {
139
- wrap(seriesTypes.arearange.prototype, method, branch);
110
+ // Set default options
111
+ each(['area', 'arearange', 'column', 'line', 'scatter'], function(type) {
112
+ if (plotOptions[type]) {
113
+ plotOptions[type].boostThreshold = 5000;
140
114
  }
141
- }
142
- });
143
-
144
- /**
145
- * Do not compute extremes when min and max are set.
146
- * If we use this in the core, we can add the hook to hasExtremes to the methods directly.
147
- */
148
- wrap(Series.prototype, 'getExtremes', function (proceed) {
149
- if (!this.hasExtremes()) {
150
- proceed.apply(this, Array.prototype.slice.call(arguments, 1));
151
- }
152
- });
153
- wrap(Series.prototype, 'setData', function (proceed) {
154
- if (!this.hasExtremes(true)) {
155
- proceed.apply(this, Array.prototype.slice.call(arguments, 1));
156
- }
157
- });
158
- wrap(Series.prototype, 'processData', function (proceed) {
159
- if (!this.hasExtremes(true)) {
160
- proceed.apply(this, Array.prototype.slice.call(arguments, 1));
161
- }
162
- });
163
-
164
-
165
- H.extend(Series.prototype, {
166
- pointRange: 0,
167
- allowDG: false, // No data grouping, let boost handle large data
168
- hasExtremes: function (checkX) {
169
- var options = this.options,
170
- data = options.data,
171
- xAxis = this.xAxis && this.xAxis.options,
172
- yAxis = this.yAxis && this.yAxis.options;
173
- return data.length > (options.boostThreshold || Number.MAX_VALUE) && isNumber(yAxis.min) && isNumber(yAxis.max) &&
174
- (!checkX || (isNumber(xAxis.min) && isNumber(xAxis.max)));
175
- },
115
+ });
176
116
 
177
117
  /**
178
- * If implemented in the core, parts of this can probably be shared with other similar
179
- * methods in Highcharts.
118
+ * Override a bunch of methods the same way. If the number of points is below the threshold,
119
+ * run the original method. If not, check for a canvas version or do nothing.
180
120
  */
181
- destroyGraphics: function () {
182
- var series = this,
183
- points = this.points,
184
- point,
185
- i;
186
-
187
- if (points) {
188
- for (i = 0; i < points.length; i = i + 1) {
189
- point = points[i];
190
- if (point && point.graphic) {
191
- point.graphic = point.graphic.destroy();
121
+ each(['translate', 'generatePoints', 'drawTracker', 'drawPoints', 'render'], function(method) {
122
+ function branch(proceed) {
123
+ var letItPass = this.options.stacking && (method === 'translate' || method === 'generatePoints');
124
+ if ((this.processedXData || this.options.data).length < (this.options.boostThreshold || Number.MAX_VALUE) ||
125
+ letItPass) {
126
+
127
+ // Clear image
128
+ if (method === 'render' && this.image) {
129
+ this.image.attr({
130
+ href: ''
131
+ });
132
+ this.animate = null; // We're zooming in, don't run animation
192
133
  }
134
+
135
+ proceed.call(this);
136
+
137
+ // If a canvas version of the method exists, like renderCanvas(), run
138
+ } else if (this[method + 'Canvas']) {
139
+
140
+ this[method + 'Canvas']();
193
141
  }
194
142
  }
143
+ wrap(Series.prototype, method, branch);
195
144
 
196
- each(['graph', 'area', 'tracker'], function (prop) {
197
- if (series[prop]) {
198
- series[prop] = series[prop].destroy();
145
+ // A special case for some types - its translate method is already wrapped
146
+ if (method === 'translate') {
147
+ if (seriesTypes.column) {
148
+ wrap(seriesTypes.column.prototype, method, branch);
149
+ }
150
+ if (seriesTypes.arearange) {
151
+ wrap(seriesTypes.arearange.prototype, method, branch);
199
152
  }
200
- });
201
- },
153
+ }
154
+ });
202
155
 
203
156
  /**
204
- * Create a hidden canvas to draw the graph on. The contents is later copied over
205
- * to an SVG image element.
157
+ * Do not compute extremes when min and max are set.
158
+ * If we use this in the core, we can add the hook to hasExtremes to the methods directly.
206
159
  */
207
- getContext: function () {
208
- var chart = this.chart,
209
- width = chart.plotWidth,
210
- height = chart.plotHeight,
211
- ctx = this.ctx,
212
- swapXY = function (proceed, x, y, a, b, c, d) {
213
- proceed.call(this, y, x, a, b, c, d);
214
- };
215
-
216
- if (!this.canvas) {
217
- this.canvas = doc.createElement('canvas');
218
- this.image = chart.renderer.image('', 0, 0, width, height).add(this.group);
219
- this.ctx = ctx = this.canvas.getContext('2d');
220
- if (chart.inverted) {
221
- each(['moveTo', 'lineTo', 'rect', 'arc'], function (fn) {
222
- wrap(ctx, fn, swapXY);
223
- });
224
- }
225
- } else {
226
- ctx.clearRect(0, 0, width, height);
160
+ wrap(Series.prototype, 'getExtremes', function(proceed) {
161
+ if (!this.hasExtremes()) {
162
+ proceed.apply(this, Array.prototype.slice.call(arguments, 1));
227
163
  }
164
+ });
165
+ wrap(Series.prototype, 'setData', function(proceed) {
166
+ if (!this.hasExtremes(true)) {
167
+ proceed.apply(this, Array.prototype.slice.call(arguments, 1));
168
+ }
169
+ });
170
+ wrap(Series.prototype, 'processData', function(proceed) {
171
+ if (!this.hasExtremes(true)) {
172
+ proceed.apply(this, Array.prototype.slice.call(arguments, 1));
173
+ }
174
+ });
175
+
176
+
177
+ H.extend(Series.prototype, {
178
+ pointRange: 0,
179
+ allowDG: false, // No data grouping, let boost handle large data
180
+ hasExtremes: function(checkX) {
181
+ var options = this.options,
182
+ data = options.data,
183
+ xAxis = this.xAxis && this.xAxis.options,
184
+ yAxis = this.yAxis && this.yAxis.options;
185
+ return data.length > (options.boostThreshold || Number.MAX_VALUE) && isNumber(yAxis.min) && isNumber(yAxis.max) &&
186
+ (!checkX || (isNumber(xAxis.min) && isNumber(xAxis.max)));
187
+ },
188
+
189
+ /**
190
+ * If implemented in the core, parts of this can probably be shared with other similar
191
+ * methods in Highcharts.
192
+ */
193
+ destroyGraphics: function() {
194
+ var series = this,
195
+ points = this.points,
196
+ point,
197
+ i;
198
+
199
+ if (points) {
200
+ for (i = 0; i < points.length; i = i + 1) {
201
+ point = points[i];
202
+ if (point && point.graphic) {
203
+ point.graphic = point.graphic.destroy();
204
+ }
205
+ }
206
+ }
228
207
 
229
- this.canvas.width = width;
230
- this.canvas.height = height;
231
- this.image.attr({
232
- width: width,
233
- height: height
234
- });
208
+ each(['graph', 'area', 'tracker'], function(prop) {
209
+ if (series[prop]) {
210
+ series[prop] = series[prop].destroy();
211
+ }
212
+ });
213
+ },
214
+
215
+ /**
216
+ * Create a hidden canvas to draw the graph on. The contents is later copied over
217
+ * to an SVG image element.
218
+ */
219
+ getContext: function() {
220
+ var chart = this.chart,
221
+ width = chart.plotWidth,
222
+ height = chart.plotHeight,
223
+ ctx = this.ctx,
224
+ swapXY = function(proceed, x, y, a, b, c, d) {
225
+ proceed.call(this, y, x, a, b, c, d);
226
+ };
235
227
 
236
- return ctx;
237
- },
228
+ if (!this.canvas) {
229
+ this.canvas = doc.createElement('canvas');
230
+ this.image = chart.renderer.image('', 0, 0, width, height).add(this.group);
231
+ this.ctx = ctx = this.canvas.getContext('2d');
232
+ if (chart.inverted) {
233
+ each(['moveTo', 'lineTo', 'rect', 'arc'], function(fn) {
234
+ wrap(ctx, fn, swapXY);
235
+ });
236
+ }
237
+ } else {
238
+ ctx.clearRect(0, 0, width, height);
239
+ }
238
240
 
239
- /**
240
- * Draw the canvas image inside an SVG image
241
- */
242
- canvasToSVG: function () {
243
- this.image.attr({ href: this.canvas.toDataURL('image/png') });
244
- },
241
+ this.canvas.width = width;
242
+ this.canvas.height = height;
243
+ this.image.attr({
244
+ width: width,
245
+ height: height
246
+ });
245
247
 
246
- cvsLineTo: function (ctx, clientX, plotY) {
247
- ctx.lineTo(clientX, plotY);
248
- },
248
+ return ctx;
249
+ },
249
250
 
250
- renderCanvas: function () {
251
- var series = this,
252
- options = series.options,
253
- chart = series.chart,
254
- xAxis = this.xAxis,
255
- yAxis = this.yAxis,
256
- ctx,
257
- c = 0,
258
- xData = series.processedXData,
259
- yData = series.processedYData,
260
- rawData = options.data,
261
- xExtremes = xAxis.getExtremes(),
262
- xMin = xExtremes.min,
263
- xMax = xExtremes.max,
264
- yExtremes = yAxis.getExtremes(),
265
- yMin = yExtremes.min,
266
- yMax = yExtremes.max,
267
- pointTaken = {},
268
- lastClientX,
269
- sampling = !!series.sampling,
270
- points,
271
- r = options.marker && options.marker.radius,
272
- cvsDrawPoint = this.cvsDrawPoint,
273
- cvsLineTo = options.lineWidth ? this.cvsLineTo : false,
274
- cvsMarker = r <= 1 ? this.cvsMarkerSquare : this.cvsMarkerCircle,
275
- enableMouseTracking = options.enableMouseTracking !== false,
276
- lastPoint,
277
- threshold = options.threshold,
278
- yBottom = yAxis.getThreshold(threshold),
279
- hasThreshold = isNumber(threshold),
280
- translatedThreshold = yBottom,
281
- doFill = this.fill,
282
- isRange = series.pointArrayMap && series.pointArrayMap.join(',') === 'low,high',
283
- isStacked = !!options.stacking,
284
- cropStart = series.cropStart || 0,
285
- loadingOptions = chart.options.loading,
286
- requireSorting = series.requireSorting,
287
- wasNull,
288
- connectNulls = options.connectNulls,
289
- useRaw = !xData,
290
- minVal,
291
- maxVal,
292
- minI,
293
- maxI,
294
- fillColor = series.fillOpacity ?
295
- new Color(series.color).setOpacity(pick(options.fillOpacity, 0.75)).get() :
296
- series.color,
297
- stroke = function () {
298
- if (doFill) {
299
- ctx.fillStyle = fillColor;
300
- ctx.fill();
301
- } else {
302
- ctx.strokeStyle = series.color;
303
- ctx.lineWidth = options.lineWidth;
304
- ctx.stroke();
305
- }
306
- },
307
- drawPoint = function (clientX, plotY, yBottom) {
308
- if (c === 0) {
309
- ctx.beginPath();
251
+ /**
252
+ * Draw the canvas image inside an SVG image
253
+ */
254
+ canvasToSVG: function() {
255
+ this.image.attr({
256
+ href: this.canvas.toDataURL('image/png')
257
+ });
258
+ },
310
259
 
311
- if (cvsLineTo) {
312
- ctx.lineJoin = 'round';
260
+ cvsLineTo: function(ctx, clientX, plotY) {
261
+ ctx.lineTo(clientX, plotY);
262
+ },
263
+
264
+ renderCanvas: function() {
265
+ var series = this,
266
+ options = series.options,
267
+ chart = series.chart,
268
+ xAxis = this.xAxis,
269
+ yAxis = this.yAxis,
270
+ ctx,
271
+ c = 0,
272
+ xData = series.processedXData,
273
+ yData = series.processedYData,
274
+ rawData = options.data,
275
+ xExtremes = xAxis.getExtremes(),
276
+ xMin = xExtremes.min,
277
+ xMax = xExtremes.max,
278
+ yExtremes = yAxis.getExtremes(),
279
+ yMin = yExtremes.min,
280
+ yMax = yExtremes.max,
281
+ pointTaken = {},
282
+ lastClientX,
283
+ sampling = !!series.sampling,
284
+ points,
285
+ r = options.marker && options.marker.radius,
286
+ cvsDrawPoint = this.cvsDrawPoint,
287
+ cvsLineTo = options.lineWidth ? this.cvsLineTo : false,
288
+ cvsMarker = r <= 1 ? this.cvsMarkerSquare : this.cvsMarkerCircle,
289
+ enableMouseTracking = options.enableMouseTracking !== false,
290
+ lastPoint,
291
+ threshold = options.threshold,
292
+ yBottom = yAxis.getThreshold(threshold),
293
+ hasThreshold = isNumber(threshold),
294
+ translatedThreshold = yBottom,
295
+ doFill = this.fill,
296
+ isRange = series.pointArrayMap && series.pointArrayMap.join(',') === 'low,high',
297
+ isStacked = !!options.stacking,
298
+ cropStart = series.cropStart || 0,
299
+ loadingOptions = chart.options.loading,
300
+ requireSorting = series.requireSorting,
301
+ wasNull,
302
+ connectNulls = options.connectNulls,
303
+ useRaw = !xData,
304
+ minVal,
305
+ maxVal,
306
+ minI,
307
+ maxI,
308
+ fillColor = series.fillOpacity ?
309
+ new Color(series.color).setOpacity(pick(options.fillOpacity, 0.75)).get() :
310
+ series.color,
311
+ stroke = function() {
312
+ if (doFill) {
313
+ ctx.fillStyle = fillColor;
314
+ ctx.fill();
315
+ } else {
316
+ ctx.strokeStyle = series.color;
317
+ ctx.lineWidth = options.lineWidth;
318
+ ctx.stroke();
313
319
  }
314
- }
320
+ },
321
+ drawPoint = function(clientX, plotY, yBottom) {
322
+ if (c === 0) {
323
+ ctx.beginPath();
315
324
 
316
- if (wasNull) {
317
- ctx.moveTo(clientX, plotY);
318
- } else {
319
- if (cvsDrawPoint) {
320
- cvsDrawPoint(ctx, clientX, plotY, yBottom, lastPoint);
321
- } else if (cvsLineTo) {
322
- cvsLineTo(ctx, clientX, plotY);
323
- } else if (cvsMarker) {
324
- cvsMarker(ctx, clientX, plotY, r);
325
+ if (cvsLineTo) {
326
+ ctx.lineJoin = 'round';
327
+ }
325
328
  }
326
- }
327
-
328
- // We need to stroke the line for every 1000 pixels. It will crash the browser
329
- // memory use if we stroke too infrequently.
330
- c = c + 1;
331
- if (c === 1000) {
332
- stroke();
333
- c = 0;
334
- }
335
329
 
336
- // Area charts need to keep track of the last point
337
- lastPoint = {
338
- clientX: clientX,
339
- plotY: plotY,
340
- yBottom: yBottom
341
- };
342
- },
343
-
344
- addKDPoint = function (clientX, plotY, i) {
345
-
346
- // The k-d tree requires series points. Reduce the amount of points, since the time to build the
347
- // tree increases exponentially.
348
- if (enableMouseTracking && !pointTaken[clientX + ',' + plotY]) {
349
- pointTaken[clientX + ',' + plotY] = true;
330
+ if (wasNull) {
331
+ ctx.moveTo(clientX, plotY);
332
+ } else {
333
+ if (cvsDrawPoint) {
334
+ cvsDrawPoint(ctx, clientX, plotY, yBottom, lastPoint);
335
+ } else if (cvsLineTo) {
336
+ cvsLineTo(ctx, clientX, plotY);
337
+ } else if (cvsMarker) {
338
+ cvsMarker(ctx, clientX, plotY, r);
339
+ }
340
+ }
350
341
 
351
- if (chart.inverted) {
352
- clientX = xAxis.len - clientX;
353
- plotY = yAxis.len - plotY;
342
+ // We need to stroke the line for every 1000 pixels. It will crash the browser
343
+ // memory use if we stroke too infrequently.
344
+ c = c + 1;
345
+ if (c === 1000) {
346
+ stroke();
347
+ c = 0;
354
348
  }
355
349
 
356
- points.push({
350
+ // Area charts need to keep track of the last point
351
+ lastPoint = {
357
352
  clientX: clientX,
358
- plotX: clientX,
359
353
  plotY: plotY,
360
- i: cropStart + i
361
- });
362
- }
363
- };
354
+ yBottom: yBottom
355
+ };
356
+ },
364
357
 
365
- // If we are zooming out from SVG mode, destroy the graphics
366
- if (this.points || this.graph) {
367
- this.destroyGraphics();
368
- }
358
+ addKDPoint = function(clientX, plotY, i) {
369
359
 
370
- // The group
371
- series.plotGroup(
372
- 'group',
373
- 'series',
374
- series.visible ? 'visible' : 'hidden',
375
- options.zIndex,
376
- chart.seriesGroup
377
- );
360
+ // The k-d tree requires series points. Reduce the amount of points, since the time to build the
361
+ // tree increases exponentially.
362
+ if (enableMouseTracking && !pointTaken[clientX + ',' + plotY]) {
363
+ pointTaken[clientX + ',' + plotY] = true;
378
364
 
379
- series.getAttribs();
380
- series.markerGroup = series.group;
381
- addEvent(series, 'destroy', function () {
382
- series.markerGroup = null;
383
- });
384
-
385
- points = this.points = [];
386
- ctx = this.getContext();
387
- series.buildKDTree = noop; // Do not start building while drawing
388
-
389
- // Display a loading indicator
390
- if (rawData.length > 99999) {
391
- chart.options.loading = merge(loadingOptions, {
392
- labelStyle: {
393
- backgroundColor: 'rgba(255,255,255,0.75)',
394
- padding: '1em',
395
- borderRadius: '0.5em'
396
- },
397
- style: {
398
- backgroundColor: 'none',
399
- opacity: 1
400
- }
365
+ if (chart.inverted) {
366
+ clientX = xAxis.len - clientX;
367
+ plotY = yAxis.len - plotY;
368
+ }
369
+
370
+ points.push({
371
+ clientX: clientX,
372
+ plotX: clientX,
373
+ plotY: plotY,
374
+ i: cropStart + i
375
+ });
376
+ }
377
+ };
378
+
379
+ // If we are zooming out from SVG mode, destroy the graphics
380
+ if (this.points || this.graph) {
381
+ this.destroyGraphics();
382
+ }
383
+
384
+ // The group
385
+ series.plotGroup(
386
+ 'group',
387
+ 'series',
388
+ series.visible ? 'visible' : 'hidden',
389
+ options.zIndex,
390
+ chart.seriesGroup
391
+ );
392
+
393
+ series.markerGroup = series.group;
394
+ addEvent(series, 'destroy', function() {
395
+ series.markerGroup = null;
401
396
  });
402
- clearTimeout(destroyLoadingDiv);
403
- chart.showLoading('Drawing...');
404
- chart.options.loading = loadingOptions; // reset
405
- }
406
397
 
407
- // Loop over the points
408
- eachAsync(isStacked ? series.data : (xData || rawData), function (d, i) {
409
- var x,
410
- y,
411
- clientX,
412
- plotY,
413
- isNull,
414
- low,
415
- chartDestroyed = typeof chart.index === 'undefined',
416
- isYInside = true;
417
-
418
- if (!chartDestroyed) {
419
- if (useRaw) {
420
- x = d[0];
421
- y = d[1];
422
- } else {
423
- x = d;
424
- y = yData[i];
425
- }
398
+ points = this.points = [];
399
+ ctx = this.getContext();
400
+ series.buildKDTree = noop; // Do not start building while drawing
401
+
402
+ // Display a loading indicator
403
+ if (rawData.length > 99999) {
404
+ chart.options.loading = merge(loadingOptions, {
405
+ labelStyle: {
406
+ backgroundColor: H.color('#ffffff').setOpacity(0.75).get(),
407
+ padding: '1em',
408
+ borderRadius: '0.5em'
409
+ },
410
+ style: {
411
+ backgroundColor: 'none',
412
+ opacity: 1
413
+ }
414
+ });
415
+ clearTimeout(destroyLoadingDiv);
416
+ chart.showLoading('Drawing...');
417
+ chart.options.loading = loadingOptions; // reset
418
+ }
426
419
 
427
- // Resolve low and high for range series
428
- if (isRange) {
420
+ // Loop over the points
421
+ eachAsync(isStacked ? series.data : (xData || rawData), function(d, i) {
422
+ var x,
423
+ y,
424
+ clientX,
425
+ plotY,
426
+ isNull,
427
+ low,
428
+ chartDestroyed = typeof chart.index === 'undefined',
429
+ isYInside = true;
430
+
431
+ if (!chartDestroyed) {
429
432
  if (useRaw) {
430
- y = d.slice(1, 3);
433
+ x = d[0];
434
+ y = d[1];
435
+ } else {
436
+ x = d;
437
+ y = yData[i];
431
438
  }
432
- low = y[0];
433
- y = y[1];
434
- } else if (isStacked) {
435
- x = d.x;
436
- y = d.stackY;
437
- low = y - d.y;
438
- }
439
439
 
440
- isNull = y === null;
440
+ // Resolve low and high for range series
441
+ if (isRange) {
442
+ if (useRaw) {
443
+ y = d.slice(1, 3);
444
+ }
445
+ low = y[0];
446
+ y = y[1];
447
+ } else if (isStacked) {
448
+ x = d.x;
449
+ y = d.stackY;
450
+ low = y - d.y;
451
+ }
441
452
 
442
- // Optimize for scatter zooming
443
- if (!requireSorting) {
444
- isYInside = y >= yMin && y <= yMax;
445
- }
453
+ isNull = y === null;
446
454
 
447
- if (!isNull && x >= xMin && x <= xMax && isYInside) {
455
+ // Optimize for scatter zooming
456
+ if (!requireSorting) {
457
+ isYInside = y >= yMin && y <= yMax;
458
+ }
448
459
 
449
- clientX = Math.round(xAxis.toPixels(x, true));
460
+ if (!isNull && x >= xMin && x <= xMax && isYInside) {
450
461
 
451
- if (sampling) {
452
- if (minI === undefined || clientX === lastClientX) {
453
- if (!isRange) {
454
- low = y;
455
- }
456
- if (maxI === undefined || y > maxVal) {
457
- maxVal = y;
458
- maxI = i;
459
- }
460
- if (minI === undefined || low < minVal) {
461
- minVal = low;
462
- minI = i;
463
- }
462
+ clientX = Math.round(xAxis.toPixels(x, true));
464
463
 
465
- }
466
- if (clientX !== lastClientX) { // Add points and reset
467
- if (minI !== undefined) { // then maxI is also a number
468
- plotY = yAxis.toPixels(maxVal, true);
469
- yBottom = yAxis.toPixels(minVal, true);
470
- drawPoint(
471
- clientX,
472
- hasThreshold ? Math.min(plotY, translatedThreshold) : plotY,
473
- hasThreshold ? Math.max(yBottom, translatedThreshold) : yBottom
474
- );
475
- addKDPoint(clientX, plotY, maxI);
476
- if (yBottom !== plotY) {
477
- addKDPoint(clientX, yBottom, minI);
464
+ if (sampling) {
465
+ if (minI === undefined || clientX === lastClientX) {
466
+ if (!isRange) {
467
+ low = y;
468
+ }
469
+ if (maxI === undefined || y > maxVal) {
470
+ maxVal = y;
471
+ maxI = i;
472
+ }
473
+ if (minI === undefined || low < minVal) {
474
+ minVal = low;
475
+ minI = i;
478
476
  }
477
+
479
478
  }
479
+ if (clientX !== lastClientX) { // Add points and reset
480
+ if (minI !== undefined) { // then maxI is also a number
481
+ plotY = yAxis.toPixels(maxVal, true);
482
+ yBottom = yAxis.toPixels(minVal, true);
483
+ drawPoint(
484
+ clientX,
485
+ hasThreshold ? Math.min(plotY, translatedThreshold) : plotY,
486
+ hasThreshold ? Math.max(yBottom, translatedThreshold) : yBottom
487
+ );
488
+ addKDPoint(clientX, plotY, maxI);
489
+ if (yBottom !== plotY) {
490
+ addKDPoint(clientX, yBottom, minI);
491
+ }
492
+ }
480
493
 
481
494
 
482
- minI = maxI = undefined;
483
- lastClientX = clientX;
495
+ minI = maxI = undefined;
496
+ lastClientX = clientX;
497
+ }
498
+ } else {
499
+ plotY = Math.round(yAxis.toPixels(y, true));
500
+ drawPoint(clientX, plotY, yBottom);
501
+ addKDPoint(clientX, plotY, i);
484
502
  }
485
- } else {
486
- plotY = Math.round(yAxis.toPixels(y, true));
487
- drawPoint(clientX, plotY, yBottom);
488
- addKDPoint(clientX, plotY, i);
503
+ }
504
+ wasNull = isNull && !connectNulls;
505
+
506
+ if (i % CHUNK_SIZE === 0) {
507
+ series.canvasToSVG();
489
508
  }
490
509
  }
491
- wasNull = isNull && !connectNulls;
492
510
 
493
- if (i % CHUNK_SIZE === 0) {
494
- series.canvasToSVG();
511
+ return !chartDestroyed;
512
+ }, function() {
513
+ var loadingDiv = chart.loadingDiv,
514
+ loadingShown = chart.loadingShown;
515
+ stroke();
516
+ series.canvasToSVG();
517
+
518
+ fireEvent(series, 'renderedCanvas');
519
+
520
+ // Do not use chart.hideLoading, as it runs JS animation and will be blocked by buildKDTree.
521
+ // CSS animation looks good, but then it must be deleted in timeout. If we add the module to core,
522
+ // change hideLoading so we can skip this block.
523
+ if (loadingShown) {
524
+ extend(loadingDiv.style, {
525
+ transition: 'opacity 250ms',
526
+ opacity: 0
527
+ });
528
+ chart.loadingShown = false;
529
+ destroyLoadingDiv = setTimeout(function() {
530
+ if (loadingDiv.parentNode) { // In exporting it is falsy
531
+ loadingDiv.parentNode.removeChild(loadingDiv);
532
+ }
533
+ chart.loadingDiv = chart.loadingSpan = null;
534
+ }, 250);
495
535
  }
496
- }
497
536
 
498
- return !chartDestroyed;
499
- }, function () {
500
- var loadingDiv = chart.loadingDiv,
501
- loadingShown = chart.loadingShown;
502
- stroke();
503
- series.canvasToSVG();
504
-
505
- fireEvent(series, 'renderedCanvas');
506
-
507
- // Do not use chart.hideLoading, as it runs JS animation and will be blocked by buildKDTree.
508
- // CSS animation looks good, but then it must be deleted in timeout. If we add the module to core,
509
- // change hideLoading so we can skip this block.
510
- if (loadingShown) {
511
- extend(loadingDiv.style, {
512
- transition: 'opacity 250ms',
513
- opacity: 0
514
- });
515
- chart.loadingShown = false;
516
- destroyLoadingDiv = setTimeout(function () {
517
- if (loadingDiv.parentNode) { // In exporting it is falsy
518
- loadingDiv.parentNode.removeChild(loadingDiv);
519
- }
520
- chart.loadingDiv = chart.loadingSpan = null;
521
- }, 250);
537
+ // Pass tests in Pointer.
538
+ // Replace this with a single property, and replace when zooming in
539
+ // below boostThreshold.
540
+ series.directTouch = false;
541
+ series.options.stickyTracking = true;
542
+
543
+ delete series.buildKDTree; // Go back to prototype, ready to build
544
+ series.buildKDTree();
545
+
546
+ // Don't do async on export, the exportChart, getSVGForExport and getSVG methods are not chained for it.
547
+ }, chart.renderer.forExport ? Number.MAX_VALUE : undefined);
548
+ }
549
+ });
550
+
551
+ seriesTypes.scatter.prototype.cvsMarkerCircle = function(ctx, clientX, plotY, r) {
552
+ ctx.moveTo(clientX, plotY);
553
+ ctx.arc(clientX, plotY, r, 0, 2 * Math.PI, false);
554
+ };
555
+
556
+ // Rect is twice as fast as arc, should be used for small markers
557
+ seriesTypes.scatter.prototype.cvsMarkerSquare = function(ctx, clientX, plotY, r) {
558
+ ctx.rect(clientX - r, plotY - r, r * 2, r * 2);
559
+ };
560
+ seriesTypes.scatter.prototype.fill = true;
561
+
562
+ extend(seriesTypes.area.prototype, {
563
+ cvsDrawPoint: function(ctx, clientX, plotY, yBottom, lastPoint) {
564
+ if (lastPoint && clientX !== lastPoint.clientX) {
565
+ ctx.moveTo(lastPoint.clientX, lastPoint.yBottom);
566
+ ctx.lineTo(lastPoint.clientX, lastPoint.plotY);
567
+ ctx.lineTo(clientX, plotY);
568
+ ctx.lineTo(clientX, yBottom);
522
569
  }
570
+ },
571
+ fill: true,
572
+ fillOpacity: true,
573
+ sampling: true
574
+ });
575
+
576
+ extend(seriesTypes.column.prototype, {
577
+ cvsDrawPoint: function(ctx, clientX, plotY, yBottom) {
578
+ ctx.rect(clientX - 1, plotY, 1, yBottom - plotY);
579
+ },
580
+ fill: true,
581
+ sampling: true
582
+ });
523
583
 
524
- // Pass tests in Pointer.
525
- // Replace this with a single property, and replace when zooming in
526
- // below boostThreshold.
527
- series.directTouch = false;
528
- series.options.stickyTracking = true;
584
+ /**
585
+ * Return a full Point object based on the index. The boost module uses stripped point objects
586
+ * for performance reasons.
587
+ * @param {Number} boostPoint A stripped-down point object
588
+ * @returns {Object} A Point object as per http://api.highcharts.com/highcharts#Point
589
+ */
590
+ Series.prototype.getPoint = function(boostPoint) {
591
+ var point = boostPoint;
529
592
 
530
- delete series.buildKDTree; // Go back to prototype, ready to build
531
- series.buildKDTree();
593
+ if (boostPoint && !(boostPoint instanceof this.pointClass)) {
594
+ point = (new this.pointClass()).init(this, this.options.data[boostPoint.i]); // eslint-disable-line new-cap
595
+ point.category = point.x;
532
596
 
533
- // Don't do async on export, the exportChart, getSVGForExport and getSVG methods are not chained for it.
534
- }, chart.renderer.forExport ? Number.MAX_VALUE : undefined);
535
- }
536
- });
537
-
538
- seriesTypes.scatter.prototype.cvsMarkerCircle = function (ctx, clientX, plotY, r) {
539
- ctx.moveTo(clientX, plotY);
540
- ctx.arc(clientX, plotY, r, 0, 2 * Math.PI, false);
541
- };
542
-
543
- // Rect is twice as fast as arc, should be used for small markers
544
- seriesTypes.scatter.prototype.cvsMarkerSquare = function (ctx, clientX, plotY, r) {
545
- ctx.rect(clientX - r, plotY - r, r * 2, r * 2);
546
- };
547
- seriesTypes.scatter.prototype.fill = true;
548
-
549
- extend(seriesTypes.area.prototype, {
550
- cvsDrawPoint: function (ctx, clientX, plotY, yBottom, lastPoint) {
551
- if (lastPoint && clientX !== lastPoint.clientX) {
552
- ctx.moveTo(lastPoint.clientX, lastPoint.yBottom);
553
- ctx.lineTo(lastPoint.clientX, lastPoint.plotY);
554
- ctx.lineTo(clientX, plotY);
555
- ctx.lineTo(clientX, yBottom);
597
+ point.dist = boostPoint.dist;
598
+ point.distX = boostPoint.distX;
599
+ point.plotX = boostPoint.plotX;
600
+ point.plotY = boostPoint.plotY;
556
601
  }
557
- },
558
- fill: true,
559
- fillOpacity: true,
560
- sampling: true
561
- });
562
-
563
- extend(seriesTypes.column.prototype, {
564
- cvsDrawPoint: function (ctx, clientX, plotY, yBottom) {
565
- ctx.rect(clientX - 1, plotY, 1, yBottom - plotY);
566
- },
567
- fill: true,
568
- sampling: true
569
- });
570
-
571
- /**
572
- * Return a full Point object based on the index. The boost module uses stripped point objects
573
- * for performance reasons.
574
- * @param {Number} boostPoint A stripped-down point object
575
- * @returns {Object} A Point object as per http://api.highcharts.com/highcharts#Point
576
- */
577
- Series.prototype.getPoint = function (boostPoint) {
578
- var point = boostPoint;
579
-
580
- if (boostPoint && !(boostPoint instanceof this.pointClass)) {
581
- point = (new this.pointClass()).init(this, this.options.data[boostPoint.i]);
582
- point.category = point.x;
583
-
584
- point.dist = boostPoint.dist;
585
- point.distX = boostPoint.distX;
586
- point.plotX = boostPoint.plotX;
587
- point.plotY = boostPoint.plotY;
588
- }
589
602
 
590
- return point;
591
- };
592
-
593
- /**
594
- * Extend series.destroy to also remove the fake k-d-tree points (#5137). Normally
595
- * this is handled by Series.destroy that calls Point.destroy, but the fake
596
- * search points are not registered like that.
597
- */
598
- wrap(Series.prototype, 'destroy', function (proceed) {
599
- var series = this,
600
- chart = series.chart;
601
- if (chart.hoverPoints) {
602
- chart.hoverPoints = grep(chart.hoverPoints, function (point) {
603
- return point.series === series;
604
- });
605
- }
603
+ return point;
604
+ };
606
605
 
607
- if (chart.hoverPoint && chart.hoverPoint.series === series) {
608
- chart.hoverPoint = null;
609
- }
610
- proceed.call(this);
611
- });
612
-
613
- /**
614
- * Return a point instance from the k-d-tree
615
- */
616
- wrap(Series.prototype, 'searchPoint', function (proceed) {
617
- return this.getPoint(
618
- proceed.apply(this, [].slice.call(arguments, 1))
619
- );
620
- });
606
+ /**
607
+ * Extend series.destroy to also remove the fake k-d-tree points (#5137). Normally
608
+ * this is handled by Series.destroy that calls Point.destroy, but the fake
609
+ * search points are not registered like that.
610
+ */
611
+ wrap(Series.prototype, 'destroy', function(proceed) {
612
+ var series = this,
613
+ chart = series.chart;
614
+ if (chart.hoverPoints) {
615
+ chart.hoverPoints = grep(chart.hoverPoints, function(point) {
616
+ return point.series === series;
617
+ });
618
+ }
619
+
620
+ if (chart.hoverPoint && chart.hoverPoint.series === series) {
621
+ chart.hoverPoint = null;
622
+ }
623
+ proceed.call(this);
624
+ });
625
+
626
+ /**
627
+ * Return a point instance from the k-d-tree
628
+ */
629
+ wrap(Series.prototype, 'searchPoint', function(proceed) {
630
+ return this.getPoint(
631
+ proceed.apply(this, [].slice.call(arguments, 1))
632
+ );
633
+ });
634
+
635
+ }(Highcharts));
621
636
  }));