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