highcharts_rails 0.1.0

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