highcharts-rails 4.2.7 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }));