highcharts-js-rails 0.1.11 → 0.2.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.
- data/CHANGELOG.md +6 -0
- data/README.md +9 -8
- data/highcharts-js-rails.gemspec +1 -1
- data/lib/highcharts.rb +3 -1
- data/vendor/assets/javascripts/highcharts-more.js +1581 -35
- data/vendor/assets/javascripts/highcharts.js +677 -507
- data/vendor/assets/javascripts/highcharts/adapters/mootools.js +15 -14
- data/vendor/assets/javascripts/highcharts/adapters/prototype.js +1 -1
- data/vendor/assets/javascripts/highcharts/modules/canvas-tools.js +1 -1
- data/vendor/assets/javascripts/highcharts/modules/data.js +248 -13
- data/vendor/assets/javascripts/highcharts/modules/exporting.js +75 -59
- metadata +4 -4
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## v0.2.0 (2013-01-07) ##
|
2
|
+
|
3
|
+
* Allow block syntax for Highcharts.new.
|
4
|
+
* Allow a Hash to be passed to xAxis, yAxis, or series instead of an Array of 1 Hash.
|
5
|
+
* Update Highcharts to 2.3.5.
|
6
|
+
|
1
7
|
## v0.1.11 (2012-12-13) ##
|
2
8
|
|
3
9
|
* Add support for series data point hashes. (Thanks tanelj!)
|
data/README.md
CHANGED
@@ -39,14 +39,15 @@ Example
|
|
39
39
|
Ruby:
|
40
40
|
|
41
41
|
```ruby
|
42
|
-
chart = Highcharts.new
|
43
|
-
chart.chart(
|
44
|
-
chart.title('Highcharts Example')
|
45
|
-
chart.xAxis(
|
46
|
-
chart.yAxis(
|
47
|
-
chart.series(
|
48
|
-
chart.legend(
|
49
|
-
chart.tooltip(
|
42
|
+
chart = Highcharts.new do |chart|
|
43
|
+
chart.chart(:renderTo => 'graph')
|
44
|
+
chart.title('Highcharts Example')
|
45
|
+
chart.xAxis(:categories => ['October 12', 'October 13', 'October 14'])
|
46
|
+
chart.yAxis(:title => 'Impressions', :min => 0)
|
47
|
+
chart.series(:name => 'Impressions', :yAxis => 0, :type => 'line', :data => [100000, 122000, 127000])
|
48
|
+
chart.legend(:layout => 'vertical', :align => 'right', :verticalAlign => 'top', :x => -10, :y => 100, :borderWidth => 0)
|
49
|
+
chart.tooltip(:formatter => "function(){ return '<b>' + this.series.name + '</b><br/>' + this.x + ': ' + this.y; }")
|
50
|
+
end
|
50
51
|
```
|
51
52
|
|
52
53
|
HTML/ERB:
|
data/highcharts-js-rails.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'highcharts-js-rails'
|
6
|
-
s.version = '0.
|
6
|
+
s.version = '0.2.0'
|
7
7
|
s.authors = ['Alex Robbin']
|
8
8
|
s.email = ['agrobbin@gmail.com']
|
9
9
|
s.homepage = 'https://github.com/agrobbin/highcharts-js-rails'
|
data/lib/highcharts.rb
CHANGED
@@ -16,6 +16,8 @@ class Highcharts < ActionView::Base
|
|
16
16
|
'xAxis' => 'Axis::X',
|
17
17
|
'yAxis' => 'Axis::Y'
|
18
18
|
}
|
19
|
+
|
20
|
+
yield self
|
19
21
|
end
|
20
22
|
|
21
23
|
def inspect
|
@@ -30,7 +32,7 @@ class Highcharts < ActionView::Base
|
|
30
32
|
# For xAxis, yAxis, and series, we need to take the array that is passed as the option
|
31
33
|
# and for each value, instantiate a new class.
|
32
34
|
@options[method] = if %w(xAxis yAxis series).include?(method.to_s)
|
33
|
-
args.first.collect {|v| determine_method_class(method).constantize.new(v)}
|
35
|
+
Array.wrap(args.first).collect {|v| determine_method_class(method).constantize.new(v)}
|
34
36
|
# For all others, just instantiate the class with the original arguments.
|
35
37
|
else
|
36
38
|
determine_method_class(method).constantize.new(*args)
|
@@ -1,35 +1,1581 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
1
|
+
// ==ClosureCompiler==
|
2
|
+
// @compilation_level SIMPLE_OPTIMIZATIONS
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @license Highcharts JS v2.3.5 (2012-12-19)
|
6
|
+
*
|
7
|
+
* (c) 2009-2011 Torstein Hønsi
|
8
|
+
*
|
9
|
+
* License: www.highcharts.com/license
|
10
|
+
*/
|
11
|
+
|
12
|
+
// JSLint options:
|
13
|
+
/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
|
14
|
+
|
15
|
+
(function (Highcharts, UNDEFINED) {
|
16
|
+
var each = Highcharts.each,
|
17
|
+
extend = Highcharts.extend,
|
18
|
+
merge = Highcharts.merge,
|
19
|
+
map = Highcharts.map,
|
20
|
+
pick = Highcharts.pick,
|
21
|
+
pInt = Highcharts.pInt,
|
22
|
+
defaultPlotOptions = Highcharts.getOptions().plotOptions,
|
23
|
+
seriesTypes = Highcharts.seriesTypes,
|
24
|
+
extendClass = Highcharts.extendClass,
|
25
|
+
splat = Highcharts.splat,
|
26
|
+
wrap = Highcharts.wrap,
|
27
|
+
Axis = Highcharts.Axis,
|
28
|
+
Tick = Highcharts.Tick,
|
29
|
+
Series = Highcharts.Series,
|
30
|
+
colProto = seriesTypes.column.prototype,
|
31
|
+
noop = function () {};/**
|
32
|
+
* The Pane object allows options that are common to a set of X and Y axes.
|
33
|
+
*
|
34
|
+
* In the future, this can be extended to basic Highcharts and Highstock.
|
35
|
+
*/
|
36
|
+
function Pane(options, chart, firstAxis) {
|
37
|
+
this.init.call(this, options, chart, firstAxis);
|
38
|
+
}
|
39
|
+
|
40
|
+
// Extend the Pane prototype
|
41
|
+
extend(Pane.prototype, {
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Initiate the Pane object
|
45
|
+
*/
|
46
|
+
init: function (options, chart, firstAxis) {
|
47
|
+
var pane = this,
|
48
|
+
backgroundOption,
|
49
|
+
defaultOptions = pane.defaultOptions;
|
50
|
+
|
51
|
+
pane.chart = chart;
|
52
|
+
|
53
|
+
// Set options
|
54
|
+
if (chart.angular) { // gauges
|
55
|
+
defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions
|
56
|
+
}
|
57
|
+
pane.options = options = merge(defaultOptions, options);
|
58
|
+
|
59
|
+
backgroundOption = options.background;
|
60
|
+
|
61
|
+
// To avoid having weighty logic to place, update and remove the backgrounds,
|
62
|
+
// push them to the first axis' plot bands and borrow the existing logic there.
|
63
|
+
if (backgroundOption) {
|
64
|
+
each([].concat(splat(backgroundOption)).reverse(), function (config) {
|
65
|
+
var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients)
|
66
|
+
config = merge(pane.defaultBackgroundOptions, config);
|
67
|
+
if (backgroundColor) {
|
68
|
+
config.backgroundColor = backgroundColor;
|
69
|
+
}
|
70
|
+
config.color = config.backgroundColor; // due to naming in plotBands
|
71
|
+
firstAxis.options.plotBands.unshift(config);
|
72
|
+
});
|
73
|
+
}
|
74
|
+
},
|
75
|
+
|
76
|
+
/**
|
77
|
+
* The default options object
|
78
|
+
*/
|
79
|
+
defaultOptions: {
|
80
|
+
// background: {conditional},
|
81
|
+
center: ['50%', '50%'],
|
82
|
+
size: '85%',
|
83
|
+
startAngle: 0
|
84
|
+
//endAngle: startAngle + 360
|
85
|
+
},
|
86
|
+
|
87
|
+
/**
|
88
|
+
* The default background options
|
89
|
+
*/
|
90
|
+
defaultBackgroundOptions: {
|
91
|
+
shape: 'circle',
|
92
|
+
borderWidth: 1,
|
93
|
+
borderColor: 'silver',
|
94
|
+
backgroundColor: {
|
95
|
+
linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
|
96
|
+
stops: [
|
97
|
+
[0, '#FFF'],
|
98
|
+
[1, '#DDD']
|
99
|
+
]
|
100
|
+
},
|
101
|
+
from: Number.MIN_VALUE, // corrected to axis min
|
102
|
+
innerRadius: 0,
|
103
|
+
to: Number.MAX_VALUE, // corrected to axis max
|
104
|
+
outerRadius: '105%'
|
105
|
+
}
|
106
|
+
|
107
|
+
});
|
108
|
+
var axisProto = Axis.prototype,
|
109
|
+
tickProto = Tick.prototype;
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
|
113
|
+
*/
|
114
|
+
var hiddenAxisMixin = {
|
115
|
+
getOffset: noop,
|
116
|
+
redraw: function () {
|
117
|
+
this.isDirty = false; // prevent setting Y axis dirty
|
118
|
+
},
|
119
|
+
render: function () {
|
120
|
+
this.isDirty = false; // prevent setting Y axis dirty
|
121
|
+
},
|
122
|
+
setScale: noop,
|
123
|
+
setCategories: noop,
|
124
|
+
setTitle: noop
|
125
|
+
};
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Augmented methods for the value axis
|
129
|
+
*/
|
130
|
+
/*jslint unparam: true*/
|
131
|
+
var radialAxisMixin = {
|
132
|
+
isRadial: true,
|
133
|
+
|
134
|
+
/**
|
135
|
+
* The default options extend defaultYAxisOptions
|
136
|
+
*/
|
137
|
+
defaultRadialGaugeOptions: {
|
138
|
+
labels: {
|
139
|
+
align: 'center',
|
140
|
+
x: 0,
|
141
|
+
y: null // auto
|
142
|
+
},
|
143
|
+
minorGridLineWidth: 0,
|
144
|
+
minorTickInterval: 'auto',
|
145
|
+
minorTickLength: 10,
|
146
|
+
minorTickPosition: 'inside',
|
147
|
+
minorTickWidth: 1,
|
148
|
+
plotBands: [],
|
149
|
+
tickLength: 10,
|
150
|
+
tickPosition: 'inside',
|
151
|
+
tickWidth: 2,
|
152
|
+
title: {
|
153
|
+
rotation: 0
|
154
|
+
},
|
155
|
+
zIndex: 2 // behind dials, points in the series group
|
156
|
+
},
|
157
|
+
|
158
|
+
// Circular axis around the perimeter of a polar chart
|
159
|
+
defaultRadialXOptions: {
|
160
|
+
gridLineWidth: 1, // spokes
|
161
|
+
labels: {
|
162
|
+
align: null, // auto
|
163
|
+
distance: 15,
|
164
|
+
x: 0,
|
165
|
+
y: null // auto
|
166
|
+
},
|
167
|
+
maxPadding: 0,
|
168
|
+
minPadding: 0,
|
169
|
+
plotBands: [],
|
170
|
+
showLastLabel: false,
|
171
|
+
tickLength: 0
|
172
|
+
},
|
173
|
+
|
174
|
+
// Radial axis, like a spoke in a polar chart
|
175
|
+
defaultRadialYOptions: {
|
176
|
+
gridLineInterpolation: 'circle',
|
177
|
+
labels: {
|
178
|
+
align: 'right',
|
179
|
+
x: -3,
|
180
|
+
y: -2
|
181
|
+
},
|
182
|
+
plotBands: [],
|
183
|
+
showLastLabel: false,
|
184
|
+
title: {
|
185
|
+
x: 4,
|
186
|
+
text: null,
|
187
|
+
rotation: 90
|
188
|
+
}
|
189
|
+
},
|
190
|
+
|
191
|
+
/**
|
192
|
+
* Merge and set options
|
193
|
+
*/
|
194
|
+
setOptions: function (userOptions) {
|
195
|
+
|
196
|
+
this.options = merge(
|
197
|
+
this.defaultOptions,
|
198
|
+
this.defaultRadialOptions,
|
199
|
+
userOptions
|
200
|
+
);
|
201
|
+
|
202
|
+
},
|
203
|
+
|
204
|
+
/**
|
205
|
+
* Wrap the getOffset method to return zero offset for title or labels in a radial
|
206
|
+
* axis
|
207
|
+
*/
|
208
|
+
getOffset: function () {
|
209
|
+
// Call the Axis prototype method (the method we're in now is on the instance)
|
210
|
+
axisProto.getOffset.call(this);
|
211
|
+
|
212
|
+
// Title or label offsets are not counted
|
213
|
+
this.chart.axisOffset[this.side] = 0;
|
214
|
+
|
215
|
+
// Set the center array
|
216
|
+
this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane);
|
217
|
+
},
|
218
|
+
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Get the path for the axis line. This method is also referenced in the getPlotLinePath
|
222
|
+
* method.
|
223
|
+
*/
|
224
|
+
getLinePath: function (lineWidth, radius) {
|
225
|
+
var center = this.center;
|
226
|
+
radius = pick(radius, center[2] / 2 - this.offset);
|
227
|
+
|
228
|
+
return this.chart.renderer.symbols.arc(
|
229
|
+
this.left + center[0],
|
230
|
+
this.top + center[1],
|
231
|
+
radius,
|
232
|
+
radius,
|
233
|
+
{
|
234
|
+
start: this.startAngleRad,
|
235
|
+
end: this.endAngleRad,
|
236
|
+
open: true,
|
237
|
+
innerR: 0
|
238
|
+
}
|
239
|
+
);
|
240
|
+
},
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Override setAxisTranslation by setting the translation to the difference
|
244
|
+
* in rotation. This allows the translate method to return angle for
|
245
|
+
* any given value.
|
246
|
+
*/
|
247
|
+
setAxisTranslation: function () {
|
248
|
+
|
249
|
+
// Call uber method
|
250
|
+
axisProto.setAxisTranslation.call(this);
|
251
|
+
|
252
|
+
// Set transA and minPixelPadding
|
253
|
+
if (this.center) { // it's not defined the first time
|
254
|
+
if (this.isCircular) {
|
255
|
+
|
256
|
+
this.transA = (this.endAngleRad - this.startAngleRad) /
|
257
|
+
((this.max - this.min) || 1);
|
258
|
+
|
259
|
+
|
260
|
+
} else {
|
261
|
+
this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
|
262
|
+
}
|
263
|
+
|
264
|
+
if (this.isXAxis) {
|
265
|
+
this.minPixelPadding = this.transA * this.minPointOffset +
|
266
|
+
(this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ???
|
267
|
+
}
|
268
|
+
}
|
269
|
+
},
|
270
|
+
|
271
|
+
/**
|
272
|
+
* In case of auto connect, add one closestPointRange to the max value right before
|
273
|
+
* tickPositions are computed, so that ticks will extend passed the real max.
|
274
|
+
*/
|
275
|
+
beforeSetTickPositions: function () {
|
276
|
+
if (this.autoConnect) {
|
277
|
+
this.max += (this.categories && 1) || this.pointRange || this.closestPointRange; // #1197
|
278
|
+
}
|
279
|
+
},
|
280
|
+
|
281
|
+
/**
|
282
|
+
* Override the setAxisSize method to use the arc's circumference as length. This
|
283
|
+
* allows tickPixelInterval to apply to pixel lengths along the perimeter
|
284
|
+
*/
|
285
|
+
setAxisSize: function () {
|
286
|
+
|
287
|
+
axisProto.setAxisSize.call(this);
|
288
|
+
|
289
|
+
if (this.center) { // it's not defined the first time
|
290
|
+
this.len = this.width = this.height = this.isCircular ?
|
291
|
+
this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 :
|
292
|
+
this.center[2] / 2;
|
293
|
+
}
|
294
|
+
},
|
295
|
+
|
296
|
+
/**
|
297
|
+
* Returns the x, y coordinate of a point given by a value and a pixel distance
|
298
|
+
* from center
|
299
|
+
*/
|
300
|
+
getPosition: function (value, length) {
|
301
|
+
if (!this.isCircular) {
|
302
|
+
length = this.translate(value);
|
303
|
+
value = this.min;
|
304
|
+
}
|
305
|
+
|
306
|
+
return this.postTranslate(
|
307
|
+
this.translate(value),
|
308
|
+
pick(length, this.center[2] / 2) - this.offset
|
309
|
+
);
|
310
|
+
},
|
311
|
+
|
312
|
+
/**
|
313
|
+
* Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.
|
314
|
+
*/
|
315
|
+
postTranslate: function (angle, radius) {
|
316
|
+
|
317
|
+
var chart = this.chart,
|
318
|
+
center = this.center;
|
319
|
+
|
320
|
+
angle = this.startAngleRad + angle;
|
321
|
+
|
322
|
+
return {
|
323
|
+
x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
|
324
|
+
y: chart.plotTop + center[1] + Math.sin(angle) * radius
|
325
|
+
};
|
326
|
+
|
327
|
+
},
|
328
|
+
|
329
|
+
/**
|
330
|
+
* Find the path for plot bands along the radial axis
|
331
|
+
*/
|
332
|
+
getPlotBandPath: function (from, to, options) {
|
333
|
+
var center = this.center,
|
334
|
+
startAngleRad = this.startAngleRad,
|
335
|
+
fullRadius = center[2] / 2,
|
336
|
+
radii = [
|
337
|
+
pick(options.outerRadius, '100%'),
|
338
|
+
options.innerRadius,
|
339
|
+
pick(options.thickness, 10)
|
340
|
+
],
|
341
|
+
percentRegex = /%$/,
|
342
|
+
start,
|
343
|
+
end,
|
344
|
+
open,
|
345
|
+
isCircular = this.isCircular, // X axis in a polar chart
|
346
|
+
ret;
|
347
|
+
|
348
|
+
// Polygonal plot bands
|
349
|
+
if (this.options.gridLineInterpolation === 'polygon') {
|
350
|
+
ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
|
351
|
+
|
352
|
+
// Circular grid bands
|
353
|
+
} else {
|
354
|
+
|
355
|
+
// Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
|
356
|
+
if (!isCircular) {
|
357
|
+
radii[0] = this.translate(from);
|
358
|
+
radii[1] = this.translate(to);
|
359
|
+
}
|
360
|
+
|
361
|
+
// Convert percentages to pixel values
|
362
|
+
radii = map(radii, function (radius) {
|
363
|
+
if (percentRegex.test(radius)) {
|
364
|
+
radius = (pInt(radius, 10) * fullRadius) / 100;
|
365
|
+
}
|
366
|
+
return radius;
|
367
|
+
});
|
368
|
+
|
369
|
+
// Handle full circle
|
370
|
+
if (options.shape === 'circle' || !isCircular) {
|
371
|
+
start = -Math.PI / 2;
|
372
|
+
end = Math.PI * 1.5;
|
373
|
+
open = true;
|
374
|
+
} else {
|
375
|
+
start = startAngleRad + this.translate(from);
|
376
|
+
end = startAngleRad + this.translate(to);
|
377
|
+
}
|
378
|
+
|
379
|
+
|
380
|
+
ret = this.chart.renderer.symbols.arc(
|
381
|
+
this.left + center[0],
|
382
|
+
this.top + center[1],
|
383
|
+
radii[0],
|
384
|
+
radii[0],
|
385
|
+
{
|
386
|
+
start: start,
|
387
|
+
end: end,
|
388
|
+
innerR: pick(radii[1], radii[0] - radii[2]),
|
389
|
+
open: open
|
390
|
+
}
|
391
|
+
);
|
392
|
+
}
|
393
|
+
|
394
|
+
return ret;
|
395
|
+
},
|
396
|
+
|
397
|
+
/**
|
398
|
+
* Find the path for plot lines perpendicular to the radial axis.
|
399
|
+
*/
|
400
|
+
getPlotLinePath: function (value, reverse) {
|
401
|
+
var axis = this,
|
402
|
+
center = axis.center,
|
403
|
+
chart = axis.chart,
|
404
|
+
end = axis.getPosition(value),
|
405
|
+
xAxis,
|
406
|
+
xy,
|
407
|
+
tickPositions,
|
408
|
+
ret;
|
409
|
+
|
410
|
+
// Spokes
|
411
|
+
if (axis.isCircular) {
|
412
|
+
ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
|
413
|
+
|
414
|
+
// Concentric circles
|
415
|
+
} else if (axis.options.gridLineInterpolation === 'circle') {
|
416
|
+
value = axis.translate(value);
|
417
|
+
if (value) { // a value of 0 is in the center
|
418
|
+
ret = axis.getLinePath(0, value);
|
419
|
+
}
|
420
|
+
// Concentric polygons
|
421
|
+
} else {
|
422
|
+
xAxis = chart.xAxis[0];
|
423
|
+
ret = [];
|
424
|
+
value = axis.translate(value);
|
425
|
+
tickPositions = xAxis.tickPositions;
|
426
|
+
if (xAxis.autoConnect) {
|
427
|
+
tickPositions = tickPositions.concat([tickPositions[0]]);
|
428
|
+
}
|
429
|
+
// Reverse the positions for concatenation of polygonal plot bands
|
430
|
+
if (reverse) {
|
431
|
+
tickPositions = [].concat(tickPositions).reverse();
|
432
|
+
}
|
433
|
+
|
434
|
+
each(tickPositions, function (pos, i) {
|
435
|
+
xy = xAxis.getPosition(pos, value);
|
436
|
+
ret.push(i ? 'L' : 'M', xy.x, xy.y);
|
437
|
+
});
|
438
|
+
|
439
|
+
}
|
440
|
+
return ret;
|
441
|
+
},
|
442
|
+
|
443
|
+
/**
|
444
|
+
* Find the position for the axis title, by default inside the gauge
|
445
|
+
*/
|
446
|
+
getTitlePosition: function () {
|
447
|
+
var center = this.center,
|
448
|
+
chart = this.chart,
|
449
|
+
titleOptions = this.options.title;
|
450
|
+
|
451
|
+
return {
|
452
|
+
x: chart.plotLeft + center[0] + (titleOptions.x || 0),
|
453
|
+
y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] *
|
454
|
+
center[2]) + (titleOptions.y || 0)
|
455
|
+
};
|
456
|
+
}
|
457
|
+
|
458
|
+
};
|
459
|
+
/*jslint unparam: false*/
|
460
|
+
|
461
|
+
/**
|
462
|
+
* Override axisProto.init to mix in special axis instance functions and function overrides
|
463
|
+
*/
|
464
|
+
wrap(axisProto, 'init', function (proceed, chart, userOptions) {
|
465
|
+
var axis = this,
|
466
|
+
angular = chart.angular,
|
467
|
+
polar = chart.polar,
|
468
|
+
isX = userOptions.isX,
|
469
|
+
isHidden = angular && isX,
|
470
|
+
isCircular,
|
471
|
+
startAngleRad,
|
472
|
+
endAngleRad,
|
473
|
+
options,
|
474
|
+
chartOptions = chart.options,
|
475
|
+
paneIndex = userOptions.pane || 0,
|
476
|
+
pane,
|
477
|
+
paneOptions;
|
478
|
+
|
479
|
+
// Before prototype.init
|
480
|
+
if (angular) {
|
481
|
+
extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
|
482
|
+
isCircular = !isX;
|
483
|
+
if (isCircular) {
|
484
|
+
this.defaultRadialOptions = this.defaultRadialGaugeOptions;
|
485
|
+
}
|
486
|
+
|
487
|
+
} else if (polar) {
|
488
|
+
//extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin);
|
489
|
+
extend(this, radialAxisMixin);
|
490
|
+
isCircular = isX;
|
491
|
+
this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
|
492
|
+
|
493
|
+
}
|
494
|
+
|
495
|
+
// Run prototype.init
|
496
|
+
proceed.call(this, chart, userOptions);
|
497
|
+
|
498
|
+
if (!isHidden && (angular || polar)) {
|
499
|
+
options = this.options;
|
500
|
+
|
501
|
+
// Create the pane and set the pane options.
|
502
|
+
if (!chart.panes) {
|
503
|
+
chart.panes = [];
|
504
|
+
}
|
505
|
+
this.pane = chart.panes[paneIndex] = pane = new Pane(
|
506
|
+
splat(chartOptions.pane)[paneIndex],
|
507
|
+
chart,
|
508
|
+
axis
|
509
|
+
);
|
510
|
+
paneOptions = pane.options;
|
511
|
+
|
512
|
+
|
513
|
+
// Disable certain features on angular and polar axes
|
514
|
+
chart.inverted = false;
|
515
|
+
chartOptions.chart.zoomType = null;
|
516
|
+
|
517
|
+
// Start and end angle options are
|
518
|
+
// given in degrees relative to top, while internal computations are
|
519
|
+
// in radians relative to right (like SVG).
|
520
|
+
this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180;
|
521
|
+
this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360) - 90) * Math.PI / 180;
|
522
|
+
this.offset = options.offset || 0;
|
523
|
+
|
524
|
+
this.isCircular = isCircular;
|
525
|
+
|
526
|
+
// Automatically connect grid lines?
|
527
|
+
if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) {
|
528
|
+
this.autoConnect = true;
|
529
|
+
}
|
530
|
+
}
|
531
|
+
|
532
|
+
});
|
533
|
+
|
534
|
+
/**
|
535
|
+
* Add special cases within the Tick class' methods for radial axes.
|
536
|
+
*/
|
537
|
+
wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
|
538
|
+
var axis = this.axis;
|
539
|
+
|
540
|
+
return axis.getPosition ?
|
541
|
+
axis.getPosition(pos) :
|
542
|
+
proceed.call(this, horiz, pos, tickmarkOffset, old);
|
543
|
+
});
|
544
|
+
|
545
|
+
/**
|
546
|
+
* Wrap the getLabelPosition function to find the center position of the label
|
547
|
+
* based on the distance option
|
548
|
+
*/
|
549
|
+
wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
|
550
|
+
var axis = this.axis,
|
551
|
+
optionsY = labelOptions.y,
|
552
|
+
ret,
|
553
|
+
align = labelOptions.align,
|
554
|
+
angle = (axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180;
|
555
|
+
|
556
|
+
if (axis.isRadial) {
|
557
|
+
ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
|
558
|
+
|
559
|
+
// Automatically rotated
|
560
|
+
if (labelOptions.rotation === 'auto') {
|
561
|
+
label.attr({
|
562
|
+
rotation: angle
|
563
|
+
});
|
564
|
+
|
565
|
+
// Vertically centered
|
566
|
+
} else if (optionsY === null) {
|
567
|
+
optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
|
568
|
+
|
569
|
+
}
|
570
|
+
|
571
|
+
// Automatic alignment
|
572
|
+
if (align === null) {
|
573
|
+
if (axis.isCircular) {
|
574
|
+
if (angle > 20 && angle < 160) {
|
575
|
+
align = 'left'; // right hemisphere
|
576
|
+
} else if (angle > 200 && angle < 340) {
|
577
|
+
align = 'right'; // left hemisphere
|
578
|
+
} else {
|
579
|
+
align = 'center'; // top or bottom
|
580
|
+
}
|
581
|
+
} else {
|
582
|
+
align = 'center';
|
583
|
+
}
|
584
|
+
label.attr({
|
585
|
+
align: align
|
586
|
+
});
|
587
|
+
}
|
588
|
+
|
589
|
+
ret.x += labelOptions.x;
|
590
|
+
ret.y += optionsY;
|
591
|
+
|
592
|
+
} else {
|
593
|
+
ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
|
594
|
+
}
|
595
|
+
return ret;
|
596
|
+
});
|
597
|
+
|
598
|
+
/**
|
599
|
+
* Wrap the getMarkPath function to return the path of the radial marker
|
600
|
+
*/
|
601
|
+
wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
|
602
|
+
var axis = this.axis,
|
603
|
+
endPoint,
|
604
|
+
ret;
|
605
|
+
|
606
|
+
if (axis.isRadial) {
|
607
|
+
endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
|
608
|
+
ret = [
|
609
|
+
'M',
|
610
|
+
x,
|
611
|
+
y,
|
612
|
+
'L',
|
613
|
+
endPoint.x,
|
614
|
+
endPoint.y
|
615
|
+
];
|
616
|
+
} else {
|
617
|
+
ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
|
618
|
+
}
|
619
|
+
return ret;
|
620
|
+
});/*
|
621
|
+
* The AreaRangeSeries class
|
622
|
+
*
|
623
|
+
*/
|
624
|
+
|
625
|
+
/**
|
626
|
+
* Extend the default options with map options
|
627
|
+
*/
|
628
|
+
defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
|
629
|
+
lineWidth: 1,
|
630
|
+
marker: null,
|
631
|
+
threshold: null,
|
632
|
+
tooltip: {
|
633
|
+
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'
|
634
|
+
},
|
635
|
+
trackByArea: true,
|
636
|
+
dataLabels: {
|
637
|
+
verticalAlign: null,
|
638
|
+
xLow: 0,
|
639
|
+
xHigh: 0,
|
640
|
+
yLow: 0,
|
641
|
+
yHigh: 0
|
642
|
+
},
|
643
|
+
shadow: false
|
644
|
+
});
|
645
|
+
|
646
|
+
/**
|
647
|
+
* Extend the point object
|
648
|
+
*/
|
649
|
+
var RangePoint = Highcharts.extendClass(Highcharts.Point, {
|
650
|
+
/**
|
651
|
+
* Apply the options containing the x and low/high data and possible some extra properties.
|
652
|
+
* This is called on point init or from point.update. Extends base Point by adding
|
653
|
+
* multiple y-like values.
|
654
|
+
*
|
655
|
+
* @param {Object} options
|
656
|
+
*/
|
657
|
+
applyOptions: function (options, x) {
|
658
|
+
var point = this,
|
659
|
+
series = point.series,
|
660
|
+
pointArrayMap = series.pointArrayMap,
|
661
|
+
i = 0,
|
662
|
+
j = 0,
|
663
|
+
valueCount = pointArrayMap.length;
|
664
|
+
|
665
|
+
|
666
|
+
// object input
|
667
|
+
if (typeof options === 'object' && typeof options.length !== 'number') {
|
668
|
+
|
669
|
+
// copy options directly to point
|
670
|
+
extend(point, options);
|
671
|
+
|
672
|
+
point.options = options;
|
673
|
+
|
674
|
+
} else if (options.length) { // array
|
675
|
+
// with leading x value
|
676
|
+
if (options.length > valueCount) {
|
677
|
+
if (typeof options[0] === 'string') {
|
678
|
+
point.name = options[0];
|
679
|
+
} else if (typeof options[0] === 'number') {
|
680
|
+
point.x = options[0];
|
681
|
+
}
|
682
|
+
i++;
|
683
|
+
}
|
684
|
+
while (j < valueCount) {
|
685
|
+
point[pointArrayMap[j++]] = options[i++];
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
// Handle null and make low alias y
|
690
|
+
/*if (point.high === null) {
|
691
|
+
point.low = null;
|
692
|
+
}*/
|
693
|
+
point.y = point[series.pointValKey];
|
694
|
+
|
695
|
+
// If no x is set by now, get auto incremented value. All points must have an
|
696
|
+
// x value, however the y value can be null to create a gap in the series
|
697
|
+
if (point.x === UNDEFINED && series) {
|
698
|
+
point.x = x === UNDEFINED ? series.autoIncrement() : x;
|
699
|
+
}
|
700
|
+
|
701
|
+
return point;
|
702
|
+
},
|
703
|
+
|
704
|
+
/**
|
705
|
+
* Return a plain array for speedy calculation
|
706
|
+
*/
|
707
|
+
toYData: function () {
|
708
|
+
return [this.low, this.high];
|
709
|
+
}
|
710
|
+
});
|
711
|
+
|
712
|
+
/**
|
713
|
+
* Add the series type
|
714
|
+
*/
|
715
|
+
seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
|
716
|
+
type: 'arearange',
|
717
|
+
pointArrayMap: ['low', 'high'],
|
718
|
+
pointClass: RangePoint,
|
719
|
+
pointValKey: 'low',
|
720
|
+
|
721
|
+
/**
|
722
|
+
* Translate data points from raw values x and y to plotX and plotY
|
723
|
+
*/
|
724
|
+
translate: function () {
|
725
|
+
var series = this,
|
726
|
+
yAxis = series.yAxis;
|
727
|
+
|
728
|
+
seriesTypes.area.prototype.translate.apply(series);
|
729
|
+
|
730
|
+
// Set plotLow and plotHigh
|
731
|
+
each(series.points, function (point) {
|
732
|
+
|
733
|
+
if (point.y !== null) {
|
734
|
+
point.plotLow = point.plotY;
|
735
|
+
point.plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
|
736
|
+
}
|
737
|
+
});
|
738
|
+
},
|
739
|
+
|
740
|
+
/**
|
741
|
+
* Extend the line series' getSegmentPath method by applying the segment
|
742
|
+
* path to both lower and higher values of the range
|
743
|
+
*/
|
744
|
+
getSegmentPath: function (segment) {
|
745
|
+
|
746
|
+
var highSegment = [],
|
747
|
+
i = segment.length,
|
748
|
+
baseGetSegmentPath = Series.prototype.getSegmentPath,
|
749
|
+
point,
|
750
|
+
linePath,
|
751
|
+
lowerPath,
|
752
|
+
options = this.options,
|
753
|
+
step = options.step,
|
754
|
+
higherPath;
|
755
|
+
|
756
|
+
// Make a segment with plotX and plotY for the top values
|
757
|
+
while (i--) {
|
758
|
+
point = segment[i];
|
759
|
+
highSegment.push({
|
760
|
+
plotX: point.plotX,
|
761
|
+
plotY: point.plotHigh
|
762
|
+
});
|
763
|
+
}
|
764
|
+
|
765
|
+
// Get the paths
|
766
|
+
lowerPath = baseGetSegmentPath.call(this, segment);
|
767
|
+
if (step) {
|
768
|
+
if (step === true) {
|
769
|
+
step = 'left';
|
770
|
+
}
|
771
|
+
options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
|
772
|
+
}
|
773
|
+
higherPath = baseGetSegmentPath.call(this, highSegment);
|
774
|
+
options.step = step;
|
775
|
+
|
776
|
+
// Create a line on both top and bottom of the range
|
777
|
+
linePath = [].concat(lowerPath, higherPath);
|
778
|
+
|
779
|
+
// For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
|
780
|
+
higherPath[0] = 'L'; // this probably doesn't work for spline
|
781
|
+
this.areaPath = this.areaPath.concat(lowerPath, higherPath);
|
782
|
+
|
783
|
+
return linePath;
|
784
|
+
},
|
785
|
+
|
786
|
+
/**
|
787
|
+
* Extend the basic drawDataLabels method by running it for both lower and higher
|
788
|
+
* values.
|
789
|
+
*/
|
790
|
+
drawDataLabels: function () {
|
791
|
+
|
792
|
+
var data = this.data,
|
793
|
+
length = data.length,
|
794
|
+
i,
|
795
|
+
originalDataLabels = [],
|
796
|
+
seriesProto = Series.prototype,
|
797
|
+
dataLabelOptions = this.options.dataLabels,
|
798
|
+
point,
|
799
|
+
inverted = this.chart.inverted;
|
800
|
+
|
801
|
+
if (dataLabelOptions.enabled || this._hasPointLabels) {
|
802
|
+
|
803
|
+
// Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
|
804
|
+
i = length;
|
805
|
+
while (i--) {
|
806
|
+
point = data[i];
|
807
|
+
|
808
|
+
// Set preliminary values
|
809
|
+
point.y = point.high;
|
810
|
+
point.plotY = point.plotHigh;
|
811
|
+
|
812
|
+
// Store original data labels and set preliminary label objects to be picked up
|
813
|
+
// in the uber method
|
814
|
+
originalDataLabels[i] = point.dataLabel;
|
815
|
+
point.dataLabel = point.dataLabelUpper;
|
816
|
+
|
817
|
+
// Set the default offset
|
818
|
+
point.below = false;
|
819
|
+
if (inverted) {
|
820
|
+
dataLabelOptions.align = 'left';
|
821
|
+
dataLabelOptions.x = dataLabelOptions.xHigh;
|
822
|
+
} else {
|
823
|
+
dataLabelOptions.y = dataLabelOptions.yHigh;
|
824
|
+
}
|
825
|
+
}
|
826
|
+
seriesProto.drawDataLabels.apply(this, arguments); // #1209
|
827
|
+
|
828
|
+
// Step 2: reorganize and handle data labels for the lower values
|
829
|
+
i = length;
|
830
|
+
while (i--) {
|
831
|
+
point = data[i];
|
832
|
+
|
833
|
+
// Move the generated labels from step 1, and reassign the original data labels
|
834
|
+
point.dataLabelUpper = point.dataLabel;
|
835
|
+
point.dataLabel = originalDataLabels[i];
|
836
|
+
|
837
|
+
// Reset values
|
838
|
+
point.y = point.low;
|
839
|
+
point.plotY = point.plotLow;
|
840
|
+
|
841
|
+
// Set the default offset
|
842
|
+
point.below = true;
|
843
|
+
if (inverted) {
|
844
|
+
dataLabelOptions.align = 'right';
|
845
|
+
dataLabelOptions.x = dataLabelOptions.xLow;
|
846
|
+
} else {
|
847
|
+
dataLabelOptions.y = dataLabelOptions.yLow;
|
848
|
+
}
|
849
|
+
}
|
850
|
+
seriesProto.drawDataLabels.apply(this, arguments);
|
851
|
+
}
|
852
|
+
|
853
|
+
},
|
854
|
+
|
855
|
+
alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
|
856
|
+
|
857
|
+
getSymbol: seriesTypes.column.prototype.getSymbol,
|
858
|
+
|
859
|
+
drawPoints: noop
|
860
|
+
});/**
|
861
|
+
* The AreaSplineRangeSeries class
|
862
|
+
*/
|
863
|
+
|
864
|
+
defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
|
865
|
+
|
866
|
+
/**
|
867
|
+
* AreaSplineRangeSeries object
|
868
|
+
*/
|
869
|
+
seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
|
870
|
+
type: 'areasplinerange',
|
871
|
+
getPointSpline: seriesTypes.spline.prototype.getPointSpline
|
872
|
+
});/**
|
873
|
+
* The ColumnRangeSeries class
|
874
|
+
*/
|
875
|
+
defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
|
876
|
+
lineWidth: 1,
|
877
|
+
pointRange: null
|
878
|
+
});
|
879
|
+
|
880
|
+
/**
|
881
|
+
* ColumnRangeSeries object
|
882
|
+
*/
|
883
|
+
seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
|
884
|
+
type: 'columnrange',
|
885
|
+
/**
|
886
|
+
* Translate data points from raw values x and y to plotX and plotY
|
887
|
+
*/
|
888
|
+
translate: function () {
|
889
|
+
var series = this,
|
890
|
+
yAxis = series.yAxis,
|
891
|
+
plotHigh;
|
892
|
+
|
893
|
+
colProto.translate.apply(series);
|
894
|
+
|
895
|
+
// Set plotLow and plotHigh
|
896
|
+
each(series.points, function (point) {
|
897
|
+
var shapeArgs = point.shapeArgs;
|
898
|
+
|
899
|
+
point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
|
900
|
+
point.plotLow = point.plotY;
|
901
|
+
|
902
|
+
// adjust shape
|
903
|
+
shapeArgs.y = plotHigh;
|
904
|
+
shapeArgs.height = point.plotY - plotHigh;
|
905
|
+
|
906
|
+
point.trackerArgs = shapeArgs;
|
907
|
+
});
|
908
|
+
},
|
909
|
+
drawGraph: noop,
|
910
|
+
pointAttrToOptions: colProto.pointAttrToOptions,
|
911
|
+
drawPoints: colProto.drawPoints,
|
912
|
+
drawTracker: colProto.drawTracker,
|
913
|
+
animate: colProto.animate
|
914
|
+
});/*
|
915
|
+
* The GaugeSeries class
|
916
|
+
*/
|
917
|
+
|
918
|
+
|
919
|
+
|
920
|
+
/**
|
921
|
+
* Extend the default options
|
922
|
+
*/
|
923
|
+
defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
|
924
|
+
dataLabels: {
|
925
|
+
enabled: true,
|
926
|
+
y: 15,
|
927
|
+
borderWidth: 1,
|
928
|
+
borderColor: 'silver',
|
929
|
+
borderRadius: 3,
|
930
|
+
style: {
|
931
|
+
fontWeight: 'bold'
|
932
|
+
},
|
933
|
+
verticalAlign: 'top',
|
934
|
+
zIndex: 2
|
935
|
+
},
|
936
|
+
dial: {
|
937
|
+
// radius: '80%',
|
938
|
+
// backgroundColor: 'black',
|
939
|
+
// borderColor: 'silver',
|
940
|
+
// borderWidth: 0,
|
941
|
+
// baseWidth: 3,
|
942
|
+
// topWidth: 1,
|
943
|
+
// baseLength: '70%' // of radius
|
944
|
+
// rearLength: '10%'
|
945
|
+
},
|
946
|
+
pivot: {
|
947
|
+
//radius: 5,
|
948
|
+
//borderWidth: 0
|
949
|
+
//borderColor: 'silver',
|
950
|
+
//backgroundColor: 'black'
|
951
|
+
},
|
952
|
+
tooltip: {
|
953
|
+
headerFormat: ''
|
954
|
+
},
|
955
|
+
showInLegend: false
|
956
|
+
});
|
957
|
+
|
958
|
+
/**
|
959
|
+
* Extend the point object
|
960
|
+
*/
|
961
|
+
var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
|
962
|
+
/**
|
963
|
+
* Don't do any hover colors or anything
|
964
|
+
*/
|
965
|
+
setState: function (state) {
|
966
|
+
this.state = state;
|
967
|
+
}
|
968
|
+
});
|
969
|
+
|
970
|
+
|
971
|
+
/**
|
972
|
+
* Add the series type
|
973
|
+
*/
|
974
|
+
var GaugeSeries = {
|
975
|
+
type: 'gauge',
|
976
|
+
pointClass: GaugePoint,
|
977
|
+
|
978
|
+
// chart.angular will be set to true when a gauge series is present, and this will
|
979
|
+
// be used on the axes
|
980
|
+
angular: true,
|
981
|
+
|
982
|
+
/* *
|
983
|
+
* Extend the bindAxes method by adding radial features to the axes
|
984
|
+
* /
|
985
|
+
_bindAxes: function () {
|
986
|
+
Series.prototype.bindAxes.call(this);
|
987
|
+
|
988
|
+
extend(this.xAxis, gaugeXAxisMixin);
|
989
|
+
extend(this.yAxis, radialAxisMixin);
|
990
|
+
this.yAxis.onBind();
|
991
|
+
},*/
|
992
|
+
|
993
|
+
/**
|
994
|
+
* Calculate paths etc
|
995
|
+
*/
|
996
|
+
translate: function () {
|
997
|
+
|
998
|
+
var series = this,
|
999
|
+
yAxis = series.yAxis,
|
1000
|
+
center = yAxis.center;
|
1001
|
+
|
1002
|
+
series.generatePoints();
|
1003
|
+
|
1004
|
+
each(series.points, function (point) {
|
1005
|
+
|
1006
|
+
var dialOptions = merge(series.options.dial, point.dial),
|
1007
|
+
radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
|
1008
|
+
baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
|
1009
|
+
rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
|
1010
|
+
baseWidth = dialOptions.baseWidth || 3,
|
1011
|
+
topWidth = dialOptions.topWidth || 1;
|
1012
|
+
|
1013
|
+
point.shapeType = 'path';
|
1014
|
+
point.shapeArgs = {
|
1015
|
+
d: dialOptions.path || [
|
1016
|
+
'M',
|
1017
|
+
-rearLength, -baseWidth / 2,
|
1018
|
+
'L',
|
1019
|
+
baseLength, -baseWidth / 2,
|
1020
|
+
radius, -topWidth / 2,
|
1021
|
+
radius, topWidth / 2,
|
1022
|
+
baseLength, baseWidth / 2,
|
1023
|
+
-rearLength, baseWidth / 2,
|
1024
|
+
'z'
|
1025
|
+
],
|
1026
|
+
translateX: center[0],
|
1027
|
+
translateY: center[1],
|
1028
|
+
rotation: (yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true)) * 180 / Math.PI
|
1029
|
+
};
|
1030
|
+
|
1031
|
+
// Positions for data label
|
1032
|
+
point.plotX = center[0];
|
1033
|
+
point.plotY = center[1];
|
1034
|
+
});
|
1035
|
+
},
|
1036
|
+
|
1037
|
+
/**
|
1038
|
+
* Draw the points where each point is one needle
|
1039
|
+
*/
|
1040
|
+
drawPoints: function () {
|
1041
|
+
|
1042
|
+
var series = this,
|
1043
|
+
center = series.yAxis.center,
|
1044
|
+
pivot = series.pivot,
|
1045
|
+
options = series.options,
|
1046
|
+
pivotOptions = options.pivot,
|
1047
|
+
renderer = series.chart.renderer;
|
1048
|
+
|
1049
|
+
each(series.points, function (point) {
|
1050
|
+
|
1051
|
+
var graphic = point.graphic,
|
1052
|
+
shapeArgs = point.shapeArgs,
|
1053
|
+
d = shapeArgs.d,
|
1054
|
+
dialOptions = merge(options.dial, point.dial); // #1233
|
1055
|
+
|
1056
|
+
if (graphic) {
|
1057
|
+
graphic.animate(shapeArgs);
|
1058
|
+
shapeArgs.d = d; // animate alters it
|
1059
|
+
} else {
|
1060
|
+
point.graphic = renderer[point.shapeType](shapeArgs)
|
1061
|
+
.attr({
|
1062
|
+
stroke: dialOptions.borderColor || 'none',
|
1063
|
+
'stroke-width': dialOptions.borderWidth || 0,
|
1064
|
+
fill: dialOptions.backgroundColor || 'black',
|
1065
|
+
rotation: shapeArgs.rotation // required by VML when animation is false
|
1066
|
+
})
|
1067
|
+
.add(series.group);
|
1068
|
+
}
|
1069
|
+
});
|
1070
|
+
|
1071
|
+
// Add or move the pivot
|
1072
|
+
if (pivot) {
|
1073
|
+
pivot.animate({ // #1235
|
1074
|
+
translateX: center[0],
|
1075
|
+
translateY: center[1]
|
1076
|
+
});
|
1077
|
+
} else {
|
1078
|
+
series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
|
1079
|
+
.attr({
|
1080
|
+
'stroke-width': pivotOptions.borderWidth || 0,
|
1081
|
+
stroke: pivotOptions.borderColor || 'silver',
|
1082
|
+
fill: pivotOptions.backgroundColor || 'black'
|
1083
|
+
})
|
1084
|
+
.translate(center[0], center[1])
|
1085
|
+
.add(series.group);
|
1086
|
+
}
|
1087
|
+
},
|
1088
|
+
|
1089
|
+
/**
|
1090
|
+
* Animate the arrow up from startAngle
|
1091
|
+
*/
|
1092
|
+
animate: function () {
|
1093
|
+
var series = this;
|
1094
|
+
|
1095
|
+
each(series.points, function (point) {
|
1096
|
+
var graphic = point.graphic;
|
1097
|
+
|
1098
|
+
if (graphic) {
|
1099
|
+
// start value
|
1100
|
+
graphic.attr({
|
1101
|
+
rotation: series.yAxis.startAngleRad * 180 / Math.PI
|
1102
|
+
});
|
1103
|
+
|
1104
|
+
// animate
|
1105
|
+
graphic.animate({
|
1106
|
+
rotation: point.shapeArgs.rotation
|
1107
|
+
}, series.options.animation);
|
1108
|
+
}
|
1109
|
+
});
|
1110
|
+
|
1111
|
+
// delete this function to allow it only once
|
1112
|
+
series.animate = null;
|
1113
|
+
},
|
1114
|
+
|
1115
|
+
render: function () {
|
1116
|
+
this.group = this.plotGroup(
|
1117
|
+
'group',
|
1118
|
+
'series',
|
1119
|
+
this.visible ? 'visible' : 'hidden',
|
1120
|
+
this.options.zIndex,
|
1121
|
+
this.chart.seriesGroup
|
1122
|
+
);
|
1123
|
+
seriesTypes.pie.prototype.render.call(this);
|
1124
|
+
this.group.clip(this.chart.clipRect);
|
1125
|
+
},
|
1126
|
+
|
1127
|
+
setData: seriesTypes.pie.prototype.setData,
|
1128
|
+
drawTracker: seriesTypes.column.prototype.drawTracker
|
1129
|
+
};
|
1130
|
+
seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/**
|
1131
|
+
* Extensions for polar charts. Additionally, much of the geometry required for polar charts is
|
1132
|
+
* gathered in RadialAxes.js.
|
1133
|
+
*
|
1134
|
+
*/
|
1135
|
+
|
1136
|
+
var seriesProto = Series.prototype,
|
1137
|
+
mouseTrackerProto = Highcharts.MouseTracker.prototype;
|
1138
|
+
|
1139
|
+
|
1140
|
+
|
1141
|
+
/**
|
1142
|
+
* Translate a point's plotX and plotY from the internal angle and radius measures to
|
1143
|
+
* true plotX, plotY coordinates
|
1144
|
+
*/
|
1145
|
+
seriesProto.toXY = function (point) {
|
1146
|
+
var xy,
|
1147
|
+
chart = this.chart,
|
1148
|
+
plotX = point.plotX,
|
1149
|
+
plotY = point.plotY;
|
1150
|
+
|
1151
|
+
// Save rectangular plotX, plotY for later computation
|
1152
|
+
point.rectPlotX = plotX;
|
1153
|
+
point.rectPlotY = plotY;
|
1154
|
+
|
1155
|
+
// Record the angle in degrees for use in tooltip
|
1156
|
+
point.deg = plotX / Math.PI * 180;
|
1157
|
+
|
1158
|
+
// Find the polar plotX and plotY
|
1159
|
+
xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
|
1160
|
+
point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
|
1161
|
+
point.plotY = point.polarPlotY = xy.y - chart.plotTop;
|
1162
|
+
};
|
1163
|
+
|
1164
|
+
|
1165
|
+
/**
|
1166
|
+
* Add some special init logic to areas and areasplines
|
1167
|
+
*/
|
1168
|
+
function initArea(proceed, chart, options) {
|
1169
|
+
proceed.call(this, chart, options);
|
1170
|
+
if (this.chart.polar) {
|
1171
|
+
|
1172
|
+
/**
|
1173
|
+
* Overridden method to close a segment path. While in a cartesian plane the area
|
1174
|
+
* goes down to the threshold, in the polar chart it goes to the center.
|
1175
|
+
*/
|
1176
|
+
this.closeSegment = function (path) {
|
1177
|
+
var center = this.xAxis.center;
|
1178
|
+
path.push(
|
1179
|
+
'L',
|
1180
|
+
center[0],
|
1181
|
+
center[1]
|
1182
|
+
);
|
1183
|
+
};
|
1184
|
+
|
1185
|
+
// Instead of complicated logic to draw an area around the inner area in a stack,
|
1186
|
+
// just draw it behind
|
1187
|
+
this.closedStacks = true;
|
1188
|
+
}
|
1189
|
+
}
|
1190
|
+
wrap(seriesTypes.area.prototype, 'init', initArea);
|
1191
|
+
wrap(seriesTypes.areaspline.prototype, 'init', initArea);
|
1192
|
+
|
1193
|
+
|
1194
|
+
/**
|
1195
|
+
* Overridden method for calculating a spline from one point to the next
|
1196
|
+
*/
|
1197
|
+
wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
|
1198
|
+
|
1199
|
+
var ret,
|
1200
|
+
smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
|
1201
|
+
denom = smoothing + 1,
|
1202
|
+
plotX,
|
1203
|
+
plotY,
|
1204
|
+
lastPoint,
|
1205
|
+
nextPoint,
|
1206
|
+
lastX,
|
1207
|
+
lastY,
|
1208
|
+
nextX,
|
1209
|
+
nextY,
|
1210
|
+
leftContX,
|
1211
|
+
leftContY,
|
1212
|
+
rightContX,
|
1213
|
+
rightContY,
|
1214
|
+
distanceLeftControlPoint,
|
1215
|
+
distanceRightControlPoint,
|
1216
|
+
leftContAngle,
|
1217
|
+
rightContAngle,
|
1218
|
+
jointAngle;
|
1219
|
+
|
1220
|
+
|
1221
|
+
if (this.chart.polar) {
|
1222
|
+
|
1223
|
+
plotX = point.plotX;
|
1224
|
+
plotY = point.plotY;
|
1225
|
+
lastPoint = segment[i - 1];
|
1226
|
+
nextPoint = segment[i + 1];
|
1227
|
+
|
1228
|
+
// Connect ends
|
1229
|
+
if (this.connectEnds) {
|
1230
|
+
if (!lastPoint) {
|
1231
|
+
lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
|
1232
|
+
}
|
1233
|
+
if (!nextPoint) {
|
1234
|
+
nextPoint = segment[1];
|
1235
|
+
}
|
1236
|
+
}
|
1237
|
+
|
1238
|
+
// find control points
|
1239
|
+
if (lastPoint && nextPoint) {
|
1240
|
+
|
1241
|
+
lastX = lastPoint.plotX;
|
1242
|
+
lastY = lastPoint.plotY;
|
1243
|
+
nextX = nextPoint.plotX;
|
1244
|
+
nextY = nextPoint.plotY;
|
1245
|
+
leftContX = (smoothing * plotX + lastX) / denom;
|
1246
|
+
leftContY = (smoothing * plotY + lastY) / denom;
|
1247
|
+
rightContX = (smoothing * plotX + nextX) / denom;
|
1248
|
+
rightContY = (smoothing * plotY + nextY) / denom;
|
1249
|
+
distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
|
1250
|
+
distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
|
1251
|
+
leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
|
1252
|
+
rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
|
1253
|
+
jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
|
1254
|
+
|
1255
|
+
|
1256
|
+
// Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
|
1257
|
+
if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
|
1258
|
+
jointAngle -= Math.PI;
|
1259
|
+
}
|
1260
|
+
|
1261
|
+
// Find the corrected control points for a spline straight through the point
|
1262
|
+
leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
|
1263
|
+
leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
|
1264
|
+
rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
|
1265
|
+
rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
|
1266
|
+
|
1267
|
+
// Record for drawing in next point
|
1268
|
+
point.rightContX = rightContX;
|
1269
|
+
point.rightContY = rightContY;
|
1270
|
+
|
1271
|
+
}
|
1272
|
+
|
1273
|
+
|
1274
|
+
// moveTo or lineTo
|
1275
|
+
if (!i) {
|
1276
|
+
ret = ['M', plotX, plotY];
|
1277
|
+
} else { // curve from last point to this
|
1278
|
+
ret = [
|
1279
|
+
'C',
|
1280
|
+
lastPoint.rightContX || lastPoint.plotX,
|
1281
|
+
lastPoint.rightContY || lastPoint.plotY,
|
1282
|
+
leftContX || plotX,
|
1283
|
+
leftContY || plotY,
|
1284
|
+
plotX,
|
1285
|
+
plotY
|
1286
|
+
];
|
1287
|
+
lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
|
1288
|
+
}
|
1289
|
+
|
1290
|
+
|
1291
|
+
} else {
|
1292
|
+
ret = proceed.call(this, segment, point, i);
|
1293
|
+
}
|
1294
|
+
return ret;
|
1295
|
+
});
|
1296
|
+
|
1297
|
+
/**
|
1298
|
+
* Extend translate. The plotX and plotY values are computed as if the polar chart were a
|
1299
|
+
* cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
|
1300
|
+
* center.
|
1301
|
+
*/
|
1302
|
+
wrap(seriesProto, 'translate', function (proceed) {
|
1303
|
+
|
1304
|
+
// Run uber method
|
1305
|
+
proceed.call(this);
|
1306
|
+
|
1307
|
+
// Postprocess plot coordinates
|
1308
|
+
if (this.chart.polar && !this.preventPostTranslate) {
|
1309
|
+
var points = this.points,
|
1310
|
+
i = points.length;
|
1311
|
+
while (i--) {
|
1312
|
+
// Translate plotX, plotY from angle and radius to true plot coordinates
|
1313
|
+
this.toXY(points[i]);
|
1314
|
+
}
|
1315
|
+
}
|
1316
|
+
});
|
1317
|
+
|
1318
|
+
/**
|
1319
|
+
* Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
|
1320
|
+
* line-like series.
|
1321
|
+
*/
|
1322
|
+
wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
|
1323
|
+
|
1324
|
+
var points = this.points;
|
1325
|
+
|
1326
|
+
// Connect the path
|
1327
|
+
if (this.chart.polar && this.options.connectEnds !== false &&
|
1328
|
+
segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
|
1329
|
+
this.connectEnds = true; // re-used in splines
|
1330
|
+
segment = [].concat(segment, [points[0]]);
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
// Run uber method
|
1334
|
+
return proceed.call(this, segment);
|
1335
|
+
|
1336
|
+
});
|
1337
|
+
|
1338
|
+
|
1339
|
+
function polarAnimate(proceed, init) {
|
1340
|
+
var chart = this.chart,
|
1341
|
+
animation = this.options.animation,
|
1342
|
+
group = this.group,
|
1343
|
+
markerGroup = this.markerGroup,
|
1344
|
+
center = this.xAxis.center,
|
1345
|
+
plotLeft = chart.plotLeft,
|
1346
|
+
plotTop = chart.plotTop,
|
1347
|
+
attribs;
|
1348
|
+
|
1349
|
+
// Specific animation for polar charts
|
1350
|
+
if (chart.polar) {
|
1351
|
+
|
1352
|
+
// Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
|
1353
|
+
// would be so slow it would't matter.
|
1354
|
+
if (chart.renderer.isSVG) {
|
1355
|
+
|
1356
|
+
if (animation === true) {
|
1357
|
+
animation = {};
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
// Initialize the animation
|
1361
|
+
if (init) {
|
1362
|
+
|
1363
|
+
// Create an SVG specific attribute setter for scaleX and scaleY
|
1364
|
+
group.attrSetters.scaleX = group.attrSetters.scaleY = function (value, key) {
|
1365
|
+
this[key] = value;
|
1366
|
+
if (this.scaleX !== UNDEFINED && this.scaleY !== UNDEFINED) {
|
1367
|
+
this.element.setAttribute('transform', 'translate(' + this.translateX + ',' + this.translateY + ') scale(' +
|
1368
|
+
this.scaleX + ',' + this.scaleY + ')');
|
1369
|
+
}
|
1370
|
+
return false;
|
1371
|
+
};
|
1372
|
+
|
1373
|
+
// Scale down the group and place it in the center
|
1374
|
+
attribs = {
|
1375
|
+
translateX: center[0] + plotLeft,
|
1376
|
+
translateY: center[1] + plotTop,
|
1377
|
+
scaleX: 0,
|
1378
|
+
scaleY: 0
|
1379
|
+
};
|
1380
|
+
|
1381
|
+
group.attr(attribs);
|
1382
|
+
if (markerGroup) {
|
1383
|
+
markerGroup.attrSetters = group.attrSetters;
|
1384
|
+
markerGroup.attr(attribs);
|
1385
|
+
}
|
1386
|
+
|
1387
|
+
// Run the animation
|
1388
|
+
} else {
|
1389
|
+
attribs = {
|
1390
|
+
translateX: plotLeft,
|
1391
|
+
translateY: plotTop,
|
1392
|
+
scaleX: 1,
|
1393
|
+
scaleY: 1
|
1394
|
+
};
|
1395
|
+
group.animate(attribs, animation);
|
1396
|
+
if (markerGroup) {
|
1397
|
+
markerGroup.animate(attribs, animation);
|
1398
|
+
}
|
1399
|
+
|
1400
|
+
// Delete this function to allow it only once
|
1401
|
+
this.animate = null;
|
1402
|
+
}
|
1403
|
+
}
|
1404
|
+
|
1405
|
+
// For non-polar charts, revert to the basic animation
|
1406
|
+
} else {
|
1407
|
+
proceed.call(this, init);
|
1408
|
+
}
|
1409
|
+
}
|
1410
|
+
|
1411
|
+
// Define the animate method for both regular series and column series and their derivatives
|
1412
|
+
wrap(seriesProto, 'animate', polarAnimate);
|
1413
|
+
wrap(colProto, 'animate', polarAnimate);
|
1414
|
+
|
1415
|
+
|
1416
|
+
/**
|
1417
|
+
* Throw in a couple of properties to let setTooltipPoints know we're indexing the points
|
1418
|
+
* in degrees (0-360), not plot pixel width.
|
1419
|
+
*/
|
1420
|
+
wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
|
1421
|
+
|
1422
|
+
if (this.chart.polar) {
|
1423
|
+
extend(this.xAxis, {
|
1424
|
+
tooltipLen: 360, // degrees are the resolution unit of the tooltipPoints array
|
1425
|
+
tooltipPosName: 'deg'
|
1426
|
+
});
|
1427
|
+
}
|
1428
|
+
|
1429
|
+
// Run uber method
|
1430
|
+
return proceed.call(this, renew);
|
1431
|
+
});
|
1432
|
+
|
1433
|
+
|
1434
|
+
/**
|
1435
|
+
* Extend the column prototype's translate method
|
1436
|
+
*/
|
1437
|
+
wrap(colProto, 'translate', function (proceed) {
|
1438
|
+
|
1439
|
+
var xAxis = this.xAxis,
|
1440
|
+
len = this.yAxis.len,
|
1441
|
+
center = xAxis.center,
|
1442
|
+
startAngleRad = xAxis.startAngleRad,
|
1443
|
+
renderer = this.chart.renderer,
|
1444
|
+
start,
|
1445
|
+
points,
|
1446
|
+
point,
|
1447
|
+
i;
|
1448
|
+
|
1449
|
+
this.preventPostTranslate = true;
|
1450
|
+
|
1451
|
+
// Run uber method
|
1452
|
+
proceed.call(this);
|
1453
|
+
|
1454
|
+
// Postprocess plot coordinates
|
1455
|
+
if (xAxis.isRadial) {
|
1456
|
+
points = this.points;
|
1457
|
+
i = points.length;
|
1458
|
+
while (i--) {
|
1459
|
+
point = points[i];
|
1460
|
+
start = point.barX + startAngleRad;
|
1461
|
+
point.shapeType = 'path';
|
1462
|
+
point.shapeArgs = {
|
1463
|
+
d: renderer.symbols.arc(
|
1464
|
+
center[0],
|
1465
|
+
center[1],
|
1466
|
+
len - point.plotY,
|
1467
|
+
null,
|
1468
|
+
{
|
1469
|
+
start: start,
|
1470
|
+
end: start + point.pointWidth,
|
1471
|
+
innerR: len - pick(point.yBottom, len)
|
1472
|
+
}
|
1473
|
+
)
|
1474
|
+
};
|
1475
|
+
this.toXY(point); // provide correct plotX, plotY for tooltip
|
1476
|
+
}
|
1477
|
+
}
|
1478
|
+
});
|
1479
|
+
|
1480
|
+
|
1481
|
+
/**
|
1482
|
+
* Align column data labels outside the columns. #1199.
|
1483
|
+
*/
|
1484
|
+
wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
|
1485
|
+
|
1486
|
+
if (this.chart.polar) {
|
1487
|
+
var angle = point.rectPlotX / Math.PI * 180,
|
1488
|
+
align,
|
1489
|
+
verticalAlign;
|
1490
|
+
|
1491
|
+
// Align nicely outside the perimeter of the columns
|
1492
|
+
if (options.align === null) {
|
1493
|
+
if (angle > 20 && angle < 160) {
|
1494
|
+
align = 'left'; // right hemisphere
|
1495
|
+
} else if (angle > 200 && angle < 340) {
|
1496
|
+
align = 'right'; // left hemisphere
|
1497
|
+
} else {
|
1498
|
+
align = 'center'; // top or bottom
|
1499
|
+
}
|
1500
|
+
options.align = align;
|
1501
|
+
}
|
1502
|
+
if (options.verticalAlign === null) {
|
1503
|
+
if (angle < 45 || angle > 315) {
|
1504
|
+
verticalAlign = 'bottom'; // top part
|
1505
|
+
} else if (angle > 135 && angle < 225) {
|
1506
|
+
verticalAlign = 'top'; // bottom part
|
1507
|
+
} else {
|
1508
|
+
verticalAlign = 'middle'; // left or right
|
1509
|
+
}
|
1510
|
+
options.verticalAlign = verticalAlign;
|
1511
|
+
}
|
1512
|
+
|
1513
|
+
seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
|
1514
|
+
} else {
|
1515
|
+
proceed.call(this, point, dataLabel, options, alignTo, isNew);
|
1516
|
+
}
|
1517
|
+
|
1518
|
+
});
|
1519
|
+
|
1520
|
+
/**
|
1521
|
+
* Extend the mouse tracker to return the tooltip position index in terms of
|
1522
|
+
* degrees rather than pixels
|
1523
|
+
*/
|
1524
|
+
wrap(mouseTrackerProto, 'getIndex', function (proceed, e) {
|
1525
|
+
var ret,
|
1526
|
+
chart = this.chart,
|
1527
|
+
center,
|
1528
|
+
x,
|
1529
|
+
y;
|
1530
|
+
|
1531
|
+
if (chart.polar) {
|
1532
|
+
center = chart.xAxis[0].center;
|
1533
|
+
x = e.chartX - center[0] - chart.plotLeft;
|
1534
|
+
y = e.chartY - center[1] - chart.plotTop;
|
1535
|
+
|
1536
|
+
ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
|
1537
|
+
|
1538
|
+
} else {
|
1539
|
+
|
1540
|
+
// Run uber method
|
1541
|
+
ret = proceed.call(this, e);
|
1542
|
+
}
|
1543
|
+
return ret;
|
1544
|
+
});
|
1545
|
+
|
1546
|
+
/**
|
1547
|
+
* Extend getMouseCoordinates to prepare for polar axis values
|
1548
|
+
*/
|
1549
|
+
wrap(mouseTrackerProto, 'getMouseCoordinates', function (proceed, e) {
|
1550
|
+
var chart = this.chart,
|
1551
|
+
ret = {
|
1552
|
+
xAxis: [],
|
1553
|
+
yAxis: []
|
1554
|
+
};
|
1555
|
+
|
1556
|
+
if (chart.polar) {
|
1557
|
+
|
1558
|
+
each(chart.axes, function (axis) {
|
1559
|
+
var isXAxis = axis.isXAxis,
|
1560
|
+
center = axis.center,
|
1561
|
+
x = e.chartX - center[0] - chart.plotLeft,
|
1562
|
+
y = e.chartY - center[1] - chart.plotTop;
|
1563
|
+
|
1564
|
+
ret[isXAxis ? 'xAxis' : 'yAxis'].push({
|
1565
|
+
axis: axis,
|
1566
|
+
value: axis.translate(
|
1567
|
+
isXAxis ?
|
1568
|
+
Math.PI - Math.atan2(x, y) : // angle
|
1569
|
+
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
|
1570
|
+
true
|
1571
|
+
)
|
1572
|
+
});
|
1573
|
+
});
|
1574
|
+
|
1575
|
+
} else {
|
1576
|
+
ret = proceed.call(this, e);
|
1577
|
+
}
|
1578
|
+
|
1579
|
+
return ret;
|
1580
|
+
});
|
1581
|
+
}(Highcharts));
|