highcharts-rails 4.1.7 → 4.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,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));