highcharts_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +106 -0
  9. data/Rakefile +6 -0
  10. data/highcharts_rails.gemspec +27 -0
  11. data/lib/highcharts_rails/version.rb +3 -0
  12. data/lib/highcharts_rails.rb +8 -0
  13. data/vendor/assets/javascripts/highcharts-3d.src.js +2139 -0
  14. data/vendor/assets/javascripts/highcharts-more.src.js +2982 -0
  15. data/vendor/assets/javascripts/highcharts.src.js +22947 -0
  16. data/vendor/assets/javascripts/js/highcharts-3d.src.js +2085 -0
  17. data/vendor/assets/javascripts/js/highcharts-more.src.js +2820 -0
  18. data/vendor/assets/javascripts/js/highcharts.src.js +20917 -0
  19. data/vendor/assets/javascripts/js/modules/accessibility.src.js +1072 -0
  20. data/vendor/assets/javascripts/js/modules/annotations.src.js +408 -0
  21. data/vendor/assets/javascripts/js/modules/boost.src.js +652 -0
  22. data/vendor/assets/javascripts/js/modules/broken-axis.src.js +338 -0
  23. data/vendor/assets/javascripts/js/modules/data.src.js +981 -0
  24. data/vendor/assets/javascripts/js/modules/drilldown.src.js +756 -0
  25. data/vendor/assets/javascripts/js/modules/exporting.src.js +953 -0
  26. data/vendor/assets/javascripts/js/modules/funnel.src.js +290 -0
  27. data/vendor/assets/javascripts/js/modules/gantt.src.js +791 -0
  28. data/vendor/assets/javascripts/js/modules/grid-axis.src.js +545 -0
  29. data/vendor/assets/javascripts/js/modules/heatmap.src.js +798 -0
  30. data/vendor/assets/javascripts/js/modules/no-data-to-display.src.js +150 -0
  31. data/vendor/assets/javascripts/js/modules/offline-exporting.src.js +492 -0
  32. data/vendor/assets/javascripts/js/modules/overlapping-datalabels.src.js +164 -0
  33. data/vendor/assets/javascripts/js/modules/series-label.src.js +606 -0
  34. data/vendor/assets/javascripts/js/modules/solid-gauge.src.js +305 -0
  35. data/vendor/assets/javascripts/js/modules/treemap.src.js +881 -0
  36. data/vendor/assets/javascripts/js/modules/xrange-series.src.js +254 -0
  37. data/vendor/assets/javascripts/js/themes/dark-blue.js +317 -0
  38. data/vendor/assets/javascripts/js/themes/dark-green.js +314 -0
  39. data/vendor/assets/javascripts/js/themes/dark-unica.js +243 -0
  40. data/vendor/assets/javascripts/js/themes/gray.js +326 -0
  41. data/vendor/assets/javascripts/js/themes/grid-light.js +99 -0
  42. data/vendor/assets/javascripts/js/themes/grid.js +131 -0
  43. data/vendor/assets/javascripts/js/themes/sand-signika.js +129 -0
  44. data/vendor/assets/javascripts/js/themes/skies.js +112 -0
  45. data/vendor/assets/javascripts/lib/canvg.src.js +3073 -0
  46. data/vendor/assets/javascripts/lib/jspdf.src.js +3031 -0
  47. data/vendor/assets/javascripts/lib/rgbcolor.src.js +299 -0
  48. data/vendor/assets/javascripts/lib/svg2pdf.src.js +1451 -0
  49. data/vendor/assets/javascripts/modules/accessibility.src.js +1072 -0
  50. data/vendor/assets/javascripts/modules/annotations.src.js +408 -0
  51. data/vendor/assets/javascripts/modules/boost.src.js +652 -0
  52. data/vendor/assets/javascripts/modules/broken-axis.src.js +338 -0
  53. data/vendor/assets/javascripts/modules/data.src.js +981 -0
  54. data/vendor/assets/javascripts/modules/drilldown.src.js +797 -0
  55. data/vendor/assets/javascripts/modules/exporting.src.js +882 -0
  56. data/vendor/assets/javascripts/modules/funnel.src.js +304 -0
  57. data/vendor/assets/javascripts/modules/gantt.src.js +815 -0
  58. data/vendor/assets/javascripts/modules/grid-axis.src.js +547 -0
  59. data/vendor/assets/javascripts/modules/heatmap.src.js +810 -0
  60. data/vendor/assets/javascripts/modules/no-data-to-display.src.js +161 -0
  61. data/vendor/assets/javascripts/modules/offline-exporting.src.js +492 -0
  62. data/vendor/assets/javascripts/modules/overlapping-datalabels.src.js +164 -0
  63. data/vendor/assets/javascripts/modules/series-label.src.js +606 -0
  64. data/vendor/assets/javascripts/modules/solid-gauge.src.js +316 -0
  65. data/vendor/assets/javascripts/modules/treemap.src.js +935 -0
  66. data/vendor/assets/javascripts/modules/xrange-series.src.js +276 -0
  67. data/vendor/assets/javascripts/themes/dark-blue.js +317 -0
  68. data/vendor/assets/javascripts/themes/dark-green.js +314 -0
  69. data/vendor/assets/javascripts/themes/dark-unica.js +243 -0
  70. data/vendor/assets/javascripts/themes/gray.js +326 -0
  71. data/vendor/assets/javascripts/themes/grid-light.js +99 -0
  72. data/vendor/assets/javascripts/themes/grid.js +131 -0
  73. data/vendor/assets/javascripts/themes/sand-signika.js +129 -0
  74. data/vendor/assets/javascripts/themes/skies.js +112 -0
  75. data/vendor/assets/stylesheets/highcharts.scss +610 -0
  76. metadata +161 -0
@@ -0,0 +1,2982 @@
1
+ /**
2
+ * @license Highcharts JS v5.0.6 (2016-12-07)
3
+ *
4
+ * (c) 2009-2016 Torstein Honsi
5
+ *
6
+ * License: www.highcharts.com/license
7
+ */
8
+ (function(factory) {
9
+ if (typeof module === 'object' && module.exports) {
10
+ module.exports = factory;
11
+ } else {
12
+ factory(Highcharts);
13
+ }
14
+ }(function(Highcharts) {
15
+ (function(H) {
16
+ /**
17
+ * (c) 2010-2016 Torstein Honsi
18
+ *
19
+ * License: www.highcharts.com/license
20
+ */
21
+ 'use strict';
22
+ var each = H.each,
23
+ extend = H.extend,
24
+ merge = H.merge,
25
+ splat = H.splat;
26
+ /**
27
+ * The Pane object allows options that are common to a set of X and Y axes.
28
+ *
29
+ * In the future, this can be extended to basic Highcharts and Highstock.
30
+ */
31
+ function Pane(options, chart, firstAxis) {
32
+ this.init(options, chart, firstAxis);
33
+ }
34
+
35
+ // Extend the Pane prototype
36
+ extend(Pane.prototype, {
37
+
38
+ /**
39
+ * Initiate the Pane object
40
+ */
41
+ init: function(options, chart, firstAxis) {
42
+ var pane = this,
43
+ backgroundOption,
44
+ defaultOptions = pane.defaultOptions;
45
+
46
+ pane.chart = chart;
47
+
48
+ // Set options. Angular charts have a default background (#3318)
49
+ pane.options = options = merge(defaultOptions, chart.angular ? {
50
+ background: {}
51
+ } : undefined, options);
52
+
53
+ backgroundOption = options.background;
54
+
55
+ // To avoid having weighty logic to place, update and remove the backgrounds,
56
+ // push them to the first axis' plot bands and borrow the existing logic there.
57
+ if (backgroundOption) {
58
+ each([].concat(splat(backgroundOption)).reverse(), function(config) {
59
+ var mConfig,
60
+ axisUserOptions = firstAxis.userOptions;
61
+ mConfig = merge(pane.defaultBackgroundOptions, config);
62
+
63
+
64
+ if (config.backgroundColor) {
65
+ mConfig.backgroundColor = config.backgroundColor;
66
+ }
67
+ mConfig.color = mConfig.backgroundColor; // due to naming in plotBands
68
+
69
+
70
+ firstAxis.options.plotBands.unshift(mConfig);
71
+ axisUserOptions.plotBands = axisUserOptions.plotBands || []; // #3176
72
+ if (axisUserOptions.plotBands !== firstAxis.options.plotBands) {
73
+ axisUserOptions.plotBands.unshift(mConfig);
74
+ }
75
+ });
76
+ }
77
+ },
78
+
79
+ /**
80
+ * The default options object
81
+ */
82
+ defaultOptions: {
83
+ // background: {conditional},
84
+ center: ['50%', '50%'],
85
+ size: '85%',
86
+ startAngle: 0
87
+ //endAngle: startAngle + 360
88
+ },
89
+
90
+ /**
91
+ * The default background options
92
+ */
93
+ defaultBackgroundOptions: {
94
+ className: 'highcharts-pane',
95
+ shape: 'circle',
96
+
97
+ borderWidth: 1,
98
+ borderColor: '#cccccc',
99
+ backgroundColor: {
100
+ linearGradient: {
101
+ x1: 0,
102
+ y1: 0,
103
+ x2: 0,
104
+ y2: 1
105
+ },
106
+ stops: [
107
+ [0, '#ffffff'],
108
+ [1, '#e6e6e6']
109
+ ]
110
+ },
111
+
112
+ from: -Number.MAX_VALUE, // corrected to axis min
113
+ innerRadius: 0,
114
+ to: Number.MAX_VALUE, // corrected to axis max
115
+ outerRadius: '105%'
116
+ }
117
+
118
+ });
119
+
120
+ H.Pane = Pane;
121
+
122
+ }(Highcharts));
123
+ (function(H) {
124
+ /**
125
+ * (c) 2010-2016 Torstein Honsi
126
+ *
127
+ * License: www.highcharts.com/license
128
+ */
129
+ 'use strict';
130
+ var Axis = H.Axis,
131
+ CenteredSeriesMixin = H.CenteredSeriesMixin,
132
+ each = H.each,
133
+ extend = H.extend,
134
+ map = H.map,
135
+ merge = H.merge,
136
+ noop = H.noop,
137
+ Pane = H.Pane,
138
+ pick = H.pick,
139
+ pInt = H.pInt,
140
+ Tick = H.Tick,
141
+ splat = H.splat,
142
+ wrap = H.wrap,
143
+
144
+
145
+ hiddenAxisMixin, // @todo Extract this to a new file
146
+ radialAxisMixin, // @todo Extract this to a new file
147
+ axisProto = Axis.prototype,
148
+ tickProto = Tick.prototype;
149
+
150
+ /**
151
+ * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
152
+ */
153
+ hiddenAxisMixin = {
154
+ getOffset: noop,
155
+ redraw: function() {
156
+ this.isDirty = false; // prevent setting Y axis dirty
157
+ },
158
+ render: function() {
159
+ this.isDirty = false; // prevent setting Y axis dirty
160
+ },
161
+ setScale: noop,
162
+ setCategories: noop,
163
+ setTitle: noop
164
+ };
165
+
166
+ /**
167
+ * Augmented methods for the value axis
168
+ */
169
+ radialAxisMixin = {
170
+
171
+ /**
172
+ * The default options extend defaultYAxisOptions
173
+ */
174
+ defaultRadialGaugeOptions: {
175
+ labels: {
176
+ align: 'center',
177
+ x: 0,
178
+ y: null // auto
179
+ },
180
+ minorGridLineWidth: 0,
181
+ minorTickInterval: 'auto',
182
+ minorTickLength: 10,
183
+ minorTickPosition: 'inside',
184
+ minorTickWidth: 1,
185
+ tickLength: 10,
186
+ tickPosition: 'inside',
187
+ tickWidth: 2,
188
+ title: {
189
+ rotation: 0
190
+ },
191
+ zIndex: 2 // behind dials, points in the series group
192
+ },
193
+
194
+ // Circular axis around the perimeter of a polar chart
195
+ defaultRadialXOptions: {
196
+ gridLineWidth: 1, // spokes
197
+ labels: {
198
+ align: null, // auto
199
+ distance: 15,
200
+ x: 0,
201
+ y: null // auto
202
+ },
203
+ maxPadding: 0,
204
+ minPadding: 0,
205
+ showLastLabel: false,
206
+ tickLength: 0
207
+ },
208
+
209
+ // Radial axis, like a spoke in a polar chart
210
+ defaultRadialYOptions: {
211
+ gridLineInterpolation: 'circle',
212
+ labels: {
213
+ align: 'right',
214
+ x: -3,
215
+ y: -2
216
+ },
217
+ showLastLabel: false,
218
+ title: {
219
+ x: 4,
220
+ text: null,
221
+ rotation: 90
222
+ }
223
+ },
224
+
225
+ /**
226
+ * Merge and set options
227
+ */
228
+ setOptions: function(userOptions) {
229
+
230
+ var options = this.options = merge(
231
+ this.defaultOptions,
232
+ this.defaultRadialOptions,
233
+ userOptions
234
+ );
235
+
236
+ // Make sure the plotBands array is instanciated for each Axis (#2649)
237
+ if (!options.plotBands) {
238
+ options.plotBands = [];
239
+ }
240
+
241
+ },
242
+
243
+ /**
244
+ * Wrap the getOffset method to return zero offset for title or labels in a radial
245
+ * axis
246
+ */
247
+ getOffset: function() {
248
+ // Call the Axis prototype method (the method we're in now is on the instance)
249
+ axisProto.getOffset.call(this);
250
+
251
+ // Title or label offsets are not counted
252
+ this.chart.axisOffset[this.side] = 0;
253
+
254
+ // Set the center array
255
+ this.center = this.pane.center = CenteredSeriesMixin.getCenter.call(this.pane);
256
+ },
257
+
258
+
259
+ /**
260
+ * Get the path for the axis line. This method is also referenced in the getPlotLinePath
261
+ * method.
262
+ */
263
+ getLinePath: function(lineWidth, radius) {
264
+ var center = this.center,
265
+ end,
266
+ chart = this.chart,
267
+ r = pick(radius, center[2] / 2 - this.offset),
268
+ path;
269
+
270
+ if (this.isCircular || radius !== undefined) {
271
+ path = this.chart.renderer.symbols.arc(
272
+ this.left + center[0],
273
+ this.top + center[1],
274
+ r,
275
+ r, {
276
+ start: this.startAngleRad,
277
+ end: this.endAngleRad,
278
+ open: true,
279
+ innerR: 0
280
+ }
281
+ );
282
+ } else {
283
+ end = this.postTranslate(this.angleRad, r);
284
+ path = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
285
+ }
286
+ return path;
287
+ },
288
+
289
+ /**
290
+ * Override setAxisTranslation by setting the translation to the difference
291
+ * in rotation. This allows the translate method to return angle for
292
+ * any given value.
293
+ */
294
+ setAxisTranslation: function() {
295
+
296
+ // Call uber method
297
+ axisProto.setAxisTranslation.call(this);
298
+
299
+ // Set transA and minPixelPadding
300
+ if (this.center) { // it's not defined the first time
301
+ if (this.isCircular) {
302
+
303
+ this.transA = (this.endAngleRad - this.startAngleRad) /
304
+ ((this.max - this.min) || 1);
305
+
306
+
307
+ } else {
308
+ this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
309
+ }
310
+
311
+ if (this.isXAxis) {
312
+ this.minPixelPadding = this.transA * this.minPointOffset;
313
+ } else {
314
+ // This is a workaround for regression #2593, but categories still don't position correctly.
315
+ this.minPixelPadding = 0;
316
+ }
317
+ }
318
+ },
319
+
320
+ /**
321
+ * In case of auto connect, add one closestPointRange to the max value right before
322
+ * tickPositions are computed, so that ticks will extend passed the real max.
323
+ */
324
+ beforeSetTickPositions: function() {
325
+ // If autoConnect is true, polygonal grid lines are connected, and one closestPointRange
326
+ // is added to the X axis to prevent the last point from overlapping the first.
327
+ this.autoConnect = this.isCircular && pick(this.userMax, this.options.max) === undefined &&
328
+ this.endAngleRad - this.startAngleRad === 2 * Math.PI;
329
+
330
+ if (this.autoConnect) {
331
+ this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260
332
+ }
333
+ },
334
+
335
+ /**
336
+ * Override the setAxisSize method to use the arc's circumference as length. This
337
+ * allows tickPixelInterval to apply to pixel lengths along the perimeter
338
+ */
339
+ setAxisSize: function() {
340
+
341
+ axisProto.setAxisSize.call(this);
342
+
343
+ if (this.isRadial) {
344
+
345
+ // Set the center array
346
+ this.center = this.pane.center = CenteredSeriesMixin.getCenter.call(this.pane);
347
+
348
+ // The sector is used in Axis.translate to compute the translation of reversed axis points (#2570)
349
+ if (this.isCircular) {
350
+ this.sector = this.endAngleRad - this.startAngleRad;
351
+ }
352
+
353
+ // Axis len is used to lay out the ticks
354
+ this.len = this.width = this.height = this.center[2] * pick(this.sector, 1) / 2;
355
+
356
+
357
+ }
358
+ },
359
+
360
+ /**
361
+ * Returns the x, y coordinate of a point given by a value and a pixel distance
362
+ * from center
363
+ */
364
+ getPosition: function(value, length) {
365
+ return this.postTranslate(
366
+ this.isCircular ? this.translate(value) : this.angleRad, // #2848
367
+ pick(this.isCircular ? length : this.translate(value), this.center[2] / 2) - this.offset
368
+ );
369
+ },
370
+
371
+ /**
372
+ * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.
373
+ */
374
+ postTranslate: function(angle, radius) {
375
+
376
+ var chart = this.chart,
377
+ center = this.center;
378
+
379
+ angle = this.startAngleRad + angle;
380
+
381
+ return {
382
+ x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
383
+ y: chart.plotTop + center[1] + Math.sin(angle) * radius
384
+ };
385
+
386
+ },
387
+
388
+ /**
389
+ * Find the path for plot bands along the radial axis
390
+ */
391
+ getPlotBandPath: function(from, to, options) {
392
+ var center = this.center,
393
+ startAngleRad = this.startAngleRad,
394
+ fullRadius = center[2] / 2,
395
+ radii = [
396
+ pick(options.outerRadius, '100%'),
397
+ options.innerRadius,
398
+ pick(options.thickness, 10)
399
+ ],
400
+ offset = Math.min(this.offset, 0),
401
+ percentRegex = /%$/,
402
+ start,
403
+ end,
404
+ open,
405
+ isCircular = this.isCircular, // X axis in a polar chart
406
+ ret;
407
+
408
+ // Polygonal plot bands
409
+ if (this.options.gridLineInterpolation === 'polygon') {
410
+ ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
411
+
412
+ // Circular grid bands
413
+ } else {
414
+
415
+ // Keep within bounds
416
+ from = Math.max(from, this.min);
417
+ to = Math.min(to, this.max);
418
+
419
+ // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
420
+ if (!isCircular) {
421
+ radii[0] = this.translate(from);
422
+ radii[1] = this.translate(to);
423
+ }
424
+
425
+ // Convert percentages to pixel values
426
+ radii = map(radii, function(radius) {
427
+ if (percentRegex.test(radius)) {
428
+ radius = (pInt(radius, 10) * fullRadius) / 100;
429
+ }
430
+ return radius;
431
+ });
432
+
433
+ // Handle full circle
434
+ if (options.shape === 'circle' || !isCircular) {
435
+ start = -Math.PI / 2;
436
+ end = Math.PI * 1.5;
437
+ open = true;
438
+ } else {
439
+ start = startAngleRad + this.translate(from);
440
+ end = startAngleRad + this.translate(to);
441
+ }
442
+
443
+ radii[0] -= offset; // #5283
444
+ radii[2] -= offset; // #5283
445
+
446
+ ret = this.chart.renderer.symbols.arc(
447
+ this.left + center[0],
448
+ this.top + center[1],
449
+ radii[0],
450
+ radii[0], {
451
+ start: Math.min(start, end), // Math is for reversed yAxis (#3606)
452
+ end: Math.max(start, end),
453
+ innerR: pick(radii[1], radii[0] - radii[2]),
454
+ open: open
455
+ }
456
+ );
457
+ }
458
+
459
+ return ret;
460
+ },
461
+
462
+ /**
463
+ * Find the path for plot lines perpendicular to the radial axis.
464
+ */
465
+ getPlotLinePath: function(value, reverse) {
466
+ var axis = this,
467
+ center = axis.center,
468
+ chart = axis.chart,
469
+ end = axis.getPosition(value),
470
+ xAxis,
471
+ xy,
472
+ tickPositions,
473
+ ret;
474
+
475
+ // Spokes
476
+ if (axis.isCircular) {
477
+ ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
478
+
479
+ // Concentric circles
480
+ } else if (axis.options.gridLineInterpolation === 'circle') {
481
+ value = axis.translate(value);
482
+ if (value) { // a value of 0 is in the center
483
+ ret = axis.getLinePath(0, value);
484
+ }
485
+ // Concentric polygons
486
+ } else {
487
+ // Find the X axis in the same pane
488
+ each(chart.xAxis, function(a) {
489
+ if (a.pane === axis.pane) {
490
+ xAxis = a;
491
+ }
492
+ });
493
+ ret = [];
494
+ value = axis.translate(value);
495
+ tickPositions = xAxis.tickPositions;
496
+ if (xAxis.autoConnect) {
497
+ tickPositions = tickPositions.concat([tickPositions[0]]);
498
+ }
499
+ // Reverse the positions for concatenation of polygonal plot bands
500
+ if (reverse) {
501
+ tickPositions = [].concat(tickPositions).reverse();
502
+ }
503
+
504
+ each(tickPositions, function(pos, i) {
505
+ xy = xAxis.getPosition(pos, value);
506
+ ret.push(i ? 'L' : 'M', xy.x, xy.y);
507
+ });
508
+
509
+ }
510
+ return ret;
511
+ },
512
+
513
+ /**
514
+ * Find the position for the axis title, by default inside the gauge
515
+ */
516
+ getTitlePosition: function() {
517
+ var center = this.center,
518
+ chart = this.chart,
519
+ titleOptions = this.options.title;
520
+
521
+ return {
522
+ x: chart.plotLeft + center[0] + (titleOptions.x || 0),
523
+ y: chart.plotTop + center[1] - ({
524
+ high: 0.5,
525
+ middle: 0.25,
526
+ low: 0
527
+ }[titleOptions.align] *
528
+ center[2]) + (titleOptions.y || 0)
529
+ };
530
+ }
531
+
532
+ };
533
+
534
+ /**
535
+ * Override axisProto.init to mix in special axis instance functions and function overrides
536
+ */
537
+ wrap(axisProto, 'init', function(proceed, chart, userOptions) {
538
+ var axis = this,
539
+ angular = chart.angular,
540
+ polar = chart.polar,
541
+ isX = userOptions.isX,
542
+ isHidden = angular && isX,
543
+ isCircular,
544
+ options,
545
+ chartOptions = chart.options,
546
+ paneIndex = userOptions.pane || 0,
547
+ pane,
548
+ paneOptions;
549
+
550
+ // Before prototype.init
551
+ if (angular) {
552
+ extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
553
+ isCircular = !isX;
554
+ if (isCircular) {
555
+ this.defaultRadialOptions = this.defaultRadialGaugeOptions;
556
+ }
557
+
558
+ } else if (polar) {
559
+ extend(this, radialAxisMixin);
560
+ isCircular = isX;
561
+ this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
562
+
563
+ }
564
+
565
+ // Disable certain features on angular and polar axes
566
+ if (angular || polar) {
567
+ this.isRadial = true;
568
+ chart.inverted = false;
569
+ chartOptions.chart.zoomType = null;
570
+ } else {
571
+ this.isRadial = false;
572
+ }
573
+
574
+ // Run prototype.init
575
+ proceed.call(this, chart, userOptions);
576
+
577
+ if (!isHidden && (angular || polar)) {
578
+ options = this.options;
579
+
580
+ // Create the pane and set the pane options.
581
+ if (!chart.panes) {
582
+ chart.panes = [];
583
+ }
584
+ this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane(
585
+ splat(chartOptions.pane)[paneIndex],
586
+ chart,
587
+ axis
588
+ );
589
+ paneOptions = pane.options;
590
+
591
+ // Start and end angle options are
592
+ // given in degrees relative to top, while internal computations are
593
+ // in radians relative to right (like SVG).
594
+ this.angleRad = (options.angle || 0) * Math.PI / 180; // Y axis in polar charts
595
+ this.startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180; // Gauges
596
+ this.endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360) - 90) * Math.PI / 180; // Gauges
597
+ this.offset = options.offset || 0;
598
+
599
+ this.isCircular = isCircular;
600
+
601
+ }
602
+
603
+ });
604
+
605
+ /**
606
+ * Wrap auto label align to avoid setting axis-wide rotation on radial axes (#4920)
607
+ * @param {Function} proceed
608
+ * @returns {String} Alignment
609
+ */
610
+ wrap(axisProto, 'autoLabelAlign', function(proceed) {
611
+ if (!this.isRadial) {
612
+ return proceed.apply(this, [].slice.call(arguments, 1));
613
+ } // else return undefined
614
+ });
615
+
616
+ /**
617
+ * Add special cases within the Tick class' methods for radial axes.
618
+ */
619
+ wrap(tickProto, 'getPosition', function(proceed, horiz, pos, tickmarkOffset, old) {
620
+ var axis = this.axis;
621
+
622
+ return axis.getPosition ?
623
+ axis.getPosition(pos) :
624
+ proceed.call(this, horiz, pos, tickmarkOffset, old);
625
+ });
626
+
627
+ /**
628
+ * Wrap the getLabelPosition function to find the center position of the label
629
+ * based on the distance option
630
+ */
631
+ wrap(tickProto, 'getLabelPosition', function(proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
632
+ var axis = this.axis,
633
+ optionsY = labelOptions.y,
634
+ ret,
635
+ centerSlot = 20, // 20 degrees to each side at the top and bottom
636
+ align = labelOptions.align,
637
+ angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360;
638
+
639
+ if (axis.isRadial) { // Both X and Y axes in a polar chart
640
+ ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
641
+
642
+ // Automatically rotated
643
+ if (labelOptions.rotation === 'auto') {
644
+ label.attr({
645
+ rotation: angle
646
+ });
647
+
648
+ // Vertically centered
649
+ } else if (optionsY === null) {
650
+ optionsY = axis.chart.renderer.fontMetrics(label.styles.fontSize).b - label.getBBox().height / 2;
651
+ }
652
+
653
+ // Automatic alignment
654
+ if (align === null) {
655
+ if (axis.isCircular) { // Y axis
656
+ if (this.label.getBBox().width > axis.len * axis.tickInterval / (axis.max - axis.min)) { // #3506
657
+ centerSlot = 0;
658
+ }
659
+ if (angle > centerSlot && angle < 180 - centerSlot) {
660
+ align = 'left'; // right hemisphere
661
+ } else if (angle > 180 + centerSlot && angle < 360 - centerSlot) {
662
+ align = 'right'; // left hemisphere
663
+ } else {
664
+ align = 'center'; // top or bottom
665
+ }
666
+ } else {
667
+ align = 'center';
668
+ }
669
+ label.attr({
670
+ align: align
671
+ });
672
+ }
673
+
674
+ ret.x += labelOptions.x;
675
+ ret.y += optionsY;
676
+
677
+ } else {
678
+ ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
679
+ }
680
+ return ret;
681
+ });
682
+
683
+ /**
684
+ * Wrap the getMarkPath function to return the path of the radial marker
685
+ */
686
+ wrap(tickProto, 'getMarkPath', function(proceed, x, y, tickLength, tickWidth, horiz, renderer) {
687
+ var axis = this.axis,
688
+ endPoint,
689
+ ret;
690
+
691
+ if (axis.isRadial) {
692
+ endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
693
+ ret = [
694
+ 'M',
695
+ x,
696
+ y,
697
+ 'L',
698
+ endPoint.x,
699
+ endPoint.y
700
+ ];
701
+ } else {
702
+ ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
703
+ }
704
+ return ret;
705
+ });
706
+
707
+ }(Highcharts));
708
+ (function(H) {
709
+ /**
710
+ * (c) 2010-2016 Torstein Honsi
711
+ *
712
+ * License: www.highcharts.com/license
713
+ */
714
+ 'use strict';
715
+ var each = H.each,
716
+ noop = H.noop,
717
+ pick = H.pick,
718
+ Series = H.Series,
719
+ seriesType = H.seriesType,
720
+ seriesTypes = H.seriesTypes;
721
+ /*
722
+ * The arearangeseries series type
723
+ */
724
+ seriesType('arearange', 'area', {
725
+
726
+ lineWidth: 1,
727
+
728
+ marker: null,
729
+ threshold: null,
730
+ tooltip: {
731
+
732
+ pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>' // eslint-disable-line no-dupe-keys
733
+
734
+ },
735
+ trackByArea: true,
736
+ dataLabels: {
737
+ align: null,
738
+ verticalAlign: null,
739
+ xLow: 0,
740
+ xHigh: 0,
741
+ yLow: 0,
742
+ yHigh: 0
743
+ },
744
+ states: {
745
+ hover: {
746
+ halo: false
747
+ }
748
+ }
749
+
750
+ // Prototype members
751
+ }, {
752
+ pointArrayMap: ['low', 'high'],
753
+ dataLabelCollections: ['dataLabel', 'dataLabelUpper'],
754
+ toYData: function(point) {
755
+ return [point.low, point.high];
756
+ },
757
+ pointValKey: 'low',
758
+ deferTranslatePolar: true,
759
+
760
+ /**
761
+ * Translate a point's plotHigh from the internal angle and radius measures to
762
+ * true plotHigh coordinates. This is an addition of the toXY method found in
763
+ * Polar.js, because it runs too early for arearanges to be considered (#3419).
764
+ */
765
+ highToXY: function(point) {
766
+ // Find the polar plotX and plotY
767
+ var chart = this.chart,
768
+ xy = this.xAxis.postTranslate(point.rectPlotX, this.yAxis.len - point.plotHigh);
769
+ point.plotHighX = xy.x - chart.plotLeft;
770
+ point.plotHigh = xy.y - chart.plotTop;
771
+ },
772
+
773
+ /**
774
+ * Translate data points from raw values x and y to plotX and plotY
775
+ */
776
+ translate: function() {
777
+ var series = this,
778
+ yAxis = series.yAxis,
779
+ hasModifyValue = !!series.modifyValue;
780
+
781
+ seriesTypes.area.prototype.translate.apply(series);
782
+
783
+ // Set plotLow and plotHigh
784
+ each(series.points, function(point) {
785
+
786
+ var low = point.low,
787
+ high = point.high,
788
+ plotY = point.plotY;
789
+
790
+ if (high === null || low === null) {
791
+ point.isNull = true;
792
+ } else {
793
+ point.plotLow = plotY;
794
+ point.plotHigh = yAxis.translate(
795
+ hasModifyValue ? series.modifyValue(high, point) : high,
796
+ 0,
797
+ 1,
798
+ 0,
799
+ 1
800
+ );
801
+ if (hasModifyValue) {
802
+ point.yBottom = point.plotHigh;
803
+ }
804
+ }
805
+ });
806
+
807
+ // Postprocess plotHigh
808
+ if (this.chart.polar) {
809
+ each(this.points, function(point) {
810
+ series.highToXY(point);
811
+ });
812
+ }
813
+ },
814
+
815
+ /**
816
+ * Extend the line series' getSegmentPath method by applying the segment
817
+ * path to both lower and higher values of the range
818
+ */
819
+ getGraphPath: function(points) {
820
+
821
+ var highPoints = [],
822
+ highAreaPoints = [],
823
+ i,
824
+ getGraphPath = seriesTypes.area.prototype.getGraphPath,
825
+ point,
826
+ pointShim,
827
+ linePath,
828
+ lowerPath,
829
+ options = this.options,
830
+ step = options.step,
831
+ higherPath,
832
+ higherAreaPath;
833
+
834
+ points = points || this.points;
835
+ i = points.length;
836
+
837
+ // Create the top line and the top part of the area fill. The area fill compensates for
838
+ // null points by drawing down to the lower graph, moving across the null gap and
839
+ // starting again at the lower graph.
840
+ i = points.length;
841
+ while (i--) {
842
+ point = points[i];
843
+
844
+ if (!point.isNull && !options.connectEnds && (!points[i + 1] || points[i + 1].isNull)) {
845
+ highAreaPoints.push({
846
+ plotX: point.plotX,
847
+ plotY: point.plotY,
848
+ doCurve: false // #5186, gaps in areasplinerange fill
849
+ });
850
+ }
851
+
852
+ pointShim = {
853
+ polarPlotY: point.polarPlotY,
854
+ rectPlotX: point.rectPlotX,
855
+ yBottom: point.yBottom,
856
+ plotX: pick(point.plotHighX, point.plotX), // plotHighX is for polar charts
857
+ plotY: point.plotHigh,
858
+ isNull: point.isNull
859
+ };
860
+ highAreaPoints.push(pointShim);
861
+ highPoints.push(pointShim);
862
+ if (!point.isNull && !options.connectEnds && (!points[i - 1] || points[i - 1].isNull)) {
863
+ highAreaPoints.push({
864
+ plotX: point.plotX,
865
+ plotY: point.plotY,
866
+ doCurve: false // #5186, gaps in areasplinerange fill
867
+ });
868
+ }
869
+ }
870
+
871
+ // Get the paths
872
+ lowerPath = getGraphPath.call(this, points);
873
+ if (step) {
874
+ if (step === true) {
875
+ step = 'left';
876
+ }
877
+ options.step = {
878
+ left: 'right',
879
+ center: 'center',
880
+ right: 'left'
881
+ }[step]; // swap for reading in getGraphPath
882
+ }
883
+ higherPath = getGraphPath.call(this, highPoints);
884
+ higherAreaPath = getGraphPath.call(this, highAreaPoints);
885
+ options.step = step;
886
+
887
+ // Create a line on both top and bottom of the range
888
+ linePath = [].concat(lowerPath, higherPath);
889
+
890
+ // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
891
+ if (!this.chart.polar && higherAreaPath[0] === 'M') {
892
+ higherAreaPath[0] = 'L'; // this probably doesn't work for spline
893
+ }
894
+
895
+ this.graphPath = linePath;
896
+ this.areaPath = this.areaPath.concat(lowerPath, higherAreaPath);
897
+
898
+ // Prepare for sideways animation
899
+ linePath.isArea = true;
900
+ linePath.xMap = lowerPath.xMap;
901
+ this.areaPath.xMap = lowerPath.xMap;
902
+
903
+ return linePath;
904
+ },
905
+
906
+ /**
907
+ * Extend the basic drawDataLabels method by running it for both lower and higher
908
+ * values.
909
+ */
910
+ drawDataLabels: function() {
911
+
912
+ var data = this.data,
913
+ length = data.length,
914
+ i,
915
+ originalDataLabels = [],
916
+ seriesProto = Series.prototype,
917
+ dataLabelOptions = this.options.dataLabels,
918
+ align = dataLabelOptions.align,
919
+ verticalAlign = dataLabelOptions.verticalAlign,
920
+ inside = dataLabelOptions.inside,
921
+ point,
922
+ up,
923
+ inverted = this.chart.inverted;
924
+
925
+ if (dataLabelOptions.enabled || this._hasPointLabels) {
926
+
927
+ // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
928
+ i = length;
929
+ while (i--) {
930
+ point = data[i];
931
+ if (point) {
932
+ up = inside ? point.plotHigh < point.plotLow : point.plotHigh > point.plotLow;
933
+
934
+ // Set preliminary values
935
+ point.y = point.high;
936
+ point._plotY = point.plotY;
937
+ point.plotY = point.plotHigh;
938
+
939
+ // Store original data labels and set preliminary label objects to be picked up
940
+ // in the uber method
941
+ originalDataLabels[i] = point.dataLabel;
942
+ point.dataLabel = point.dataLabelUpper;
943
+
944
+ // Set the default offset
945
+ point.below = up;
946
+ if (inverted) {
947
+ if (!align) {
948
+ dataLabelOptions.align = up ? 'right' : 'left';
949
+ }
950
+ } else {
951
+ if (!verticalAlign) {
952
+ dataLabelOptions.verticalAlign = up ? 'top' : 'bottom';
953
+ }
954
+ }
955
+
956
+ dataLabelOptions.x = dataLabelOptions.xHigh;
957
+ dataLabelOptions.y = dataLabelOptions.yHigh;
958
+ }
959
+ }
960
+
961
+ if (seriesProto.drawDataLabels) {
962
+ seriesProto.drawDataLabels.apply(this, arguments); // #1209
963
+ }
964
+
965
+ // Step 2: reorganize and handle data labels for the lower values
966
+ i = length;
967
+ while (i--) {
968
+ point = data[i];
969
+ if (point) {
970
+ up = inside ? point.plotHigh < point.plotLow : point.plotHigh > point.plotLow;
971
+
972
+ // Move the generated labels from step 1, and reassign the original data labels
973
+ point.dataLabelUpper = point.dataLabel;
974
+ point.dataLabel = originalDataLabels[i];
975
+
976
+ // Reset values
977
+ point.y = point.low;
978
+ point.plotY = point._plotY;
979
+
980
+ // Set the default offset
981
+ point.below = !up;
982
+ if (inverted) {
983
+ if (!align) {
984
+ dataLabelOptions.align = up ? 'left' : 'right';
985
+ }
986
+ } else {
987
+ if (!verticalAlign) {
988
+ dataLabelOptions.verticalAlign = up ? 'bottom' : 'top';
989
+ }
990
+
991
+ }
992
+
993
+ dataLabelOptions.x = dataLabelOptions.xLow;
994
+ dataLabelOptions.y = dataLabelOptions.yLow;
995
+ }
996
+ }
997
+ if (seriesProto.drawDataLabels) {
998
+ seriesProto.drawDataLabels.apply(this, arguments);
999
+ }
1000
+ }
1001
+
1002
+ dataLabelOptions.align = align;
1003
+ dataLabelOptions.verticalAlign = verticalAlign;
1004
+ },
1005
+
1006
+ alignDataLabel: function() {
1007
+ seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
1008
+ },
1009
+
1010
+ setStackedPoints: noop,
1011
+
1012
+ getSymbol: noop,
1013
+
1014
+ drawPoints: noop
1015
+ });
1016
+
1017
+ }(Highcharts));
1018
+ (function(H) {
1019
+ /**
1020
+ * (c) 2010-2016 Torstein Honsi
1021
+ *
1022
+ * License: www.highcharts.com/license
1023
+ */
1024
+ 'use strict';
1025
+
1026
+ var seriesType = H.seriesType,
1027
+ seriesTypes = H.seriesTypes;
1028
+
1029
+ /**
1030
+ * The areasplinerange series type
1031
+ */
1032
+ seriesType('areasplinerange', 'arearange', null, {
1033
+ getPointSpline: seriesTypes.spline.prototype.getPointSpline
1034
+ });
1035
+
1036
+ }(Highcharts));
1037
+ (function(H) {
1038
+ /**
1039
+ * (c) 2010-2016 Torstein Honsi
1040
+ *
1041
+ * License: www.highcharts.com/license
1042
+ */
1043
+ 'use strict';
1044
+ var defaultPlotOptions = H.defaultPlotOptions,
1045
+ each = H.each,
1046
+ merge = H.merge,
1047
+ noop = H.noop,
1048
+ pick = H.pick,
1049
+ seriesType = H.seriesType,
1050
+ seriesTypes = H.seriesTypes;
1051
+
1052
+ var colProto = seriesTypes.column.prototype;
1053
+
1054
+ /**
1055
+ * The ColumnRangeSeries class
1056
+ */
1057
+ seriesType('columnrange', 'arearange', merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
1058
+ lineWidth: 1,
1059
+ pointRange: null
1060
+
1061
+ // Prototype members
1062
+ }), {
1063
+ /**
1064
+ * Translate data points from raw values x and y to plotX and plotY
1065
+ */
1066
+ translate: function() {
1067
+ var series = this,
1068
+ yAxis = series.yAxis,
1069
+ xAxis = series.xAxis,
1070
+ startAngleRad = xAxis.startAngleRad,
1071
+ start,
1072
+ chart = series.chart,
1073
+ isRadial = series.xAxis.isRadial,
1074
+ plotHigh;
1075
+
1076
+ colProto.translate.apply(series);
1077
+
1078
+ // Set plotLow and plotHigh
1079
+ each(series.points, function(point) {
1080
+ var shapeArgs = point.shapeArgs,
1081
+ minPointLength = series.options.minPointLength,
1082
+ heightDifference,
1083
+ height,
1084
+ y;
1085
+
1086
+ point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
1087
+ point.plotLow = point.plotY;
1088
+
1089
+ // adjust shape
1090
+ y = plotHigh;
1091
+ height = pick(point.rectPlotY, point.plotY) - plotHigh;
1092
+
1093
+ // Adjust for minPointLength
1094
+ if (Math.abs(height) < minPointLength) {
1095
+ heightDifference = (minPointLength - height);
1096
+ height += heightDifference;
1097
+ y -= heightDifference / 2;
1098
+
1099
+ // Adjust for negative ranges or reversed Y axis (#1457)
1100
+ } else if (height < 0) {
1101
+ height *= -1;
1102
+ y -= height;
1103
+ }
1104
+
1105
+ if (isRadial) {
1106
+
1107
+ start = point.barX + startAngleRad;
1108
+ point.shapeType = 'path';
1109
+ point.shapeArgs = {
1110
+ d: series.polarArc(y + height, y, start, start + point.pointWidth)
1111
+ };
1112
+ } else {
1113
+ shapeArgs.height = height;
1114
+ shapeArgs.y = y;
1115
+
1116
+ point.tooltipPos = chart.inverted ? [
1117
+ yAxis.len + yAxis.pos - chart.plotLeft - y - height / 2,
1118
+ xAxis.len + xAxis.pos - chart.plotTop - shapeArgs.x - shapeArgs.width / 2,
1119
+ height
1120
+ ] : [
1121
+ xAxis.left - chart.plotLeft + shapeArgs.x + shapeArgs.width / 2,
1122
+ yAxis.pos - chart.plotTop + y + height / 2,
1123
+ height
1124
+ ]; // don't inherit from column tooltip position - #3372
1125
+ }
1126
+ });
1127
+ },
1128
+ directTouch: true,
1129
+ trackerGroups: ['group', 'dataLabelsGroup'],
1130
+ drawGraph: noop,
1131
+ crispCol: colProto.crispCol,
1132
+ drawPoints: colProto.drawPoints,
1133
+ drawTracker: colProto.drawTracker,
1134
+ getColumnMetrics: colProto.getColumnMetrics,
1135
+ animate: function() {
1136
+ return colProto.animate.apply(this, arguments);
1137
+ },
1138
+ polarArc: function() {
1139
+ return colProto.polarArc.apply(this, arguments);
1140
+ },
1141
+ pointAttribs: colProto.pointAttribs
1142
+ });
1143
+
1144
+ }(Highcharts));
1145
+ (function(H) {
1146
+ /**
1147
+ * (c) 2010-2016 Torstein Honsi
1148
+ *
1149
+ * License: www.highcharts.com/license
1150
+ */
1151
+ 'use strict';
1152
+ var each = H.each,
1153
+ isNumber = H.isNumber,
1154
+ merge = H.merge,
1155
+ noop = H.noop,
1156
+ pick = H.pick,
1157
+ pInt = H.pInt,
1158
+ Series = H.Series,
1159
+ seriesType = H.seriesType,
1160
+ TrackerMixin = H.TrackerMixin;
1161
+ /*
1162
+ * The GaugeSeries class
1163
+ */
1164
+ seriesType('gauge', 'line', {
1165
+ dataLabels: {
1166
+ enabled: true,
1167
+ defer: false,
1168
+ y: 15,
1169
+ borderRadius: 3,
1170
+ crop: false,
1171
+ verticalAlign: 'top',
1172
+ zIndex: 2,
1173
+
1174
+ // Presentational
1175
+ borderWidth: 1,
1176
+ borderColor: '#cccccc'
1177
+
1178
+ },
1179
+ dial: {
1180
+ // radius: '80%',
1181
+ // baseWidth: 3,
1182
+ // topWidth: 1,
1183
+ // baseLength: '70%' // of radius
1184
+ // rearLength: '10%'
1185
+
1186
+ // backgroundColor: '#000000',
1187
+ // borderColor: '#cccccc',
1188
+ // borderWidth: 0
1189
+
1190
+
1191
+ },
1192
+ pivot: {
1193
+ //radius: 5,
1194
+
1195
+ //borderWidth: 0
1196
+ //borderColor: '#cccccc',
1197
+ //backgroundColor: '#000000'
1198
+
1199
+ },
1200
+ tooltip: {
1201
+ headerFormat: ''
1202
+ },
1203
+ showInLegend: false
1204
+
1205
+ // Prototype members
1206
+ }, {
1207
+ // chart.angular will be set to true when a gauge series is present, and this will
1208
+ // be used on the axes
1209
+ angular: true,
1210
+ directTouch: true, // #5063
1211
+ drawGraph: noop,
1212
+ fixedBox: true,
1213
+ forceDL: true,
1214
+ noSharedTooltip: true,
1215
+ trackerGroups: ['group', 'dataLabelsGroup'],
1216
+
1217
+ /**
1218
+ * Calculate paths etc
1219
+ */
1220
+ translate: function() {
1221
+
1222
+ var series = this,
1223
+ yAxis = series.yAxis,
1224
+ options = series.options,
1225
+ center = yAxis.center;
1226
+
1227
+ series.generatePoints();
1228
+
1229
+ each(series.points, function(point) {
1230
+
1231
+ var dialOptions = merge(options.dial, point.dial),
1232
+ radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
1233
+ baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
1234
+ rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
1235
+ baseWidth = dialOptions.baseWidth || 3,
1236
+ topWidth = dialOptions.topWidth || 1,
1237
+ overshoot = options.overshoot,
1238
+ rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
1239
+
1240
+ // Handle the wrap and overshoot options
1241
+ if (isNumber(overshoot)) {
1242
+ overshoot = overshoot / 180 * Math.PI;
1243
+ rotation = Math.max(yAxis.startAngleRad - overshoot, Math.min(yAxis.endAngleRad + overshoot, rotation));
1244
+
1245
+ } else if (options.wrap === false) {
1246
+ rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
1247
+ }
1248
+
1249
+ rotation = rotation * 180 / Math.PI;
1250
+
1251
+ point.shapeType = 'path';
1252
+ point.shapeArgs = {
1253
+ d: dialOptions.path || [
1254
+ 'M', -rearLength, -baseWidth / 2,
1255
+ 'L',
1256
+ baseLength, -baseWidth / 2,
1257
+ radius, -topWidth / 2,
1258
+ radius, topWidth / 2,
1259
+ baseLength, baseWidth / 2, -rearLength, baseWidth / 2,
1260
+ 'z'
1261
+ ],
1262
+ translateX: center[0],
1263
+ translateY: center[1],
1264
+ rotation: rotation
1265
+ };
1266
+
1267
+ // Positions for data label
1268
+ point.plotX = center[0];
1269
+ point.plotY = center[1];
1270
+ });
1271
+ },
1272
+
1273
+ /**
1274
+ * Draw the points where each point is one needle
1275
+ */
1276
+ drawPoints: function() {
1277
+
1278
+ var series = this,
1279
+ center = series.yAxis.center,
1280
+ pivot = series.pivot,
1281
+ options = series.options,
1282
+ pivotOptions = options.pivot,
1283
+ renderer = series.chart.renderer;
1284
+
1285
+ each(series.points, function(point) {
1286
+
1287
+ var graphic = point.graphic,
1288
+ shapeArgs = point.shapeArgs,
1289
+ d = shapeArgs.d,
1290
+ dialOptions = merge(options.dial, point.dial); // #1233
1291
+
1292
+ if (graphic) {
1293
+ graphic.animate(shapeArgs);
1294
+ shapeArgs.d = d; // animate alters it
1295
+ } else {
1296
+ point.graphic = renderer[point.shapeType](shapeArgs)
1297
+ .attr({
1298
+ rotation: shapeArgs.rotation, // required by VML when animation is false
1299
+ zIndex: 1
1300
+ })
1301
+ .addClass('highcharts-dial')
1302
+ .add(series.group);
1303
+
1304
+
1305
+ // Presentational attributes
1306
+ point.graphic.attr({
1307
+ stroke: dialOptions.borderColor || 'none',
1308
+ 'stroke-width': dialOptions.borderWidth || 0,
1309
+ fill: dialOptions.backgroundColor || '#000000'
1310
+ });
1311
+
1312
+ }
1313
+ });
1314
+
1315
+ // Add or move the pivot
1316
+ if (pivot) {
1317
+ pivot.animate({ // #1235
1318
+ translateX: center[0],
1319
+ translateY: center[1]
1320
+ });
1321
+ } else {
1322
+ series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
1323
+ .attr({
1324
+ zIndex: 2
1325
+ })
1326
+ .addClass('highcharts-pivot')
1327
+ .translate(center[0], center[1])
1328
+ .add(series.group);
1329
+
1330
+
1331
+ // Presentational attributes
1332
+ series.pivot.attr({
1333
+ 'stroke-width': pivotOptions.borderWidth || 0,
1334
+ stroke: pivotOptions.borderColor || '#cccccc',
1335
+ fill: pivotOptions.backgroundColor || '#000000'
1336
+ });
1337
+
1338
+ }
1339
+ },
1340
+
1341
+ /**
1342
+ * Animate the arrow up from startAngle
1343
+ */
1344
+ animate: function(init) {
1345
+ var series = this;
1346
+
1347
+ if (!init) {
1348
+ each(series.points, function(point) {
1349
+ var graphic = point.graphic;
1350
+
1351
+ if (graphic) {
1352
+ // start value
1353
+ graphic.attr({
1354
+ rotation: series.yAxis.startAngleRad * 180 / Math.PI
1355
+ });
1356
+
1357
+ // animate
1358
+ graphic.animate({
1359
+ rotation: point.shapeArgs.rotation
1360
+ }, series.options.animation);
1361
+ }
1362
+ });
1363
+
1364
+ // delete this function to allow it only once
1365
+ series.animate = null;
1366
+ }
1367
+ },
1368
+
1369
+ render: function() {
1370
+ this.group = this.plotGroup(
1371
+ 'group',
1372
+ 'series',
1373
+ this.visible ? 'visible' : 'hidden',
1374
+ this.options.zIndex,
1375
+ this.chart.seriesGroup
1376
+ );
1377
+ Series.prototype.render.call(this);
1378
+ this.group.clip(this.chart.clipRect);
1379
+ },
1380
+
1381
+ /**
1382
+ * Extend the basic setData method by running processData and generatePoints immediately,
1383
+ * in order to access the points from the legend.
1384
+ */
1385
+ setData: function(data, redraw) {
1386
+ Series.prototype.setData.call(this, data, false);
1387
+ this.processData();
1388
+ this.generatePoints();
1389
+ if (pick(redraw, true)) {
1390
+ this.chart.redraw();
1391
+ }
1392
+ },
1393
+
1394
+ /**
1395
+ * If the tracking module is loaded, add the point tracker
1396
+ */
1397
+ drawTracker: TrackerMixin && TrackerMixin.drawTrackerPoint
1398
+
1399
+ // Point members
1400
+ }, {
1401
+ /**
1402
+ * Don't do any hover colors or anything
1403
+ */
1404
+ setState: function(state) {
1405
+ this.state = state;
1406
+ }
1407
+ });
1408
+
1409
+ }(Highcharts));
1410
+ (function(H) {
1411
+ /**
1412
+ * (c) 2010-2016 Torstein Honsi
1413
+ *
1414
+ * License: www.highcharts.com/license
1415
+ */
1416
+ 'use strict';
1417
+ var each = H.each,
1418
+ noop = H.noop,
1419
+ pick = H.pick,
1420
+ seriesType = H.seriesType,
1421
+ seriesTypes = H.seriesTypes;
1422
+
1423
+ /**
1424
+ * The boxplot series type.
1425
+ *
1426
+ * @constructor seriesTypes.boxplot
1427
+ * @augments seriesTypes.column
1428
+ */
1429
+ seriesType('boxplot', 'column', {
1430
+ threshold: null,
1431
+ tooltip: {
1432
+
1433
+ pointFormat: '<span style="color:{point.color}">\u25CF</span> <b> {series.name}</b><br/>' + // eslint-disable-line no-dupe-keys
1434
+ 'Maximum: {point.high}<br/>' +
1435
+ 'Upper quartile: {point.q3}<br/>' +
1436
+ 'Median: {point.median}<br/>' +
1437
+ 'Lower quartile: {point.q1}<br/>' +
1438
+ 'Minimum: {point.low}<br/>'
1439
+
1440
+ },
1441
+ whiskerLength: '50%',
1442
+
1443
+ fillColor: '#ffffff',
1444
+ lineWidth: 1,
1445
+ //medianColor: null,
1446
+ medianWidth: 2,
1447
+ states: {
1448
+ hover: {
1449
+ brightness: -0.3
1450
+ }
1451
+ },
1452
+ //stemColor: null,
1453
+ //stemDashStyle: 'solid'
1454
+ //stemWidth: null,
1455
+
1456
+ //whiskerColor: null,
1457
+ whiskerWidth: 2
1458
+
1459
+
1460
+ }, /** @lends seriesTypes.boxplot */ {
1461
+ pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
1462
+ toYData: function(point) { // return a plain array for speedy calculation
1463
+ return [point.low, point.q1, point.median, point.q3, point.high];
1464
+ },
1465
+ pointValKey: 'high', // defines the top of the tracker
1466
+
1467
+
1468
+ /**
1469
+ * Get presentational attributes
1470
+ */
1471
+ pointAttribs: function(point) {
1472
+ var options = this.options,
1473
+ color = (point && point.color) || this.color;
1474
+
1475
+ return {
1476
+ 'fill': point.fillColor || options.fillColor || color,
1477
+ 'stroke': options.lineColor || color,
1478
+ 'stroke-width': options.lineWidth || 0
1479
+ };
1480
+ },
1481
+
1482
+
1483
+ /**
1484
+ * Disable data labels for box plot
1485
+ */
1486
+ drawDataLabels: noop,
1487
+
1488
+ /**
1489
+ * Translate data points from raw values x and y to plotX and plotY
1490
+ */
1491
+ translate: function() {
1492
+ var series = this,
1493
+ yAxis = series.yAxis,
1494
+ pointArrayMap = series.pointArrayMap;
1495
+
1496
+ seriesTypes.column.prototype.translate.apply(series);
1497
+
1498
+ // do the translation on each point dimension
1499
+ each(series.points, function(point) {
1500
+ each(pointArrayMap, function(key) {
1501
+ if (point[key] !== null) {
1502
+ point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
1503
+ }
1504
+ });
1505
+ });
1506
+ },
1507
+
1508
+ /**
1509
+ * Draw the data points
1510
+ */
1511
+ drawPoints: function() {
1512
+ var series = this, //state = series.state,
1513
+ points = series.points,
1514
+ options = series.options,
1515
+ chart = series.chart,
1516
+ renderer = chart.renderer,
1517
+ q1Plot,
1518
+ q3Plot,
1519
+ highPlot,
1520
+ lowPlot,
1521
+ medianPlot,
1522
+ medianPath,
1523
+ crispCorr,
1524
+ crispX = 0,
1525
+ boxPath,
1526
+ width,
1527
+ left,
1528
+ right,
1529
+ halfWidth,
1530
+ doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
1531
+ pointWiskerLength,
1532
+ whiskerLength = series.options.whiskerLength;
1533
+
1534
+
1535
+ each(points, function(point) {
1536
+
1537
+ var graphic = point.graphic,
1538
+ verb = graphic ? 'animate' : 'attr',
1539
+ shapeArgs = point.shapeArgs; // the box
1540
+
1541
+
1542
+ var boxAttr,
1543
+ stemAttr = {},
1544
+ whiskersAttr = {},
1545
+ medianAttr = {},
1546
+ color = point.color || series.color;
1547
+
1548
+
1549
+ if (point.plotY !== undefined) {
1550
+
1551
+ // crisp vector coordinates
1552
+ width = shapeArgs.width;
1553
+ left = Math.floor(shapeArgs.x);
1554
+ right = left + width;
1555
+ halfWidth = Math.round(width / 2);
1556
+ q1Plot = Math.floor(doQuartiles ? point.q1Plot : point.lowPlot);
1557
+ q3Plot = Math.floor(doQuartiles ? point.q3Plot : point.lowPlot);
1558
+ highPlot = Math.floor(point.highPlot);
1559
+ lowPlot = Math.floor(point.lowPlot);
1560
+
1561
+ if (!graphic) {
1562
+ point.graphic = graphic = renderer.g('point')
1563
+ .add(series.group);
1564
+
1565
+ point.stem = renderer.path()
1566
+ .addClass('highcharts-boxplot-stem')
1567
+ .add(graphic);
1568
+
1569
+ if (whiskerLength) {
1570
+ point.whiskers = renderer.path()
1571
+ .addClass('highcharts-boxplot-whisker')
1572
+ .add(graphic);
1573
+ }
1574
+ if (doQuartiles) {
1575
+ point.box = renderer.path(boxPath)
1576
+ .addClass('highcharts-boxplot-box')
1577
+ .add(graphic);
1578
+ }
1579
+ point.medianShape = renderer.path(medianPath)
1580
+ .addClass('highcharts-boxplot-median')
1581
+ .add(graphic);
1582
+
1583
+
1584
+
1585
+
1586
+ // Stem attributes
1587
+ stemAttr.stroke = point.stemColor || options.stemColor || color;
1588
+ stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
1589
+ stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
1590
+ point.stem.attr(stemAttr);
1591
+
1592
+ // Whiskers attributes
1593
+ if (whiskerLength) {
1594
+ whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
1595
+ whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
1596
+ point.whiskers.attr(whiskersAttr);
1597
+ }
1598
+
1599
+ if (doQuartiles) {
1600
+ boxAttr = series.pointAttribs(point);
1601
+ point.box.attr(boxAttr);
1602
+ }
1603
+
1604
+
1605
+ // Median attributes
1606
+ medianAttr.stroke = point.medianColor || options.medianColor || color;
1607
+ medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
1608
+ point.medianShape.attr(medianAttr);
1609
+
1610
+
1611
+ }
1612
+
1613
+
1614
+
1615
+ // The stem
1616
+ crispCorr = (point.stem.strokeWidth() % 2) / 2;
1617
+ crispX = left + halfWidth + crispCorr;
1618
+ point.stem[verb]({
1619
+ d: [
1620
+ // stem up
1621
+ 'M',
1622
+ crispX, q3Plot,
1623
+ 'L',
1624
+ crispX, highPlot,
1625
+
1626
+ // stem down
1627
+ 'M',
1628
+ crispX, q1Plot,
1629
+ 'L',
1630
+ crispX, lowPlot
1631
+ ]
1632
+ });
1633
+
1634
+ // The box
1635
+ if (doQuartiles) {
1636
+ crispCorr = (point.box.strokeWidth() % 2) / 2;
1637
+ q1Plot = Math.floor(q1Plot) + crispCorr;
1638
+ q3Plot = Math.floor(q3Plot) + crispCorr;
1639
+ left += crispCorr;
1640
+ right += crispCorr;
1641
+ point.box[verb]({
1642
+ d: [
1643
+ 'M',
1644
+ left, q3Plot,
1645
+ 'L',
1646
+ left, q1Plot,
1647
+ 'L',
1648
+ right, q1Plot,
1649
+ 'L',
1650
+ right, q3Plot,
1651
+ 'L',
1652
+ left, q3Plot,
1653
+ 'z'
1654
+ ]
1655
+ });
1656
+ }
1657
+
1658
+ // The whiskers
1659
+ if (whiskerLength) {
1660
+ crispCorr = (point.whiskers.strokeWidth() % 2) / 2;
1661
+ highPlot = highPlot + crispCorr;
1662
+ lowPlot = lowPlot + crispCorr;
1663
+ pointWiskerLength = (/%$/).test(whiskerLength) ? halfWidth * parseFloat(whiskerLength) / 100 : whiskerLength / 2;
1664
+ point.whiskers[verb]({
1665
+ d: [
1666
+ // High whisker
1667
+ 'M',
1668
+ crispX - pointWiskerLength,
1669
+ highPlot,
1670
+ 'L',
1671
+ crispX + pointWiskerLength,
1672
+ highPlot,
1673
+
1674
+ // Low whisker
1675
+ 'M',
1676
+ crispX - pointWiskerLength,
1677
+ lowPlot,
1678
+ 'L',
1679
+ crispX + pointWiskerLength,
1680
+ lowPlot
1681
+ ]
1682
+ });
1683
+ }
1684
+
1685
+ // The median
1686
+ medianPlot = Math.round(point.medianPlot);
1687
+ crispCorr = (point.medianShape.strokeWidth() % 2) / 2;
1688
+ medianPlot = medianPlot + crispCorr;
1689
+
1690
+ point.medianShape[verb]({
1691
+ d: [
1692
+ 'M',
1693
+ left,
1694
+ medianPlot,
1695
+ 'L',
1696
+ right,
1697
+ medianPlot
1698
+ ]
1699
+ });
1700
+ }
1701
+ });
1702
+
1703
+ },
1704
+ setStackedPoints: noop // #3890
1705
+
1706
+
1707
+ });
1708
+
1709
+ /* ****************************************************************************
1710
+ * End Box plot series code *
1711
+ *****************************************************************************/
1712
+
1713
+ }(Highcharts));
1714
+ (function(H) {
1715
+ /**
1716
+ * (c) 2010-2016 Torstein Honsi
1717
+ *
1718
+ * License: www.highcharts.com/license
1719
+ */
1720
+ 'use strict';
1721
+ var each = H.each,
1722
+ noop = H.noop,
1723
+ seriesType = H.seriesType,
1724
+ seriesTypes = H.seriesTypes;
1725
+
1726
+
1727
+ /* ****************************************************************************
1728
+ * Start error bar series code *
1729
+ *****************************************************************************/
1730
+ seriesType('errorbar', 'boxplot', {
1731
+
1732
+ color: '#000000',
1733
+
1734
+ grouping: false,
1735
+ linkedTo: ':previous',
1736
+ tooltip: {
1737
+ pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'
1738
+ },
1739
+ whiskerWidth: null
1740
+
1741
+ // Prototype members
1742
+ }, {
1743
+ type: 'errorbar',
1744
+ pointArrayMap: ['low', 'high'], // array point configs are mapped to this
1745
+ toYData: function(point) { // return a plain array for speedy calculation
1746
+ return [point.low, point.high];
1747
+ },
1748
+ pointValKey: 'high', // defines the top of the tracker
1749
+ doQuartiles: false,
1750
+ drawDataLabels: seriesTypes.arearange ? function() {
1751
+ var valKey = this.pointValKey;
1752
+ seriesTypes.arearange.prototype.drawDataLabels.call(this);
1753
+ // Arearange drawDataLabels does not reset point.y to high, but to low after drawing. #4133
1754
+ each(this.data, function(point) {
1755
+ point.y = point[valKey];
1756
+ });
1757
+ } : noop,
1758
+
1759
+ /**
1760
+ * Get the width and X offset, either on top of the linked series column
1761
+ * or standalone
1762
+ */
1763
+ getColumnMetrics: function() {
1764
+ return (this.linkedParent && this.linkedParent.columnMetrics) ||
1765
+ seriesTypes.column.prototype.getColumnMetrics.call(this);
1766
+ }
1767
+ });
1768
+
1769
+ /* ****************************************************************************
1770
+ * End error bar series code *
1771
+ *****************************************************************************/
1772
+
1773
+ }(Highcharts));
1774
+ (function(H) {
1775
+ /**
1776
+ * (c) 2010-2016 Torstein Honsi
1777
+ *
1778
+ * License: www.highcharts.com/license
1779
+ */
1780
+ 'use strict';
1781
+ var correctFloat = H.correctFloat,
1782
+ isNumber = H.isNumber,
1783
+ noop = H.noop,
1784
+ pick = H.pick,
1785
+ Point = H.Point,
1786
+ Series = H.Series,
1787
+ seriesType = H.seriesType,
1788
+ seriesTypes = H.seriesTypes;
1789
+
1790
+ /* ****************************************************************************
1791
+ * Start Waterfall series code *
1792
+ *****************************************************************************/
1793
+ seriesType('waterfall', 'column', {
1794
+ dataLabels: {
1795
+ inside: true
1796
+ },
1797
+
1798
+ lineWidth: 1,
1799
+ lineColor: '#333333',
1800
+ dashStyle: 'dot',
1801
+ borderColor: '#333333',
1802
+ states: {
1803
+ hover: {
1804
+ lineWidthPlus: 0 // #3126
1805
+ }
1806
+ }
1807
+
1808
+
1809
+ // Prototype members
1810
+ }, {
1811
+ pointValKey: 'y',
1812
+
1813
+ /**
1814
+ * Translate data points from raw values
1815
+ */
1816
+ translate: function() {
1817
+ var series = this,
1818
+ options = series.options,
1819
+ yAxis = series.yAxis,
1820
+ len,
1821
+ i,
1822
+ points,
1823
+ point,
1824
+ shapeArgs,
1825
+ stack,
1826
+ y,
1827
+ yValue,
1828
+ previousY,
1829
+ previousIntermediate,
1830
+ range,
1831
+ minPointLength = pick(options.minPointLength, 5),
1832
+ threshold = options.threshold,
1833
+ stacking = options.stacking,
1834
+ // Separate offsets for negative and positive columns:
1835
+ positiveOffset = 0,
1836
+ negativeOffset = 0,
1837
+ stackIndicator,
1838
+ tooltipY;
1839
+
1840
+ // run column series translate
1841
+ seriesTypes.column.prototype.translate.apply(this);
1842
+
1843
+ previousY = previousIntermediate = threshold;
1844
+ points = series.points;
1845
+
1846
+ for (i = 0, len = points.length; i < len; i++) {
1847
+ // cache current point object
1848
+ point = points[i];
1849
+ yValue = this.processedYData[i];
1850
+ shapeArgs = point.shapeArgs;
1851
+
1852
+ // get current stack
1853
+ stack = stacking && yAxis.stacks[(series.negStacks && yValue < threshold ? '-' : '') + series.stackKey];
1854
+ stackIndicator = series.getStackIndicator(stackIndicator, point.x);
1855
+ range = stack ?
1856
+ stack[point.x].points[series.index + ',' + i + ',' + stackIndicator.index] : [0, yValue];
1857
+
1858
+ // override point value for sums
1859
+ // #3710 Update point does not propagate to sum
1860
+ if (point.isSum) {
1861
+ point.y = correctFloat(yValue);
1862
+ } else if (point.isIntermediateSum) {
1863
+ point.y = correctFloat(yValue - previousIntermediate); // #3840
1864
+ }
1865
+ // up points
1866
+ y = Math.max(previousY, previousY + point.y) + range[0];
1867
+ shapeArgs.y = yAxis.toPixels(y, true);
1868
+
1869
+
1870
+ // sum points
1871
+ if (point.isSum) {
1872
+ shapeArgs.y = yAxis.toPixels(range[1], true);
1873
+ shapeArgs.height = Math.min(yAxis.toPixels(range[0], true), yAxis.len) -
1874
+ shapeArgs.y + positiveOffset + negativeOffset; // #4256
1875
+
1876
+ } else if (point.isIntermediateSum) {
1877
+ shapeArgs.y = yAxis.toPixels(range[1], true);
1878
+ shapeArgs.height = Math.min(yAxis.toPixels(previousIntermediate, true), yAxis.len) -
1879
+ shapeArgs.y + positiveOffset + negativeOffset;
1880
+ previousIntermediate = range[1];
1881
+
1882
+ // If it's not the sum point, update previous stack end position and get
1883
+ // shape height (#3886)
1884
+ } else {
1885
+ shapeArgs.height = yValue > 0 ?
1886
+ yAxis.toPixels(previousY, true) - shapeArgs.y :
1887
+ yAxis.toPixels(previousY, true) - yAxis.toPixels(previousY - yValue, true);
1888
+ previousY += yValue;
1889
+ }
1890
+ // #3952 Negative sum or intermediate sum not rendered correctly
1891
+ if (shapeArgs.height < 0) {
1892
+ shapeArgs.y += shapeArgs.height;
1893
+ shapeArgs.height *= -1;
1894
+ }
1895
+
1896
+ point.plotY = shapeArgs.y = Math.round(shapeArgs.y) - (series.borderWidth % 2) / 2;
1897
+ shapeArgs.height = Math.max(Math.round(shapeArgs.height), 0.001); // #3151
1898
+ point.yBottom = shapeArgs.y + shapeArgs.height;
1899
+
1900
+ // Before minPointLength, apply negative offset:
1901
+ shapeArgs.y -= negativeOffset;
1902
+
1903
+
1904
+ if (shapeArgs.height <= minPointLength && !point.isNull) {
1905
+ shapeArgs.height = minPointLength;
1906
+ if (point.y < 0) {
1907
+ negativeOffset -= minPointLength;
1908
+ } else {
1909
+ positiveOffset += minPointLength;
1910
+ }
1911
+ }
1912
+
1913
+ // After minPointLength is updated, apply positive offset:
1914
+ shapeArgs.y -= positiveOffset;
1915
+
1916
+ // Correct tooltip placement (#3014)
1917
+ tooltipY = point.plotY - negativeOffset - positiveOffset +
1918
+ (point.negative && negativeOffset >= 0 ? shapeArgs.height : 0);
1919
+ if (series.chart.inverted) {
1920
+ point.tooltipPos[0] = yAxis.len - tooltipY;
1921
+ } else {
1922
+ point.tooltipPos[1] = tooltipY;
1923
+ }
1924
+ }
1925
+ },
1926
+
1927
+ /**
1928
+ * Call default processData then override yData to reflect waterfall's extremes on yAxis
1929
+ */
1930
+ processData: function(force) {
1931
+ var series = this,
1932
+ options = series.options,
1933
+ yData = series.yData,
1934
+ points = series.options.data, // #3710 Update point does not propagate to sum
1935
+ point,
1936
+ dataLength = yData.length,
1937
+ threshold = options.threshold || 0,
1938
+ subSum,
1939
+ sum,
1940
+ dataMin,
1941
+ dataMax,
1942
+ y,
1943
+ i;
1944
+
1945
+ sum = subSum = dataMin = dataMax = threshold;
1946
+
1947
+ for (i = 0; i < dataLength; i++) {
1948
+ y = yData[i];
1949
+ point = points && points[i] ? points[i] : {};
1950
+
1951
+ if (y === 'sum' || point.isSum) {
1952
+ yData[i] = correctFloat(sum);
1953
+ } else if (y === 'intermediateSum' || point.isIntermediateSum) {
1954
+ yData[i] = correctFloat(subSum);
1955
+ } else {
1956
+ sum += y;
1957
+ subSum += y;
1958
+ }
1959
+ dataMin = Math.min(sum, dataMin);
1960
+ dataMax = Math.max(sum, dataMax);
1961
+ }
1962
+
1963
+ Series.prototype.processData.call(this, force);
1964
+
1965
+ // Record extremes
1966
+ series.dataMin = dataMin;
1967
+ series.dataMax = dataMax;
1968
+ },
1969
+
1970
+ /**
1971
+ * Return y value or string if point is sum
1972
+ */
1973
+ toYData: function(pt) {
1974
+ if (pt.isSum) {
1975
+ return (pt.x === 0 ? null : 'sum'); //#3245 Error when first element is Sum or Intermediate Sum
1976
+ }
1977
+ if (pt.isIntermediateSum) {
1978
+ return (pt.x === 0 ? null : 'intermediateSum'); //#3245
1979
+ }
1980
+ return pt.y;
1981
+ },
1982
+
1983
+
1984
+ /**
1985
+ * Postprocess mapping between options and SVG attributes
1986
+ */
1987
+ pointAttribs: function(point, state) {
1988
+
1989
+ var upColor = this.options.upColor,
1990
+ attr;
1991
+
1992
+ // Set or reset up color (#3710, update to negative)
1993
+ if (upColor && !point.options.color) {
1994
+ point.color = point.y > 0 ? upColor : null;
1995
+ }
1996
+
1997
+ attr = seriesTypes.column.prototype.pointAttribs.call(this, point, state);
1998
+
1999
+ // The dashStyle option in waterfall applies to the graph, not
2000
+ // the points
2001
+ delete attr.dashstyle;
2002
+
2003
+ return attr;
2004
+ },
2005
+
2006
+
2007
+ /**
2008
+ * Return an empty path initially, because we need to know the stroke-width in order
2009
+ * to set the final path.
2010
+ */
2011
+ getGraphPath: function() {
2012
+ return ['M', 0, 0];
2013
+ },
2014
+
2015
+ /**
2016
+ * Draw columns' connector lines
2017
+ */
2018
+ getCrispPath: function() {
2019
+
2020
+ var data = this.data,
2021
+ length = data.length,
2022
+ lineWidth = this.graph.strokeWidth() + this.borderWidth,
2023
+ normalizer = Math.round(lineWidth) % 2 / 2,
2024
+ path = [],
2025
+ prevArgs,
2026
+ pointArgs,
2027
+ i,
2028
+ d;
2029
+
2030
+ for (i = 1; i < length; i++) {
2031
+ pointArgs = data[i].shapeArgs;
2032
+ prevArgs = data[i - 1].shapeArgs;
2033
+
2034
+ d = [
2035
+ 'M',
2036
+ prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
2037
+ 'L',
2038
+ pointArgs.x, prevArgs.y + normalizer
2039
+ ];
2040
+
2041
+ if (data[i - 1].y < 0) {
2042
+ d[2] += prevArgs.height;
2043
+ d[5] += prevArgs.height;
2044
+ }
2045
+
2046
+ path = path.concat(d);
2047
+ }
2048
+
2049
+ return path;
2050
+ },
2051
+
2052
+ /**
2053
+ * The graph is initally drawn with an empty definition, then updated with
2054
+ * crisp rendering.
2055
+ */
2056
+ drawGraph: function() {
2057
+ Series.prototype.drawGraph.call(this);
2058
+ this.graph.attr({
2059
+ d: this.getCrispPath()
2060
+ });
2061
+ },
2062
+
2063
+ /**
2064
+ * Extremes are recorded in processData
2065
+ */
2066
+ getExtremes: noop
2067
+
2068
+ // Point members
2069
+ }, {
2070
+ getClassName: function() {
2071
+ var className = Point.prototype.getClassName.call(this);
2072
+
2073
+ if (this.isSum) {
2074
+ className += ' highcharts-sum';
2075
+ } else if (this.isIntermediateSum) {
2076
+ className += ' highcharts-intermediate-sum';
2077
+ }
2078
+ return className;
2079
+ },
2080
+ /**
2081
+ * Pass the null test in ColumnSeries.translate.
2082
+ */
2083
+ isValid: function() {
2084
+ return isNumber(this.y, true) || this.isSum || this.isIntermediateSum;
2085
+ }
2086
+
2087
+ });
2088
+
2089
+ /* ****************************************************************************
2090
+ * End Waterfall series code *
2091
+ *****************************************************************************/
2092
+
2093
+ }(Highcharts));
2094
+ (function(H) {
2095
+ /**
2096
+ * (c) 2010-2016 Torstein Honsi
2097
+ *
2098
+ * License: www.highcharts.com/license
2099
+ */
2100
+ 'use strict';
2101
+ var LegendSymbolMixin = H.LegendSymbolMixin,
2102
+ noop = H.noop,
2103
+ Series = H.Series,
2104
+ seriesType = H.seriesType,
2105
+ seriesTypes = H.seriesTypes;
2106
+ /**
2107
+ * The polygon series prototype
2108
+ */
2109
+ seriesType('polygon', 'scatter', {
2110
+ marker: {
2111
+ enabled: false,
2112
+ states: {
2113
+ hover: {
2114
+ enabled: false
2115
+ }
2116
+ }
2117
+ },
2118
+ stickyTracking: false,
2119
+ tooltip: {
2120
+ followPointer: true,
2121
+ pointFormat: ''
2122
+ },
2123
+ trackByArea: true
2124
+
2125
+ // Prototype members
2126
+ }, {
2127
+ type: 'polygon',
2128
+ getGraphPath: function() {
2129
+
2130
+ var graphPath = Series.prototype.getGraphPath.call(this),
2131
+ i = graphPath.length + 1;
2132
+
2133
+ // Close all segments
2134
+ while (i--) {
2135
+ if ((i === graphPath.length || graphPath[i] === 'M') && i > 0) {
2136
+ graphPath.splice(i, 0, 'z');
2137
+ }
2138
+ }
2139
+ this.areaPath = graphPath;
2140
+ return graphPath;
2141
+ },
2142
+ drawGraph: function() {
2143
+
2144
+ this.options.fillColor = this.color; // Hack into the fill logic in area.drawGraph
2145
+
2146
+ seriesTypes.area.prototype.drawGraph.call(this);
2147
+ },
2148
+ drawLegendSymbol: LegendSymbolMixin.drawRectangle,
2149
+ drawTracker: Series.prototype.drawTracker,
2150
+ setStackedPoints: noop // No stacking points on polygons (#5310)
2151
+ });
2152
+
2153
+ }(Highcharts));
2154
+ (function(H) {
2155
+ /**
2156
+ * (c) 2010-2016 Torstein Honsi
2157
+ *
2158
+ * License: www.highcharts.com/license
2159
+ */
2160
+ 'use strict';
2161
+ var arrayMax = H.arrayMax,
2162
+ arrayMin = H.arrayMin,
2163
+ Axis = H.Axis,
2164
+ color = H.color,
2165
+ each = H.each,
2166
+ isNumber = H.isNumber,
2167
+ noop = H.noop,
2168
+ pick = H.pick,
2169
+ pInt = H.pInt,
2170
+ Point = H.Point,
2171
+ Series = H.Series,
2172
+ seriesType = H.seriesType,
2173
+ seriesTypes = H.seriesTypes;
2174
+
2175
+ /* ****************************************************************************
2176
+ * Start Bubble series code *
2177
+ *****************************************************************************/
2178
+
2179
+ seriesType('bubble', 'scatter', {
2180
+ dataLabels: {
2181
+ formatter: function() { // #2945
2182
+ return this.point.z;
2183
+ },
2184
+ inside: true,
2185
+ verticalAlign: 'middle'
2186
+ },
2187
+ // displayNegative: true,
2188
+ marker: {
2189
+
2190
+ // fillOpacity: 0.5,
2191
+ lineColor: null, // inherit from series.color
2192
+ lineWidth: 1,
2193
+
2194
+ // Avoid offset in Point.setState
2195
+ radius: null,
2196
+ states: {
2197
+ hover: {
2198
+ radiusPlus: 0
2199
+ }
2200
+ }
2201
+ },
2202
+ minSize: 8,
2203
+ maxSize: '20%',
2204
+ // negativeColor: null,
2205
+ // sizeBy: 'area'
2206
+ softThreshold: false,
2207
+ states: {
2208
+ hover: {
2209
+ halo: {
2210
+ size: 5
2211
+ }
2212
+ }
2213
+ },
2214
+ tooltip: {
2215
+ pointFormat: '({point.x}, {point.y}), Size: {point.z}'
2216
+ },
2217
+ turboThreshold: 0,
2218
+ zThreshold: 0,
2219
+ zoneAxis: 'z'
2220
+
2221
+ // Prototype members
2222
+ }, {
2223
+ pointArrayMap: ['y', 'z'],
2224
+ parallelArrays: ['x', 'y', 'z'],
2225
+ trackerGroups: ['group', 'dataLabelsGroup'],
2226
+ bubblePadding: true,
2227
+ zoneAxis: 'z',
2228
+ markerAttribs: null,
2229
+
2230
+
2231
+ pointAttribs: function(point, state) {
2232
+ var markerOptions = this.options.marker,
2233
+ fillOpacity = pick(markerOptions.fillOpacity, 0.5),
2234
+ attr = Series.prototype.pointAttribs.call(this, point, state);
2235
+
2236
+ if (fillOpacity !== 1) {
2237
+ attr.fill = color(attr.fill).setOpacity(fillOpacity).get('rgba');
2238
+ }
2239
+
2240
+ return attr;
2241
+ },
2242
+
2243
+
2244
+ /**
2245
+ * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
2246
+ * must be done prior to Series.translate because the axis needs to add padding in
2247
+ * accordance with the point sizes.
2248
+ */
2249
+ getRadii: function(zMin, zMax, minSize, maxSize) {
2250
+ var len,
2251
+ i,
2252
+ pos,
2253
+ zData = this.zData,
2254
+ radii = [],
2255
+ options = this.options,
2256
+ sizeByArea = options.sizeBy !== 'width',
2257
+ zThreshold = options.zThreshold,
2258
+ zRange = zMax - zMin,
2259
+ value,
2260
+ radius;
2261
+
2262
+ // Set the shape type and arguments to be picked up in drawPoints
2263
+ for (i = 0, len = zData.length; i < len; i++) {
2264
+
2265
+ value = zData[i];
2266
+
2267
+ // When sizing by threshold, the absolute value of z determines the size
2268
+ // of the bubble.
2269
+ if (options.sizeByAbsoluteValue && value !== null) {
2270
+ value = Math.abs(value - zThreshold);
2271
+ zMax = Math.max(zMax - zThreshold, Math.abs(zMin - zThreshold));
2272
+ zMin = 0;
2273
+ }
2274
+
2275
+ if (value === null) {
2276
+ radius = null;
2277
+ // Issue #4419 - if value is less than zMin, push a radius that's always smaller than the minimum size
2278
+ } else if (value < zMin) {
2279
+ radius = minSize / 2 - 1;
2280
+ } else {
2281
+ // Relative size, a number between 0 and 1
2282
+ pos = zRange > 0 ? (value - zMin) / zRange : 0.5;
2283
+
2284
+ if (sizeByArea && pos >= 0) {
2285
+ pos = Math.sqrt(pos);
2286
+ }
2287
+ radius = Math.ceil(minSize + pos * (maxSize - minSize)) / 2;
2288
+ }
2289
+ radii.push(radius);
2290
+ }
2291
+ this.radii = radii;
2292
+ },
2293
+
2294
+ /**
2295
+ * Perform animation on the bubbles
2296
+ */
2297
+ animate: function(init) {
2298
+ var animation = this.options.animation;
2299
+
2300
+ if (!init) { // run the animation
2301
+ each(this.points, function(point) {
2302
+ var graphic = point.graphic,
2303
+ shapeArgs = point.shapeArgs;
2304
+
2305
+ if (graphic && shapeArgs) {
2306
+ // start values
2307
+ graphic.attr('r', 1);
2308
+
2309
+ // animate
2310
+ graphic.animate({
2311
+ r: shapeArgs.r
2312
+ }, animation);
2313
+ }
2314
+ });
2315
+
2316
+ // delete this function to allow it only once
2317
+ this.animate = null;
2318
+ }
2319
+ },
2320
+
2321
+ /**
2322
+ * Extend the base translate method to handle bubble size
2323
+ */
2324
+ translate: function() {
2325
+
2326
+ var i,
2327
+ data = this.data,
2328
+ point,
2329
+ radius,
2330
+ radii = this.radii;
2331
+
2332
+ // Run the parent method
2333
+ seriesTypes.scatter.prototype.translate.call(this);
2334
+
2335
+ // Set the shape type and arguments to be picked up in drawPoints
2336
+ i = data.length;
2337
+
2338
+ while (i--) {
2339
+ point = data[i];
2340
+ radius = radii ? radii[i] : 0; // #1737
2341
+
2342
+ if (isNumber(radius) && radius >= this.minPxSize / 2) {
2343
+ // Shape arguments
2344
+ point.shapeType = 'circle';
2345
+ point.shapeArgs = {
2346
+ x: point.plotX,
2347
+ y: point.plotY,
2348
+ r: radius
2349
+ };
2350
+
2351
+ // Alignment box for the data label
2352
+ point.dlBox = {
2353
+ x: point.plotX - radius,
2354
+ y: point.plotY - radius,
2355
+ width: 2 * radius,
2356
+ height: 2 * radius
2357
+ };
2358
+ } else { // below zThreshold
2359
+ point.shapeArgs = point.plotY = point.dlBox = undefined; // #1691
2360
+ }
2361
+ }
2362
+ },
2363
+
2364
+ /**
2365
+ * Get the series' symbol in the legend
2366
+ *
2367
+ * @param {Object} legend The legend object
2368
+ * @param {Object} item The series (this) or point
2369
+ */
2370
+ drawLegendSymbol: function(legend, item) {
2371
+ var renderer = this.chart.renderer,
2372
+ radius = renderer.fontMetrics(
2373
+ legend.itemStyle && legend.itemStyle.fontSize,
2374
+ item.legendItem
2375
+ ).f / 2;
2376
+
2377
+ item.legendSymbol = renderer.circle(
2378
+ radius,
2379
+ legend.baseline - radius,
2380
+ radius
2381
+ ).attr({
2382
+ zIndex: 3
2383
+ }).add(item.legendGroup);
2384
+ item.legendSymbol.isMarker = true;
2385
+
2386
+ },
2387
+
2388
+ drawPoints: seriesTypes.column.prototype.drawPoints,
2389
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
2390
+ buildKDTree: noop,
2391
+ applyZones: noop
2392
+
2393
+ // Point class
2394
+ }, {
2395
+ haloPath: function(size) {
2396
+ return Point.prototype.haloPath.call(
2397
+ this,
2398
+ size === 0 ? 0 : this.shapeArgs.r + size // #6067
2399
+ );
2400
+ },
2401
+ ttBelow: false
2402
+ });
2403
+
2404
+ /**
2405
+ * Add logic to pad each axis with the amount of pixels
2406
+ * necessary to avoid the bubbles to overflow.
2407
+ */
2408
+ Axis.prototype.beforePadding = function() {
2409
+ var axis = this,
2410
+ axisLength = this.len,
2411
+ chart = this.chart,
2412
+ pxMin = 0,
2413
+ pxMax = axisLength,
2414
+ isXAxis = this.isXAxis,
2415
+ dataKey = isXAxis ? 'xData' : 'yData',
2416
+ min = this.min,
2417
+ extremes = {},
2418
+ smallestSize = Math.min(chart.plotWidth, chart.plotHeight),
2419
+ zMin = Number.MAX_VALUE,
2420
+ zMax = -Number.MAX_VALUE,
2421
+ range = this.max - min,
2422
+ transA = axisLength / range,
2423
+ activeSeries = [];
2424
+
2425
+ // Handle padding on the second pass, or on redraw
2426
+ each(this.series, function(series) {
2427
+
2428
+ var seriesOptions = series.options,
2429
+ zData;
2430
+
2431
+ if (series.bubblePadding && (series.visible || !chart.options.chart.ignoreHiddenSeries)) {
2432
+
2433
+ // Correction for #1673
2434
+ axis.allowZoomOutside = true;
2435
+
2436
+ // Cache it
2437
+ activeSeries.push(series);
2438
+
2439
+ if (isXAxis) { // because X axis is evaluated first
2440
+
2441
+ // For each series, translate the size extremes to pixel values
2442
+ each(['minSize', 'maxSize'], function(prop) {
2443
+ var length = seriesOptions[prop],
2444
+ isPercent = /%$/.test(length);
2445
+
2446
+ length = pInt(length);
2447
+ extremes[prop] = isPercent ?
2448
+ smallestSize * length / 100 :
2449
+ length;
2450
+
2451
+ });
2452
+ series.minPxSize = extremes.minSize;
2453
+ // Prioritize min size if conflict to make sure bubbles are
2454
+ // always visible. #5873
2455
+ series.maxPxSize = Math.max(extremes.maxSize, extremes.minSize);
2456
+
2457
+ // Find the min and max Z
2458
+ zData = series.zData;
2459
+ if (zData.length) { // #1735
2460
+ zMin = pick(seriesOptions.zMin, Math.min(
2461
+ zMin,
2462
+ Math.max(
2463
+ arrayMin(zData),
2464
+ seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
2465
+ )
2466
+ ));
2467
+ zMax = pick(seriesOptions.zMax, Math.max(zMax, arrayMax(zData)));
2468
+ }
2469
+ }
2470
+ }
2471
+ });
2472
+
2473
+ each(activeSeries, function(series) {
2474
+
2475
+ var data = series[dataKey],
2476
+ i = data.length,
2477
+ radius;
2478
+
2479
+ if (isXAxis) {
2480
+ series.getRadii(zMin, zMax, series.minPxSize, series.maxPxSize);
2481
+ }
2482
+
2483
+ if (range > 0) {
2484
+ while (i--) {
2485
+ if (isNumber(data[i]) && axis.dataMin <= data[i] && data[i] <= axis.dataMax) {
2486
+ radius = series.radii[i];
2487
+ pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
2488
+ pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
2489
+ }
2490
+ }
2491
+ }
2492
+ });
2493
+
2494
+ if (activeSeries.length && range > 0 && !this.isLog) {
2495
+ pxMax -= axisLength;
2496
+ transA *= (axisLength + pxMin - pxMax) / axisLength;
2497
+ each([
2498
+ ['min', 'userMin', pxMin],
2499
+ ['max', 'userMax', pxMax]
2500
+ ], function(keys) {
2501
+ if (pick(axis.options[keys[0]], axis[keys[1]]) === undefined) {
2502
+ axis[keys[0]] += keys[2] / transA;
2503
+ }
2504
+ });
2505
+ }
2506
+ };
2507
+
2508
+ /* ****************************************************************************
2509
+ * End Bubble series code *
2510
+ *****************************************************************************/
2511
+
2512
+ }(Highcharts));
2513
+ (function(H) {
2514
+ /**
2515
+ * (c) 2010-2016 Torstein Honsi
2516
+ *
2517
+ * License: www.highcharts.com/license
2518
+ */
2519
+ 'use strict';
2520
+
2521
+ /**
2522
+ * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
2523
+ * gathered in RadialAxes.js.
2524
+ *
2525
+ */
2526
+
2527
+ var each = H.each,
2528
+ pick = H.pick,
2529
+ Pointer = H.Pointer,
2530
+ Series = H.Series,
2531
+ seriesTypes = H.seriesTypes,
2532
+ wrap = H.wrap,
2533
+
2534
+ seriesProto = Series.prototype,
2535
+ pointerProto = Pointer.prototype,
2536
+ colProto;
2537
+
2538
+ /**
2539
+ * Search a k-d tree by the point angle, used for shared tooltips in polar charts
2540
+ */
2541
+ seriesProto.searchPointByAngle = function(e) {
2542
+ var series = this,
2543
+ chart = series.chart,
2544
+ xAxis = series.xAxis,
2545
+ center = xAxis.pane.center,
2546
+ plotX = e.chartX - center[0] - chart.plotLeft,
2547
+ plotY = e.chartY - center[1] - chart.plotTop;
2548
+
2549
+ return this.searchKDTree({
2550
+ clientX: 180 + (Math.atan2(plotX, plotY) * (-180 / Math.PI))
2551
+ });
2552
+
2553
+ };
2554
+
2555
+ /**
2556
+ * Wrap the buildKDTree function so that it searches by angle (clientX) in case of shared tooltip,
2557
+ * and by two dimensional distance in case of non-shared.
2558
+ */
2559
+ wrap(seriesProto, 'buildKDTree', function(proceed) {
2560
+ if (this.chart.polar) {
2561
+ if (this.kdByAngle) {
2562
+ this.searchPoint = this.searchPointByAngle;
2563
+ } else {
2564
+ this.kdDimensions = 2;
2565
+ }
2566
+ }
2567
+ proceed.apply(this);
2568
+ });
2569
+
2570
+ /**
2571
+ * Translate a point's plotX and plotY from the internal angle and radius measures to
2572
+ * true plotX, plotY coordinates
2573
+ */
2574
+ seriesProto.toXY = function(point) {
2575
+ var xy,
2576
+ chart = this.chart,
2577
+ plotX = point.plotX,
2578
+ plotY = point.plotY,
2579
+ clientX;
2580
+
2581
+ // Save rectangular plotX, plotY for later computation
2582
+ point.rectPlotX = plotX;
2583
+ point.rectPlotY = plotY;
2584
+
2585
+ // Find the polar plotX and plotY
2586
+ xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
2587
+ point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
2588
+ point.plotY = point.polarPlotY = xy.y - chart.plotTop;
2589
+
2590
+ // If shared tooltip, record the angle in degrees in order to align X points. Otherwise,
2591
+ // use a standard k-d tree to get the nearest point in two dimensions.
2592
+ if (this.kdByAngle) {
2593
+ clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360;
2594
+ if (clientX < 0) { // #2665
2595
+ clientX += 360;
2596
+ }
2597
+ point.clientX = clientX;
2598
+ } else {
2599
+ point.clientX = point.plotX;
2600
+ }
2601
+ };
2602
+
2603
+ if (seriesTypes.spline) {
2604
+ /**
2605
+ * Overridden method for calculating a spline from one point to the next
2606
+ */
2607
+ wrap(seriesTypes.spline.prototype, 'getPointSpline', function(proceed, segment, point, i) {
2608
+
2609
+ var ret,
2610
+ smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
2611
+ denom = smoothing + 1,
2612
+ plotX,
2613
+ plotY,
2614
+ lastPoint,
2615
+ nextPoint,
2616
+ lastX,
2617
+ lastY,
2618
+ nextX,
2619
+ nextY,
2620
+ leftContX,
2621
+ leftContY,
2622
+ rightContX,
2623
+ rightContY,
2624
+ distanceLeftControlPoint,
2625
+ distanceRightControlPoint,
2626
+ leftContAngle,
2627
+ rightContAngle,
2628
+ jointAngle;
2629
+
2630
+
2631
+ if (this.chart.polar) {
2632
+
2633
+ plotX = point.plotX;
2634
+ plotY = point.plotY;
2635
+ lastPoint = segment[i - 1];
2636
+ nextPoint = segment[i + 1];
2637
+
2638
+ // Connect ends
2639
+ if (this.connectEnds) {
2640
+ if (!lastPoint) {
2641
+ lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
2642
+ }
2643
+ if (!nextPoint) {
2644
+ nextPoint = segment[1];
2645
+ }
2646
+ }
2647
+
2648
+ // find control points
2649
+ if (lastPoint && nextPoint) {
2650
+
2651
+ lastX = lastPoint.plotX;
2652
+ lastY = lastPoint.plotY;
2653
+ nextX = nextPoint.plotX;
2654
+ nextY = nextPoint.plotY;
2655
+ leftContX = (smoothing * plotX + lastX) / denom;
2656
+ leftContY = (smoothing * plotY + lastY) / denom;
2657
+ rightContX = (smoothing * plotX + nextX) / denom;
2658
+ rightContY = (smoothing * plotY + nextY) / denom;
2659
+ distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
2660
+ distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
2661
+ leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
2662
+ rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
2663
+ jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
2664
+
2665
+
2666
+ // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
2667
+ if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
2668
+ jointAngle -= Math.PI;
2669
+ }
2670
+
2671
+ // Find the corrected control points for a spline straight through the point
2672
+ leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
2673
+ leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
2674
+ rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
2675
+ rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
2676
+
2677
+ // Record for drawing in next point
2678
+ point.rightContX = rightContX;
2679
+ point.rightContY = rightContY;
2680
+
2681
+ }
2682
+
2683
+
2684
+ // moveTo or lineTo
2685
+ if (!i) {
2686
+ ret = ['M', plotX, plotY];
2687
+ } else { // curve from last point to this
2688
+ ret = [
2689
+ 'C',
2690
+ lastPoint.rightContX || lastPoint.plotX,
2691
+ lastPoint.rightContY || lastPoint.plotY,
2692
+ leftContX || plotX,
2693
+ leftContY || plotY,
2694
+ plotX,
2695
+ plotY
2696
+ ];
2697
+ lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
2698
+ }
2699
+
2700
+
2701
+ } else {
2702
+ ret = proceed.call(this, segment, point, i);
2703
+ }
2704
+ return ret;
2705
+ });
2706
+ }
2707
+
2708
+ /**
2709
+ * Extend translate. The plotX and plotY values are computed as if the polar chart were a
2710
+ * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
2711
+ * center.
2712
+ */
2713
+ wrap(seriesProto, 'translate', function(proceed) {
2714
+ var chart = this.chart,
2715
+ points,
2716
+ i;
2717
+
2718
+ // Run uber method
2719
+ proceed.call(this);
2720
+
2721
+ // Postprocess plot coordinates
2722
+ if (chart.polar) {
2723
+ this.kdByAngle = chart.tooltip && chart.tooltip.shared;
2724
+
2725
+ if (!this.preventPostTranslate) {
2726
+ points = this.points;
2727
+ i = points.length;
2728
+
2729
+ while (i--) {
2730
+ // Translate plotX, plotY from angle and radius to true plot coordinates
2731
+ this.toXY(points[i]);
2732
+ }
2733
+ }
2734
+ }
2735
+ });
2736
+
2737
+ /**
2738
+ * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
2739
+ * line-like series.
2740
+ */
2741
+ wrap(seriesProto, 'getGraphPath', function(proceed, points) {
2742
+ var series = this,
2743
+ i,
2744
+ firstValid;
2745
+
2746
+ // Connect the path
2747
+ if (this.chart.polar) {
2748
+ points = points || this.points;
2749
+
2750
+ // Append first valid point in order to connect the ends
2751
+ for (i = 0; i < points.length; i++) {
2752
+ if (!points[i].isNull) {
2753
+ firstValid = i;
2754
+ break;
2755
+ }
2756
+ }
2757
+ if (this.options.connectEnds !== false && firstValid !== undefined) {
2758
+ this.connectEnds = true; // re-used in splines
2759
+ points.splice(points.length, 0, points[firstValid]);
2760
+ }
2761
+
2762
+ // For area charts, pseudo points are added to the graph, now we need to translate these
2763
+ each(points, function(point) {
2764
+ if (point.polarPlotY === undefined) {
2765
+ series.toXY(point);
2766
+ }
2767
+ });
2768
+ }
2769
+
2770
+ // Run uber method
2771
+ return proceed.apply(this, [].slice.call(arguments, 1));
2772
+
2773
+ });
2774
+
2775
+
2776
+ function polarAnimate(proceed, init) {
2777
+ var chart = this.chart,
2778
+ animation = this.options.animation,
2779
+ group = this.group,
2780
+ markerGroup = this.markerGroup,
2781
+ center = this.xAxis.center,
2782
+ plotLeft = chart.plotLeft,
2783
+ plotTop = chart.plotTop,
2784
+ attribs;
2785
+
2786
+ // Specific animation for polar charts
2787
+ if (chart.polar) {
2788
+
2789
+ // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
2790
+ // would be so slow it would't matter.
2791
+ if (chart.renderer.isSVG) {
2792
+
2793
+ if (animation === true) {
2794
+ animation = {};
2795
+ }
2796
+
2797
+ // Initialize the animation
2798
+ if (init) {
2799
+
2800
+ // Scale down the group and place it in the center
2801
+ attribs = {
2802
+ translateX: center[0] + plotLeft,
2803
+ translateY: center[1] + plotTop,
2804
+ scaleX: 0.001, // #1499
2805
+ scaleY: 0.001
2806
+ };
2807
+
2808
+ group.attr(attribs);
2809
+ if (markerGroup) {
2810
+ //markerGroup.attrSetters = group.attrSetters;
2811
+ markerGroup.attr(attribs);
2812
+ }
2813
+
2814
+ // Run the animation
2815
+ } else {
2816
+ attribs = {
2817
+ translateX: plotLeft,
2818
+ translateY: plotTop,
2819
+ scaleX: 1,
2820
+ scaleY: 1
2821
+ };
2822
+ group.animate(attribs, animation);
2823
+ if (markerGroup) {
2824
+ markerGroup.animate(attribs, animation);
2825
+ }
2826
+
2827
+ // Delete this function to allow it only once
2828
+ this.animate = null;
2829
+ }
2830
+ }
2831
+
2832
+ // For non-polar charts, revert to the basic animation
2833
+ } else {
2834
+ proceed.call(this, init);
2835
+ }
2836
+ }
2837
+
2838
+ // Define the animate method for regular series
2839
+ wrap(seriesProto, 'animate', polarAnimate);
2840
+
2841
+
2842
+ if (seriesTypes.column) {
2843
+
2844
+ colProto = seriesTypes.column.prototype;
2845
+
2846
+ colProto.polarArc = function(low, high, start, end) {
2847
+ var center = this.xAxis.center,
2848
+ len = this.yAxis.len;
2849
+
2850
+ return this.chart.renderer.symbols.arc(
2851
+ center[0],
2852
+ center[1],
2853
+ len - high,
2854
+ null, {
2855
+ start: start,
2856
+ end: end,
2857
+ innerR: len - pick(low, len)
2858
+ }
2859
+ );
2860
+ };
2861
+
2862
+ /**
2863
+ * Define the animate method for columnseries
2864
+ */
2865
+ wrap(colProto, 'animate', polarAnimate);
2866
+
2867
+
2868
+ /**
2869
+ * Extend the column prototype's translate method
2870
+ */
2871
+ wrap(colProto, 'translate', function(proceed) {
2872
+
2873
+ var xAxis = this.xAxis,
2874
+ startAngleRad = xAxis.startAngleRad,
2875
+ start,
2876
+ points,
2877
+ point,
2878
+ i;
2879
+
2880
+ this.preventPostTranslate = true;
2881
+
2882
+ // Run uber method
2883
+ proceed.call(this);
2884
+
2885
+ // Postprocess plot coordinates
2886
+ if (xAxis.isRadial) {
2887
+ points = this.points;
2888
+ i = points.length;
2889
+ while (i--) {
2890
+ point = points[i];
2891
+ start = point.barX + startAngleRad;
2892
+ point.shapeType = 'path';
2893
+ point.shapeArgs = {
2894
+ d: this.polarArc(point.yBottom, point.plotY, start, start + point.pointWidth)
2895
+ };
2896
+ // Provide correct plotX, plotY for tooltip
2897
+ this.toXY(point);
2898
+ point.tooltipPos = [point.plotX, point.plotY];
2899
+ point.ttBelow = point.plotY > xAxis.center[1];
2900
+ }
2901
+ }
2902
+ });
2903
+
2904
+
2905
+ /**
2906
+ * Align column data labels outside the columns. #1199.
2907
+ */
2908
+ wrap(colProto, 'alignDataLabel', function(proceed, point, dataLabel, options, alignTo, isNew) {
2909
+
2910
+ if (this.chart.polar) {
2911
+ var angle = point.rectPlotX / Math.PI * 180,
2912
+ align,
2913
+ verticalAlign;
2914
+
2915
+ // Align nicely outside the perimeter of the columns
2916
+ if (options.align === null) {
2917
+ if (angle > 20 && angle < 160) {
2918
+ align = 'left'; // right hemisphere
2919
+ } else if (angle > 200 && angle < 340) {
2920
+ align = 'right'; // left hemisphere
2921
+ } else {
2922
+ align = 'center'; // top or bottom
2923
+ }
2924
+ options.align = align;
2925
+ }
2926
+ if (options.verticalAlign === null) {
2927
+ if (angle < 45 || angle > 315) {
2928
+ verticalAlign = 'bottom'; // top part
2929
+ } else if (angle > 135 && angle < 225) {
2930
+ verticalAlign = 'top'; // bottom part
2931
+ } else {
2932
+ verticalAlign = 'middle'; // left or right
2933
+ }
2934
+ options.verticalAlign = verticalAlign;
2935
+ }
2936
+
2937
+ seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
2938
+ } else {
2939
+ proceed.call(this, point, dataLabel, options, alignTo, isNew);
2940
+ }
2941
+
2942
+ });
2943
+ }
2944
+
2945
+ /**
2946
+ * Extend getCoordinates to prepare for polar axis values
2947
+ */
2948
+ wrap(pointerProto, 'getCoordinates', function(proceed, e) {
2949
+ var chart = this.chart,
2950
+ ret = {
2951
+ xAxis: [],
2952
+ yAxis: []
2953
+ };
2954
+
2955
+ if (chart.polar) {
2956
+
2957
+ each(chart.axes, function(axis) {
2958
+ var isXAxis = axis.isXAxis,
2959
+ center = axis.center,
2960
+ x = e.chartX - center[0] - chart.plotLeft,
2961
+ y = e.chartY - center[1] - chart.plotTop;
2962
+
2963
+ ret[isXAxis ? 'xAxis' : 'yAxis'].push({
2964
+ axis: axis,
2965
+ value: axis.translate(
2966
+ isXAxis ?
2967
+ Math.PI - Math.atan2(x, y) : // angle
2968
+ Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
2969
+ true
2970
+ )
2971
+ });
2972
+ });
2973
+
2974
+ } else {
2975
+ ret = proceed.call(this, e);
2976
+ }
2977
+
2978
+ return ret;
2979
+ });
2980
+
2981
+ }(Highcharts));
2982
+ }));