highcharts-rails 4.1.7 → 4.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.1.7 (2015-06-26)
2
+ * @license Highcharts JS v4.1.8 (2015-08-20)
3
3
  *
4
4
  * Standalone Highcharts Framework
5
5
  *
@@ -405,7 +405,8 @@ return {
405
405
  end,
406
406
  fx,
407
407
  args,
408
- name;
408
+ name,
409
+ PX = 'px';
409
410
 
410
411
  el.stopAnimation = false; // ready for new
411
412
 
@@ -441,13 +442,16 @@ return {
441
442
  } else {
442
443
  start = parseFloat(HighchartsAdapter._getStyle(el, name)) || 0;
443
444
  if (name !== 'opacity') {
444
- unit = 'px';
445
+ unit = PX;
445
446
  }
446
447
  }
447
448
 
448
449
  if (!end) {
449
450
  end = prop[name];
450
451
  }
452
+ if (end.match && end.match(PX)) {
453
+ end = end.replace(/px/g, ''); // #4351
454
+ }
451
455
  fx.custom(start, end, unit);
452
456
  }
453
457
  };
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v4.1.7 (2015-06-26)
5
+ * @license Highcharts JS v4.1.8 (2015-08-20)
6
6
  *
7
7
  * (c) 2009-2013 Torstein Hønsi
8
8
  *
@@ -380,6 +380,18 @@ Highcharts.SVGRenderer.prototype.arc3d = function (shapeArgs) {
380
380
  _args: args
381
381
  }, {
382
382
  duration: duration,
383
+ start: function () {
384
+ var args = arguments,
385
+ fx = args[0],
386
+ elem = fx.elem,
387
+ end = elem._shapeArgs;
388
+
389
+ if (end.fill !== elem.color) {
390
+ elem.attr({
391
+ fill: end.fill
392
+ });
393
+ }
394
+ },
383
395
  step: function () {
384
396
  var args = arguments,
385
397
  fx = args[1],
@@ -647,15 +659,16 @@ Highcharts.Chart.prototype.retrieveStacks = function (stacking) {
647
659
  /***
648
660
  EXTENSION TO THE AXIS
649
661
  ***/
650
- Highcharts.wrap(Highcharts.Axis.prototype, 'init', function (proceed) {
651
- var args = arguments;
652
- if (args[1].is3d()) {
653
- args[2].tickWidth = Highcharts.pick(args[2].tickWidth, 0);
654
- args[2].gridLineWidth = Highcharts.pick(args[2].gridLineWidth, 1);
662
+ Highcharts.wrap(Highcharts.Axis.prototype, 'setOptions', function (proceed, userOptions) {
663
+ var options;
664
+ proceed.call(this, userOptions);
665
+ if (this.chart.is3d()) {
666
+ options = this.options;
667
+ options.tickWidth = Highcharts.pick(options.tickWidth, 0);
668
+ options.gridLineWidth = Highcharts.pick(options.gridLineWidth, 1);
655
669
  }
670
+ });
656
671
 
657
- proceed.apply(this, [].slice.call(arguments, 1));
658
- });
659
672
  Highcharts.wrap(Highcharts.Axis.prototype, 'render', function (proceed) {
660
673
  proceed.apply(this, [].slice.call(arguments, 1));
661
674
 
@@ -774,9 +787,9 @@ Highcharts.wrap(Highcharts.Axis.prototype, 'getPlotLinePath', function (proceed)
774
787
  return path;
775
788
  });
776
789
 
777
- Highcharts.wrap(Highcharts.Axis.prototype, 'getLinePath', function () {
778
- // do not draw axislines in 3D ?
779
- return [];
790
+ // Do not draw axislines in 3D
791
+ Highcharts.wrap(Highcharts.Axis.prototype, 'getLinePath', function (proceed) {
792
+ return this.chart.is3d() ? [] : proceed.apply(this, [].slice.call(arguments, 1));
780
793
  });
781
794
 
782
795
  Highcharts.wrap(Highcharts.Axis.prototype, 'getPlotBandPath', function (proceed) {
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v4.1.7 (2015-06-26)
5
+ * @license Highcharts JS v4.1.8 (2015-08-20)
6
6
  *
7
7
  * (c) 2009-2014 Torstein Honsi
8
8
  *
@@ -686,6 +686,7 @@ defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
686
686
  seriesTypes.arearange = extendClass(seriesTypes.area, {
687
687
  type: 'arearange',
688
688
  pointArrayMap: ['low', 'high'],
689
+ dataLabelCollections: ['dataLabel', 'dataLabelUpper'],
689
690
  toYData: function (point) {
690
691
  return [point.low, point.high];
691
692
  },
@@ -1932,18 +1933,23 @@ seriesTypes.bubble = extendClass(seriesTypes.scatter, {
1932
1933
  zData = this.zData,
1933
1934
  radii = [],
1934
1935
  sizeByArea = this.options.sizeBy !== 'width',
1935
- zRange;
1936
-
1936
+ zRange = zMax - zMin;
1937
+
1937
1938
  // Set the shape type and arguments to be picked up in drawPoints
1938
1939
  for (i = 0, len = zData.length; i < len; i++) {
1939
- zRange = zMax - zMin;
1940
- pos = zRange > 0 ? // relative size, a number between 0 and 1
1941
- (zData[i] - zMin) / (zMax - zMin) :
1942
- 0.5;
1943
- if (sizeByArea && pos >= 0) {
1944
- pos = Math.sqrt(pos);
1940
+ // Issue #4419 - if value is less than zMin, push a radius that's always smaller than the minimum size
1941
+ if (zData[i] < zMin) {
1942
+ radii.push(minSize / 2 - 1);
1943
+ } else {
1944
+ // Relative size, a number between 0 and 1
1945
+ pos = zRange > 0 ? (zData[i] - zMin) / zRange : 0.5;
1946
+
1947
+ if (sizeByArea && pos >= 0) {
1948
+ pos = Math.sqrt(pos);
1949
+ }
1950
+
1951
+ radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
1945
1952
  }
1946
- radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
1947
1953
  }
1948
1954
  this.radii = radii;
1949
1955
  },
@@ -2093,6 +2099,7 @@ Axis.prototype.beforePadding = function () {
2093
2099
 
2094
2100
  });
2095
2101
  series.minPxSize = extremes.minSize;
2102
+ series.maxPxSize = extremes.maxSize;
2096
2103
 
2097
2104
  // Find the min and max Z
2098
2105
  zData = series.zData;
@@ -2117,7 +2124,7 @@ Axis.prototype.beforePadding = function () {
2117
2124
  radius;
2118
2125
 
2119
2126
  if (isXAxis) {
2120
- series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
2127
+ series.getRadii(zMin, zMax, series.minPxSize, series.maxPxSize);
2121
2128
  }
2122
2129
 
2123
2130
  if (range > 0) {
@@ -2131,11 +2138,15 @@ Axis.prototype.beforePadding = function () {
2131
2138
  }
2132
2139
  });
2133
2140
 
2134
- if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
2141
+
2142
+ if (activeSeries.length && range > 0 && !this.isLog) {
2135
2143
  pxMax -= axisLength;
2136
2144
  transA *= (axisLength + pxMin - pxMax) / axisLength;
2137
- this.min += pxMin / transA;
2138
- this.max += pxMax / transA;
2145
+ each([['min', 'userMin', pxMin], ['max', 'userMax', pxMax]], function (keys) {
2146
+ if (pick(axis.options[keys[0]], axis[keys[1]]) === UNDEFINED) {
2147
+ axis[keys[0]] += keys[2] / transA;
2148
+ }
2149
+ });
2139
2150
  }
2140
2151
  };
2141
2152
 
@@ -0,0 +1,554 @@
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).
6
+ *
7
+ * Author: Torstein Honsi
8
+ *
9
+ *
10
+ * Development plan
11
+ * - Column range.
12
+ * - Heatmap.
13
+ * - Treemap.
14
+ * - Check how it works with Highstock and data grouping.
15
+ * - Check inverted charts.
16
+ * - Check reversed axes.
17
+ * - Chart callback should be async after last series is drawn. (But not necessarily, we don't do
18
+ that with initial series animation).
19
+ * - Cache full-size image so we don't have to redraw on hide/show and zoom up. But k-d-tree still
20
+ * needs to be built.
21
+ * - Test IE9 and IE10.
22
+ * - Stacking is not perhaps not correct since it doesn't use the translation given in
23
+ * the translate method. If this gets to complicated, a possible way out would be to
24
+ * have a simplified renderCanvas method that simply draws the areaPath on a canvas.
25
+ *
26
+ * If this module is taken in as part of the core
27
+ * - All the loading logic should be merged with core. Update styles in the core.
28
+ * - Most of the method wraps should probably be added directly in parent methods.
29
+ *
30
+ * Notes for boost mode
31
+ * - Area lines are not drawn
32
+ * - Point markers are not drawn
33
+ * - Zones and negativeColor don't work
34
+ * - Columns are always one pixel wide. Don't set the threshold too low.
35
+ *
36
+ * Optimizing tips for users
37
+ * - For scatter plots, use a marker.radius of 1 or less. It results in a rectangle being drawn, which is
38
+ * considerably faster than a circle.
39
+ * - Set extremes (min, max) explicitly on the axes in order for Highcharts to avoid computing extremes.
40
+ * - Set enableMouseTracking to false on the series to improve total rendering time.
41
+ * - The default threshold is set based on one series. If you have multiple, dense series, the combined
42
+ * number of points drawn gets higher, and you may want to set the threshold lower in order to
43
+ * use optimizations.
44
+ */
45
+ /*global document, Highcharts, HighchartsAdapter, setTimeout */
46
+ (function (H, HA) {
47
+
48
+ 'use strict';
49
+
50
+ var noop = function () { return undefined; },
51
+ Color = H.Color,
52
+ Series = H.Series,
53
+ seriesTypes = H.seriesTypes,
54
+ each = H.each,
55
+ extend = H.extend,
56
+ addEvent = HA.addEvent,
57
+ fireEvent = HA.fireEvent,
58
+ merge = H.merge,
59
+ pick = H.pick,
60
+ wrap = H.wrap,
61
+ plotOptions = H.getOptions().plotOptions,
62
+ CHUNK_SIZE = 50000;
63
+
64
+ function eachAsync(arr, fn, callback, chunkSize, i) {
65
+ i = i || 0;
66
+ chunkSize = chunkSize || CHUNK_SIZE;
67
+ each(arr.slice(i, i + chunkSize), fn);
68
+ if (i + chunkSize < arr.length) {
69
+ setTimeout(function () {
70
+ eachAsync(arr, fn, callback, chunkSize, i + chunkSize);
71
+ });
72
+ } else if (callback) {
73
+ callback();
74
+ }
75
+ }
76
+
77
+ // Set default options
78
+ each(['area', 'arearange', 'column', 'line', 'scatter'], function (type) {
79
+ if (plotOptions[type]) {
80
+ plotOptions[type].boostThreshold = 5000;
81
+ }
82
+ });
83
+
84
+ /**
85
+ * Override a bunch of methods the same way. If the number of points is below the threshold,
86
+ * run the original method. If not, check for a canvas version or do nothing.
87
+ */
88
+ each(['translate', 'generatePoints', 'drawTracker', 'drawPoints', 'render'], function (method) {
89
+ function branch(proceed) {
90
+ var letItPass = this.options.stacking && (method === 'translate' || method === 'generatePoints');
91
+ if ((this.processedXData || this.options.data).length < (this.options.boostThreshold || Number.MAX_VALUE) ||
92
+ letItPass) {
93
+
94
+ // Clear image
95
+ if (method === 'render' && this.image) {
96
+ this.image.attr({ href: '' });
97
+ this.animate = null; // We're zooming in, don't run animation
98
+ }
99
+
100
+ proceed.call(this);
101
+
102
+ // If a canvas version of the method exists, like renderCanvas(), run
103
+ } else if (this[method + 'Canvas']) {
104
+
105
+ this[method + 'Canvas']();
106
+ }
107
+ }
108
+ wrap(Series.prototype, method, branch);
109
+
110
+ // A special case for some types - its translate method is already wrapped
111
+ if (method === 'translate') {
112
+ if (seriesTypes.column) {
113
+ wrap(seriesTypes.column.prototype, method, branch);
114
+ }
115
+ if (seriesTypes.arearange) {
116
+ wrap(seriesTypes.arearange.prototype, method, branch);
117
+ }
118
+ }
119
+ });
120
+
121
+ /**
122
+ * Do not compute extremes when min and max are set.
123
+ * If we use this in the core, we can add the hook to hasExtremes to the methods directly.
124
+ */
125
+ wrap(Series.prototype, 'getExtremes', function (proceed) {
126
+ if (!this.hasExtremes()) {
127
+ proceed.apply(this, Array.prototype.slice.call(arguments, 1));
128
+ }
129
+ });
130
+ wrap(Series.prototype, 'setData', function (proceed) {
131
+ if (!this.hasExtremes(true)) {
132
+ proceed.apply(this, Array.prototype.slice.call(arguments, 1));
133
+ }
134
+ });
135
+ wrap(Series.prototype, 'processData', function (proceed) {
136
+ if (!this.hasExtremes(true)) {
137
+ proceed.apply(this, Array.prototype.slice.call(arguments, 1));
138
+ }
139
+ });
140
+
141
+
142
+ H.extend(Series.prototype, {
143
+ pointRange: 0,
144
+
145
+ hasExtremes: function (checkX) {
146
+ var options = this.options,
147
+ data = options.data,
148
+ xAxis = this.xAxis.options,
149
+ yAxis = this.yAxis.options;
150
+ return data.length > (options.boostThreshold || Number.MAX_VALUE) && typeof yAxis.min === 'number' && typeof yAxis.max === 'number' &&
151
+ (!checkX || (typeof xAxis.min === 'number' && typeof xAxis.max === 'number'));
152
+ },
153
+
154
+ /**
155
+ * If implemented in the core, parts of this can probably be shared with other similar
156
+ * methods in Highcharts.
157
+ */
158
+ destroyGraphics: function () {
159
+ var series = this,
160
+ points = this.points,
161
+ point,
162
+ i;
163
+
164
+ for (i = 0; i < points.length; i = i + 1) {
165
+ point = points[i];
166
+ if (point && point.graphic) {
167
+ point.graphic = point.graphic.destroy();
168
+ }
169
+ }
170
+
171
+ each(['graph', 'area'], function (prop) {
172
+ if (series[prop]) {
173
+ series[prop] = series[prop].destroy();
174
+ }
175
+ });
176
+ },
177
+
178
+ /**
179
+ * Create a hidden canvas to draw the graph on. The contents is later copied over
180
+ * to an SVG image element.
181
+ */
182
+ getContext: function () {
183
+ var width = this.chart.plotWidth,
184
+ height = this.chart.plotHeight;
185
+
186
+ if (!this.canvas) {
187
+ this.canvas = document.createElement('canvas');
188
+ this.image = this.chart.renderer.image('', 0, 0, width, height).add(this.group);
189
+ this.ctx = this.canvas.getContext('2d');
190
+ } else {
191
+ this.ctx.clearRect(0, 0, width, height);
192
+ }
193
+
194
+ this.canvas.setAttribute('width', width);
195
+ this.canvas.setAttribute('height', height);
196
+ this.image.attr({
197
+ width: width,
198
+ height: height
199
+ });
200
+
201
+ return this.ctx;
202
+ },
203
+
204
+ /**
205
+ * Draw the canvas image inside an SVG image
206
+ */
207
+ canvasToSVG: function () {
208
+ this.image.attr({ href: this.canvas.toDataURL('image/png') });
209
+ },
210
+
211
+ cvsLineTo: function (ctx, clientX, plotY) {
212
+ ctx.lineTo(clientX, plotY);
213
+ },
214
+
215
+ renderCanvas: function () {
216
+ var series = this,
217
+ options = series.options,
218
+ chart = series.chart,
219
+ xAxis = this.xAxis,
220
+ yAxis = this.yAxis,
221
+ ctx,
222
+ i,
223
+ c = 0,
224
+ xData = series.processedXData,
225
+ yData = series.processedYData,
226
+ rawData = options.data,
227
+ xExtremes = xAxis.getExtremes(),
228
+ xMin = xExtremes.min,
229
+ xMax = xExtremes.max,
230
+ yExtremes = yAxis.getExtremes(),
231
+ yMin = yExtremes.min,
232
+ yMax = yExtremes.max,
233
+ pointTaken = {},
234
+ lastClientX,
235
+ sampling = !!series.sampling,
236
+ points,
237
+ r = options.marker && options.marker.radius,
238
+ cvsDrawPoint = this.cvsDrawPoint,
239
+ cvsLineTo = options.lineWidth ? this.cvsLineTo : false,
240
+ cvsMarker = r <= 1 ? this.cvsMarkerSquare : this.cvsMarkerCircle,
241
+ enableMouseTracking = options.enableMouseTracking !== false,
242
+ lastPoint,
243
+ threshold = options.threshold,
244
+ yBottom = yAxis.getThreshold(threshold),
245
+ hasThreshold = typeof threshold === 'number',
246
+ translatedThreshold = yBottom,
247
+ doFill = this.fill,
248
+ isRange = series.pointArrayMap && series.pointArrayMap.join(',') === 'low,high',
249
+ isStacked = !!options.stacking,
250
+ cropStart = series.cropStart || 0,
251
+ loadingOptions = chart.options.loading,
252
+ requireSorting = series.requireSorting,
253
+ wasNull,
254
+ connectNulls = options.connectNulls,
255
+ useRaw = !xData,
256
+ minVal,
257
+ maxVal,
258
+ minI,
259
+ maxI,
260
+ fillColor = series.fillOpacity ?
261
+ new Color(series.color).setOpacity(pick(options.fillOpacity, 0.75)).get() :
262
+ series.color,
263
+ stroke = function () {
264
+ if (doFill) {
265
+ ctx.fillStyle = fillColor;
266
+ ctx.fill();
267
+ } else {
268
+ ctx.strokeStyle = series.color;
269
+ ctx.lineWidth = options.lineWidth;
270
+ ctx.stroke();
271
+ }
272
+ },
273
+ drawPoint = function (clientX, plotY, yBottom) {
274
+ if (c === 0) {
275
+ ctx.beginPath();
276
+ }
277
+
278
+ if (wasNull) {
279
+ ctx.moveTo(clientX, plotY);
280
+ } else {
281
+ if (cvsDrawPoint) {
282
+ cvsDrawPoint(ctx, clientX, plotY, yBottom, lastPoint);
283
+ } else if (cvsLineTo) {
284
+ cvsLineTo(ctx, clientX, plotY);
285
+ } else if (cvsMarker) {
286
+ cvsMarker(ctx, clientX, plotY, r);
287
+ }
288
+ }
289
+
290
+ // We need to stroke the line for every 1000 pixels. It will crash the browser
291
+ // memory use if we stroke too infrequently.
292
+ c = c + 1;
293
+ if (c === 1000) {
294
+ stroke();
295
+ c = 0;
296
+ }
297
+
298
+ // Area charts need to keep track of the last point
299
+ lastPoint = {
300
+ clientX: clientX,
301
+ plotY: plotY,
302
+ yBottom: yBottom
303
+ };
304
+ },
305
+
306
+ addKDPoint = function (clientX, plotY, i) {
307
+
308
+ // The k-d tree requires series points. Reduce the amount of points, since the time to build the
309
+ // tree increases exponentially.
310
+ if (enableMouseTracking && !pointTaken[clientX + ',' + plotY]) {
311
+ points.push({
312
+ clientX: clientX,
313
+ plotX: clientX,
314
+ plotY: plotY,
315
+ i: cropStart + i
316
+ });
317
+ pointTaken[clientX + ',' + plotY] = true;
318
+ }
319
+ };
320
+
321
+ // If we are zooming out from SVG mode, destroy the graphics
322
+ if (this.points) {
323
+ this.destroyGraphics();
324
+ }
325
+
326
+ // The group
327
+ series.plotGroup(
328
+ 'group',
329
+ 'series',
330
+ series.visible ? 'visible' : 'hidden',
331
+ options.zIndex,
332
+ chart.seriesGroup
333
+ );
334
+
335
+ series.getAttribs();
336
+ series.markerGroup = series.group;
337
+ addEvent(series, 'destroy', function () {
338
+ series.markerGroup = null;
339
+ });
340
+
341
+ points = this.points = [];
342
+ ctx = this.getContext();
343
+ series.buildKDTree = noop; // Do not start building while drawing
344
+
345
+ // Display a loading indicator
346
+ if (rawData.length > 99999) {
347
+ chart.options.loading = merge(loadingOptions, {
348
+ labelStyle: {
349
+ backgroundColor: 'rgba(255,255,255,0.75)',
350
+ padding: '1em',
351
+ borderRadius: '0.5em'
352
+ },
353
+ style: {
354
+ backgroundColor: 'none',
355
+ opacity: 1
356
+ }
357
+ });
358
+ chart.showLoading('Drawing...');
359
+ chart.options.loading = loadingOptions; // reset
360
+ if (chart.loadingShown === true) {
361
+ chart.loadingShown = 1;
362
+ } else {
363
+ chart.loadingShown = chart.loadingShown + 1;
364
+ }
365
+ }
366
+
367
+ // Loop over the points
368
+ i = 0;
369
+ eachAsync(isStacked ? series.data : (xData || rawData), function (d) {
370
+
371
+ var x,
372
+ y,
373
+ clientX,
374
+ plotY,
375
+ isNull,
376
+ low,
377
+ isYInside = true;
378
+
379
+ if (useRaw) {
380
+ x = d[0];
381
+ y = d[1];
382
+ } else {
383
+ x = d;
384
+ y = yData[i];
385
+ }
386
+
387
+ // Resolve low and high for range series
388
+ if (isRange) {
389
+ if (useRaw) {
390
+ y = d.slice(1, 3);
391
+ }
392
+ low = y[0];
393
+ y = y[1];
394
+ } else if (isStacked) {
395
+ x = d.x;
396
+ y = d.stackY;
397
+ low = y - d.y;
398
+ }
399
+
400
+ isNull = y === null;
401
+
402
+ // Optimize for scatter zooming
403
+ if (!requireSorting) {
404
+ isYInside = y >= yMin && y <= yMax;
405
+ }
406
+
407
+ if (!isNull && x >= xMin && x <= xMax && isYInside) {
408
+
409
+ clientX = Math.round(xAxis.toPixels(x, true));
410
+
411
+ if (sampling) {
412
+ if (minI === undefined || clientX === lastClientX) {
413
+ if (!isRange) {
414
+ low = y;
415
+ }
416
+ if (maxI === undefined || y > maxVal) {
417
+ maxVal = y;
418
+ maxI = i;
419
+ }
420
+ if (minI === undefined || low < minVal) {
421
+ minVal = low;
422
+ minI = i;
423
+ }
424
+
425
+ }
426
+ if (clientX !== lastClientX) { // Add points and reset
427
+ if (minI !== undefined) { // then maxI is also a number
428
+ plotY = yAxis.toPixels(maxVal, true);
429
+ yBottom = yAxis.toPixels(minVal, true);
430
+ drawPoint(
431
+ clientX,
432
+ hasThreshold ? Math.min(plotY, translatedThreshold) : plotY,
433
+ hasThreshold ? Math.max(yBottom, translatedThreshold) : yBottom
434
+ );
435
+ addKDPoint(clientX, plotY, maxI);
436
+ if (yBottom !== plotY) {
437
+ addKDPoint(clientX, yBottom, minI);
438
+ }
439
+ }
440
+
441
+
442
+ minI = maxI = undefined;
443
+ lastClientX = clientX;
444
+ }
445
+ } else {
446
+ plotY = Math.round(yAxis.toPixels(y, true));
447
+ drawPoint(clientX, plotY, yBottom);
448
+ addKDPoint(clientX, plotY, i);
449
+ }
450
+ }
451
+ wasNull = isNull && !connectNulls;
452
+
453
+ i = i + 1;
454
+
455
+ if (i % CHUNK_SIZE === 0) {
456
+ series.canvasToSVG();
457
+ }
458
+
459
+ }, function () {
460
+
461
+ var loadingDiv = chart.loadingDiv,
462
+ loadingShown = +chart.loadingShown;
463
+
464
+ stroke();
465
+ series.canvasToSVG();
466
+
467
+ fireEvent(series, 'renderedCanvas');
468
+
469
+ // Do not use chart.hideLoading, as it runs JS animation and will be blocked by buildKDTree.
470
+ // CSS animation looks good, but then it must be deleted in timeout. If we add the module to core,
471
+ // change hideLoading so we can skip this block.
472
+ if (loadingShown === 1) {
473
+ extend(loadingDiv.style, {
474
+ transition: 'opacity 250ms',
475
+ opacity: 0
476
+ });
477
+
478
+ chart.loadingShown = false;
479
+ setTimeout(function () {
480
+ if (loadingDiv.parentNode) { // In exporting it is falsy
481
+ loadingDiv.parentNode.removeChild(loadingDiv);
482
+ }
483
+ chart.loadingDiv = chart.loadingSpan = null;
484
+ }, 250);
485
+ }
486
+ if (loadingShown) {
487
+ chart.loadingShown = loadingShown - 1;
488
+ }
489
+
490
+ // Pass tests in Pointer.
491
+ // TODO: Replace this with a single property, and replace when zooming in
492
+ // below boostThreshold.
493
+ series.directTouch = false;
494
+ series.options.stickyTracking = true;
495
+
496
+ delete series.buildKDTree; // Go back to prototype, ready to build
497
+ series.buildKDTree();
498
+
499
+ // Don't do async on export, the exportChart, getSVGForExport and getSVG methods are not chained for it.
500
+ }, chart.renderer.forExport ? Number.MAX_VALUE : undefined);
501
+ }
502
+ });
503
+
504
+ seriesTypes.scatter.prototype.cvsMarkerCircle = function (ctx, clientX, plotY, r) {
505
+ ctx.moveTo(clientX, plotY);
506
+ ctx.arc(clientX, plotY, r, 0, 2 * Math.PI, false);
507
+ };
508
+
509
+ // Rect is twice as fast as arc, should be used for small markers
510
+ seriesTypes.scatter.prototype.cvsMarkerSquare = function (ctx, clientX, plotY, r) {
511
+ ctx.moveTo(clientX, plotY);
512
+ ctx.rect(clientX - r, plotY - r, r * 2, r * 2);
513
+ };
514
+ seriesTypes.scatter.prototype.fill = true;
515
+
516
+ extend(seriesTypes.area.prototype, {
517
+ cvsDrawPoint: function (ctx, clientX, plotY, yBottom, lastPoint) {
518
+ if (lastPoint && clientX !== lastPoint.clientX) {
519
+ ctx.moveTo(lastPoint.clientX, lastPoint.yBottom);
520
+ ctx.lineTo(lastPoint.clientX, lastPoint.plotY);
521
+ ctx.lineTo(clientX, plotY);
522
+ ctx.lineTo(clientX, yBottom);
523
+ }
524
+ },
525
+ fill: true,
526
+ fillOpacity: true,
527
+ sampling: true
528
+ });
529
+
530
+ extend(seriesTypes.column.prototype, {
531
+ cvsDrawPoint: function (ctx, clientX, plotY, yBottom) {
532
+ ctx.rect(clientX - 1, plotY, 1, yBottom - plotY);
533
+ },
534
+ fill: true,
535
+ sampling: true
536
+ });
537
+
538
+ /**
539
+ * Return a point instance from the k-d-tree
540
+ */
541
+ wrap(Series.prototype, 'searchPoint', function (proceed, e) {
542
+ var point = proceed.call(this, e),
543
+ ret = point;
544
+
545
+ if (point && !(point instanceof this.pointClass)) {
546
+ ret = (new this.pointClass()).init(this, this.options.data[point.i]);
547
+ ret.dist = point.dist;
548
+ ret.category = ret.x;
549
+ ret.plotX = point.plotX;
550
+ ret.plotY = point.plotY;
551
+ }
552
+ return ret;
553
+ });
554
+ }(Highcharts, HighchartsAdapter));