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