Yandex Metrica - Version 1.0.2

Version Description

  • Capability checking before display temporary dashboard widget
Download this release

Release Info

Developer m_uysl
Plugin Icon 128x128 Yandex Metrica
Version 1.0.2
Comparing to
See all releases

Code changes from version 1.0.1 to 1.0.2

js/highcharts/adapters/mootools-adapter.js CHANGED
@@ -1,13 +1,13 @@
1
- /*
2
- Highcharts JS v3.0.2 (2013-06-05)
3
- MooTools adapter
4
-
5
- (c) 2010-2013 Torstein Hønsi
6
-
7
- License: www.highcharts.com/license
8
- */
9
- (function(){var e=window,h=document,f=e.MooTools.version.substring(0,3),i=f==="1.2"||f==="1.1",j=i||f==="1.3",g=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this,
10
- arguments)}},adapterRun:function(a,b){if(b==="width"||b==="height")return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=h.getElementsByTagName("head")[0],d=h.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var a=arguments;this.attr.call(this,a[0],a[1][0])},a.$family=function(){return!0};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:$(a),
11
- g({transition:Fx.Transitions.Quad.easeInOut},c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return i?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){a=a.getPosition();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName?$(a):g(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b===
12
- "unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&a.addEvent&&(b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=g(b,c);if(!b.target&&b.event)b.target=b.event.target;b.preventDefault=function(){d=null};a.fireEvent&&a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){if(a.page)a.pageX=
13
- a.page.x,a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})();
1
+ /*
2
+ Highcharts JS v3.0.2 (2013-06-05)
3
+ MooTools adapter
4
+
5
+ (c) 2010-2013 Torstein Hønsi
6
+
7
+ License: www.highcharts.com/license
8
+ */
9
+ (function(){var e=window,h=document,f=e.MooTools.version.substring(0,3),i=f==="1.2"||f==="1.1",j=i||f==="1.3",g=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this,
10
+ arguments)}},adapterRun:function(a,b){if(b==="width"||b==="height")return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=h.getElementsByTagName("head")[0],d=h.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var a=arguments;this.attr.call(this,a[0],a[1][0])},a.$family=function(){return!0};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:$(a),
11
+ g({transition:Fx.Transitions.Quad.easeInOut},c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return i?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){a=a.getPosition();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName?$(a):g(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b===
12
+ "unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&a.addEvent&&(b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=g(b,c);if(!b.target&&b.event)b.target=b.event.target;b.preventDefault=function(){d=null};a.fireEvent&&a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){if(a.page)a.pageX=
13
+ a.page.x,a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})();
js/highcharts/adapters/mootools-adapter.src.js CHANGED
@@ -1,313 +1,313 @@
1
- /**
2
- * @license Highcharts JS v3.0.2 (2013-06-05)
3
- * MooTools adapter
4
- *
5
- * (c) 2010-2013 Torstein Hønsi
6
- *
7
- * License: www.highcharts.com/license
8
- */
9
-
10
- // JSLint options:
11
- /*global Fx, $, $extend, $each, $merge, Events, Event, DOMEvent */
12
-
13
- (function () {
14
-
15
- var win = window,
16
- doc = document,
17
- mooVersion = win.MooTools.version.substring(0, 3), // Get the first three characters of the version number
18
- legacy = mooVersion === '1.2' || mooVersion === '1.1', // 1.1 && 1.2 considered legacy, 1.3 is not.
19
- legacyEvent = legacy || mooVersion === '1.3', // In versions 1.1 - 1.3 the event class is named Event, in newer versions it is named DOMEvent.
20
- $extend = win.$extend || function () {
21
- return Object.append.apply(Object, arguments);
22
- };
23
-
24
- win.HighchartsAdapter = {
25
- /**
26
- * Initialize the adapter. This is run once as Highcharts is first run.
27
- * @param {Object} pathAnim The helper object to do animations across adapters.
28
- */
29
- init: function (pathAnim) {
30
- var fxProto = Fx.prototype,
31
- fxStart = fxProto.start,
32
- morphProto = Fx.Morph.prototype,
33
- morphCompute = morphProto.compute;
34
-
35
- // override Fx.start to allow animation of SVG element wrappers
36
- /*jslint unparam: true*//* allow unused parameters in fx functions */
37
- fxProto.start = function (from, to) {
38
- var fx = this,
39
- elem = fx.element;
40
-
41
- // special for animating paths
42
- if (from.d) {
43
- //this.fromD = this.element.d.split(' ');
44
- fx.paths = pathAnim.init(
45
- elem,
46
- elem.d,
47
- fx.toD
48
- );
49
- }
50
- fxStart.apply(fx, arguments);
51
-
52
- return this; // chainable
53
- };
54
-
55
- // override Fx.step to allow animation of SVG element wrappers
56
- morphProto.compute = function (from, to, delta) {
57
- var fx = this,
58
- paths = fx.paths;
59
-
60
- if (paths) {
61
- fx.element.attr(
62
- 'd',
63
- pathAnim.step(paths[0], paths[1], delta, fx.toD)
64
- );
65
- } else {
66
- return morphCompute.apply(fx, arguments);
67
- }
68
- };
69
- /*jslint unparam: false*/
70
- },
71
-
72
- /**
73
- * Run a general method on the framework, following jQuery syntax
74
- * @param {Object} el The HTML element
75
- * @param {String} method Which method to run on the wrapped element
76
- */
77
- adapterRun: function (el, method) {
78
-
79
- // This currently works for getting inner width and height. If adding
80
- // more methods later, we need a conditional implementation for each.
81
- if (method === 'width' || method === 'height') {
82
- return parseInt($(el).getStyle(method), 10);
83
- }
84
- },
85
-
86
- /**
87
- * Downloads a script and executes a callback when done.
88
- * @param {String} scriptLocation
89
- * @param {Function} callback
90
- */
91
- getScript: function (scriptLocation, callback) {
92
- // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script.
93
- var head = doc.getElementsByTagName('head')[0];
94
- var script = doc.createElement('script');
95
-
96
- script.type = 'text/javascript';
97
- script.src = scriptLocation;
98
- script.onload = callback;
99
-
100
- head.appendChild(script);
101
- },
102
-
103
- /**
104
- * Animate a HTML element or SVG element wrapper
105
- * @param {Object} el
106
- * @param {Object} params
107
- * @param {Object} options jQuery-like animation options: duration, easing, callback
108
- */
109
- animate: function (el, params, options) {
110
- var isSVGElement = el.attr,
111
- effect,
112
- complete = options && options.complete;
113
-
114
- if (isSVGElement && !el.setStyle) {
115
- // add setStyle and getStyle methods for internal use in Moo
116
- el.getStyle = el.attr;
117
- el.setStyle = function () { // property value is given as array in Moo - break it down
118
- var args = arguments;
119
- this.attr.call(this, args[0], args[1][0]);
120
- };
121
- // dirty hack to trick Moo into handling el as an element wrapper
122
- el.$family = function () { return true; };
123
- }
124
-
125
- // stop running animations
126
- win.HighchartsAdapter.stop(el);
127
-
128
- // define and run the effect
129
- effect = new Fx.Morph(
130
- isSVGElement ? el : $(el),
131
- $extend({
132
- transition: Fx.Transitions.Quad.easeInOut
133
- }, options)
134
- );
135
-
136
- // Make sure that the element reference is set when animating svg elements
137
- if (isSVGElement) {
138
- effect.element = el;
139
- }
140
-
141
- // special treatment for paths
142
- if (params.d) {
143
- effect.toD = params.d;
144
- }
145
-
146
- // jQuery-like events
147
- if (complete) {
148
- effect.addEvent('complete', complete);
149
- }
150
-
151
- // run
152
- effect.start(params);
153
-
154
- // record for use in stop method
155
- el.fx = effect;
156
- },
157
-
158
- /**
159
- * MooTool's each function
160
- *
161
- */
162
- each: function (arr, fn) {
163
- return legacy ?
164
- $each(arr, fn) :
165
- Array.each(arr, fn);
166
- },
167
-
168
- /**
169
- * Map an array
170
- * @param {Array} arr
171
- * @param {Function} fn
172
- */
173
- map: function (arr, fn) {
174
- return arr.map(fn);
175
- },
176
-
177
- /**
178
- * Grep or filter an array
179
- * @param {Array} arr
180
- * @param {Function} fn
181
- */
182
- grep: function (arr, fn) {
183
- return arr.filter(fn);
184
- },
185
-
186
- /**
187
- * Return the index of an item in an array, or -1 if not matched
188
- */
189
- inArray: function (item, arr, from) {
190
- return arr ? arr.indexOf(item, from) : -1;
191
- },
192
-
193
- /**
194
- * Get the offset of an element relative to the top left corner of the web page
195
- */
196
- offset: function (el) {
197
- var offsets = el.getPosition(); // #1496
198
- return {
199
- left: offsets.x,
200
- top: offsets.y
201
- };
202
- },
203
-
204
- /**
205
- * Extends an object with Events, if its not done
206
- */
207
- extendWithEvents: function (el) {
208
- // if the addEvent method is not defined, el is a custom Highcharts object
209
- // like series or point
210
- if (!el.addEvent) {
211
- if (el.nodeName) {
212
- el = $(el); // a dynamically generated node
213
- } else {
214
- $extend(el, new Events()); // a custom object
215
- }
216
- }
217
- },
218
-
219
- /**
220
- * Add an event listener
221
- * @param {Object} el HTML element or custom object
222
- * @param {String} type Event type
223
- * @param {Function} fn Event handler
224
- */
225
- addEvent: function (el, type, fn) {
226
- if (typeof type === 'string') { // chart broke due to el being string, type function
227
-
228
- if (type === 'unload') { // Moo self destructs before custom unload events
229
- type = 'beforeunload';
230
- }
231
-
232
- win.HighchartsAdapter.extendWithEvents(el);
233
-
234
- el.addEvent(type, fn);
235
- }
236
- },
237
-
238
- removeEvent: function (el, type, fn) {
239
- if (typeof el === 'string') {
240
- // el.removeEvents below apperantly calls this method again. Do not quite understand why, so for now just bail out.
241
- return;
242
- }
243
-
244
- if (el.addEvent) { // If el doesn't have an addEvent method, there are no events to remove
245
- if (type) {
246
- if (type === 'unload') { // Moo self destructs before custom unload events
247
- type = 'beforeunload';
248
- }
249
-
250
- if (fn) {
251
- el.removeEvent(type, fn);
252
- } else if (el.removeEvents) { // #958
253
- el.removeEvents(type);
254
- }
255
- } else {
256
- el.removeEvents();
257
- }
258
- }
259
- },
260
-
261
- fireEvent: function (el, event, eventArguments, defaultFunction) {
262
- var eventArgs = {
263
- type: event,
264
- target: el
265
- };
266
- // create an event object that keeps all functions
267
- event = legacyEvent ? new Event(eventArgs) : new DOMEvent(eventArgs);
268
- event = $extend(event, eventArguments);
269
-
270
- // When running an event on the Chart.prototype, MooTools nests the target in event.event
271
- if (!event.target && event.event) {
272
- event.target = event.event.target;
273
- }
274
-
275
- // override the preventDefault function to be able to use
276
- // this for custom events
277
- event.preventDefault = function () {
278
- defaultFunction = null;
279
- };
280
- // if fireEvent is not available on the object, there hasn't been added
281
- // any events to it above
282
- if (el.fireEvent) {
283
- el.fireEvent(event.type, event);
284
- }
285
-
286
- // fire the default if it is passed and it is not prevented above
287
- if (defaultFunction) {
288
- defaultFunction(event);
289
- }
290
- },
291
-
292
- /**
293
- * Set back e.pageX and e.pageY that MooTools has abstracted away. #1165, #1346.
294
- */
295
- washMouseEvent: function (e) {
296
- if (e.page) {
297
- e.pageX = e.page.x;
298
- e.pageY = e.page.y;
299
- }
300
- return e;
301
- },
302
-
303
- /**
304
- * Stop running animations on the object
305
- */
306
- stop: function (el) {
307
- if (el.fx) {
308
- el.fx.cancel();
309
- }
310
- }
311
- };
312
-
313
- }());
1
+ /**
2
+ * @license Highcharts JS v3.0.2 (2013-06-05)
3
+ * MooTools adapter
4
+ *
5
+ * (c) 2010-2013 Torstein Hønsi
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+
10
+ // JSLint options:
11
+ /*global Fx, $, $extend, $each, $merge, Events, Event, DOMEvent */
12
+
13
+ (function () {
14
+
15
+ var win = window,
16
+ doc = document,
17
+ mooVersion = win.MooTools.version.substring(0, 3), // Get the first three characters of the version number
18
+ legacy = mooVersion === '1.2' || mooVersion === '1.1', // 1.1 && 1.2 considered legacy, 1.3 is not.
19
+ legacyEvent = legacy || mooVersion === '1.3', // In versions 1.1 - 1.3 the event class is named Event, in newer versions it is named DOMEvent.
20
+ $extend = win.$extend || function () {
21
+ return Object.append.apply(Object, arguments);
22
+ };
23
+
24
+ win.HighchartsAdapter = {
25
+ /**
26
+ * Initialize the adapter. This is run once as Highcharts is first run.
27
+ * @param {Object} pathAnim The helper object to do animations across adapters.
28
+ */
29
+ init: function (pathAnim) {
30
+ var fxProto = Fx.prototype,
31
+ fxStart = fxProto.start,
32
+ morphProto = Fx.Morph.prototype,
33
+ morphCompute = morphProto.compute;
34
+
35
+ // override Fx.start to allow animation of SVG element wrappers
36
+ /*jslint unparam: true*//* allow unused parameters in fx functions */
37
+ fxProto.start = function (from, to) {
38
+ var fx = this,
39
+ elem = fx.element;
40
+
41
+ // special for animating paths
42
+ if (from.d) {
43
+ //this.fromD = this.element.d.split(' ');
44
+ fx.paths = pathAnim.init(
45
+ elem,
46
+ elem.d,
47
+ fx.toD
48
+ );
49
+ }
50
+ fxStart.apply(fx, arguments);
51
+
52
+ return this; // chainable
53
+ };
54
+
55
+ // override Fx.step to allow animation of SVG element wrappers
56
+ morphProto.compute = function (from, to, delta) {
57
+ var fx = this,
58
+ paths = fx.paths;
59
+
60
+ if (paths) {
61
+ fx.element.attr(
62
+ 'd',
63
+ pathAnim.step(paths[0], paths[1], delta, fx.toD)
64
+ );
65
+ } else {
66
+ return morphCompute.apply(fx, arguments);
67
+ }
68
+ };
69
+ /*jslint unparam: false*/
70
+ },
71
+
72
+ /**
73
+ * Run a general method on the framework, following jQuery syntax
74
+ * @param {Object} el The HTML element
75
+ * @param {String} method Which method to run on the wrapped element
76
+ */
77
+ adapterRun: function (el, method) {
78
+
79
+ // This currently works for getting inner width and height. If adding
80
+ // more methods later, we need a conditional implementation for each.
81
+ if (method === 'width' || method === 'height') {
82
+ return parseInt($(el).getStyle(method), 10);
83
+ }
84
+ },
85
+
86
+ /**
87
+ * Downloads a script and executes a callback when done.
88
+ * @param {String} scriptLocation
89
+ * @param {Function} callback
90
+ */
91
+ getScript: function (scriptLocation, callback) {
92
+ // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script.
93
+ var head = doc.getElementsByTagName('head')[0];
94
+ var script = doc.createElement('script');
95
+
96
+ script.type = 'text/javascript';
97
+ script.src = scriptLocation;
98
+ script.onload = callback;
99
+
100
+ head.appendChild(script);
101
+ },
102
+
103
+ /**
104
+ * Animate a HTML element or SVG element wrapper
105
+ * @param {Object} el
106
+ * @param {Object} params
107
+ * @param {Object} options jQuery-like animation options: duration, easing, callback
108
+ */
109
+ animate: function (el, params, options) {
110
+ var isSVGElement = el.attr,
111
+ effect,
112
+ complete = options && options.complete;
113
+
114
+ if (isSVGElement && !el.setStyle) {
115
+ // add setStyle and getStyle methods for internal use in Moo
116
+ el.getStyle = el.attr;
117
+ el.setStyle = function () { // property value is given as array in Moo - break it down
118
+ var args = arguments;
119
+ this.attr.call(this, args[0], args[1][0]);
120
+ };
121
+ // dirty hack to trick Moo into handling el as an element wrapper
122
+ el.$family = function () { return true; };
123
+ }
124
+
125
+ // stop running animations
126
+ win.HighchartsAdapter.stop(el);
127
+
128
+ // define and run the effect
129
+ effect = new Fx.Morph(
130
+ isSVGElement ? el : $(el),
131
+ $extend({
132
+ transition: Fx.Transitions.Quad.easeInOut
133
+ }, options)
134
+ );
135
+
136
+ // Make sure that the element reference is set when animating svg elements
137
+ if (isSVGElement) {
138
+ effect.element = el;
139
+ }
140
+
141
+ // special treatment for paths
142
+ if (params.d) {
143
+ effect.toD = params.d;
144
+ }
145
+
146
+ // jQuery-like events
147
+ if (complete) {
148
+ effect.addEvent('complete', complete);
149
+ }
150
+
151
+ // run
152
+ effect.start(params);
153
+
154
+ // record for use in stop method
155
+ el.fx = effect;
156
+ },
157
+
158
+ /**
159
+ * MooTool's each function
160
+ *
161
+ */
162
+ each: function (arr, fn) {
163
+ return legacy ?
164
+ $each(arr, fn) :
165
+ Array.each(arr, fn);
166
+ },
167
+
168
+ /**
169
+ * Map an array
170
+ * @param {Array} arr
171
+ * @param {Function} fn
172
+ */
173
+ map: function (arr, fn) {
174
+ return arr.map(fn);
175
+ },
176
+
177
+ /**
178
+ * Grep or filter an array
179
+ * @param {Array} arr
180
+ * @param {Function} fn
181
+ */
182
+ grep: function (arr, fn) {
183
+ return arr.filter(fn);
184
+ },
185
+
186
+ /**
187
+ * Return the index of an item in an array, or -1 if not matched
188
+ */
189
+ inArray: function (item, arr, from) {
190
+ return arr ? arr.indexOf(item, from) : -1;
191
+ },
192
+
193
+ /**
194
+ * Get the offset of an element relative to the top left corner of the web page
195
+ */
196
+ offset: function (el) {
197
+ var offsets = el.getPosition(); // #1496
198
+ return {
199
+ left: offsets.x,
200
+ top: offsets.y
201
+ };
202
+ },
203
+
204
+ /**
205
+ * Extends an object with Events, if its not done
206
+ */
207
+ extendWithEvents: function (el) {
208
+ // if the addEvent method is not defined, el is a custom Highcharts object
209
+ // like series or point
210
+ if (!el.addEvent) {
211
+ if (el.nodeName) {
212
+ el = $(el); // a dynamically generated node
213
+ } else {
214
+ $extend(el, new Events()); // a custom object
215
+ }
216
+ }
217
+ },
218
+
219
+ /**
220
+ * Add an event listener
221
+ * @param {Object} el HTML element or custom object
222
+ * @param {String} type Event type
223
+ * @param {Function} fn Event handler
224
+ */
225
+ addEvent: function (el, type, fn) {
226
+ if (typeof type === 'string') { // chart broke due to el being string, type function
227
+
228
+ if (type === 'unload') { // Moo self destructs before custom unload events
229
+ type = 'beforeunload';
230
+ }
231
+
232
+ win.HighchartsAdapter.extendWithEvents(el);
233
+
234
+ el.addEvent(type, fn);
235
+ }
236
+ },
237
+
238
+ removeEvent: function (el, type, fn) {
239
+ if (typeof el === 'string') {
240
+ // el.removeEvents below apperantly calls this method again. Do not quite understand why, so for now just bail out.
241
+ return;
242
+ }
243
+
244
+ if (el.addEvent) { // If el doesn't have an addEvent method, there are no events to remove
245
+ if (type) {
246
+ if (type === 'unload') { // Moo self destructs before custom unload events
247
+ type = 'beforeunload';
248
+ }
249
+
250
+ if (fn) {
251
+ el.removeEvent(type, fn);
252
+ } else if (el.removeEvents) { // #958
253
+ el.removeEvents(type);
254
+ }
255
+ } else {
256
+ el.removeEvents();
257
+ }
258
+ }
259
+ },
260
+
261
+ fireEvent: function (el, event, eventArguments, defaultFunction) {
262
+ var eventArgs = {
263
+ type: event,
264
+ target: el
265
+ };
266
+ // create an event object that keeps all functions
267
+ event = legacyEvent ? new Event(eventArgs) : new DOMEvent(eventArgs);
268
+ event = $extend(event, eventArguments);
269
+
270
+ // When running an event on the Chart.prototype, MooTools nests the target in event.event
271
+ if (!event.target && event.event) {
272
+ event.target = event.event.target;
273
+ }
274
+
275
+ // override the preventDefault function to be able to use
276
+ // this for custom events
277
+ event.preventDefault = function () {
278
+ defaultFunction = null;
279
+ };
280
+ // if fireEvent is not available on the object, there hasn't been added
281
+ // any events to it above
282
+ if (el.fireEvent) {
283
+ el.fireEvent(event.type, event);
284
+ }
285
+
286
+ // fire the default if it is passed and it is not prevented above
287
+ if (defaultFunction) {
288
+ defaultFunction(event);
289
+ }
290
+ },
291
+
292
+ /**
293
+ * Set back e.pageX and e.pageY that MooTools has abstracted away. #1165, #1346.
294
+ */
295
+ washMouseEvent: function (e) {
296
+ if (e.page) {
297
+ e.pageX = e.page.x;
298
+ e.pageY = e.page.y;
299
+ }
300
+ return e;
301
+ },
302
+
303
+ /**
304
+ * Stop running animations on the object
305
+ */
306
+ stop: function (el) {
307
+ if (el.fx) {
308
+ el.fx.cancel();
309
+ }
310
+ }
311
+ };
312
+
313
+ }());
js/highcharts/adapters/prototype-adapter.js CHANGED
@@ -1,15 +1,15 @@
1
- /*
2
- Highcharts JS v3.0.2 (2013-06-05)
3
- Prototype adapter
4
-
5
- @author Michael Nelson, Torstein Hønsi.
6
-
7
- Feel free to use and modify this script.
8
- Highcharts license: www.highcharts.com/license.
9
- */
10
- var HighchartsAdapter=function(){var f=typeof Effect!=="undefined";return{init:function(a){if(f)Effect.HighchartsTransition=Class.create(Effect.Base,{initialize:function(b,c,d,g){var e;this.element=b;this.key=c;e=b.attr?b.attr(c):$(b).getStyle(c);if(c==="d")this.paths=a.init(b,b.d,d),this.toD=d,e=0,d=1;this.start(Object.extend(g||{},{from:e,to:d,attribute:c}))},setup:function(){HighchartsAdapter._extend(this.element);if(!this.element._highchart_animation)this.element._highchart_animation={};this.element._highchart_animation[this.key]=
11
- this},update:function(b){var c=this.paths,d=this.element;c&&(b=a.step(c[0],c[1],b,this.toD));d.attr?d.element&&d.attr(this.options.attribute,b):(c={},c[this.options.attribute]=b,$(d).setStyle(c))},finish:function(){this.element&&this.element._highchart_animation&&delete this.element._highchart_animation[this.key]}})},adapterRun:function(a,b){return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=$$("head")[0];c&&c.appendChild((new Element("script",{type:"text/javascript",src:a})).observe("load",
12
- b))},addNS:function(a){var b=/^(?:click|mouse(?:down|up|over|move|out))$/;return/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/.test(a)||b.test(a)?a:"h:"+a},addEvent:function(a,b,c){a.addEventListener||a.attachEvent?Event.observe($(a),HighchartsAdapter.addNS(b),c):(HighchartsAdapter._extend(a),a._highcharts_observe(b,c))},animate:function(a,b,c){var d,c=c||{};c.delay=0;c.duration=(c.duration||500)/1E3;c.afterFinish=c.complete;if(f)for(d in b)new Effect.HighchartsTransition($(a),
13
- d,b[d],c);else{if(a.attr)for(d in b)a.attr(d,b[d]);c.complete&&c.complete()}a.attr||$(a).setStyle(b)},stop:function(a){var b;if(a._highcharts_extended&&a._highchart_animation)for(b in a._highchart_animation)a._highchart_animation[b].cancel()},each:function(a,b){$A(a).each(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){return $(a).cumulativeOffset()},fireEvent:function(a,b,c,d){a.fire?a.fire(HighchartsAdapter.addNS(b),c):a._highcharts_extended&&(c=c||{},a._highcharts_fire(b,
14
- c));c&&c.defaultPrevented&&(d=null);d&&d(c)},removeEvent:function(a,b,c){$(a).stopObserving&&(b&&(b=HighchartsAdapter.addNS(b)),$(a).stopObserving(b,c));window===a?Event.stopObserving(a,b,c):(HighchartsAdapter._extend(a),a._highcharts_stop_observing(b,c))},washMouseEvent:function(a){return a},grep:function(a,b){return a.findAll(b)},map:function(a,b){return a.map(b)},_extend:function(a){a._highcharts_extended||Object.extend(a,{_highchart_events:{},_highchart_animation:null,_highcharts_extended:!0,
15
- _highcharts_observe:function(b,a){this._highchart_events[b]=[this._highchart_events[b],a].compact().flatten()},_highcharts_stop_observing:function(b,a){b?a?this._highchart_events[b]=[this._highchart_events[b]].compact().flatten().without(a):delete this._highchart_events[b]:this._highchart_events={}},_highcharts_fire:function(a,c){var d=this;(this._highchart_events[a]||[]).each(function(a){if(!c.stopped)c.preventDefault=function(){c.defaultPrevented=!0},c.target=d,a.bind(this)(c)===!1&&c.preventDefault()}.bind(this))}})}}}();
1
+ /*
2
+ Highcharts JS v3.0.2 (2013-06-05)
3
+ Prototype adapter
4
+
5
+ @author Michael Nelson, Torstein Hønsi.
6
+
7
+ Feel free to use and modify this script.
8
+ Highcharts license: www.highcharts.com/license.
9
+ */
10
+ var HighchartsAdapter=function(){var f=typeof Effect!=="undefined";return{init:function(a){if(f)Effect.HighchartsTransition=Class.create(Effect.Base,{initialize:function(b,c,d,g){var e;this.element=b;this.key=c;e=b.attr?b.attr(c):$(b).getStyle(c);if(c==="d")this.paths=a.init(b,b.d,d),this.toD=d,e=0,d=1;this.start(Object.extend(g||{},{from:e,to:d,attribute:c}))},setup:function(){HighchartsAdapter._extend(this.element);if(!this.element._highchart_animation)this.element._highchart_animation={};this.element._highchart_animation[this.key]=
11
+ this},update:function(b){var c=this.paths,d=this.element;c&&(b=a.step(c[0],c[1],b,this.toD));d.attr?d.element&&d.attr(this.options.attribute,b):(c={},c[this.options.attribute]=b,$(d).setStyle(c))},finish:function(){this.element&&this.element._highchart_animation&&delete this.element._highchart_animation[this.key]}})},adapterRun:function(a,b){return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=$$("head")[0];c&&c.appendChild((new Element("script",{type:"text/javascript",src:a})).observe("load",
12
+ b))},addNS:function(a){var b=/^(?:click|mouse(?:down|up|over|move|out))$/;return/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/.test(a)||b.test(a)?a:"h:"+a},addEvent:function(a,b,c){a.addEventListener||a.attachEvent?Event.observe($(a),HighchartsAdapter.addNS(b),c):(HighchartsAdapter._extend(a),a._highcharts_observe(b,c))},animate:function(a,b,c){var d,c=c||{};c.delay=0;c.duration=(c.duration||500)/1E3;c.afterFinish=c.complete;if(f)for(d in b)new Effect.HighchartsTransition($(a),
13
+ d,b[d],c);else{if(a.attr)for(d in b)a.attr(d,b[d]);c.complete&&c.complete()}a.attr||$(a).setStyle(b)},stop:function(a){var b;if(a._highcharts_extended&&a._highchart_animation)for(b in a._highchart_animation)a._highchart_animation[b].cancel()},each:function(a,b){$A(a).each(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){return $(a).cumulativeOffset()},fireEvent:function(a,b,c,d){a.fire?a.fire(HighchartsAdapter.addNS(b),c):a._highcharts_extended&&(c=c||{},a._highcharts_fire(b,
14
+ c));c&&c.defaultPrevented&&(d=null);d&&d(c)},removeEvent:function(a,b,c){$(a).stopObserving&&(b&&(b=HighchartsAdapter.addNS(b)),$(a).stopObserving(b,c));window===a?Event.stopObserving(a,b,c):(HighchartsAdapter._extend(a),a._highcharts_stop_observing(b,c))},washMouseEvent:function(a){return a},grep:function(a,b){return a.findAll(b)},map:function(a,b){return a.map(b)},_extend:function(a){a._highcharts_extended||Object.extend(a,{_highchart_events:{},_highchart_animation:null,_highcharts_extended:!0,
15
+ _highcharts_observe:function(b,a){this._highchart_events[b]=[this._highchart_events[b],a].compact().flatten()},_highcharts_stop_observing:function(b,a){b?a?this._highchart_events[b]=[this._highchart_events[b]].compact().flatten().without(a):delete this._highchart_events[b]:this._highchart_events={}},_highcharts_fire:function(a,c){var d=this;(this._highchart_events[a]||[]).each(function(a){if(!c.stopped)c.preventDefault=function(){c.defaultPrevented=!0},c.target=d,a.bind(this)(c)===!1&&c.preventDefault()}.bind(this))}})}}}();
js/highcharts/adapters/prototype-adapter.src.js CHANGED
@@ -1,316 +1,316 @@
1
- /**
2
- * @license Highcharts JS v3.0.2 (2013-06-05)
3
- * Prototype adapter
4
- *
5
- * @author Michael Nelson, Torstein Hønsi.
6
- *
7
- * Feel free to use and modify this script.
8
- * Highcharts license: www.highcharts.com/license.
9
- */
10
-
11
- // JSLint options:
12
- /*global Effect, Class, Event, Element, $, $$, $A */
13
-
14
- // Adapter interface between prototype and the Highcharts charting library
15
- var HighchartsAdapter = (function () {
16
-
17
- var hasEffect = typeof Effect !== 'undefined';
18
-
19
- return {
20
-
21
- /**
22
- * Initialize the adapter. This is run once as Highcharts is first run.
23
- * @param {Object} pathAnim The helper object to do animations across adapters.
24
- */
25
- init: function (pathAnim) {
26
- if (hasEffect) {
27
- /**
28
- * Animation for Highcharts SVG element wrappers only
29
- * @param {Object} element
30
- * @param {Object} attribute
31
- * @param {Object} to
32
- * @param {Object} options
33
- */
34
- Effect.HighchartsTransition = Class.create(Effect.Base, {
35
- initialize: function (element, attr, to, options) {
36
- var from,
37
- opts;
38
-
39
- this.element = element;
40
- this.key = attr;
41
- from = element.attr ? element.attr(attr) : $(element).getStyle(attr);
42
-
43
- // special treatment for paths
44
- if (attr === 'd') {
45
- this.paths = pathAnim.init(
46
- element,
47
- element.d,
48
- to
49
- );
50
- this.toD = to;
51
-
52
-
53
- // fake values in order to read relative position as a float in update
54
- from = 0;
55
- to = 1;
56
- }
57
-
58
- opts = Object.extend((options || {}), {
59
- from: from,
60
- to: to,
61
- attribute: attr
62
- });
63
- this.start(opts);
64
- },
65
- setup: function () {
66
- HighchartsAdapter._extend(this.element);
67
- // If this is the first animation on this object, create the _highcharts_animation helper that
68
- // contain pointers to the animation objects.
69
- if (!this.element._highchart_animation) {
70
- this.element._highchart_animation = {};
71
- }
72
-
73
- // Store a reference to this animation instance.
74
- this.element._highchart_animation[this.key] = this;
75
- },
76
- update: function (position) {
77
- var paths = this.paths,
78
- element = this.element,
79
- obj;
80
-
81
- if (paths) {
82
- position = pathAnim.step(paths[0], paths[1], position, this.toD);
83
- }
84
-
85
- if (element.attr) { // SVGElement
86
-
87
- if (element.element) { // If not, it has been destroyed (#1405)
88
- element.attr(this.options.attribute, position);
89
- }
90
-
91
- } else { // HTML, #409
92
- obj = {};
93
- obj[this.options.attribute] = position;
94
- $(element).setStyle(obj);
95
- }
96
-
97
- },
98
- finish: function () {
99
- // Delete the property that holds this animation now that it is finished.
100
- // Both canceled animations and complete ones gets a 'finish' call.
101
- if (this.element && this.element._highchart_animation) { // #1405
102
- delete this.element._highchart_animation[this.key];
103
- }
104
- }
105
- });
106
- }
107
- },
108
-
109
- /**
110
- * Run a general method on the framework, following jQuery syntax
111
- * @param {Object} el The HTML element
112
- * @param {String} method Which method to run on the wrapped element
113
- */
114
- adapterRun: function (el, method) {
115
-
116
- // This currently works for getting inner width and height. If adding
117
- // more methods later, we need a conditional implementation for each.
118
- return parseInt($(el).getStyle(method), 10);
119
-
120
- },
121
-
122
- /**
123
- * Downloads a script and executes a callback when done.
124
- * @param {String} scriptLocation
125
- * @param {Function} callback
126
- */
127
- getScript: function (scriptLocation, callback) {
128
- var head = $$('head')[0]; // Returns an array, so pick the first element.
129
- if (head) {
130
- // Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback
131
- head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback));
132
- }
133
- },
134
-
135
- /**
136
- * Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of
137
- * events that are not recognized as native.
138
- */
139
- addNS: function (eventName) {
140
- var HTMLEvents = /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
141
- MouseEvents = /^(?:click|mouse(?:down|up|over|move|out))$/;
142
- return (HTMLEvents.test(eventName) || MouseEvents.test(eventName)) ?
143
- eventName :
144
- 'h:' + eventName;
145
- },
146
-
147
- // el needs an event to be attached. el is not necessarily a dom element
148
- addEvent: function (el, event, fn) {
149
- if (el.addEventListener || el.attachEvent) {
150
- Event.observe($(el), HighchartsAdapter.addNS(event), fn);
151
-
152
- } else {
153
- HighchartsAdapter._extend(el);
154
- el._highcharts_observe(event, fn);
155
- }
156
- },
157
-
158
- // motion makes things pretty. use it if effects is loaded, if not... still get to the end result.
159
- animate: function (el, params, options) {
160
- var key,
161
- fx;
162
-
163
- // default options
164
- options = options || {};
165
- options.delay = 0;
166
- options.duration = (options.duration || 500) / 1000;
167
- options.afterFinish = options.complete;
168
-
169
- // animate wrappers and DOM elements
170
- if (hasEffect) {
171
- for (key in params) {
172
- // The fx variable is seemingly thrown away here, but the Effect.setup will add itself to the _highcharts_animation object
173
- // on the element itself so its not really lost.
174
- fx = new Effect.HighchartsTransition($(el), key, params[key], options);
175
- }
176
- } else {
177
- if (el.attr) { // #409 without effects
178
- for (key in params) {
179
- el.attr(key, params[key]);
180
- }
181
- }
182
- if (options.complete) {
183
- options.complete();
184
- }
185
- }
186
-
187
- if (!el.attr) { // HTML element, #409
188
- $(el).setStyle(params);
189
- }
190
- },
191
-
192
- // this only occurs in higcharts 2.0+
193
- stop: function (el) {
194
- var key;
195
- if (el._highcharts_extended && el._highchart_animation) {
196
- for (key in el._highchart_animation) {
197
- // Cancel the animation
198
- // The 'finish' function in the Effect object will remove the reference
199
- el._highchart_animation[key].cancel();
200
- }
201
- }
202
- },
203
-
204
- // um.. each
205
- each: function (arr, fn) {
206
- $A(arr).each(fn);
207
- },
208
-
209
- inArray: function (item, arr, from) {
210
- return arr ? arr.indexOf(item, from) : -1;
211
- },
212
-
213
- /**
214
- * Get the cumulative offset relative to the top left of the page. This method, unlike its
215
- * jQuery and MooTools counterpart, still suffers from issue #208 regarding the position
216
- * of a chart within a fixed container.
217
- */
218
- offset: function (el) {
219
- return $(el).cumulativeOffset();
220
- },
221
-
222
- // fire an event based on an event name (event) and an object (el).
223
- // again, el may not be a dom element
224
- fireEvent: function (el, event, eventArguments, defaultFunction) {
225
- if (el.fire) {
226
- el.fire(HighchartsAdapter.addNS(event), eventArguments);
227
- } else if (el._highcharts_extended) {
228
- eventArguments = eventArguments || {};
229
- el._highcharts_fire(event, eventArguments);
230
- }
231
-
232
- if (eventArguments && eventArguments.defaultPrevented) {
233
- defaultFunction = null;
234
- }
235
-
236
- if (defaultFunction) {
237
- defaultFunction(eventArguments);
238
- }
239
- },
240
-
241
- removeEvent: function (el, event, handler) {
242
- if ($(el).stopObserving) {
243
- if (event) {
244
- event = HighchartsAdapter.addNS(event);
245
- }
246
- $(el).stopObserving(event, handler);
247
- } if (window === el) {
248
- Event.stopObserving(el, event, handler);
249
- } else {
250
- HighchartsAdapter._extend(el);
251
- el._highcharts_stop_observing(event, handler);
252
- }
253
- },
254
-
255
- washMouseEvent: function (e) {
256
- return e;
257
- },
258
-
259
- // um, grep
260
- grep: function (arr, fn) {
261
- return arr.findAll(fn);
262
- },
263
-
264
- // um, map
265
- map: function (arr, fn) {
266
- return arr.map(fn);
267
- },
268
-
269
- // extend an object to handle highchart events (highchart objects, not svg elements).
270
- // this is a very simple way of handling events but whatever, it works (i think)
271
- _extend: function (object) {
272
- if (!object._highcharts_extended) {
273
- Object.extend(object, {
274
- _highchart_events: {},
275
- _highchart_animation: null,
276
- _highcharts_extended: true,
277
- _highcharts_observe: function (name, fn) {
278
- this._highchart_events[name] = [this._highchart_events[name], fn].compact().flatten();
279
- },
280
- _highcharts_stop_observing: function (name, fn) {
281
- if (name) {
282
- if (fn) {
283
- this._highchart_events[name] = [this._highchart_events[name]].compact().flatten().without(fn);
284
- } else {
285
- delete this._highchart_events[name];
286
- }
287
- } else {
288
- this._highchart_events = {};
289
- }
290
- },
291
- _highcharts_fire: function (name, args) {
292
- var target = this;
293
- (this._highchart_events[name] || []).each(function (fn) {
294
- // args is never null here
295
- if (args.stopped) {
296
- return; // "throw $break" wasn't working. i think because of the scope of 'this'.
297
- }
298
-
299
- // Attach a simple preventDefault function to skip default handler if called
300
- args.preventDefault = function () {
301
- args.defaultPrevented = true;
302
- };
303
- args.target = target;
304
-
305
- // If the event handler return false, prevent the default handler from executing
306
- if (fn.bind(this)(args) === false) {
307
- args.preventDefault();
308
- }
309
- }
310
- .bind(this));
311
- }
312
- });
313
- }
314
- }
315
- };
316
- }());
1
+ /**
2
+ * @license Highcharts JS v3.0.2 (2013-06-05)
3
+ * Prototype adapter
4
+ *
5
+ * @author Michael Nelson, Torstein Hønsi.
6
+ *
7
+ * Feel free to use and modify this script.
8
+ * Highcharts license: www.highcharts.com/license.
9
+ */
10
+
11
+ // JSLint options:
12
+ /*global Effect, Class, Event, Element, $, $$, $A */
13
+
14
+ // Adapter interface between prototype and the Highcharts charting library
15
+ var HighchartsAdapter = (function () {
16
+
17
+ var hasEffect = typeof Effect !== 'undefined';
18
+
19
+ return {
20
+
21
+ /**
22
+ * Initialize the adapter. This is run once as Highcharts is first run.
23
+ * @param {Object} pathAnim The helper object to do animations across adapters.
24
+ */
25
+ init: function (pathAnim) {
26
+ if (hasEffect) {
27
+ /**
28
+ * Animation for Highcharts SVG element wrappers only
29
+ * @param {Object} element
30
+ * @param {Object} attribute
31
+ * @param {Object} to
32
+ * @param {Object} options
33
+ */
34
+ Effect.HighchartsTransition = Class.create(Effect.Base, {
35
+ initialize: function (element, attr, to, options) {
36
+ var from,
37
+ opts;
38
+
39
+ this.element = element;
40
+ this.key = attr;
41
+ from = element.attr ? element.attr(attr) : $(element).getStyle(attr);
42
+
43
+ // special treatment for paths
44
+ if (attr === 'd') {
45
+ this.paths = pathAnim.init(
46
+ element,
47
+ element.d,
48
+ to
49
+ );
50
+ this.toD = to;
51
+
52
+
53
+ // fake values in order to read relative position as a float in update
54
+ from = 0;
55
+ to = 1;
56
+ }
57
+
58
+ opts = Object.extend((options || {}), {
59
+ from: from,
60
+ to: to,
61
+ attribute: attr
62
+ });
63
+ this.start(opts);
64
+ },
65
+ setup: function () {
66
+ HighchartsAdapter._extend(this.element);
67
+ // If this is the first animation on this object, create the _highcharts_animation helper that
68
+ // contain pointers to the animation objects.
69
+ if (!this.element._highchart_animation) {
70
+ this.element._highchart_animation = {};
71
+ }
72
+
73
+ // Store a reference to this animation instance.
74
+ this.element._highchart_animation[this.key] = this;
75
+ },
76
+ update: function (position) {
77
+ var paths = this.paths,
78
+ element = this.element,
79
+ obj;
80
+
81
+ if (paths) {
82
+ position = pathAnim.step(paths[0], paths[1], position, this.toD);
83
+ }
84
+
85
+ if (element.attr) { // SVGElement
86
+
87
+ if (element.element) { // If not, it has been destroyed (#1405)
88
+ element.attr(this.options.attribute, position);
89
+ }
90
+
91
+ } else { // HTML, #409
92
+ obj = {};
93
+ obj[this.options.attribute] = position;
94
+ $(element).setStyle(obj);
95
+ }
96
+
97
+ },
98
+ finish: function () {
99
+ // Delete the property that holds this animation now that it is finished.
100
+ // Both canceled animations and complete ones gets a 'finish' call.
101
+ if (this.element && this.element._highchart_animation) { // #1405
102
+ delete this.element._highchart_animation[this.key];
103
+ }
104
+ }
105
+ });
106
+ }
107
+ },
108
+
109
+ /**
110
+ * Run a general method on the framework, following jQuery syntax
111
+ * @param {Object} el The HTML element
112
+ * @param {String} method Which method to run on the wrapped element
113
+ */
114
+ adapterRun: function (el, method) {
115
+
116
+ // This currently works for getting inner width and height. If adding
117
+ // more methods later, we need a conditional implementation for each.
118
+ return parseInt($(el).getStyle(method), 10);
119
+
120
+ },
121
+
122
+ /**
123
+ * Downloads a script and executes a callback when done.
124
+ * @param {String} scriptLocation
125
+ * @param {Function} callback
126
+ */
127
+ getScript: function (scriptLocation, callback) {
128
+ var head = $$('head')[0]; // Returns an array, so pick the first element.
129
+ if (head) {
130
+ // Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback
131
+ head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback));
132
+ }
133
+ },
134
+
135
+ /**
136
+ * Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of
137
+ * events that are not recognized as native.
138
+ */
139
+ addNS: function (eventName) {
140
+ var HTMLEvents = /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
141
+ MouseEvents = /^(?:click|mouse(?:down|up|over|move|out))$/;
142
+ return (HTMLEvents.test(eventName) || MouseEvents.test(eventName)) ?
143
+ eventName :
144
+ 'h:' + eventName;
145
+ },
146
+
147
+ // el needs an event to be attached. el is not necessarily a dom element
148
+ addEvent: function (el, event, fn) {
149
+ if (el.addEventListener || el.attachEvent) {
150
+ Event.observe($(el), HighchartsAdapter.addNS(event), fn);
151
+
152
+ } else {
153
+ HighchartsAdapter._extend(el);
154
+ el._highcharts_observe(event, fn);
155
+ }
156
+ },
157
+
158
+ // motion makes things pretty. use it if effects is loaded, if not... still get to the end result.
159
+ animate: function (el, params, options) {
160
+ var key,
161
+ fx;
162
+
163
+ // default options
164
+ options = options || {};
165
+ options.delay = 0;
166
+ options.duration = (options.duration || 500) / 1000;
167
+ options.afterFinish = options.complete;
168
+
169
+ // animate wrappers and DOM elements
170
+ if (hasEffect) {
171
+ for (key in params) {
172
+ // The fx variable is seemingly thrown away here, but the Effect.setup will add itself to the _highcharts_animation object
173
+ // on the element itself so its not really lost.
174
+ fx = new Effect.HighchartsTransition($(el), key, params[key], options);
175
+ }
176
+ } else {
177
+ if (el.attr) { // #409 without effects
178
+ for (key in params) {
179
+ el.attr(key, params[key]);
180
+ }
181
+ }
182
+ if (options.complete) {
183
+ options.complete();
184
+ }
185
+ }
186
+
187
+ if (!el.attr) { // HTML element, #409
188
+ $(el).setStyle(params);
189
+ }
190
+ },
191
+
192
+ // this only occurs in higcharts 2.0+
193
+ stop: function (el) {
194
+ var key;
195
+ if (el._highcharts_extended && el._highchart_animation) {
196
+ for (key in el._highchart_animation) {
197
+ // Cancel the animation
198
+ // The 'finish' function in the Effect object will remove the reference
199
+ el._highchart_animation[key].cancel();
200
+ }
201
+ }
202
+ },
203
+
204
+ // um.. each
205
+ each: function (arr, fn) {
206
+ $A(arr).each(fn);
207
+ },
208
+
209
+ inArray: function (item, arr, from) {
210
+ return arr ? arr.indexOf(item, from) : -1;
211
+ },
212
+
213
+ /**
214
+ * Get the cumulative offset relative to the top left of the page. This method, unlike its
215
+ * jQuery and MooTools counterpart, still suffers from issue #208 regarding the position
216
+ * of a chart within a fixed container.
217
+ */
218
+ offset: function (el) {
219
+ return $(el).cumulativeOffset();
220
+ },
221
+
222
+ // fire an event based on an event name (event) and an object (el).
223
+ // again, el may not be a dom element
224
+ fireEvent: function (el, event, eventArguments, defaultFunction) {
225
+ if (el.fire) {
226
+ el.fire(HighchartsAdapter.addNS(event), eventArguments);
227
+ } else if (el._highcharts_extended) {
228
+ eventArguments = eventArguments || {};
229
+ el._highcharts_fire(event, eventArguments);
230
+ }
231
+
232
+ if (eventArguments && eventArguments.defaultPrevented) {
233
+ defaultFunction = null;
234
+ }
235
+
236
+ if (defaultFunction) {
237
+ defaultFunction(eventArguments);
238
+ }
239
+ },
240
+
241
+ removeEvent: function (el, event, handler) {
242
+ if ($(el).stopObserving) {
243
+ if (event) {
244
+ event = HighchartsAdapter.addNS(event);
245
+ }
246
+ $(el).stopObserving(event, handler);
247
+ } if (window === el) {
248
+ Event.stopObserving(el, event, handler);
249
+ } else {
250
+ HighchartsAdapter._extend(el);
251
+ el._highcharts_stop_observing(event, handler);
252
+ }
253
+ },
254
+
255
+ washMouseEvent: function (e) {
256
+ return e;
257
+ },
258
+
259
+ // um, grep
260
+ grep: function (arr, fn) {
261
+ return arr.findAll(fn);
262
+ },
263
+
264
+ // um, map
265
+ map: function (arr, fn) {
266
+ return arr.map(fn);
267
+ },
268
+
269
+ // extend an object to handle highchart events (highchart objects, not svg elements).
270
+ // this is a very simple way of handling events but whatever, it works (i think)
271
+ _extend: function (object) {
272
+ if (!object._highcharts_extended) {
273
+ Object.extend(object, {
274
+ _highchart_events: {},
275
+ _highchart_animation: null,
276
+ _highcharts_extended: true,
277
+ _highcharts_observe: function (name, fn) {
278
+ this._highchart_events[name] = [this._highchart_events[name], fn].compact().flatten();
279
+ },
280
+ _highcharts_stop_observing: function (name, fn) {
281
+ if (name) {
282
+ if (fn) {
283
+ this._highchart_events[name] = [this._highchart_events[name]].compact().flatten().without(fn);
284
+ } else {
285
+ delete this._highchart_events[name];
286
+ }
287
+ } else {
288
+ this._highchart_events = {};
289
+ }
290
+ },
291
+ _highcharts_fire: function (name, args) {
292
+ var target = this;
293
+ (this._highchart_events[name] || []).each(function (fn) {
294
+ // args is never null here
295
+ if (args.stopped) {
296
+ return; // "throw $break" wasn't working. i think because of the scope of 'this'.
297
+ }
298
+
299
+ // Attach a simple preventDefault function to skip default handler if called
300
+ args.preventDefault = function () {
301
+ args.defaultPrevented = true;
302
+ };
303
+ args.target = target;
304
+
305
+ // If the event handler return false, prevent the default handler from executing
306
+ if (fn.bind(this)(args) === false) {
307
+ args.preventDefault();
308
+ }
309
+ }
310
+ .bind(this));
311
+ }
312
+ });
313
+ }
314
+ }
315
+ };
316
+ }());
js/highcharts/highcharts-more.js CHANGED
@@ -1,51 +1,51 @@
1
- /*
2
- Highcharts JS v3.0.2 (2013-06-05)
3
-
4
- (c) 2009-2013 Torstein Hønsi
5
-
6
- License: www.highcharts.com/license
7
- */
8
- (function(k,F){function K(a,b,c){this.init.call(this,a,b,c)}function L(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var c=this.xAxis.center;a.push("L",c[0],c[1])},this.closedStacks=!0}function M(a,b){var c=this.chart,d=this.options.animation,e=this.group,f=this.markerGroup,g=this.xAxis.center,j=c.plotLeft,l=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(c={translateX:g[0]+j,translateY:g[1]+l,scaleX:0.001,scaleY:0.001},e.attr(c),f)f.attrSetters=e.attrSetters,
9
- f.attr(c)}else c={translateX:j,translateY:l,scaleX:1,scaleY:1},e.animate(c,d),f&&f.animate(c,d),this.animate=null}else a.call(this,b)}var Q=k.arrayMin,R=k.arrayMax,r=k.each,H=k.extend,m=k.merge,S=k.map,q=k.pick,x=k.pInt,n=k.getOptions().plotOptions,h=k.seriesTypes,A=k.extendClass,N=k.splat,p=k.wrap,O=k.Axis,v=k.Tick,B=k.Series,y=h.column.prototype,u=Math,I=u.round,C=u.floor,J=u.ceil,T=u.min,U=u.max,t=function(){};H(K.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background=
10
- {};d.options=a=m(e,a);(a=a.background)&&r([].concat(N(a)).reverse(),function(a){var b=a.backgroundColor,a=m(d.defaultBackgroundOptions,a);if(b)a.backgroundColor=b;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE,
11
- outerRadius:"105%"}});var G=O.prototype,v=v.prototype,V={getOffset:t,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:t,setCategories:t,setTitle:t},P={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,
12
- distance:15,x:0,y:null},maxPadding:0,minPadding:0,plotBands:[],showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},plotBands:[],showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){this.options=m(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=h.pie.prototype.getCenter.call(this.pane)},getLinePath:function(a,
13
- b){var c=this.center,b=q(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.isXAxis))this.minPixelPadding=this.transA*this.minPointOffset+(this.reversed?(this.endAngleRad-this.startAngleRad)/
14
- 4:0)},beforeSetTickPositions:function(){this.autoConnect&&(this.max+=this.categories&&1||this.pointRange||this.closestPointRange)},setAxisSize:function(){G.setAxisSize.call(this);if(this.center)this.len=this.width=this.height=this.isCircular?this.center[2]*(this.endAngleRad-this.startAngleRad)/2:this.center[2]/2},getPosition:function(a,b){if(!this.isCircular)b=this.translate(a),a=this.min;return this.postTranslate(this.translate(a),q(b,this.center[2]/2)-this.offset)},postTranslate:function(a,b){var c=
15
- this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,f=d[2]/2,g=[q(c.outerRadius,"100%"),c.innerRadius,q(c.thickness,10)],j=/%$/,l,o=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(o||(g[0]=this.translate(a),g[1]=this.translate(b)),g=S(g,function(a){j.test(a)&&(a=x(a,10)*f/100);return a}),
16
- c.shape==="circle"||!o?(a=-Math.PI/2,b=Math.PI*1.5,l=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],g[0],g[0],{start:a,end:b,innerR:q(g[1],g[0]-g[2]),open:l}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,e=this.getPosition(a),f,g,j;this.isCircular?j=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",e.x,e.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(j=this.getLinePath(0,a)):(f=d.xAxis[0],
17
- j=[],a=this.translate(a),c=f.tickPositions,f.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),r(c,function(c,b){g=f.getPosition(c,a);j.push(b?"L":"M",g.x,g.y)}));return j},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};p(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,f=c.isX,g=d&&f,j,l;l=b.options;var o=c.pane||0;if(d){if(H(this,g?V:P),
18
- j=!f)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)H(this,P),this.defaultRadialOptions=(j=f)?this.defaultRadialXOptions:m(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!g&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[o]=b.panes[o]||new K(N(l.pane)[o],b,this),o=i);o=o.options;b.inverted=!1;l.chart.zoomType=null;this.startAngleRad=b=(o.startAngle-90)*Math.PI/180;this.endAngleRad=l=(q(o.endAngle,o.startAngle+360)-90)*Math.PI/180;this.offset=
19
- a.offset||0;if((this.isCircular=j)&&c.max===F&&l-b===2*Math.PI)this.autoConnect=!0}});p(v,"getPosition",function(a,b,c,d,e){var f=this.axis;return f.getPosition?f.getPosition(c):a.call(this,b,c,d,e)});p(v,"getLabelPosition",function(a,b,c,d,e,f,g,j,l){var o=this.axis,i=f.y,h=f.align,k=(o.translate(this.pos)+o.startAngleRad+Math.PI/2)/Math.PI*180;o.isRadial?(a=o.getPosition(this.pos,o.center[2]/2+q(f.distance,-25)),f.rotation==="auto"?d.attr({rotation:k}):i===null&&(i=x(d.styles.lineHeight)*0.9-d.getBBox().height/
20
- 2),h===null&&(h=o.isCircular?k>20&&k<160?"left":k>200&&k<340?"right":"center":"center",d.attr({align:h})),a.x+=f.x,a.y+=i):a=a.call(this,b,c,d,e,f,g,j,l);return a});p(v,"getMarkPath",function(a,b,c,d,e,f,g){var j=this.axis;j.isRadial?(a=j.getPosition(this.pos,j.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,f,g);return b});n.arearange=m(n.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
21
- trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}});h.arearange=k.extendClass(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;r(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});B.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);
22
- r(this.points,function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.toPixels(d,!0)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.toPixels(d,!0))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=B.prototype.getSegmentPath,f,g;g=this.options;var j=g.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotX,plotY:f.plotHigh});a=e.call(this,b);if(j)j===
23
- !0&&(j="left"),g.step={left:"right",center:"center",right:"left"}[j];c=e.call(this,c);g.step=j;g=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return g},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=B.prototype,f=this.options.dataLabels,g,j=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)g=a[c],g.y=g.high,g.plotY=g.plotHigh,d[c]=g.dataLabel,g.dataLabel=g.dataLabelUpper,g.below=!1,j?(f.align="left",f.x=f.xHigh):f.y=f.yHigh;e.drawDataLabels.apply(this,
24
- arguments);for(c=b;c--;)g=a[c],g.dataLabelUpper=g.dataLabel,g.dataLabel=d[c],g.y=g.low,g.plotY=g.plotLow,g.below=!0,j?(f.align="right",f.x=f.xLow):f.y=f.yLow;e.drawDataLabels.apply(this,arguments)}},alignDataLabel:h.column.prototype.alignDataLabel,getSymbol:h.column.prototype.getSymbol,drawPoints:t});n.areasplinerange=m(n.arearange);h.areasplinerange=A(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});n.columnrange=m(n.column,n.arearange,{lineWidth:1,pointRange:null});
25
- h.columnrange=A(h.arearange,{type:"columnrange",translate:function(){var a=this.yAxis,b;y.translate.apply(this);r(this.points,function(c){var d=c.shapeArgs;c.plotHigh=b=a.translate(c.high,0,1,0,1);c.plotLow=c.plotY;d.y=b;d.height=c.plotY-b})},trackerGroups:["group","dataLabels"],drawGraph:t,pointAttrToOptions:y.pointAttrToOptions,drawPoints:y.drawPoints,drawTracker:y.drawTracker,animate:y.animate,getColumnMetrics:y.getColumnMetrics});n.gauge=m(n.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver",
26
- borderRadius:3,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});v={type:"gauge",pointClass:k.extendClass(k.Point,{setState:function(a){this.state=a}}),angular:!0,drawGraph:t,trackerGroups:["group","dataLabels"],translate:function(){var a=this.yAxis,b=this.options,c=a.center;this.generatePoints();r(this.points,function(d){var e=m(b.dial,d.dial),f=x(q(e.radius,80))*c[2]/200,g=x(q(e.baseLength,70))*f/100,j=x(q(e.rearLength,10))*f/100,
27
- l=e.baseWidth||3,o=e.topWidth||1,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs={d:e.path||["M",-j,-l/2,"L",g,-l/2,f,-o/2,f,o/2,g,l/2,-j,l/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,f=a.chart.renderer;r(a.points,function(c){var b=c.graphic,e=c.shapeArgs,o=e.d,
28
- i=m(d.dial,c.dial);b?(b.animate(e),e.d=o):c.graphic=f[c.shapeType](e).attr({stroke:i.borderColor||"none","stroke-width":i.borderWidth||0,fill:i.backgroundColor||"black",rotation:e.rotation}).add(a.group)});c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=f.circle(0,0,q(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor||"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)r(b.points,function(a){var d=a.graphic;
29
- d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);h.pie.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:h.pie.prototype.setData,drawTracker:h.column.prototype.drawTracker};h.gauge=k.extendClass(h.line,v);n.boxplot=m(n.column,{fillColor:"#FFFFFF",lineWidth:1,
30
- medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>Minimum: {point.low}<br/>Lower quartile: {point.q1}<br/>Median: {point.median}<br/>Higher quartile: {point.q3}<br/>Maximum: {point.high}<br/>'},whiskerLength:"50%",whiskerWidth:2});h.boxplot=A(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",
31
- pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:t,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);r(this.points,function(c){r(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=this,b=a.points,c=a.options,d=a.chart.renderer,e,f,g,j,l,o,i,h,k,n,s,E,p,w,m,u,y,t,x,v,B,A,z=a.doQuartiles!==!1,D=parseInt(a.options.whiskerLength,10)/100;r(b,function(b){k=b.graphic;
32
- B=b.shapeArgs;s={};w={};u={};A=b.color||a.color;if(b.plotY!==F)if(e=b.pointAttr[b.selected?"selected":""],y=B.width,t=C(B.x),x=t+y,v=I(y/2),f=C(z?b.q1Plot:b.lowPlot),g=C(z?b.q3Plot:b.lowPlot),j=C(b.highPlot),l=C(b.lowPlot),s.stroke=b.stemColor||c.stemColor||A,s["stroke-width"]=q(b.stemWidth,c.stemWidth,c.lineWidth),s.dashstyle=b.stemDashStyle||c.stemDashStyle,w.stroke=b.whiskerColor||c.whiskerColor||A,w["stroke-width"]=q(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor||
33
- A,u["stroke-width"]=q(b.medianWidth,c.medianWidth,c.lineWidth),i=s["stroke-width"]%2/2,h=t+v+i,n=["M",h,g,"L",h,j,"M",h,f,"L",h,l,"z"],z&&(i=e["stroke-width"]%2/2,h=C(h)+i,f=C(f)+i,g=C(g)+i,t+=i,x+=i,E=["M",t,g,"L",t,f,"L",x,f,"L",x,g,"L",t,g,"z"]),D&&(i=w["stroke-width"]%2/2,j+=i,l+=i,p=["M",h-v*D,j,"L",h+v*D,j,"M",h-v*D,l,"L",h+v*D,l]),i=u["stroke-width"]%2/2,o=I(b.medianPlot)+i,m=["M",t,o,"L",x,o,"z"],k)b.stem.animate({d:n}),D&&b.whiskers.animate({d:p}),z&&b.box.animate({d:E}),b.medianShape.animate({d:m});
34
- else{b.graphic=k=d.g().add(a.group);b.stem=d.path(n).attr(s).add(k);if(D)b.whiskers=d.path(p).attr(w).add(k);if(z)b.box=d.path(E).attr(e).add(k);b.medianShape=d.path(m).attr(u).add(k)}})}});n.errorbar=m(n.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:n.arearange.tooltip.pointFormat},whiskerWidth:null});h.errorbar=A(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,getColumnMetrics:function(){return this.linkedParent&&
35
- this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});p(G,"getSeriesExtremes",function(a,b){a.call(this,b);if(!this.isXAxis){var c=this,d=[],e=!0;r(c.series,function(a){if(a.visible&&a.stackKey&&!(a.type!=="waterfall"||HighchartsAdapter.inArray(a.stackKey)!==-1)){if(e)c.dataMin=c.dataMax=null,e=!1;var b=a.processedYData,j=b.length,l=b[0],h=b[0],i=a.options.threshold,k=c.stacks,n=a.stackKey,p="-"+n,s,E,m,w;for(w=0;w<j;w++){m=b[w]<i?p:n;s=k[m][w].total;if(w>i)s+=E,k[m][w].setTotal(s),
36
- k[m][w]._cum=null;s<l&&(l=s);s>h&&(h=s);E=s}a.dataMin=l;a.dataMax=h;c.dataMin=T(q(c.dataMin,l),l,i);c.dataMax=U(q(c.dataMax,h),h,i);d.push(a.stackKey);if(typeof i==="number")if(c.dataMin>=i)c.dataMin=i,c.ignoreMinPadding=!0;else if(c.dataMax<i)c.dataMax=i,c.ignoreMaxPadding=!0}})}});n.waterfall=m(n.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=A(h.column,{type:"waterfall",upColorProp:"fill",pointArrayMap:["y","low"],pointValKey:"y",init:function(a,b){b.stacking=
37
- !0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.yAxis,b,c,d,e,f,g,j,l,o,i,k,n,m,p=this.options.borderWidth%2/2;h.column.prototype.translate.apply(this);d=this.points;o=j=d[0];g=l=d[0].y;for(c=1,b=d.length;c<b;c++)if(e=d[c],f=e.shapeArgs,m=this.getStack(c),k=this.getStack(c-1),n=this.getStackY(k),o===null&&(o=e,l=0),e.y&&!e.isSum&&!e.isIntermediateSum&&(g+=e.y,l+=e.y),e.isSum||e.isIntermediateSum)e.isIntermediateSum?(i=this.getSumEdges(o,d[c-1]),e.y=l,o=null):(i=this.getSumEdges(j,
38
- d[c-1]),e.y=g),f.y=e.plotY=i[1],f.height=i[0]-i[1];else if(i=m._cum===null?k.total:m._cum,m._cum=i+e.y,e.y<0)f.y=J(a.translate(i,0,1))-p,f.height=J(a.translate(m._cum,0,1)-f.y);else{if(k.total+e.y<0)f.y=a.translate(m._cum,0,1);f.height=C(n-f.y)}},processData:function(a){B.prototype.processData.call(this,a);var a=this.yData,b=a.length,c,d;for(d=0;d<b;d++)c=a[d],c!==null&&typeof c!=="number"&&(a[d]=c==="sum"?null:c==="intermediateSum"?null:c[0])},toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum";
39
- return[a.y]},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,c=a.upColor||this.color,a=k.Color(c).brighten(0.1).get(),d=m(this.pointAttr),e=this.upColorProp;d[""][e]=c;d.hover[e]=b.hover.upColor||a;d.select[e]=b.select.upColor||c;r(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,c=I(this.options.lineWidth+this.options.borderWidth)%2/2,d=[],e,f,g;for(g=1;g<b;g++)f=a[g].shapeArgs,
40
- e=a[g-1].shapeArgs,f=["M",e.x+e.width,e.y+c,"L",f.x,e.y+c],a[g-1].y<0&&(f[2]+=e.height,f[5]+=e.height),d=d.concat(f);return d},getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},getStackY:function(a){return J(this.yAxis.translate(a.total,null,!0))},getSumEdges:function(a,b){var c,d,e;d=this.options.threshold;c=a.y>=d?a.shapeArgs.y+a.shapeArgs.height:a.shapeArgs.y;d=b.y>=d?b.shapeArgs.y:b.shapeArgs.y+b.shapeArgs.height;
41
- d>c&&(e=c,c=d,d=e);return[c,d]},drawGraph:B.prototype.drawGraph});n.bubble=m(n.scatter,{dataLabels:{inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},zThreshold:0});h.bubble=A(h.scatter,{type:"bubble",pointArrayMap:["y","z"],trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},
42
- applyOpacity:function(a){var b=this.options.marker,c=q(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=k.Color(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=B.prototype.convertAttribs.apply(this,arguments);a.fill=this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,f,g,j=this.zData,h=[];for(f=0,e=j.length;f<e;f++)g=b-a,g=g>0?(j[f]-a)/(b-a):0.5,h.push(u.ceil(c+g*(d-c))/2);this.radii=h},animate:function(a){var b=this.options.animation;if(!a)r(this.points,
43
- function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=F},drawLegendSymbol:function(a,b){var c=x(a.itemStyle.fontSize)/
44
- 2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup)},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel});O.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,f=this.isXAxis,g=f?"xData":"yData",j=this.min,h={},k=u.min(c.plotWidth,c.plotHeight),i=Number.MAX_VALUE,m=-Number.MAX_VALUE,n=this.max-j,p=b/n,s=[];this.tickPositions&&(r(this.series,function(b){var c=b.options;if(b.type==="bubble"&&
45
- b.visible&&(a.allowZoomOutside=!0,s.push(b),f))r(["minSize","maxSize"],function(a){var b=c[a],d=/%$/.test(b),b=x(b);h[a]=d?k*b/100:b}),b.minPxSize=h.minSize,b=b.zData,b.length&&(i=u.min(i,u.max(Q(b),c.displayNegative===!1?c.zThreshold:-Number.MAX_VALUE)),m=u.max(m,R(b)))}),r(s,function(a){var b=a[g],c=b.length,k;f&&a.getRadii(i,m,h.minSize,h.maxSize);if(n>0)for(;c--;)k=a.radii[c],d=Math.min((b[c]-j)*p-k,d),e=Math.max((b[c]-j)*p+k,e)}),n>0&&q(this.options.min,this.userMin)===F&&q(this.options.max,
46
- this.userMax)===F&&(e-=b,p*=(b+d-e)/b,this.min+=d/p,this.max+=e/p))};var z=B.prototype,n=k.Pointer.prototype;z.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.clientX=b/Math.PI*180;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};p(h.area.prototype,"init",L);p(h.areaspline.prototype,"init",L);p(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,f,g,j,h,k,i;if(this.chart.polar){e=
47
- c.plotX;f=c.plotY;a=b[d-1];g=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),g||(g=b[1]));if(a&&g)j=a.plotX,h=a.plotY,b=g.plotX,k=g.plotY,j=(1.5*e+j)/2.5,h=(1.5*f+h)/2.5,g=(1.5*e+b)/2.5,i=(1.5*f+k)/2.5,b=Math.sqrt(Math.pow(j-e,2)+Math.pow(h-f,2)),k=Math.sqrt(Math.pow(g-e,2)+Math.pow(i-f,2)),j=Math.atan2(h-f,j-e),h=Math.atan2(i-f,g-e),i=Math.PI/2+(j+h)/2,Math.abs(j-i)>Math.PI/2&&(i-=Math.PI),j=e+Math.cos(i)*b,h=f+Math.sin(i)*b,g=e+Math.cos(Math.PI+i)*k,i=f+Math.sin(Math.PI+i)*k,c.rightContX=g,c.rightContY=
48
- i;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,j||e,h||f,e,f],a.rightContX=a.rightContY=null):c=["M",e,f]}else c=a.call(this,b,c,d);return c});p(z,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});p(z,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,
49
- b)});p(z,"animate",M);p(y,"animate",M);p(z,"setTooltipPoints",function(a,b){this.chart.polar&&H(this.xAxis,{tooltipLen:360});return a.call(this,b)});p(y,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,f=this.chart.renderer,g,h;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points;for(h=b.length;h--;)g=b[h],a=g.barX+e,g.shapeType="path",g.shapeArgs={d:f.symbols.arc(d[0],d[1],c-g.plotY,null,{start:a,end:a+g.pointWidth,innerR:c-q(g.yBottom,c)})},
50
- this.toXY(g)}});p(y,"alignDataLabel",function(a,b,c,d,e,f){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(d.align===null)d.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(d.verticalAlign===null)d.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";z.alignDataLabel.call(this,b,c,d,e,f)}else a.call(this,b,c,d,e,f)});p(n,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,
51
- d)/Math.PI*180)):c=a.call(this,b);return c});p(n,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?r(c.axes,function(a){var f=a.isXAxis,g=a.center,h=b.chartX-g[0]-c.plotLeft,g=b.chartY-g[1]-c.plotTop;d[f?"xAxis":"yAxis"].push({axis:a,value:a.translate(f?Math.PI-Math.atan2(h,g):Math.sqrt(Math.pow(h,2)+Math.pow(g,2)),!0)})}):d=a.call(this,b);return d})})(Highcharts);
1
+ /*
2
+ Highcharts JS v3.0.2 (2013-06-05)
3
+
4
+ (c) 2009-2013 Torstein Hønsi
5
+
6
+ License: www.highcharts.com/license
7
+ */
8
+ (function(k,F){function K(a,b,c){this.init.call(this,a,b,c)}function L(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var c=this.xAxis.center;a.push("L",c[0],c[1])},this.closedStacks=!0}function M(a,b){var c=this.chart,d=this.options.animation,e=this.group,f=this.markerGroup,g=this.xAxis.center,j=c.plotLeft,l=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(c={translateX:g[0]+j,translateY:g[1]+l,scaleX:0.001,scaleY:0.001},e.attr(c),f)f.attrSetters=e.attrSetters,
9
+ f.attr(c)}else c={translateX:j,translateY:l,scaleX:1,scaleY:1},e.animate(c,d),f&&f.animate(c,d),this.animate=null}else a.call(this,b)}var Q=k.arrayMin,R=k.arrayMax,r=k.each,H=k.extend,m=k.merge,S=k.map,q=k.pick,x=k.pInt,n=k.getOptions().plotOptions,h=k.seriesTypes,A=k.extendClass,N=k.splat,p=k.wrap,O=k.Axis,v=k.Tick,B=k.Series,y=h.column.prototype,u=Math,I=u.round,C=u.floor,J=u.ceil,T=u.min,U=u.max,t=function(){};H(K.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background=
10
+ {};d.options=a=m(e,a);(a=a.background)&&r([].concat(N(a)).reverse(),function(a){var b=a.backgroundColor,a=m(d.defaultBackgroundOptions,a);if(b)a.backgroundColor=b;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE,
11
+ outerRadius:"105%"}});var G=O.prototype,v=v.prototype,V={getOffset:t,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:t,setCategories:t,setTitle:t},P={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,
12
+ distance:15,x:0,y:null},maxPadding:0,minPadding:0,plotBands:[],showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},plotBands:[],showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){this.options=m(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=h.pie.prototype.getCenter.call(this.pane)},getLinePath:function(a,
13
+ b){var c=this.center,b=q(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.isXAxis))this.minPixelPadding=this.transA*this.minPointOffset+(this.reversed?(this.endAngleRad-this.startAngleRad)/
14
+ 4:0)},beforeSetTickPositions:function(){this.autoConnect&&(this.max+=this.categories&&1||this.pointRange||this.closestPointRange)},setAxisSize:function(){G.setAxisSize.call(this);if(this.center)this.len=this.width=this.height=this.isCircular?this.center[2]*(this.endAngleRad-this.startAngleRad)/2:this.center[2]/2},getPosition:function(a,b){if(!this.isCircular)b=this.translate(a),a=this.min;return this.postTranslate(this.translate(a),q(b,this.center[2]/2)-this.offset)},postTranslate:function(a,b){var c=
15
+ this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,f=d[2]/2,g=[q(c.outerRadius,"100%"),c.innerRadius,q(c.thickness,10)],j=/%$/,l,o=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(o||(g[0]=this.translate(a),g[1]=this.translate(b)),g=S(g,function(a){j.test(a)&&(a=x(a,10)*f/100);return a}),
16
+ c.shape==="circle"||!o?(a=-Math.PI/2,b=Math.PI*1.5,l=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],g[0],g[0],{start:a,end:b,innerR:q(g[1],g[0]-g[2]),open:l}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,e=this.getPosition(a),f,g,j;this.isCircular?j=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",e.x,e.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(j=this.getLinePath(0,a)):(f=d.xAxis[0],
17
+ j=[],a=this.translate(a),c=f.tickPositions,f.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),r(c,function(c,b){g=f.getPosition(c,a);j.push(b?"L":"M",g.x,g.y)}));return j},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};p(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,f=c.isX,g=d&&f,j,l;l=b.options;var o=c.pane||0;if(d){if(H(this,g?V:P),
18
+ j=!f)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)H(this,P),this.defaultRadialOptions=(j=f)?this.defaultRadialXOptions:m(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!g&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[o]=b.panes[o]||new K(N(l.pane)[o],b,this),o=i);o=o.options;b.inverted=!1;l.chart.zoomType=null;this.startAngleRad=b=(o.startAngle-90)*Math.PI/180;this.endAngleRad=l=(q(o.endAngle,o.startAngle+360)-90)*Math.PI/180;this.offset=
19
+ a.offset||0;if((this.isCircular=j)&&c.max===F&&l-b===2*Math.PI)this.autoConnect=!0}});p(v,"getPosition",function(a,b,c,d,e){var f=this.axis;return f.getPosition?f.getPosition(c):a.call(this,b,c,d,e)});p(v,"getLabelPosition",function(a,b,c,d,e,f,g,j,l){var o=this.axis,i=f.y,h=f.align,k=(o.translate(this.pos)+o.startAngleRad+Math.PI/2)/Math.PI*180;o.isRadial?(a=o.getPosition(this.pos,o.center[2]/2+q(f.distance,-25)),f.rotation==="auto"?d.attr({rotation:k}):i===null&&(i=x(d.styles.lineHeight)*0.9-d.getBBox().height/
20
+ 2),h===null&&(h=o.isCircular?k>20&&k<160?"left":k>200&&k<340?"right":"center":"center",d.attr({align:h})),a.x+=f.x,a.y+=i):a=a.call(this,b,c,d,e,f,g,j,l);return a});p(v,"getMarkPath",function(a,b,c,d,e,f,g){var j=this.axis;j.isRadial?(a=j.getPosition(this.pos,j.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,f,g);return b});n.arearange=m(n.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
21
+ trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}});h.arearange=k.extendClass(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;r(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});B.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);
22
+ r(this.points,function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.toPixels(d,!0)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.toPixels(d,!0))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=B.prototype.getSegmentPath,f,g;g=this.options;var j=g.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotX,plotY:f.plotHigh});a=e.call(this,b);if(j)j===
23
+ !0&&(j="left"),g.step={left:"right",center:"center",right:"left"}[j];c=e.call(this,c);g.step=j;g=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return g},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=B.prototype,f=this.options.dataLabels,g,j=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)g=a[c],g.y=g.high,g.plotY=g.plotHigh,d[c]=g.dataLabel,g.dataLabel=g.dataLabelUpper,g.below=!1,j?(f.align="left",f.x=f.xHigh):f.y=f.yHigh;e.drawDataLabels.apply(this,
24
+ arguments);for(c=b;c--;)g=a[c],g.dataLabelUpper=g.dataLabel,g.dataLabel=d[c],g.y=g.low,g.plotY=g.plotLow,g.below=!0,j?(f.align="right",f.x=f.xLow):f.y=f.yLow;e.drawDataLabels.apply(this,arguments)}},alignDataLabel:h.column.prototype.alignDataLabel,getSymbol:h.column.prototype.getSymbol,drawPoints:t});n.areasplinerange=m(n.arearange);h.areasplinerange=A(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});n.columnrange=m(n.column,n.arearange,{lineWidth:1,pointRange:null});
25
+ h.columnrange=A(h.arearange,{type:"columnrange",translate:function(){var a=this.yAxis,b;y.translate.apply(this);r(this.points,function(c){var d=c.shapeArgs;c.plotHigh=b=a.translate(c.high,0,1,0,1);c.plotLow=c.plotY;d.y=b;d.height=c.plotY-b})},trackerGroups:["group","dataLabels"],drawGraph:t,pointAttrToOptions:y.pointAttrToOptions,drawPoints:y.drawPoints,drawTracker:y.drawTracker,animate:y.animate,getColumnMetrics:y.getColumnMetrics});n.gauge=m(n.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver",
26
+ borderRadius:3,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});v={type:"gauge",pointClass:k.extendClass(k.Point,{setState:function(a){this.state=a}}),angular:!0,drawGraph:t,trackerGroups:["group","dataLabels"],translate:function(){var a=this.yAxis,b=this.options,c=a.center;this.generatePoints();r(this.points,function(d){var e=m(b.dial,d.dial),f=x(q(e.radius,80))*c[2]/200,g=x(q(e.baseLength,70))*f/100,j=x(q(e.rearLength,10))*f/100,
27
+ l=e.baseWidth||3,o=e.topWidth||1,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs={d:e.path||["M",-j,-l/2,"L",g,-l/2,f,-o/2,f,o/2,g,l/2,-j,l/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,f=a.chart.renderer;r(a.points,function(c){var b=c.graphic,e=c.shapeArgs,o=e.d,
28
+ i=m(d.dial,c.dial);b?(b.animate(e),e.d=o):c.graphic=f[c.shapeType](e).attr({stroke:i.borderColor||"none","stroke-width":i.borderWidth||0,fill:i.backgroundColor||"black",rotation:e.rotation}).add(a.group)});c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=f.circle(0,0,q(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor||"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)r(b.points,function(a){var d=a.graphic;
29
+ d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);h.pie.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:h.pie.prototype.setData,drawTracker:h.column.prototype.drawTracker};h.gauge=k.extendClass(h.line,v);n.boxplot=m(n.column,{fillColor:"#FFFFFF",lineWidth:1,
30
+ medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>Minimum: {point.low}<br/>Lower quartile: {point.q1}<br/>Median: {point.median}<br/>Higher quartile: {point.q3}<br/>Maximum: {point.high}<br/>'},whiskerLength:"50%",whiskerWidth:2});h.boxplot=A(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",
31
+ pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:t,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);r(this.points,function(c){r(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=this,b=a.points,c=a.options,d=a.chart.renderer,e,f,g,j,l,o,i,h,k,n,s,E,p,w,m,u,y,t,x,v,B,A,z=a.doQuartiles!==!1,D=parseInt(a.options.whiskerLength,10)/100;r(b,function(b){k=b.graphic;
32
+ B=b.shapeArgs;s={};w={};u={};A=b.color||a.color;if(b.plotY!==F)if(e=b.pointAttr[b.selected?"selected":""],y=B.width,t=C(B.x),x=t+y,v=I(y/2),f=C(z?b.q1Plot:b.lowPlot),g=C(z?b.q3Plot:b.lowPlot),j=C(b.highPlot),l=C(b.lowPlot),s.stroke=b.stemColor||c.stemColor||A,s["stroke-width"]=q(b.stemWidth,c.stemWidth,c.lineWidth),s.dashstyle=b.stemDashStyle||c.stemDashStyle,w.stroke=b.whiskerColor||c.whiskerColor||A,w["stroke-width"]=q(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor||
33
+ A,u["stroke-width"]=q(b.medianWidth,c.medianWidth,c.lineWidth),i=s["stroke-width"]%2/2,h=t+v+i,n=["M",h,g,"L",h,j,"M",h,f,"L",h,l,"z"],z&&(i=e["stroke-width"]%2/2,h=C(h)+i,f=C(f)+i,g=C(g)+i,t+=i,x+=i,E=["M",t,g,"L",t,f,"L",x,f,"L",x,g,"L",t,g,"z"]),D&&(i=w["stroke-width"]%2/2,j+=i,l+=i,p=["M",h-v*D,j,"L",h+v*D,j,"M",h-v*D,l,"L",h+v*D,l]),i=u["stroke-width"]%2/2,o=I(b.medianPlot)+i,m=["M",t,o,"L",x,o,"z"],k)b.stem.animate({d:n}),D&&b.whiskers.animate({d:p}),z&&b.box.animate({d:E}),b.medianShape.animate({d:m});
34
+ else{b.graphic=k=d.g().add(a.group);b.stem=d.path(n).attr(s).add(k);if(D)b.whiskers=d.path(p).attr(w).add(k);if(z)b.box=d.path(E).attr(e).add(k);b.medianShape=d.path(m).attr(u).add(k)}})}});n.errorbar=m(n.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:n.arearange.tooltip.pointFormat},whiskerWidth:null});h.errorbar=A(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,getColumnMetrics:function(){return this.linkedParent&&
35
+ this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});p(G,"getSeriesExtremes",function(a,b){a.call(this,b);if(!this.isXAxis){var c=this,d=[],e=!0;r(c.series,function(a){if(a.visible&&a.stackKey&&!(a.type!=="waterfall"||HighchartsAdapter.inArray(a.stackKey)!==-1)){if(e)c.dataMin=c.dataMax=null,e=!1;var b=a.processedYData,j=b.length,l=b[0],h=b[0],i=a.options.threshold,k=c.stacks,n=a.stackKey,p="-"+n,s,E,m,w;for(w=0;w<j;w++){m=b[w]<i?p:n;s=k[m][w].total;if(w>i)s+=E,k[m][w].setTotal(s),
36
+ k[m][w]._cum=null;s<l&&(l=s);s>h&&(h=s);E=s}a.dataMin=l;a.dataMax=h;c.dataMin=T(q(c.dataMin,l),l,i);c.dataMax=U(q(c.dataMax,h),h,i);d.push(a.stackKey);if(typeof i==="number")if(c.dataMin>=i)c.dataMin=i,c.ignoreMinPadding=!0;else if(c.dataMax<i)c.dataMax=i,c.ignoreMaxPadding=!0}})}});n.waterfall=m(n.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=A(h.column,{type:"waterfall",upColorProp:"fill",pointArrayMap:["y","low"],pointValKey:"y",init:function(a,b){b.stacking=
37
+ !0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.yAxis,b,c,d,e,f,g,j,l,o,i,k,n,m,p=this.options.borderWidth%2/2;h.column.prototype.translate.apply(this);d=this.points;o=j=d[0];g=l=d[0].y;for(c=1,b=d.length;c<b;c++)if(e=d[c],f=e.shapeArgs,m=this.getStack(c),k=this.getStack(c-1),n=this.getStackY(k),o===null&&(o=e,l=0),e.y&&!e.isSum&&!e.isIntermediateSum&&(g+=e.y,l+=e.y),e.isSum||e.isIntermediateSum)e.isIntermediateSum?(i=this.getSumEdges(o,d[c-1]),e.y=l,o=null):(i=this.getSumEdges(j,
38
+ d[c-1]),e.y=g),f.y=e.plotY=i[1],f.height=i[0]-i[1];else if(i=m._cum===null?k.total:m._cum,m._cum=i+e.y,e.y<0)f.y=J(a.translate(i,0,1))-p,f.height=J(a.translate(m._cum,0,1)-f.y);else{if(k.total+e.y<0)f.y=a.translate(m._cum,0,1);f.height=C(n-f.y)}},processData:function(a){B.prototype.processData.call(this,a);var a=this.yData,b=a.length,c,d;for(d=0;d<b;d++)c=a[d],c!==null&&typeof c!=="number"&&(a[d]=c==="sum"?null:c==="intermediateSum"?null:c[0])},toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum";
39
+ return[a.y]},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,c=a.upColor||this.color,a=k.Color(c).brighten(0.1).get(),d=m(this.pointAttr),e=this.upColorProp;d[""][e]=c;d.hover[e]=b.hover.upColor||a;d.select[e]=b.select.upColor||c;r(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,c=I(this.options.lineWidth+this.options.borderWidth)%2/2,d=[],e,f,g;for(g=1;g<b;g++)f=a[g].shapeArgs,
40
+ e=a[g-1].shapeArgs,f=["M",e.x+e.width,e.y+c,"L",f.x,e.y+c],a[g-1].y<0&&(f[2]+=e.height,f[5]+=e.height),d=d.concat(f);return d},getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},getStackY:function(a){return J(this.yAxis.translate(a.total,null,!0))},getSumEdges:function(a,b){var c,d,e;d=this.options.threshold;c=a.y>=d?a.shapeArgs.y+a.shapeArgs.height:a.shapeArgs.y;d=b.y>=d?b.shapeArgs.y:b.shapeArgs.y+b.shapeArgs.height;
41
+ d>c&&(e=c,c=d,d=e);return[c,d]},drawGraph:B.prototype.drawGraph});n.bubble=m(n.scatter,{dataLabels:{inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},zThreshold:0});h.bubble=A(h.scatter,{type:"bubble",pointArrayMap:["y","z"],trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},
42
+ applyOpacity:function(a){var b=this.options.marker,c=q(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=k.Color(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=B.prototype.convertAttribs.apply(this,arguments);a.fill=this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,f,g,j=this.zData,h=[];for(f=0,e=j.length;f<e;f++)g=b-a,g=g>0?(j[f]-a)/(b-a):0.5,h.push(u.ceil(c+g*(d-c))/2);this.radii=h},animate:function(a){var b=this.options.animation;if(!a)r(this.points,
43
+ function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=F},drawLegendSymbol:function(a,b){var c=x(a.itemStyle.fontSize)/
44
+ 2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup)},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel});O.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,f=this.isXAxis,g=f?"xData":"yData",j=this.min,h={},k=u.min(c.plotWidth,c.plotHeight),i=Number.MAX_VALUE,m=-Number.MAX_VALUE,n=this.max-j,p=b/n,s=[];this.tickPositions&&(r(this.series,function(b){var c=b.options;if(b.type==="bubble"&&
45
+ b.visible&&(a.allowZoomOutside=!0,s.push(b),f))r(["minSize","maxSize"],function(a){var b=c[a],d=/%$/.test(b),b=x(b);h[a]=d?k*b/100:b}),b.minPxSize=h.minSize,b=b.zData,b.length&&(i=u.min(i,u.max(Q(b),c.displayNegative===!1?c.zThreshold:-Number.MAX_VALUE)),m=u.max(m,R(b)))}),r(s,function(a){var b=a[g],c=b.length,k;f&&a.getRadii(i,m,h.minSize,h.maxSize);if(n>0)for(;c--;)k=a.radii[c],d=Math.min((b[c]-j)*p-k,d),e=Math.max((b[c]-j)*p+k,e)}),n>0&&q(this.options.min,this.userMin)===F&&q(this.options.max,
46
+ this.userMax)===F&&(e-=b,p*=(b+d-e)/b,this.min+=d/p,this.max+=e/p))};var z=B.prototype,n=k.Pointer.prototype;z.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.clientX=b/Math.PI*180;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};p(h.area.prototype,"init",L);p(h.areaspline.prototype,"init",L);p(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,f,g,j,h,k,i;if(this.chart.polar){e=
47
+ c.plotX;f=c.plotY;a=b[d-1];g=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),g||(g=b[1]));if(a&&g)j=a.plotX,h=a.plotY,b=g.plotX,k=g.plotY,j=(1.5*e+j)/2.5,h=(1.5*f+h)/2.5,g=(1.5*e+b)/2.5,i=(1.5*f+k)/2.5,b=Math.sqrt(Math.pow(j-e,2)+Math.pow(h-f,2)),k=Math.sqrt(Math.pow(g-e,2)+Math.pow(i-f,2)),j=Math.atan2(h-f,j-e),h=Math.atan2(i-f,g-e),i=Math.PI/2+(j+h)/2,Math.abs(j-i)>Math.PI/2&&(i-=Math.PI),j=e+Math.cos(i)*b,h=f+Math.sin(i)*b,g=e+Math.cos(Math.PI+i)*k,i=f+Math.sin(Math.PI+i)*k,c.rightContX=g,c.rightContY=
48
+ i;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,j||e,h||f,e,f],a.rightContX=a.rightContY=null):c=["M",e,f]}else c=a.call(this,b,c,d);return c});p(z,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});p(z,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,
49
+ b)});p(z,"animate",M);p(y,"animate",M);p(z,"setTooltipPoints",function(a,b){this.chart.polar&&H(this.xAxis,{tooltipLen:360});return a.call(this,b)});p(y,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,f=this.chart.renderer,g,h;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points;for(h=b.length;h--;)g=b[h],a=g.barX+e,g.shapeType="path",g.shapeArgs={d:f.symbols.arc(d[0],d[1],c-g.plotY,null,{start:a,end:a+g.pointWidth,innerR:c-q(g.yBottom,c)})},
50
+ this.toXY(g)}});p(y,"alignDataLabel",function(a,b,c,d,e,f){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(d.align===null)d.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(d.verticalAlign===null)d.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";z.alignDataLabel.call(this,b,c,d,e,f)}else a.call(this,b,c,d,e,f)});p(n,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,
51
+ d)/Math.PI*180)):c=a.call(this,b);return c});p(n,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?r(c.axes,function(a){var f=a.isXAxis,g=a.center,h=b.chartX-g[0]-c.plotLeft,g=b.chartY-g[1]-c.plotTop;d[f?"xAxis":"yAxis"].push({axis:a,value:a.translate(f?Math.PI-Math.atan2(h,g):Math.sqrt(Math.pow(h,2)+Math.pow(g,2)),!0)})}):d=a.call(this,b);return d})})(Highcharts);
js/highcharts/highcharts-more.src.js CHANGED
@@ -1,2525 +1,2525 @@
1
- // ==ClosureCompiler==
2
- // @compilation_level SIMPLE_OPTIMIZATIONS
3
-
4
- /**
5
- * @license Highcharts JS v3.0.2 (2013-06-05)
6
- *
7
- * (c) 2009-2013 Torstein Hønsi
8
- *
9
- * License: www.highcharts.com/license
10
- */
11
-
12
- // JSLint options:
13
- /*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
14
-
15
- (function (Highcharts, UNDEFINED) {
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 () {};/**
40
- * The Pane object allows options that are common to a set of X and Y axes.
41
- *
42
- * In the future, this can be extended to basic Highcharts and Highstock.
43
- */
44
- function Pane(options, chart, firstAxis) {
45
- this.init.call(this, options, chart, firstAxis);
46
- }
47
-
48
- // Extend the Pane prototype
49
- extend(Pane.prototype, {
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
-
115
- });
116
- var axisProto = Axis.prototype,
117
- tickProto = Tick.prototype;
118
-
119
- /**
120
- * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
121
- */
122
- var hiddenAxisMixin = {
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
133
- };
134
-
135
- /**
136
- * Augmented methods for the value axis
137
- */
138
- /*jslint unparam: true*/
139
- var radialAxisMixin = {
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
-
466
- };
467
- /*jslint unparam: false*/
468
-
469
- /**
470
- * Override axisProto.init to mix in special axis instance functions and function overrides
471
- */
472
- wrap(axisProto, 'init', function (proceed, chart, userOptions) {
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
-
540
- });
541
-
542
- /**
543
- * Add special cases within the Tick class' methods for radial axes.
544
- */
545
- wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
546
- var axis = this.axis;
547
-
548
- return axis.getPosition ?
549
- axis.getPosition(pos) :
550
- proceed.call(this, horiz, pos, tickmarkOffset, old);
551
- });
552
-
553
- /**
554
- * Wrap the getLabelPosition function to find the center position of the label
555
- * based on the distance option
556
- */
557
- wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
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;
604
- });
605
-
606
- /**
607
- * Wrap the getMarkPath function to return the path of the radial marker
608
- */
609
- wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
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
- });/*
629
- * The AreaRangeSeries class
630
- *
631
- */
632
-
633
- /**
634
- * Extend the default options with map options
635
- */
636
- defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
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
- }
651
- });
652
-
653
- /**
654
- * Add the series type
655
- */
656
- seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
657
- type: 'arearange',
658
- pointArrayMap: ['low', 'high'],
659
- toYData: function (point) {
660
- return [point.low, point.high];
661
- },
662
- pointValKey: 'low',
663
-
664
- /**
665
- * Extend getSegments to force null points if the higher value is null. #1703.
666
- */
667
- getSegments: function () {
668
- var series = this;
669
-
670
- each(series.points, function (point) {
671
- if (!series.options.connectNulls && (point.low === null || point.high === null)) {
672
- point.y = null;
673
- } else if (point.low === null && point.high !== null) {
674
- point.y = point.high;
675
- }
676
- });
677
- Series.prototype.getSegments.call(this);
678
- },
679
-
680
- /**
681
- * Translate data points from raw values x and y to plotX and plotY
682
- */
683
- translate: function () {
684
- var series = this,
685
- yAxis = series.yAxis;
686
-
687
- seriesTypes.area.prototype.translate.apply(series);
688
-
689
- // Set plotLow and plotHigh
690
- each(series.points, function (point) {
691
-
692
- var low = point.low,
693
- high = point.high,
694
- plotY = point.plotY;
695
-
696
- if (high === null && low === null) {
697
- point.y = null;
698
- } else if (low === null) {
699
- point.plotLow = point.plotY = null;
700
- point.plotHigh = yAxis.toPixels(high, true);
701
- } else if (high === null) {
702
- point.plotLow = plotY;
703
- point.plotHigh = null;
704
- } else {
705
- point.plotLow = plotY;
706
- point.plotHigh = yAxis.toPixels(high, true);
707
- }
708
- });
709
- },
710
-
711
- /**
712
- * Extend the line series' getSegmentPath method by applying the segment
713
- * path to both lower and higher values of the range
714
- */
715
- getSegmentPath: function (segment) {
716
-
717
- var lowSegment,
718
- highSegment = [],
719
- i = segment.length,
720
- baseGetSegmentPath = Series.prototype.getSegmentPath,
721
- point,
722
- linePath,
723
- lowerPath,
724
- options = this.options,
725
- step = options.step,
726
- higherPath;
727
-
728
- // Remove nulls from low segment
729
- lowSegment = HighchartsAdapter.grep(segment, function (point) {
730
- return point.plotLow !== null;
731
- });
732
-
733
- // Make a segment with plotX and plotY for the top values
734
- while (i--) {
735
- point = segment[i];
736
- if (point.plotHigh !== null) {
737
- highSegment.push({
738
- plotX: point.plotX,
739
- plotY: point.plotHigh
740
- });
741
- }
742
- }
743
-
744
- // Get the paths
745
- lowerPath = baseGetSegmentPath.call(this, lowSegment);
746
- if (step) {
747
- if (step === true) {
748
- step = 'left';
749
- }
750
- options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
751
- }
752
- higherPath = baseGetSegmentPath.call(this, highSegment);
753
- options.step = step;
754
-
755
- // Create a line on both top and bottom of the range
756
- linePath = [].concat(lowerPath, higherPath);
757
-
758
- // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
759
- higherPath[0] = 'L'; // this probably doesn't work for spline
760
- this.areaPath = this.areaPath.concat(lowerPath, higherPath);
761
-
762
- return linePath;
763
- },
764
-
765
- /**
766
- * Extend the basic drawDataLabels method by running it for both lower and higher
767
- * values.
768
- */
769
- drawDataLabels: function () {
770
-
771
- var data = this.data,
772
- length = data.length,
773
- i,
774
- originalDataLabels = [],
775
- seriesProto = Series.prototype,
776
- dataLabelOptions = this.options.dataLabels,
777
- point,
778
- inverted = this.chart.inverted;
779
-
780
- if (dataLabelOptions.enabled || this._hasPointLabels) {
781
-
782
- // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
783
- i = length;
784
- while (i--) {
785
- point = data[i];
786
-
787
- // Set preliminary values
788
- point.y = point.high;
789
- point.plotY = point.plotHigh;
790
-
791
- // Store original data labels and set preliminary label objects to be picked up
792
- // in the uber method
793
- originalDataLabels[i] = point.dataLabel;
794
- point.dataLabel = point.dataLabelUpper;
795
-
796
- // Set the default offset
797
- point.below = false;
798
- if (inverted) {
799
- dataLabelOptions.align = 'left';
800
- dataLabelOptions.x = dataLabelOptions.xHigh;
801
- } else {
802
- dataLabelOptions.y = dataLabelOptions.yHigh;
803
- }
804
- }
805
- seriesProto.drawDataLabels.apply(this, arguments); // #1209
806
-
807
- // Step 2: reorganize and handle data labels for the lower values
808
- i = length;
809
- while (i--) {
810
- point = data[i];
811
-
812
- // Move the generated labels from step 1, and reassign the original data labels
813
- point.dataLabelUpper = point.dataLabel;
814
- point.dataLabel = originalDataLabels[i];
815
-
816
- // Reset values
817
- point.y = point.low;
818
- point.plotY = point.plotLow;
819
-
820
- // Set the default offset
821
- point.below = true;
822
- if (inverted) {
823
- dataLabelOptions.align = 'right';
824
- dataLabelOptions.x = dataLabelOptions.xLow;
825
- } else {
826
- dataLabelOptions.y = dataLabelOptions.yLow;
827
- }
828
- }
829
- seriesProto.drawDataLabels.apply(this, arguments);
830
- }
831
-
832
- },
833
-
834
- alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
835
-
836
- getSymbol: seriesTypes.column.prototype.getSymbol,
837
-
838
- drawPoints: noop
839
- });/**
840
- * The AreaSplineRangeSeries class
841
- */
842
-
843
- defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
844
-
845
- /**
846
- * AreaSplineRangeSeries object
847
- */
848
- seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
849
- type: 'areasplinerange',
850
- getPointSpline: seriesTypes.spline.prototype.getPointSpline
851
- });/**
852
- * The ColumnRangeSeries class
853
- */
854
- defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
855
- lineWidth: 1,
856
- pointRange: null
857
- });
858
-
859
- /**
860
- * ColumnRangeSeries object
861
- */
862
- seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
863
- type: 'columnrange',
864
- /**
865
- * Translate data points from raw values x and y to plotX and plotY
866
- */
867
- translate: function () {
868
- var series = this,
869
- yAxis = series.yAxis,
870
- plotHigh;
871
-
872
- colProto.translate.apply(series);
873
-
874
- // Set plotLow and plotHigh
875
- each(series.points, function (point) {
876
- var shapeArgs = point.shapeArgs;
877
-
878
- point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
879
- point.plotLow = point.plotY;
880
-
881
- // adjust shape
882
- shapeArgs.y = plotHigh;
883
- shapeArgs.height = point.plotY - plotHigh;
884
-
885
- });
886
- },
887
- trackerGroups: ['group', 'dataLabels'],
888
- drawGraph: noop,
889
- pointAttrToOptions: colProto.pointAttrToOptions,
890
- drawPoints: colProto.drawPoints,
891
- drawTracker: colProto.drawTracker,
892
- animate: colProto.animate,
893
- getColumnMetrics: colProto.getColumnMetrics
894
- });/*
895
- * The GaugeSeries class
896
- */
897
-
898
-
899
-
900
- /**
901
- * Extend the default options
902
- */
903
- defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
904
- dataLabels: {
905
- enabled: true,
906
- y: 15,
907
- borderWidth: 1,
908
- borderColor: 'silver',
909
- borderRadius: 3,
910
- style: {
911
- fontWeight: 'bold'
912
- },
913
- verticalAlign: 'top',
914
- zIndex: 2
915
- },
916
- dial: {
917
- // radius: '80%',
918
- // backgroundColor: 'black',
919
- // borderColor: 'silver',
920
- // borderWidth: 0,
921
- // baseWidth: 3,
922
- // topWidth: 1,
923
- // baseLength: '70%' // of radius
924
- // rearLength: '10%'
925
- },
926
- pivot: {
927
- //radius: 5,
928
- //borderWidth: 0
929
- //borderColor: 'silver',
930
- //backgroundColor: 'black'
931
- },
932
- tooltip: {
933
- headerFormat: ''
934
- },
935
- showInLegend: false
936
- });
937
-
938
- /**
939
- * Extend the point object
940
- */
941
- var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
942
- /**
943
- * Don't do any hover colors or anything
944
- */
945
- setState: function (state) {
946
- this.state = state;
947
- }
948
- });
949
-
950
-
951
- /**
952
- * Add the series type
953
- */
954
- var GaugeSeries = {
955
- type: 'gauge',
956
- pointClass: GaugePoint,
957
-
958
- // chart.angular will be set to true when a gauge series is present, and this will
959
- // be used on the axes
960
- angular: true,
961
- drawGraph: noop,
962
- trackerGroups: ['group', 'dataLabels'],
963
-
964
- /**
965
- * Calculate paths etc
966
- */
967
- translate: function () {
968
-
969
- var series = this,
970
- yAxis = series.yAxis,
971
- options = series.options,
972
- center = yAxis.center;
973
-
974
- series.generatePoints();
975
-
976
- each(series.points, function (point) {
977
-
978
- var dialOptions = merge(options.dial, point.dial),
979
- radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
980
- baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
981
- rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
982
- baseWidth = dialOptions.baseWidth || 3,
983
- topWidth = dialOptions.topWidth || 1,
984
- rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
985
-
986
- // Handle the wrap option
987
- if (options.wrap === false) {
988
- rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
989
- }
990
- rotation = rotation * 180 / Math.PI;
991
-
992
- point.shapeType = 'path';
993
- point.shapeArgs = {
994
- d: dialOptions.path || [
995
- 'M',
996
- -rearLength, -baseWidth / 2,
997
- 'L',
998
- baseLength, -baseWidth / 2,
999
- radius, -topWidth / 2,
1000
- radius, topWidth / 2,
1001
- baseLength, baseWidth / 2,
1002
- -rearLength, baseWidth / 2,
1003
- 'z'
1004
- ],
1005
- translateX: center[0],
1006
- translateY: center[1],
1007
- rotation: rotation
1008
- };
1009
-
1010
- // Positions for data label
1011
- point.plotX = center[0];
1012
- point.plotY = center[1];
1013
- });
1014
- },
1015
-
1016
- /**
1017
- * Draw the points where each point is one needle
1018
- */
1019
- drawPoints: function () {
1020
-
1021
- var series = this,
1022
- center = series.yAxis.center,
1023
- pivot = series.pivot,
1024
- options = series.options,
1025
- pivotOptions = options.pivot,
1026
- renderer = series.chart.renderer;
1027
-
1028
- each(series.points, function (point) {
1029
-
1030
- var graphic = point.graphic,
1031
- shapeArgs = point.shapeArgs,
1032
- d = shapeArgs.d,
1033
- dialOptions = merge(options.dial, point.dial); // #1233
1034
-
1035
- if (graphic) {
1036
- graphic.animate(shapeArgs);
1037
- shapeArgs.d = d; // animate alters it
1038
- } else {
1039
- point.graphic = renderer[point.shapeType](shapeArgs)
1040
- .attr({
1041
- stroke: dialOptions.borderColor || 'none',
1042
- 'stroke-width': dialOptions.borderWidth || 0,
1043
- fill: dialOptions.backgroundColor || 'black',
1044
- rotation: shapeArgs.rotation // required by VML when animation is false
1045
- })
1046
- .add(series.group);
1047
- }
1048
- });
1049
-
1050
- // Add or move the pivot
1051
- if (pivot) {
1052
- pivot.animate({ // #1235
1053
- translateX: center[0],
1054
- translateY: center[1]
1055
- });
1056
- } else {
1057
- series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
1058
- .attr({
1059
- 'stroke-width': pivotOptions.borderWidth || 0,
1060
- stroke: pivotOptions.borderColor || 'silver',
1061
- fill: pivotOptions.backgroundColor || 'black'
1062
- })
1063
- .translate(center[0], center[1])
1064
- .add(series.group);
1065
- }
1066
- },
1067
-
1068
- /**
1069
- * Animate the arrow up from startAngle
1070
- */
1071
- animate: function (init) {
1072
- var series = this;
1073
-
1074
- if (!init) {
1075
- each(series.points, function (point) {
1076
- var graphic = point.graphic;
1077
-
1078
- if (graphic) {
1079
- // start value
1080
- graphic.attr({
1081
- rotation: series.yAxis.startAngleRad * 180 / Math.PI
1082
- });
1083
-
1084
- // animate
1085
- graphic.animate({
1086
- rotation: point.shapeArgs.rotation
1087
- }, series.options.animation);
1088
- }
1089
- });
1090
-
1091
- // delete this function to allow it only once
1092
- series.animate = null;
1093
- }
1094
- },
1095
-
1096
- render: function () {
1097
- this.group = this.plotGroup(
1098
- 'group',
1099
- 'series',
1100
- this.visible ? 'visible' : 'hidden',
1101
- this.options.zIndex,
1102
- this.chart.seriesGroup
1103
- );
1104
- seriesTypes.pie.prototype.render.call(this);
1105
- this.group.clip(this.chart.clipRect);
1106
- },
1107
-
1108
- setData: seriesTypes.pie.prototype.setData,
1109
- drawTracker: seriesTypes.column.prototype.drawTracker
1110
- };
1111
- seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* ****************************************************************************
1112
- * Start Box plot series code *
1113
- *****************************************************************************/
1114
-
1115
- // Set default options
1116
- defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, {
1117
- fillColor: '#FFFFFF',
1118
- lineWidth: 1,
1119
- //medianColor: null,
1120
- medianWidth: 2,
1121
- states: {
1122
- hover: {
1123
- brightness: -0.3
1124
- }
1125
- },
1126
- //stemColor: null,
1127
- //stemDashStyle: 'solid'
1128
- //stemWidth: null,
1129
- threshold: null,
1130
- tooltip: {
1131
- pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' +
1132
- 'Minimum: {point.low}<br/>' +
1133
- 'Lower quartile: {point.q1}<br/>' +
1134
- 'Median: {point.median}<br/>' +
1135
- 'Higher quartile: {point.q3}<br/>' +
1136
- 'Maximum: {point.high}<br/>'
1137
- },
1138
- //whiskerColor: null,
1139
- whiskerLength: '50%',
1140
- whiskerWidth: 2
1141
- });
1142
-
1143
- // Create the series object
1144
- seriesTypes.boxplot = extendClass(seriesTypes.column, {
1145
- type: 'boxplot',
1146
- pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
1147
- toYData: function (point) { // return a plain array for speedy calculation
1148
- return [point.low, point.q1, point.median, point.q3, point.high];
1149
- },
1150
- pointValKey: 'high', // defines the top of the tracker
1151
-
1152
- /**
1153
- * One-to-one mapping from options to SVG attributes
1154
- */
1155
- pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
1156
- fill: 'fillColor',
1157
- stroke: 'color',
1158
- 'stroke-width': 'lineWidth'
1159
- },
1160
-
1161
- /**
1162
- * Disable data labels for box plot
1163
- */
1164
- drawDataLabels: noop,
1165
-
1166
- /**
1167
- * Translate data points from raw values x and y to plotX and plotY
1168
- */
1169
- translate: function () {
1170
- var series = this,
1171
- yAxis = series.yAxis,
1172
- pointArrayMap = series.pointArrayMap;
1173
-
1174
- seriesTypes.column.prototype.translate.apply(series);
1175
-
1176
- // do the translation on each point dimension
1177
- each(series.points, function (point) {
1178
- each(pointArrayMap, function (key) {
1179
- if (point[key] !== null) {
1180
- point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
1181
- }
1182
- });
1183
- });
1184
- },
1185
-
1186
- /**
1187
- * Draw the data points
1188
- */
1189
- drawPoints: function () {
1190
- var series = this, //state = series.state,
1191
- points = series.points,
1192
- options = series.options,
1193
- chart = series.chart,
1194
- renderer = chart.renderer,
1195
- pointAttr,
1196
- q1Plot,
1197
- q3Plot,
1198
- highPlot,
1199
- lowPlot,
1200
- medianPlot,
1201
- crispCorr,
1202
- crispX,
1203
- graphic,
1204
- stemPath,
1205
- stemAttr,
1206
- boxPath,
1207
- whiskersPath,
1208
- whiskersAttr,
1209
- medianPath,
1210
- medianAttr,
1211
- width,
1212
- left,
1213
- right,
1214
- halfWidth,
1215
- shapeArgs,
1216
- color,
1217
- doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
1218
- whiskerLength = parseInt(series.options.whiskerLength, 10) / 100;
1219
-
1220
-
1221
- each(points, function (point) {
1222
-
1223
- graphic = point.graphic;
1224
- shapeArgs = point.shapeArgs; // the box
1225
- stemAttr = {};
1226
- whiskersAttr = {};
1227
- medianAttr = {};
1228
- color = point.color || series.color;
1229
-
1230
- if (point.plotY !== UNDEFINED) {
1231
-
1232
- pointAttr = point.pointAttr[point.selected ? 'selected' : ''];
1233
-
1234
- // crisp vector coordinates
1235
- width = shapeArgs.width;
1236
- left = mathFloor(shapeArgs.x);
1237
- right = left + width;
1238
- halfWidth = mathRound(width / 2);
1239
- //crispX = mathRound(left + halfWidth) + crispCorr;
1240
- q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr;
1241
- q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr;
1242
- highPlot = mathFloor(point.highPlot);// + crispCorr;
1243
- lowPlot = mathFloor(point.lowPlot);// + crispCorr;
1244
-
1245
- // Stem attributes
1246
- stemAttr.stroke = point.stemColor || options.stemColor || color;
1247
- stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
1248
- stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
1249
-
1250
- // Whiskers attributes
1251
- whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
1252
- whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
1253
-
1254
- // Median attributes
1255
- medianAttr.stroke = point.medianColor || options.medianColor || color;
1256
- medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
1257
-
1258
-
1259
- // The stem
1260
- crispCorr = (stemAttr['stroke-width'] % 2) / 2;
1261
- crispX = left + halfWidth + crispCorr;
1262
- stemPath = [
1263
- // stem up
1264
- 'M',
1265
- crispX, q3Plot,
1266
- 'L',
1267
- crispX, highPlot,
1268
-
1269
- // stem down
1270
- 'M',
1271
- crispX, q1Plot,
1272
- 'L',
1273
- crispX, lowPlot,
1274
- 'z'
1275
- ];
1276
-
1277
- // The box
1278
- if (doQuartiles) {
1279
- crispCorr = (pointAttr['stroke-width'] % 2) / 2;
1280
- crispX = mathFloor(crispX) + crispCorr;
1281
- q1Plot = mathFloor(q1Plot) + crispCorr;
1282
- q3Plot = mathFloor(q3Plot) + crispCorr;
1283
- left += crispCorr;
1284
- right += crispCorr;
1285
- boxPath = [
1286
- 'M',
1287
- left, q3Plot,
1288
- 'L',
1289
- left, q1Plot,
1290
- 'L',
1291
- right, q1Plot,
1292
- 'L',
1293
- right, q3Plot,
1294
- 'L',
1295
- left, q3Plot,
1296
- 'z'
1297
- ];
1298
- }
1299
-
1300
- // The whiskers
1301
- if (whiskerLength) {
1302
- crispCorr = (whiskersAttr['stroke-width'] % 2) / 2;
1303
- highPlot = highPlot + crispCorr;
1304
- lowPlot = lowPlot + crispCorr;
1305
- whiskersPath = [
1306
- // High whisker
1307
- 'M',
1308
- crispX - halfWidth * whiskerLength,
1309
- highPlot,
1310
- 'L',
1311
- crispX + halfWidth * whiskerLength,
1312
- highPlot,
1313
-
1314
- // Low whisker
1315
- 'M',
1316
- crispX - halfWidth * whiskerLength,
1317
- lowPlot,
1318
- 'L',
1319
- crispX + halfWidth * whiskerLength,
1320
- lowPlot
1321
- ];
1322
- }
1323
-
1324
- // The median
1325
- crispCorr = (medianAttr['stroke-width'] % 2) / 2;
1326
- medianPlot = mathRound(point.medianPlot) + crispCorr;
1327
- medianPath = [
1328
- 'M',
1329
- left,
1330
- medianPlot,
1331
- 'L',
1332
- right,
1333
- medianPlot,
1334
- 'z'
1335
- ];
1336
-
1337
- // Create or update the graphics
1338
- if (graphic) { // update
1339
-
1340
- point.stem.animate({ d: stemPath });
1341
- if (whiskerLength) {
1342
- point.whiskers.animate({ d: whiskersPath });
1343
- }
1344
- if (doQuartiles) {
1345
- point.box.animate({ d: boxPath });
1346
- }
1347
- point.medianShape.animate({ d: medianPath });
1348
-
1349
- } else { // create new
1350
- point.graphic = graphic = renderer.g()
1351
- .add(series.group);
1352
-
1353
- point.stem = renderer.path(stemPath)
1354
- .attr(stemAttr)
1355
- .add(graphic);
1356
-
1357
- if (whiskerLength) {
1358
- point.whiskers = renderer.path(whiskersPath)
1359
- .attr(whiskersAttr)
1360
- .add(graphic);
1361
- }
1362
- if (doQuartiles) {
1363
- point.box = renderer.path(boxPath)
1364
- .attr(pointAttr)
1365
- .add(graphic);
1366
- }
1367
- point.medianShape = renderer.path(medianPath)
1368
- .attr(medianAttr)
1369
- .add(graphic);
1370
- }
1371
- }
1372
- });
1373
-
1374
- }
1375
-
1376
-
1377
- });
1378
-
1379
- /* ****************************************************************************
1380
- * End Box plot series code *
1381
- *****************************************************************************/
1382
- /* ****************************************************************************
1383
- * Start error bar series code *
1384
- *****************************************************************************/
1385
-
1386
- // 1 - set default options
1387
- defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, {
1388
- color: '#000000',
1389
- grouping: false,
1390
- linkedTo: ':previous',
1391
- tooltip: {
1392
- pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat
1393
- },
1394
- whiskerWidth: null
1395
- });
1396
-
1397
- // 2 - Create the series object
1398
- seriesTypes.errorbar = extendClass(seriesTypes.boxplot, {
1399
- type: 'errorbar',
1400
- pointArrayMap: ['low', 'high'], // array point configs are mapped to this
1401
- toYData: function (point) { // return a plain array for speedy calculation
1402
- return [point.low, point.high];
1403
- },
1404
- pointValKey: 'high', // defines the top of the tracker
1405
- doQuartiles: false,
1406
-
1407
- /**
1408
- * Get the width and X offset, either on top of the linked series column
1409
- * or standalone
1410
- */
1411
- getColumnMetrics: function () {
1412
- return (this.linkedParent && this.linkedParent.columnMetrics) ||
1413
- seriesTypes.column.prototype.getColumnMetrics.call(this);
1414
- }
1415
- });
1416
-
1417
- /* ****************************************************************************
1418
- * End error bar series code *
1419
- *****************************************************************************/
1420
- /* ****************************************************************************
1421
- * Start Waterfall series code *
1422
- *****************************************************************************/
1423
-
1424
- wrap(axisProto, 'getSeriesExtremes', function (proceed, renew) {
1425
- // Run uber method
1426
- proceed.call(this, renew);
1427
-
1428
- if (this.isXAxis) {
1429
- return;
1430
- }
1431
-
1432
- var axis = this,
1433
- visitedStacks = [],
1434
- resetMinMax = true;
1435
-
1436
-
1437
- // recalculate extremes for each waterfall stack
1438
- each(axis.series, function (series) {
1439
- // process only visible, waterfall series, one from each stack
1440
- if (!series.visible || !series.stackKey || series.type !== 'waterfall' || HighchartsAdapter.inArray(series.stackKey) !== -1) {
1441
- return;
1442
- }
1443
-
1444
- // reset previously found dataMin and dataMax, do it only once
1445
- if (resetMinMax) {
1446
- axis.dataMin = axis.dataMax = null;
1447
- resetMinMax = false;
1448
- }
1449
-
1450
-
1451
- var yData = series.processedYData,
1452
- yDataLength = yData.length,
1453
- seriesDataMin = yData[0],
1454
- seriesDataMax = yData[0],
1455
- threshold = series.options.threshold,
1456
- stacks = axis.stacks,
1457
- stackKey = series.stackKey,
1458
- negKey = '-' + stackKey,
1459
- total,
1460
- previous,
1461
- key,
1462
- i;
1463
-
1464
-
1465
- // set new stack totals including preceding values, finds new min and max values
1466
- for (i = 0; i < yDataLength; i++) {
1467
- key = yData[i] < threshold ? negKey : stackKey;
1468
- total = stacks[key][i].total;
1469
-
1470
- if (i > threshold) {
1471
- total += previous;
1472
- stacks[key][i].setTotal(total);
1473
-
1474
- // _cum is used to avoid conflict with Series.translate method
1475
- stacks[key][i]._cum = null;
1476
- }
1477
-
1478
-
1479
- // find min / max values
1480
- if (total < seriesDataMin) {
1481
- seriesDataMin = total;
1482
- }
1483
-
1484
- if (total > seriesDataMax) {
1485
- seriesDataMax = total;
1486
- }
1487
-
1488
- previous = total;
1489
- }
1490
-
1491
-
1492
- // set new extremes
1493
- series.dataMin = seriesDataMin;
1494
- series.dataMax = seriesDataMax;
1495
- axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin, threshold);
1496
- axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax, threshold);
1497
-
1498
- // remember series' stack key
1499
- visitedStacks.push(series.stackKey);
1500
-
1501
-
1502
-
1503
- // Adjust to threshold. This code is duplicated from the parent getSeriesExtremes method.
1504
- if (typeof threshold === 'number') {
1505
- if (axis.dataMin >= threshold) {
1506
- axis.dataMin = threshold;
1507
- axis.ignoreMinPadding = true;
1508
- } else if (axis.dataMax < threshold) {
1509
- axis.dataMax = threshold;
1510
- axis.ignoreMaxPadding = true;
1511
- }
1512
- }
1513
- });
1514
- });
1515
-
1516
-
1517
- // 1 - set default options
1518
- defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
1519
- lineWidth: 1,
1520
- lineColor: '#333',
1521
- dashStyle: 'dot',
1522
- borderColor: '#333'
1523
- });
1524
-
1525
-
1526
- // 2 - Create the series object
1527
- seriesTypes.waterfall = extendClass(seriesTypes.column, {
1528
- type: 'waterfall',
1529
-
1530
- upColorProp: 'fill',
1531
-
1532
- pointArrayMap: ['y', 'low'],
1533
-
1534
- pointValKey: 'y',
1535
-
1536
- /**
1537
- * Init waterfall series, force stacking
1538
- */
1539
- init: function (chart, options) {
1540
- options.stacking = true;
1541
- seriesTypes.column.prototype.init.call(this, chart, options);
1542
- },
1543
-
1544
-
1545
- /**
1546
- * Translate data points from raw values
1547
- */
1548
- translate: function () {
1549
- var series = this,
1550
- options = series.options,
1551
- axis = series.yAxis,
1552
- len,
1553
- i,
1554
-
1555
- points,
1556
- point,
1557
- shapeArgs,
1558
- sum,
1559
- sumStart,
1560
- subSum,
1561
- subSumStart,
1562
- edges,
1563
- cumulative,
1564
- prevStack,
1565
- prevY,
1566
- stack,
1567
- crispCorr = (options.borderWidth % 2) / 2;
1568
-
1569
- // run column series translate
1570
- seriesTypes.column.prototype.translate.apply(this);
1571
-
1572
-
1573
- points = this.points;
1574
- subSumStart = sumStart = points[0];
1575
- sum = subSum = points[0].y;
1576
-
1577
- for (i = 1, len = points.length; i < len; i++) {
1578
- // cache current point object
1579
- point = points[i];
1580
- shapeArgs = point.shapeArgs;
1581
-
1582
- // get current and previous stack
1583
- stack = series.getStack(i);
1584
- prevStack = series.getStack(i - 1);
1585
- prevY = series.getStackY(prevStack);
1586
-
1587
- // set new intermediate sum values after reset
1588
- if (subSumStart === null) {
1589
- subSumStart = point;
1590
- subSum = 0;
1591
- }
1592
-
1593
- // sum only points with value, not intermediate or total sum
1594
- if (point.y && !point.isSum && !point.isIntermediateSum) {
1595
- sum += point.y;
1596
- subSum += point.y;
1597
- }
1598
-
1599
- // calculate sum points
1600
- if (point.isSum || point.isIntermediateSum) {
1601
-
1602
- if (point.isIntermediateSum) {
1603
- edges = series.getSumEdges(subSumStart, points[i - 1]);
1604
- point.y = subSum;
1605
- subSumStart = null;
1606
- } else {
1607
- edges = series.getSumEdges(sumStart, points[i - 1]);
1608
- point.y = sum;
1609
- }
1610
-
1611
- shapeArgs.y = point.plotY = edges[1];
1612
- shapeArgs.height = edges[0] - edges[1];
1613
-
1614
- // calculate other (up or down) points based on y value
1615
- } else {
1616
- // use "_cum" instead of already calculated "cum" to avoid reverse ordering negative columns
1617
- cumulative = stack._cum === null ? prevStack.total : stack._cum;
1618
- stack._cum = cumulative + point.y;
1619
-
1620
- if (point.y < 0) {
1621
- shapeArgs.y = mathCeil(axis.translate(cumulative, 0, 1)) - crispCorr;
1622
- shapeArgs.height = mathCeil(axis.translate(stack._cum, 0, 1) - shapeArgs.y);
1623
- } else {
1624
- if (prevStack.total + point.y < 0) {
1625
- shapeArgs.y = axis.translate(stack._cum, 0, 1);
1626
- }
1627
-
1628
- shapeArgs.height = mathFloor(prevY - shapeArgs.y);
1629
- }
1630
-
1631
- }
1632
- }
1633
- },
1634
-
1635
- /**
1636
- * Call default processData then override yData to reflect waterfall's extremes on yAxis
1637
- */
1638
- processData: function (force) {
1639
- Series.prototype.processData.call(this, force);
1640
-
1641
- var series = this,
1642
- options = series.options,
1643
- yData = series.yData,
1644
- length = yData.length,
1645
- prev,
1646
- curr,
1647
- subSum,
1648
- sum,
1649
- i;
1650
-
1651
- prev = sum = subSum = options.threshold;
1652
-
1653
- for (i = 0; i < length; i++) {
1654
- curr = yData[i];
1655
-
1656
- // processed yData only if it's not already processed
1657
- if (curr !== null && typeof curr !== 'number') {
1658
-
1659
- if (curr === "sum") {
1660
- yData[i] = null;
1661
-
1662
- } else if (curr === "intermediateSum") {
1663
- yData[i] = null;
1664
- subSum = prev;
1665
-
1666
- } else {
1667
- yData[i] = curr[0];// + prev;
1668
- }
1669
-
1670
- prev = yData[i];
1671
- }
1672
- }
1673
- },
1674
-
1675
- /**
1676
- * Return [y, low] array, if low is not defined, it's replaced with null for further calculations
1677
- */
1678
- toYData: function (pt) {
1679
- if (pt.isSum) {
1680
- return "sum";
1681
- } else if (pt.isIntermediateSum) {
1682
- return "intermediateSum";
1683
- }
1684
-
1685
- return [pt.y];
1686
- },
1687
-
1688
- /**
1689
- * Postprocess mapping between options and SVG attributes
1690
- */
1691
- getAttribs: function () {
1692
- seriesTypes.column.prototype.getAttribs.apply(this, arguments);
1693
-
1694
- var series = this,
1695
- options = series.options,
1696
- stateOptions = options.states,
1697
- upColor = options.upColor || series.color,
1698
- hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
1699
- seriesDownPointAttr = merge(series.pointAttr),
1700
- upColorProp = series.upColorProp;
1701
-
1702
- seriesDownPointAttr[''][upColorProp] = upColor;
1703
- seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor;
1704
- seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor;
1705
-
1706
- each(series.points, function (point) {
1707
- if (point.y > 0 && !point.color) {
1708
- point.pointAttr = seriesDownPointAttr;
1709
- point.color = upColor;
1710
- }
1711
- });
1712
- },
1713
-
1714
- /**
1715
- * Draw columns' connector lines
1716
- */
1717
- getGraphPath: function () {
1718
-
1719
- var data = this.data,
1720
- length = data.length,
1721
- lineWidth = this.options.lineWidth + this.options.borderWidth,
1722
- normalizer = mathRound(lineWidth) % 2 / 2,
1723
- path = [],
1724
- M = 'M',
1725
- L = 'L',
1726
- prevArgs,
1727
- pointArgs,
1728
- i,
1729
- d;
1730
-
1731
- for (i = 1; i < length; i++) {
1732
- pointArgs = data[i].shapeArgs;
1733
- prevArgs = data[i - 1].shapeArgs;
1734
-
1735
- d = [
1736
- M,
1737
- prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
1738
- L,
1739
- pointArgs.x, prevArgs.y + normalizer
1740
- ];
1741
-
1742
- if (data[i - 1].y < 0) {
1743
- d[2] += prevArgs.height;
1744
- d[5] += prevArgs.height;
1745
- }
1746
-
1747
- path = path.concat(d);
1748
- }
1749
-
1750
- return path;
1751
- },
1752
-
1753
- getStack: function (i) {
1754
- var axis = this.yAxis,
1755
- stacks = axis.stacks,
1756
- key = this.stackKey;
1757
-
1758
- if (this.processedYData[i] < this.options.threshold) {
1759
- key = '-' + key;
1760
- }
1761
-
1762
- return stacks[key][i];
1763
- },
1764
-
1765
- getStackY: function (stack) {
1766
- return mathCeil(this.yAxis.translate(stack.total, null, true));
1767
- },
1768
-
1769
- /**
1770
- * Return array of top and bottom position for sum column based on given edge points
1771
- */
1772
- getSumEdges: function (pointA, pointB) {
1773
- var valueA,
1774
- valueB,
1775
- tmp,
1776
- threshold = this.options.threshold;
1777
-
1778
- valueA = pointA.y >= threshold ? pointA.shapeArgs.y + pointA.shapeArgs.height : pointA.shapeArgs.y;
1779
- valueB = pointB.y >= threshold ? pointB.shapeArgs.y : pointB.shapeArgs.y + pointB.shapeArgs.height;
1780
-
1781
- if (valueB > valueA) {
1782
- tmp = valueA;
1783
- valueA = valueB;
1784
- valueB = tmp;
1785
- }
1786
-
1787
- return [valueA, valueB];
1788
- },
1789
-
1790
- drawGraph: Series.prototype.drawGraph
1791
- });
1792
-
1793
- /* ****************************************************************************
1794
- * End Waterfall series code *
1795
- *****************************************************************************/
1796
- /* ****************************************************************************
1797
- * Start Bubble series code *
1798
- *****************************************************************************/
1799
-
1800
- // 1 - set default options
1801
- defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
1802
- dataLabels: {
1803
- inside: true,
1804
- style: {
1805
- color: 'white',
1806
- textShadow: '0px 0px 3px black'
1807
- },
1808
- verticalAlign: 'middle'
1809
- },
1810
- // displayNegative: true,
1811
- marker: {
1812
- // fillOpacity: 0.5,
1813
- lineColor: null, // inherit from series.color
1814
- lineWidth: 1
1815
- },
1816
- minSize: 8,
1817
- maxSize: '20%',
1818
- // negativeColor: null,
1819
- tooltip: {
1820
- pointFormat: '({point.x}, {point.y}), Size: {point.z}'
1821
- },
1822
- zThreshold: 0
1823
- });
1824
-
1825
- // 2 - Create the series object
1826
- seriesTypes.bubble = extendClass(seriesTypes.scatter, {
1827
- type: 'bubble',
1828
- pointArrayMap: ['y', 'z'],
1829
- trackerGroups: ['group', 'dataLabelsGroup'],
1830
-
1831
- /**
1832
- * Mapping between SVG attributes and the corresponding options
1833
- */
1834
- pointAttrToOptions: {
1835
- stroke: 'lineColor',
1836
- 'stroke-width': 'lineWidth',
1837
- fill: 'fillColor'
1838
- },
1839
-
1840
- /**
1841
- * Apply the fillOpacity to all fill positions
1842
- */
1843
- applyOpacity: function (fill) {
1844
- var markerOptions = this.options.marker,
1845
- fillOpacity = pick(markerOptions.fillOpacity, 0.5);
1846
-
1847
- // When called from Legend.colorizeItem, the fill isn't predefined
1848
- fill = fill || markerOptions.fillColor || this.color;
1849
-
1850
- if (fillOpacity !== 1) {
1851
- fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba');
1852
- }
1853
- return fill;
1854
- },
1855
-
1856
- /**
1857
- * Extend the convertAttribs method by applying opacity to the fill
1858
- */
1859
- convertAttribs: function () {
1860
- var obj = Series.prototype.convertAttribs.apply(this, arguments);
1861
-
1862
- obj.fill = this.applyOpacity(obj.fill);
1863
-
1864
- return obj;
1865
- },
1866
-
1867
- /**
1868
- * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
1869
- * must be done prior to Series.translate because the axis needs to add padding in
1870
- * accordance with the point sizes.
1871
- */
1872
- getRadii: function (zMin, zMax, minSize, maxSize) {
1873
- var len,
1874
- i,
1875
- pos,
1876
- zData = this.zData,
1877
- radii = [],
1878
- zRange;
1879
-
1880
- // Set the shape type and arguments to be picked up in drawPoints
1881
- for (i = 0, len = zData.length; i < len; i++) {
1882
- zRange = zMax - zMin;
1883
- pos = zRange > 0 ? // relative size, a number between 0 and 1
1884
- (zData[i] - zMin) / (zMax - zMin) :
1885
- 0.5;
1886
- radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
1887
- }
1888
- this.radii = radii;
1889
- },
1890
-
1891
- /**
1892
- * Perform animation on the bubbles
1893
- */
1894
- animate: function (init) {
1895
- var animation = this.options.animation;
1896
-
1897
- if (!init) { // run the animation
1898
- each(this.points, function (point) {
1899
- var graphic = point.graphic,
1900
- shapeArgs = point.shapeArgs;
1901
-
1902
- if (graphic && shapeArgs) {
1903
- // start values
1904
- graphic.attr('r', 1);
1905
-
1906
- // animate
1907
- graphic.animate({
1908
- r: shapeArgs.r
1909
- }, animation);
1910
- }
1911
- });
1912
-
1913
- // delete this function to allow it only once
1914
- this.animate = null;
1915
- }
1916
- },
1917
-
1918
- /**
1919
- * Extend the base translate method to handle bubble size
1920
- */
1921
- translate: function () {
1922
-
1923
- var i,
1924
- data = this.data,
1925
- point,
1926
- radius,
1927
- radii = this.radii;
1928
-
1929
- // Run the parent method
1930
- seriesTypes.scatter.prototype.translate.call(this);
1931
-
1932
- // Set the shape type and arguments to be picked up in drawPoints
1933
- i = data.length;
1934
-
1935
- while (i--) {
1936
- point = data[i];
1937
- radius = radii ? radii[i] : 0; // #1737
1938
-
1939
- // Flag for negativeColor to be applied in Series.js
1940
- point.negative = point.z < (this.options.zThreshold || 0);
1941
-
1942
- if (radius >= this.minPxSize / 2) {
1943
- // Shape arguments
1944
- point.shapeType = 'circle';
1945
- point.shapeArgs = {
1946
- x: point.plotX,
1947
- y: point.plotY,
1948
- r: radius
1949
- };
1950
-
1951
- // Alignment box for the data label
1952
- point.dlBox = {
1953
- x: point.plotX - radius,
1954
- y: point.plotY - radius,
1955
- width: 2 * radius,
1956
- height: 2 * radius
1957
- };
1958
- } else { // below zThreshold
1959
- point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691
1960
- }
1961
- }
1962
- },
1963
-
1964
- /**
1965
- * Get the series' symbol in the legend
1966
- *
1967
- * @param {Object} legend The legend object
1968
- * @param {Object} item The series (this) or point
1969
- */
1970
- drawLegendSymbol: function (legend, item) {
1971
- var radius = pInt(legend.itemStyle.fontSize) / 2;
1972
-
1973
- item.legendSymbol = this.chart.renderer.circle(
1974
- radius,
1975
- legend.baseline - radius,
1976
- radius
1977
- ).attr({
1978
- zIndex: 3
1979
- }).add(item.legendGroup);
1980
-
1981
- },
1982
-
1983
- drawPoints: seriesTypes.column.prototype.drawPoints,
1984
- alignDataLabel: seriesTypes.column.prototype.alignDataLabel
1985
- });
1986
-
1987
- /**
1988
- * Add logic to pad each axis with the amount of pixels
1989
- * necessary to avoid the bubbles to overflow.
1990
- */
1991
- Axis.prototype.beforePadding = function () {
1992
- var axis = this,
1993
- axisLength = this.len,
1994
- chart = this.chart,
1995
- pxMin = 0,
1996
- pxMax = axisLength,
1997
- isXAxis = this.isXAxis,
1998
- dataKey = isXAxis ? 'xData' : 'yData',
1999
- min = this.min,
2000
- extremes = {},
2001
- smallestSize = math.min(chart.plotWidth, chart.plotHeight),
2002
- zMin = Number.MAX_VALUE,
2003
- zMax = -Number.MAX_VALUE,
2004
- range = this.max - min,
2005
- transA = axisLength / range,
2006
- activeSeries = [];
2007
-
2008
- // Handle padding on the second pass, or on redraw
2009
- if (this.tickPositions) {
2010
- each(this.series, function (series) {
2011
-
2012
- var seriesOptions = series.options,
2013
- zData;
2014
-
2015
- if (series.type === 'bubble' && series.visible) {
2016
-
2017
- // Correction for #1673
2018
- axis.allowZoomOutside = true;
2019
-
2020
- // Cache it
2021
- activeSeries.push(series);
2022
-
2023
- if (isXAxis) { // because X axis is evaluated first
2024
-
2025
- // For each series, translate the size extremes to pixel values
2026
- each(['minSize', 'maxSize'], function (prop) {
2027
- var length = seriesOptions[prop],
2028
- isPercent = /%$/.test(length);
2029
-
2030
- length = pInt(length);
2031
- extremes[prop] = isPercent ?
2032
- smallestSize * length / 100 :
2033
- length;
2034
-
2035
- });
2036
- series.minPxSize = extremes.minSize;
2037
-
2038
- // Find the min and max Z
2039
- zData = series.zData;
2040
- if (zData.length) { // #1735
2041
- zMin = math.min(
2042
- zMin,
2043
- math.max(
2044
- arrayMin(zData),
2045
- seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
2046
- )
2047
- );
2048
- zMax = math.max(zMax, arrayMax(zData));
2049
- }
2050
- }
2051
- }
2052
- });
2053
-
2054
- each(activeSeries, function (series) {
2055
-
2056
- var data = series[dataKey],
2057
- i = data.length,
2058
- radius;
2059
-
2060
- if (isXAxis) {
2061
- series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
2062
- }
2063
-
2064
- if (range > 0) {
2065
- while (i--) {
2066
- radius = series.radii[i];
2067
- pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
2068
- pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
2069
- }
2070
- }
2071
- });
2072
-
2073
- if (range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
2074
- pxMax -= axisLength;
2075
- transA *= (axisLength + pxMin - pxMax) / axisLength;
2076
- this.min += pxMin / transA;
2077
- this.max += pxMax / transA;
2078
- }
2079
- }
2080
- };
2081
-
2082
- /* ****************************************************************************
2083
- * End Bubble series code *
2084
- *****************************************************************************/
2085
- /**
2086
- * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
2087
- * gathered in RadialAxes.js.
2088
- *
2089
- */
2090
-
2091
- var seriesProto = Series.prototype,
2092
- pointerProto = Highcharts.Pointer.prototype;
2093
-
2094
-
2095
-
2096
- /**
2097
- * Translate a point's plotX and plotY from the internal angle and radius measures to
2098
- * true plotX, plotY coordinates
2099
- */
2100
- seriesProto.toXY = function (point) {
2101
- var xy,
2102
- chart = this.chart,
2103
- plotX = point.plotX,
2104
- plotY = point.plotY;
2105
-
2106
- // Save rectangular plotX, plotY for later computation
2107
- point.rectPlotX = plotX;
2108
- point.rectPlotY = plotY;
2109
-
2110
- // Record the angle in degrees for use in tooltip
2111
- point.clientX = plotX / Math.PI * 180;
2112
-
2113
- // Find the polar plotX and plotY
2114
- xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
2115
- point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
2116
- point.plotY = point.polarPlotY = xy.y - chart.plotTop;
2117
- };
2118
-
2119
-
2120
- /**
2121
- * Add some special init logic to areas and areasplines
2122
- */
2123
- function initArea(proceed, chart, options) {
2124
- proceed.call(this, chart, options);
2125
- if (this.chart.polar) {
2126
-
2127
- /**
2128
- * Overridden method to close a segment path. While in a cartesian plane the area
2129
- * goes down to the threshold, in the polar chart it goes to the center.
2130
- */
2131
- this.closeSegment = function (path) {
2132
- var center = this.xAxis.center;
2133
- path.push(
2134
- 'L',
2135
- center[0],
2136
- center[1]
2137
- );
2138
- };
2139
-
2140
- // Instead of complicated logic to draw an area around the inner area in a stack,
2141
- // just draw it behind
2142
- this.closedStacks = true;
2143
- }
2144
- }
2145
- wrap(seriesTypes.area.prototype, 'init', initArea);
2146
- wrap(seriesTypes.areaspline.prototype, 'init', initArea);
2147
-
2148
-
2149
- /**
2150
- * Overridden method for calculating a spline from one point to the next
2151
- */
2152
- wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
2153
-
2154
- var ret,
2155
- smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
2156
- denom = smoothing + 1,
2157
- plotX,
2158
- plotY,
2159
- lastPoint,
2160
- nextPoint,
2161
- lastX,
2162
- lastY,
2163
- nextX,
2164
- nextY,
2165
- leftContX,
2166
- leftContY,
2167
- rightContX,
2168
- rightContY,
2169
- distanceLeftControlPoint,
2170
- distanceRightControlPoint,
2171
- leftContAngle,
2172
- rightContAngle,
2173
- jointAngle;
2174
-
2175
-
2176
- if (this.chart.polar) {
2177
-
2178
- plotX = point.plotX;
2179
- plotY = point.plotY;
2180
- lastPoint = segment[i - 1];
2181
- nextPoint = segment[i + 1];
2182
-
2183
- // Connect ends
2184
- if (this.connectEnds) {
2185
- if (!lastPoint) {
2186
- lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
2187
- }
2188
- if (!nextPoint) {
2189
- nextPoint = segment[1];
2190
- }
2191
- }
2192
-
2193
- // find control points
2194
- if (lastPoint && nextPoint) {
2195
-
2196
- lastX = lastPoint.plotX;
2197
- lastY = lastPoint.plotY;
2198
- nextX = nextPoint.plotX;
2199
- nextY = nextPoint.plotY;
2200
- leftContX = (smoothing * plotX + lastX) / denom;
2201
- leftContY = (smoothing * plotY + lastY) / denom;
2202
- rightContX = (smoothing * plotX + nextX) / denom;
2203
- rightContY = (smoothing * plotY + nextY) / denom;
2204
- distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
2205
- distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
2206
- leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
2207
- rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
2208
- jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
2209
-
2210
-
2211
- // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
2212
- if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
2213
- jointAngle -= Math.PI;
2214
- }
2215
-
2216
- // Find the corrected control points for a spline straight through the point
2217
- leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
2218
- leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
2219
- rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
2220
- rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
2221
-
2222
- // Record for drawing in next point
2223
- point.rightContX = rightContX;
2224
- point.rightContY = rightContY;
2225
-
2226
- }
2227
-
2228
-
2229
- // moveTo or lineTo
2230
- if (!i) {
2231
- ret = ['M', plotX, plotY];
2232
- } else { // curve from last point to this
2233
- ret = [
2234
- 'C',
2235
- lastPoint.rightContX || lastPoint.plotX,
2236
- lastPoint.rightContY || lastPoint.plotY,
2237
- leftContX || plotX,
2238
- leftContY || plotY,
2239
- plotX,
2240
- plotY
2241
- ];
2242
- lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
2243
- }
2244
-
2245
-
2246
- } else {
2247
- ret = proceed.call(this, segment, point, i);
2248
- }
2249
- return ret;
2250
- });
2251
-
2252
- /**
2253
- * Extend translate. The plotX and plotY values are computed as if the polar chart were a
2254
- * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
2255
- * center.
2256
- */
2257
- wrap(seriesProto, 'translate', function (proceed) {
2258
-
2259
- // Run uber method
2260
- proceed.call(this);
2261
-
2262
- // Postprocess plot coordinates
2263
- if (this.chart.polar && !this.preventPostTranslate) {
2264
- var points = this.points,
2265
- i = points.length;
2266
- while (i--) {
2267
- // Translate plotX, plotY from angle and radius to true plot coordinates
2268
- this.toXY(points[i]);
2269
- }
2270
- }
2271
- });
2272
-
2273
- /**
2274
- * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
2275
- * line-like series.
2276
- */
2277
- wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
2278
-
2279
- var points = this.points;
2280
-
2281
- // Connect the path
2282
- if (this.chart.polar && this.options.connectEnds !== false &&
2283
- segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
2284
- this.connectEnds = true; // re-used in splines
2285
- segment = [].concat(segment, [points[0]]);
2286
- }
2287
-
2288
- // Run uber method
2289
- return proceed.call(this, segment);
2290
-
2291
- });
2292
-
2293
-
2294
- function polarAnimate(proceed, init) {
2295
- var chart = this.chart,
2296
- animation = this.options.animation,
2297
- group = this.group,
2298
- markerGroup = this.markerGroup,
2299
- center = this.xAxis.center,
2300
- plotLeft = chart.plotLeft,
2301
- plotTop = chart.plotTop,
2302
- attribs;
2303
-
2304
- // Specific animation for polar charts
2305
- if (chart.polar) {
2306
-
2307
- // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
2308
- // would be so slow it would't matter.
2309
- if (chart.renderer.isSVG) {
2310
-
2311
- if (animation === true) {
2312
- animation = {};
2313
- }
2314
-
2315
- // Initialize the animation
2316
- if (init) {
2317
-
2318
- // Scale down the group and place it in the center
2319
- attribs = {
2320
- translateX: center[0] + plotLeft,
2321
- translateY: center[1] + plotTop,
2322
- scaleX: 0.001, // #1499
2323
- scaleY: 0.001
2324
- };
2325
-
2326
- group.attr(attribs);
2327
- if (markerGroup) {
2328
- markerGroup.attrSetters = group.attrSetters;
2329
- markerGroup.attr(attribs);
2330
- }
2331
-
2332
- // Run the animation
2333
- } else {
2334
- attribs = {
2335
- translateX: plotLeft,
2336
- translateY: plotTop,
2337
- scaleX: 1,
2338
- scaleY: 1
2339
- };
2340
- group.animate(attribs, animation);
2341
- if (markerGroup) {
2342
- markerGroup.animate(attribs, animation);
2343
- }
2344
-
2345
- // Delete this function to allow it only once
2346
- this.animate = null;
2347
- }
2348
- }
2349
-
2350
- // For non-polar charts, revert to the basic animation
2351
- } else {
2352
- proceed.call(this, init);
2353
- }
2354
- }
2355
-
2356
- // Define the animate method for both regular series and column series and their derivatives
2357
- wrap(seriesProto, 'animate', polarAnimate);
2358
- wrap(colProto, 'animate', polarAnimate);
2359
-
2360
-
2361
- /**
2362
- * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
2363
- * in degrees (0-360), not plot pixel width.
2364
- */
2365
- wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
2366
-
2367
- if (this.chart.polar) {
2368
- extend(this.xAxis, {
2369
- tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array
2370
- });
2371
- }
2372
-
2373
- // Run uber method
2374
- return proceed.call(this, renew);
2375
- });
2376
-
2377
-
2378
- /**
2379
- * Extend the column prototype's translate method
2380
- */
2381
- wrap(colProto, 'translate', function (proceed) {
2382
-
2383
- var xAxis = this.xAxis,
2384
- len = this.yAxis.len,
2385
- center = xAxis.center,
2386
- startAngleRad = xAxis.startAngleRad,
2387
- renderer = this.chart.renderer,
2388
- start,
2389
- points,
2390
- point,
2391
- i;
2392
-
2393
- this.preventPostTranslate = true;
2394
-
2395
- // Run uber method
2396
- proceed.call(this);
2397
-
2398
- // Postprocess plot coordinates
2399
- if (xAxis.isRadial) {
2400
- points = this.points;
2401
- i = points.length;
2402
- while (i--) {
2403
- point = points[i];
2404
- start = point.barX + startAngleRad;
2405
- point.shapeType = 'path';
2406
- point.shapeArgs = {
2407
- d: renderer.symbols.arc(
2408
- center[0],
2409
- center[1],
2410
- len - point.plotY,
2411
- null,
2412
- {
2413
- start: start,
2414
- end: start + point.pointWidth,
2415
- innerR: len - pick(point.yBottom, len)
2416
- }
2417
- )
2418
- };
2419
- this.toXY(point); // provide correct plotX, plotY for tooltip
2420
- }
2421
- }
2422
- });
2423
-
2424
-
2425
- /**
2426
- * Align column data labels outside the columns. #1199.
2427
- */
2428
- wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
2429
-
2430
- if (this.chart.polar) {
2431
- var angle = point.rectPlotX / Math.PI * 180,
2432
- align,
2433
- verticalAlign;
2434
-
2435
- // Align nicely outside the perimeter of the columns
2436
- if (options.align === null) {
2437
- if (angle > 20 && angle < 160) {
2438
- align = 'left'; // right hemisphere
2439
- } else if (angle > 200 && angle < 340) {
2440
- align = 'right'; // left hemisphere
2441
- } else {
2442
- align = 'center'; // top or bottom
2443
- }
2444
- options.align = align;
2445
- }
2446
- if (options.verticalAlign === null) {
2447
- if (angle < 45 || angle > 315) {
2448
- verticalAlign = 'bottom'; // top part
2449
- } else if (angle > 135 && angle < 225) {
2450
- verticalAlign = 'top'; // bottom part
2451
- } else {
2452
- verticalAlign = 'middle'; // left or right
2453
- }
2454
- options.verticalAlign = verticalAlign;
2455
- }
2456
-
2457
- seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
2458
- } else {
2459
- proceed.call(this, point, dataLabel, options, alignTo, isNew);
2460
- }
2461
-
2462
- });
2463
-
2464
- /**
2465
- * Extend the mouse tracker to return the tooltip position index in terms of
2466
- * degrees rather than pixels
2467
- */
2468
- wrap(pointerProto, 'getIndex', function (proceed, e) {
2469
- var ret,
2470
- chart = this.chart,
2471
- center,
2472
- x,
2473
- y;
2474
-
2475
- if (chart.polar) {
2476
- center = chart.xAxis[0].center;
2477
- x = e.chartX - center[0] - chart.plotLeft;
2478
- y = e.chartY - center[1] - chart.plotTop;
2479
-
2480
- ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
2481
-
2482
- } else {
2483
-
2484
- // Run uber method
2485
- ret = proceed.call(this, e);
2486
- }
2487
- return ret;
2488
- });
2489
-
2490
- /**
2491
- * Extend getCoordinates to prepare for polar axis values
2492
- */
2493
- wrap(pointerProto, 'getCoordinates', function (proceed, e) {
2494
- var chart = this.chart,
2495
- ret = {
2496
- xAxis: [],
2497
- yAxis: []
2498
- };
2499
-
2500
- if (chart.polar) {
2501
-
2502
- each(chart.axes, function (axis) {
2503
- var isXAxis = axis.isXAxis,
2504
- center = axis.center,
2505
- x = e.chartX - center[0] - chart.plotLeft,
2506
- y = e.chartY - center[1] - chart.plotTop;
2507
-
2508
- ret[isXAxis ? 'xAxis' : 'yAxis'].push({
2509
- axis: axis,
2510
- value: axis.translate(
2511
- isXAxis ?
2512
- Math.PI - Math.atan2(x, y) : // angle
2513
- Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
2514
- true
2515
- )
2516
- });
2517
- });
2518
-
2519
- } else {
2520
- ret = proceed.call(this, e);
2521
- }
2522
-
2523
- return ret;
2524
- });
2525
- }(Highcharts));
1
+ // ==ClosureCompiler==
2
+ // @compilation_level SIMPLE_OPTIMIZATIONS
3
+
4
+ /**
5
+ * @license Highcharts JS v3.0.2 (2013-06-05)
6
+ *
7
+ * (c) 2009-2013 Torstein Hønsi
8
+ *
9
+ * License: www.highcharts.com/license
10
+ */
11
+
12
+ // JSLint options:
13
+ /*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
14
+
15
+ (function (Highcharts, UNDEFINED) {
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 () {};/**
40
+ * The Pane object allows options that are common to a set of X and Y axes.
41
+ *
42
+ * In the future, this can be extended to basic Highcharts and Highstock.
43
+ */
44
+ function Pane(options, chart, firstAxis) {
45
+ this.init.call(this, options, chart, firstAxis);
46
+ }
47
+
48
+ // Extend the Pane prototype
49
+ extend(Pane.prototype, {
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
+
115
+ });
116
+ var axisProto = Axis.prototype,
117
+ tickProto = Tick.prototype;
118
+
119
+ /**
120
+ * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
121
+ */
122
+ var hiddenAxisMixin = {
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
133
+ };
134
+
135
+ /**
136
+ * Augmented methods for the value axis
137
+ */
138
+ /*jslint unparam: true*/
139
+ var radialAxisMixin = {
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
+
466
+ };
467
+ /*jslint unparam: false*/
468
+
469
+ /**
470
+ * Override axisProto.init to mix in special axis instance functions and function overrides
471
+ */
472
+ wrap(axisProto, 'init', function (proceed, chart, userOptions) {
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
+
540
+ });
541
+
542
+ /**
543
+ * Add special cases within the Tick class' methods for radial axes.
544
+ */
545
+ wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
546
+ var axis = this.axis;
547
+
548
+ return axis.getPosition ?
549
+ axis.getPosition(pos) :
550
+ proceed.call(this, horiz, pos, tickmarkOffset, old);
551
+ });
552
+
553
+ /**
554
+ * Wrap the getLabelPosition function to find the center position of the label
555
+ * based on the distance option
556
+ */
557
+ wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
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;
604
+ });
605
+
606
+ /**
607
+ * Wrap the getMarkPath function to return the path of the radial marker
608
+ */
609
+ wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
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
+ });/*
629
+ * The AreaRangeSeries class
630
+ *
631
+ */
632
+
633
+ /**
634
+ * Extend the default options with map options
635
+ */
636
+ defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
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
+ }
651
+ });
652
+
653
+ /**
654
+ * Add the series type
655
+ */
656
+ seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
657
+ type: 'arearange',
658
+ pointArrayMap: ['low', 'high'],
659
+ toYData: function (point) {
660
+ return [point.low, point.high];
661
+ },
662
+ pointValKey: 'low',
663
+
664
+ /**
665
+ * Extend getSegments to force null points if the higher value is null. #1703.
666
+ */
667
+ getSegments: function () {
668
+ var series = this;
669
+
670
+ each(series.points, function (point) {
671
+ if (!series.options.connectNulls && (point.low === null || point.high === null)) {
672
+ point.y = null;
673
+ } else if (point.low === null && point.high !== null) {
674
+ point.y = point.high;
675
+ }
676
+ });
677
+ Series.prototype.getSegments.call(this);
678
+ },
679
+
680
+ /**
681
+ * Translate data points from raw values x and y to plotX and plotY
682
+ */
683
+ translate: function () {
684
+ var series = this,
685
+ yAxis = series.yAxis;
686
+
687
+ seriesTypes.area.prototype.translate.apply(series);
688
+
689
+ // Set plotLow and plotHigh
690
+ each(series.points, function (point) {
691
+
692
+ var low = point.low,
693
+ high = point.high,
694
+ plotY = point.plotY;
695
+
696
+ if (high === null && low === null) {
697
+ point.y = null;
698
+ } else if (low === null) {
699
+ point.plotLow = point.plotY = null;
700
+ point.plotHigh = yAxis.toPixels(high, true);
701
+ } else if (high === null) {
702
+ point.plotLow = plotY;
703
+ point.plotHigh = null;
704
+ } else {
705
+ point.plotLow = plotY;
706
+ point.plotHigh = yAxis.toPixels(high, true);
707
+ }
708
+ });
709
+ },
710
+
711
+ /**
712
+ * Extend the line series' getSegmentPath method by applying the segment
713
+ * path to both lower and higher values of the range
714
+ */
715
+ getSegmentPath: function (segment) {
716
+
717
+ var lowSegment,
718
+ highSegment = [],
719
+ i = segment.length,
720
+ baseGetSegmentPath = Series.prototype.getSegmentPath,
721
+ point,
722
+ linePath,
723
+ lowerPath,
724
+ options = this.options,
725
+ step = options.step,
726
+ higherPath;
727
+
728
+ // Remove nulls from low segment
729
+ lowSegment = HighchartsAdapter.grep(segment, function (point) {
730
+ return point.plotLow !== null;
731
+ });
732
+
733
+ // Make a segment with plotX and plotY for the top values
734
+ while (i--) {
735
+ point = segment[i];
736
+ if (point.plotHigh !== null) {
737
+ highSegment.push({
738
+ plotX: point.plotX,
739
+ plotY: point.plotHigh
740
+ });
741
+ }
742
+ }
743
+
744
+ // Get the paths
745
+ lowerPath = baseGetSegmentPath.call(this, lowSegment);
746
+ if (step) {
747
+ if (step === true) {
748
+ step = 'left';
749
+ }
750
+ options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
751
+ }
752
+ higherPath = baseGetSegmentPath.call(this, highSegment);
753
+ options.step = step;
754
+
755
+ // Create a line on both top and bottom of the range
756
+ linePath = [].concat(lowerPath, higherPath);
757
+
758
+ // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
759
+ higherPath[0] = 'L'; // this probably doesn't work for spline
760
+ this.areaPath = this.areaPath.concat(lowerPath, higherPath);
761
+
762
+ return linePath;
763
+ },
764
+
765
+ /**
766
+ * Extend the basic drawDataLabels method by running it for both lower and higher
767
+ * values.
768
+ */
769
+ drawDataLabels: function () {
770
+
771
+ var data = this.data,
772
+ length = data.length,
773
+ i,
774
+ originalDataLabels = [],
775
+ seriesProto = Series.prototype,
776
+ dataLabelOptions = this.options.dataLabels,
777
+ point,
778
+ inverted = this.chart.inverted;
779
+
780
+ if (dataLabelOptions.enabled || this._hasPointLabels) {
781
+
782
+ // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
783
+ i = length;
784
+ while (i--) {
785
+ point = data[i];
786
+
787
+ // Set preliminary values
788
+ point.y = point.high;
789
+ point.plotY = point.plotHigh;
790
+
791
+ // Store original data labels and set preliminary label objects to be picked up
792
+ // in the uber method
793
+ originalDataLabels[i] = point.dataLabel;
794
+ point.dataLabel = point.dataLabelUpper;
795
+
796
+ // Set the default offset
797
+ point.below = false;
798
+ if (inverted) {
799
+ dataLabelOptions.align = 'left';
800
+ dataLabelOptions.x = dataLabelOptions.xHigh;
801
+ } else {
802
+ dataLabelOptions.y = dataLabelOptions.yHigh;
803
+ }
804
+ }
805
+ seriesProto.drawDataLabels.apply(this, arguments); // #1209
806
+
807
+ // Step 2: reorganize and handle data labels for the lower values
808
+ i = length;
809
+ while (i--) {
810
+ point = data[i];
811
+
812
+ // Move the generated labels from step 1, and reassign the original data labels
813
+ point.dataLabelUpper = point.dataLabel;
814
+ point.dataLabel = originalDataLabels[i];
815
+
816
+ // Reset values
817
+ point.y = point.low;
818
+ point.plotY = point.plotLow;
819
+
820
+ // Set the default offset
821
+ point.below = true;
822
+ if (inverted) {
823
+ dataLabelOptions.align = 'right';
824
+ dataLabelOptions.x = dataLabelOptions.xLow;
825
+ } else {
826
+ dataLabelOptions.y = dataLabelOptions.yLow;
827
+ }
828
+ }
829
+ seriesProto.drawDataLabels.apply(this, arguments);
830
+ }
831
+
832
+ },
833
+
834
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
835
+
836
+ getSymbol: seriesTypes.column.prototype.getSymbol,
837
+
838
+ drawPoints: noop
839
+ });/**
840
+ * The AreaSplineRangeSeries class
841
+ */
842
+
843
+ defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
844
+
845
+ /**
846
+ * AreaSplineRangeSeries object
847
+ */
848
+ seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
849
+ type: 'areasplinerange',
850
+ getPointSpline: seriesTypes.spline.prototype.getPointSpline
851
+ });/**
852
+ * The ColumnRangeSeries class
853
+ */
854
+ defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
855
+ lineWidth: 1,
856
+ pointRange: null
857
+ });
858
+
859
+ /**
860
+ * ColumnRangeSeries object
861
+ */
862
+ seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
863
+ type: 'columnrange',
864
+ /**
865
+ * Translate data points from raw values x and y to plotX and plotY
866
+ */
867
+ translate: function () {
868
+ var series = this,
869
+ yAxis = series.yAxis,
870
+ plotHigh;
871
+
872
+ colProto.translate.apply(series);
873
+
874
+ // Set plotLow and plotHigh
875
+ each(series.points, function (point) {
876
+ var shapeArgs = point.shapeArgs;
877
+
878
+ point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
879
+ point.plotLow = point.plotY;
880
+
881
+ // adjust shape
882
+ shapeArgs.y = plotHigh;
883
+ shapeArgs.height = point.plotY - plotHigh;
884
+
885
+ });
886
+ },
887
+ trackerGroups: ['group', 'dataLabels'],
888
+ drawGraph: noop,
889
+ pointAttrToOptions: colProto.pointAttrToOptions,
890
+ drawPoints: colProto.drawPoints,
891
+ drawTracker: colProto.drawTracker,
892
+ animate: colProto.animate,
893
+ getColumnMetrics: colProto.getColumnMetrics
894
+ });/*
895
+ * The GaugeSeries class
896
+ */
897
+
898
+
899
+
900
+ /**
901
+ * Extend the default options
902
+ */
903
+ defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
904
+ dataLabels: {
905
+ enabled: true,
906
+ y: 15,
907
+ borderWidth: 1,
908
+ borderColor: 'silver',
909
+ borderRadius: 3,
910
+ style: {
911
+ fontWeight: 'bold'
912
+ },
913
+ verticalAlign: 'top',
914
+ zIndex: 2
915
+ },
916
+ dial: {
917
+ // radius: '80%',
918
+ // backgroundColor: 'black',
919
+ // borderColor: 'silver',
920
+ // borderWidth: 0,
921
+ // baseWidth: 3,
922
+ // topWidth: 1,
923
+ // baseLength: '70%' // of radius
924
+ // rearLength: '10%'
925
+ },
926
+ pivot: {
927
+ //radius: 5,
928
+ //borderWidth: 0
929
+ //borderColor: 'silver',
930
+ //backgroundColor: 'black'
931
+ },
932
+ tooltip: {
933
+ headerFormat: ''
934
+ },
935
+ showInLegend: false
936
+ });
937
+
938
+ /**
939
+ * Extend the point object
940
+ */
941
+ var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
942
+ /**
943
+ * Don't do any hover colors or anything
944
+ */
945
+ setState: function (state) {
946
+ this.state = state;
947
+ }
948
+ });
949
+
950
+
951
+ /**
952
+ * Add the series type
953
+ */
954
+ var GaugeSeries = {
955
+ type: 'gauge',
956
+ pointClass: GaugePoint,
957
+
958
+ // chart.angular will be set to true when a gauge series is present, and this will
959
+ // be used on the axes
960
+ angular: true,
961
+ drawGraph: noop,
962
+ trackerGroups: ['group', 'dataLabels'],
963
+
964
+ /**
965
+ * Calculate paths etc
966
+ */
967
+ translate: function () {
968
+
969
+ var series = this,
970
+ yAxis = series.yAxis,
971
+ options = series.options,
972
+ center = yAxis.center;
973
+
974
+ series.generatePoints();
975
+
976
+ each(series.points, function (point) {
977
+
978
+ var dialOptions = merge(options.dial, point.dial),
979
+ radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
980
+ baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
981
+ rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
982
+ baseWidth = dialOptions.baseWidth || 3,
983
+ topWidth = dialOptions.topWidth || 1,
984
+ rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
985
+
986
+ // Handle the wrap option
987
+ if (options.wrap === false) {
988
+ rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
989
+ }
990
+ rotation = rotation * 180 / Math.PI;
991
+
992
+ point.shapeType = 'path';
993
+ point.shapeArgs = {
994
+ d: dialOptions.path || [
995
+ 'M',
996
+ -rearLength, -baseWidth / 2,
997
+ 'L',
998
+ baseLength, -baseWidth / 2,
999
+ radius, -topWidth / 2,
1000
+ radius, topWidth / 2,
1001
+ baseLength, baseWidth / 2,
1002
+ -rearLength, baseWidth / 2,
1003
+ 'z'
1004
+ ],
1005
+ translateX: center[0],
1006
+ translateY: center[1],
1007
+ rotation: rotation
1008
+ };
1009
+
1010
+ // Positions for data label
1011
+ point.plotX = center[0];
1012
+ point.plotY = center[1];
1013
+ });
1014
+ },
1015
+
1016
+ /**
1017
+ * Draw the points where each point is one needle
1018
+ */
1019
+ drawPoints: function () {
1020
+
1021
+ var series = this,
1022
+ center = series.yAxis.center,
1023
+ pivot = series.pivot,
1024
+ options = series.options,
1025
+ pivotOptions = options.pivot,
1026
+ renderer = series.chart.renderer;
1027
+
1028
+ each(series.points, function (point) {
1029
+
1030
+ var graphic = point.graphic,
1031
+ shapeArgs = point.shapeArgs,
1032
+ d = shapeArgs.d,
1033
+ dialOptions = merge(options.dial, point.dial); // #1233
1034
+
1035
+ if (graphic) {
1036
+ graphic.animate(shapeArgs);
1037
+ shapeArgs.d = d; // animate alters it
1038
+ } else {
1039
+ point.graphic = renderer[point.shapeType](shapeArgs)
1040
+ .attr({
1041
+ stroke: dialOptions.borderColor || 'none',
1042
+ 'stroke-width': dialOptions.borderWidth || 0,
1043
+ fill: dialOptions.backgroundColor || 'black',
1044
+ rotation: shapeArgs.rotation // required by VML when animation is false
1045
+ })
1046
+ .add(series.group);
1047
+ }
1048
+ });
1049
+
1050
+ // Add or move the pivot
1051
+ if (pivot) {
1052
+ pivot.animate({ // #1235
1053
+ translateX: center[0],
1054
+ translateY: center[1]
1055
+ });
1056
+ } else {
1057
+ series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
1058
+ .attr({
1059
+ 'stroke-width': pivotOptions.borderWidth || 0,
1060
+ stroke: pivotOptions.borderColor || 'silver',
1061
+ fill: pivotOptions.backgroundColor || 'black'
1062
+ })
1063
+ .translate(center[0], center[1])
1064
+ .add(series.group);
1065
+ }
1066
+ },
1067
+
1068
+ /**
1069
+ * Animate the arrow up from startAngle
1070
+ */
1071
+ animate: function (init) {
1072
+ var series = this;
1073
+
1074
+ if (!init) {
1075
+ each(series.points, function (point) {
1076
+ var graphic = point.graphic;
1077
+
1078
+ if (graphic) {
1079
+ // start value
1080
+ graphic.attr({
1081
+ rotation: series.yAxis.startAngleRad * 180 / Math.PI
1082
+ });
1083
+
1084
+ // animate
1085
+ graphic.animate({
1086
+ rotation: point.shapeArgs.rotation
1087
+ }, series.options.animation);
1088
+ }
1089
+ });
1090
+
1091
+ // delete this function to allow it only once
1092
+ series.animate = null;
1093
+ }
1094
+ },
1095
+
1096
+ render: function () {
1097
+ this.group = this.plotGroup(
1098
+ 'group',
1099
+ 'series',
1100
+ this.visible ? 'visible' : 'hidden',
1101
+ this.options.zIndex,
1102
+ this.chart.seriesGroup
1103
+ );
1104
+ seriesTypes.pie.prototype.render.call(this);
1105
+ this.group.clip(this.chart.clipRect);
1106
+ },
1107
+
1108
+ setData: seriesTypes.pie.prototype.setData,
1109
+ drawTracker: seriesTypes.column.prototype.drawTracker
1110
+ };
1111
+ seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* ****************************************************************************
1112
+ * Start Box plot series code *
1113
+ *****************************************************************************/
1114
+
1115
+ // Set default options
1116
+ defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, {
1117
+ fillColor: '#FFFFFF',
1118
+ lineWidth: 1,
1119
+ //medianColor: null,
1120
+ medianWidth: 2,
1121
+ states: {
1122
+ hover: {
1123
+ brightness: -0.3
1124
+ }
1125
+ },
1126
+ //stemColor: null,
1127
+ //stemDashStyle: 'solid'
1128
+ //stemWidth: null,
1129
+ threshold: null,
1130
+ tooltip: {
1131
+ pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' +
1132
+ 'Minimum: {point.low}<br/>' +
1133
+ 'Lower quartile: {point.q1}<br/>' +
1134
+ 'Median: {point.median}<br/>' +
1135
+ 'Higher quartile: {point.q3}<br/>' +
1136
+ 'Maximum: {point.high}<br/>'
1137
+ },
1138
+ //whiskerColor: null,
1139
+ whiskerLength: '50%',
1140
+ whiskerWidth: 2
1141
+ });
1142
+
1143
+ // Create the series object
1144
+ seriesTypes.boxplot = extendClass(seriesTypes.column, {
1145
+ type: 'boxplot',
1146
+ pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
1147
+ toYData: function (point) { // return a plain array for speedy calculation
1148
+ return [point.low, point.q1, point.median, point.q3, point.high];
1149
+ },
1150
+ pointValKey: 'high', // defines the top of the tracker
1151
+
1152
+ /**
1153
+ * One-to-one mapping from options to SVG attributes
1154
+ */
1155
+ pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
1156
+ fill: 'fillColor',
1157
+ stroke: 'color',
1158
+ 'stroke-width': 'lineWidth'
1159
+ },
1160
+
1161
+ /**
1162
+ * Disable data labels for box plot
1163
+ */
1164
+ drawDataLabels: noop,
1165
+
1166
+ /**
1167
+ * Translate data points from raw values x and y to plotX and plotY
1168
+ */
1169
+ translate: function () {
1170
+ var series = this,
1171
+ yAxis = series.yAxis,
1172
+ pointArrayMap = series.pointArrayMap;
1173
+
1174
+ seriesTypes.column.prototype.translate.apply(series);
1175
+
1176
+ // do the translation on each point dimension
1177
+ each(series.points, function (point) {
1178
+ each(pointArrayMap, function (key) {
1179
+ if (point[key] !== null) {
1180
+ point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
1181
+ }
1182
+ });
1183
+ });
1184
+ },
1185
+
1186
+ /**
1187
+ * Draw the data points
1188
+ */
1189
+ drawPoints: function () {
1190
+ var series = this, //state = series.state,
1191
+ points = series.points,
1192
+ options = series.options,
1193
+ chart = series.chart,
1194
+ renderer = chart.renderer,
1195
+ pointAttr,
1196
+ q1Plot,
1197
+ q3Plot,
1198
+ highPlot,
1199
+ lowPlot,
1200
+ medianPlot,
1201
+ crispCorr,
1202
+ crispX,
1203
+ graphic,
1204
+ stemPath,
1205
+ stemAttr,
1206
+ boxPath,
1207
+ whiskersPath,
1208
+ whiskersAttr,
1209
+ medianPath,
1210
+ medianAttr,
1211
+ width,
1212
+ left,
1213
+ right,
1214
+ halfWidth,
1215
+ shapeArgs,
1216
+ color,
1217
+ doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
1218
+ whiskerLength = parseInt(series.options.whiskerLength, 10) / 100;
1219
+
1220
+
1221
+ each(points, function (point) {
1222
+
1223
+ graphic = point.graphic;
1224
+ shapeArgs = point.shapeArgs; // the box
1225
+ stemAttr = {};
1226
+ whiskersAttr = {};
1227
+ medianAttr = {};
1228
+ color = point.color || series.color;
1229
+
1230
+ if (point.plotY !== UNDEFINED) {
1231
+
1232
+ pointAttr = point.pointAttr[point.selected ? 'selected' : ''];
1233
+
1234
+ // crisp vector coordinates
1235
+ width = shapeArgs.width;
1236
+ left = mathFloor(shapeArgs.x);
1237
+ right = left + width;
1238
+ halfWidth = mathRound(width / 2);
1239
+ //crispX = mathRound(left + halfWidth) + crispCorr;
1240
+ q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr;
1241
+ q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr;
1242
+ highPlot = mathFloor(point.highPlot);// + crispCorr;
1243
+ lowPlot = mathFloor(point.lowPlot);// + crispCorr;
1244
+
1245
+ // Stem attributes
1246
+ stemAttr.stroke = point.stemColor || options.stemColor || color;
1247
+ stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
1248
+ stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
1249
+
1250
+ // Whiskers attributes
1251
+ whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
1252
+ whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
1253
+
1254
+ // Median attributes
1255
+ medianAttr.stroke = point.medianColor || options.medianColor || color;
1256
+ medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
1257
+
1258
+
1259
+ // The stem
1260
+ crispCorr = (stemAttr['stroke-width'] % 2) / 2;
1261
+ crispX = left + halfWidth + crispCorr;
1262
+ stemPath = [
1263
+ // stem up
1264
+ 'M',
1265
+ crispX, q3Plot,
1266
+ 'L',
1267
+ crispX, highPlot,
1268
+
1269
+ // stem down
1270
+ 'M',
1271
+ crispX, q1Plot,
1272
+ 'L',
1273
+ crispX, lowPlot,
1274
+ 'z'
1275
+ ];
1276
+
1277
+ // The box
1278
+ if (doQuartiles) {
1279
+ crispCorr = (pointAttr['stroke-width'] % 2) / 2;
1280
+ crispX = mathFloor(crispX) + crispCorr;
1281
+ q1Plot = mathFloor(q1Plot) + crispCorr;
1282
+ q3Plot = mathFloor(q3Plot) + crispCorr;
1283
+ left += crispCorr;
1284
+ right += crispCorr;
1285
+ boxPath = [
1286
+ 'M',
1287
+ left, q3Plot,
1288
+ 'L',
1289
+ left, q1Plot,
1290
+ 'L',
1291
+ right, q1Plot,
1292
+ 'L',
1293
+ right, q3Plot,
1294
+ 'L',
1295
+ left, q3Plot,
1296
+ 'z'
1297
+ ];
1298
+ }
1299
+
1300
+ // The whiskers
1301
+ if (whiskerLength) {
1302
+ crispCorr = (whiskersAttr['stroke-width'] % 2) / 2;
1303
+ highPlot = highPlot + crispCorr;
1304
+ lowPlot = lowPlot + crispCorr;
1305
+ whiskersPath = [
1306
+ // High whisker
1307
+ 'M',
1308
+ crispX - halfWidth * whiskerLength,
1309
+ highPlot,
1310
+ 'L',
1311
+ crispX + halfWidth * whiskerLength,
1312
+ highPlot,
1313
+
1314
+ // Low whisker
1315
+ 'M',
1316
+ crispX - halfWidth * whiskerLength,
1317
+ lowPlot,
1318
+ 'L',
1319
+ crispX + halfWidth * whiskerLength,
1320
+ lowPlot
1321
+ ];
1322
+ }
1323
+
1324
+ // The median
1325
+ crispCorr = (medianAttr['stroke-width'] % 2) / 2;
1326
+ medianPlot = mathRound(point.medianPlot) + crispCorr;
1327
+ medianPath = [
1328
+ 'M',
1329
+ left,
1330
+ medianPlot,
1331
+ 'L',
1332
+ right,
1333
+ medianPlot,
1334
+ 'z'
1335
+ ];
1336
+
1337
+ // Create or update the graphics
1338
+ if (graphic) { // update
1339
+
1340
+ point.stem.animate({ d: stemPath });
1341
+ if (whiskerLength) {
1342
+ point.whiskers.animate({ d: whiskersPath });
1343
+ }
1344
+ if (doQuartiles) {
1345
+ point.box.animate({ d: boxPath });
1346
+ }
1347
+ point.medianShape.animate({ d: medianPath });
1348
+
1349
+ } else { // create new
1350
+ point.graphic = graphic = renderer.g()
1351
+ .add(series.group);
1352
+
1353
+ point.stem = renderer.path(stemPath)
1354
+ .attr(stemAttr)
1355
+ .add(graphic);
1356
+
1357
+ if (whiskerLength) {
1358
+ point.whiskers = renderer.path(whiskersPath)
1359
+ .attr(whiskersAttr)
1360
+ .add(graphic);
1361
+ }
1362
+ if (doQuartiles) {
1363
+ point.box = renderer.path(boxPath)
1364
+ .attr(pointAttr)
1365
+ .add(graphic);
1366
+ }
1367
+ point.medianShape = renderer.path(medianPath)
1368
+ .attr(medianAttr)
1369
+ .add(graphic);
1370
+ }
1371
+ }
1372
+ });
1373
+
1374
+ }
1375
+
1376
+
1377
+ });
1378
+
1379
+ /* ****************************************************************************
1380
+ * End Box plot series code *
1381
+ *****************************************************************************/
1382
+ /* ****************************************************************************
1383
+ * Start error bar series code *
1384
+ *****************************************************************************/
1385
+
1386
+ // 1 - set default options
1387
+ defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, {
1388
+ color: '#000000',
1389
+ grouping: false,
1390
+ linkedTo: ':previous',
1391
+ tooltip: {
1392
+ pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat
1393
+ },
1394
+ whiskerWidth: null
1395
+ });
1396
+
1397
+ // 2 - Create the series object
1398
+ seriesTypes.errorbar = extendClass(seriesTypes.boxplot, {
1399
+ type: 'errorbar',
1400
+ pointArrayMap: ['low', 'high'], // array point configs are mapped to this
1401
+ toYData: function (point) { // return a plain array for speedy calculation
1402
+ return [point.low, point.high];
1403
+ },
1404
+ pointValKey: 'high', // defines the top of the tracker
1405
+ doQuartiles: false,
1406
+
1407
+ /**
1408
+ * Get the width and X offset, either on top of the linked series column
1409
+ * or standalone
1410
+ */
1411
+ getColumnMetrics: function () {
1412
+ return (this.linkedParent && this.linkedParent.columnMetrics) ||
1413
+ seriesTypes.column.prototype.getColumnMetrics.call(this);
1414
+ }
1415
+ });
1416
+
1417
+ /* ****************************************************************************
1418
+ * End error bar series code *
1419
+ *****************************************************************************/
1420
+ /* ****************************************************************************
1421
+ * Start Waterfall series code *
1422
+ *****************************************************************************/
1423
+
1424
+ wrap(axisProto, 'getSeriesExtremes', function (proceed, renew) {
1425
+ // Run uber method
1426
+ proceed.call(this, renew);
1427
+
1428
+ if (this.isXAxis) {
1429
+ return;
1430
+ }
1431
+
1432
+ var axis = this,
1433
+ visitedStacks = [],
1434
+ resetMinMax = true;
1435
+
1436
+
1437
+ // recalculate extremes for each waterfall stack
1438
+ each(axis.series, function (series) {
1439
+ // process only visible, waterfall series, one from each stack
1440
+ if (!series.visible || !series.stackKey || series.type !== 'waterfall' || HighchartsAdapter.inArray(series.stackKey) !== -1) {
1441
+ return;
1442
+ }
1443
+
1444
+ // reset previously found dataMin and dataMax, do it only once
1445
+ if (resetMinMax) {
1446
+ axis.dataMin = axis.dataMax = null;
1447
+ resetMinMax = false;
1448
+ }
1449
+
1450
+
1451
+ var yData = series.processedYData,
1452
+ yDataLength = yData.length,
1453
+ seriesDataMin = yData[0],
1454
+ seriesDataMax = yData[0],
1455
+ threshold = series.options.threshold,
1456
+ stacks = axis.stacks,
1457
+ stackKey = series.stackKey,
1458
+ negKey = '-' + stackKey,
1459
+ total,
1460
+ previous,
1461
+ key,
1462
+ i;
1463
+
1464
+
1465
+ // set new stack totals including preceding values, finds new min and max values
1466
+ for (i = 0; i < yDataLength; i++) {
1467
+ key = yData[i] < threshold ? negKey : stackKey;
1468
+ total = stacks[key][i].total;
1469
+
1470
+ if (i > threshold) {
1471
+ total += previous;
1472
+ stacks[key][i].setTotal(total);
1473
+
1474
+ // _cum is used to avoid conflict with Series.translate method
1475
+ stacks[key][i]._cum = null;
1476
+ }
1477
+
1478
+
1479
+ // find min / max values
1480
+ if (total < seriesDataMin) {
1481
+ seriesDataMin = total;
1482
+ }
1483
+
1484
+ if (total > seriesDataMax) {
1485
+ seriesDataMax = total;
1486
+ }
1487
+
1488
+ previous = total;
1489
+ }
1490
+
1491
+
1492
+ // set new extremes
1493
+ series.dataMin = seriesDataMin;
1494
+ series.dataMax = seriesDataMax;
1495
+ axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin, threshold);
1496
+ axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax, threshold);
1497
+
1498
+ // remember series' stack key
1499
+ visitedStacks.push(series.stackKey);
1500
+
1501
+
1502
+
1503
+ // Adjust to threshold. This code is duplicated from the parent getSeriesExtremes method.
1504
+ if (typeof threshold === 'number') {
1505
+ if (axis.dataMin >= threshold) {
1506
+ axis.dataMin = threshold;
1507
+ axis.ignoreMinPadding = true;
1508
+ } else if (axis.dataMax < threshold) {
1509
+ axis.dataMax = threshold;
1510
+ axis.ignoreMaxPadding = true;
1511
+ }
1512
+ }
1513
+ });
1514
+ });
1515
+
1516
+
1517
+ // 1 - set default options
1518
+ defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
1519
+ lineWidth: 1,
1520
+ lineColor: '#333',
1521
+ dashStyle: 'dot',
1522
+ borderColor: '#333'
1523
+ });
1524
+
1525
+
1526
+ // 2 - Create the series object
1527
+ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1528
+ type: 'waterfall',
1529
+
1530
+ upColorProp: 'fill',
1531
+
1532
+ pointArrayMap: ['y', 'low'],
1533
+
1534
+ pointValKey: 'y',
1535
+
1536
+ /**
1537
+ * Init waterfall series, force stacking
1538
+ */
1539
+ init: function (chart, options) {
1540
+ options.stacking = true;
1541
+ seriesTypes.column.prototype.init.call(this, chart, options);
1542
+ },
1543
+
1544
+
1545
+ /**
1546
+ * Translate data points from raw values
1547
+ */
1548
+ translate: function () {
1549
+ var series = this,
1550
+ options = series.options,
1551
+ axis = series.yAxis,
1552
+ len,
1553
+ i,
1554
+
1555
+ points,
1556
+ point,
1557
+ shapeArgs,
1558
+ sum,
1559
+ sumStart,
1560
+ subSum,
1561
+ subSumStart,
1562
+ edges,
1563
+ cumulative,
1564
+ prevStack,
1565
+ prevY,
1566
+ stack,
1567
+ crispCorr = (options.borderWidth % 2) / 2;
1568
+
1569
+ // run column series translate
1570
+ seriesTypes.column.prototype.translate.apply(this);
1571
+
1572
+
1573
+ points = this.points;
1574
+ subSumStart = sumStart = points[0];
1575
+ sum = subSum = points[0].y;
1576
+
1577
+ for (i = 1, len = points.length; i < len; i++) {
1578
+ // cache current point object
1579
+ point = points[i];
1580
+ shapeArgs = point.shapeArgs;
1581
+
1582
+ // get current and previous stack
1583
+ stack = series.getStack(i);
1584
+ prevStack = series.getStack(i - 1);
1585
+ prevY = series.getStackY(prevStack);
1586
+
1587
+ // set new intermediate sum values after reset
1588
+ if (subSumStart === null) {
1589
+ subSumStart = point;
1590
+ subSum = 0;
1591
+ }
1592
+
1593
+ // sum only points with value, not intermediate or total sum
1594
+ if (point.y && !point.isSum && !point.isIntermediateSum) {
1595
+ sum += point.y;
1596
+ subSum += point.y;
1597
+ }
1598
+
1599
+ // calculate sum points
1600
+ if (point.isSum || point.isIntermediateSum) {
1601
+
1602
+ if (point.isIntermediateSum) {
1603
+ edges = series.getSumEdges(subSumStart, points[i - 1]);
1604
+ point.y = subSum;
1605
+ subSumStart = null;
1606
+ } else {
1607
+ edges = series.getSumEdges(sumStart, points[i - 1]);
1608
+ point.y = sum;
1609
+ }
1610
+
1611
+ shapeArgs.y = point.plotY = edges[1];
1612
+ shapeArgs.height = edges[0] - edges[1];
1613
+
1614
+ // calculate other (up or down) points based on y value
1615
+ } else {
1616
+ // use "_cum" instead of already calculated "cum" to avoid reverse ordering negative columns
1617
+ cumulative = stack._cum === null ? prevStack.total : stack._cum;
1618
+ stack._cum = cumulative + point.y;
1619
+
1620
+ if (point.y < 0) {
1621
+ shapeArgs.y = mathCeil(axis.translate(cumulative, 0, 1)) - crispCorr;
1622
+ shapeArgs.height = mathCeil(axis.translate(stack._cum, 0, 1) - shapeArgs.y);
1623
+ } else {
1624
+ if (prevStack.total + point.y < 0) {
1625
+ shapeArgs.y = axis.translate(stack._cum, 0, 1);
1626
+ }
1627
+
1628
+ shapeArgs.height = mathFloor(prevY - shapeArgs.y);
1629
+ }
1630
+
1631
+ }
1632
+ }
1633
+ },
1634
+
1635
+ /**
1636
+ * Call default processData then override yData to reflect waterfall's extremes on yAxis
1637
+ */
1638
+ processData: function (force) {
1639
+ Series.prototype.processData.call(this, force);
1640
+
1641
+ var series = this,
1642
+ options = series.options,
1643
+ yData = series.yData,
1644
+ length = yData.length,
1645
+ prev,
1646
+ curr,
1647
+ subSum,
1648
+ sum,
1649
+ i;
1650
+
1651
+ prev = sum = subSum = options.threshold;
1652
+
1653
+ for (i = 0; i < length; i++) {
1654
+ curr = yData[i];
1655
+
1656
+ // processed yData only if it's not already processed
1657
+ if (curr !== null && typeof curr !== 'number') {
1658
+
1659
+ if (curr === "sum") {
1660
+ yData[i] = null;
1661
+
1662
+ } else if (curr === "intermediateSum") {
1663
+ yData[i] = null;
1664
+ subSum = prev;
1665
+
1666
+ } else {
1667
+ yData[i] = curr[0];// + prev;
1668
+ }
1669
+
1670
+ prev = yData[i];
1671
+ }
1672
+ }
1673
+ },
1674
+
1675
+ /**
1676
+ * Return [y, low] array, if low is not defined, it's replaced with null for further calculations
1677
+ */
1678
+ toYData: function (pt) {
1679
+ if (pt.isSum) {
1680
+ return "sum";
1681
+ } else if (pt.isIntermediateSum) {
1682
+ return "intermediateSum";
1683
+ }
1684
+
1685
+ return [pt.y];
1686
+ },
1687
+
1688
+ /**
1689
+ * Postprocess mapping between options and SVG attributes
1690
+ */
1691
+ getAttribs: function () {
1692
+ seriesTypes.column.prototype.getAttribs.apply(this, arguments);
1693
+
1694
+ var series = this,
1695
+ options = series.options,
1696
+ stateOptions = options.states,
1697
+ upColor = options.upColor || series.color,
1698
+ hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
1699
+ seriesDownPointAttr = merge(series.pointAttr),
1700
+ upColorProp = series.upColorProp;
1701
+
1702
+ seriesDownPointAttr[''][upColorProp] = upColor;
1703
+ seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor;
1704
+ seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor;
1705
+
1706
+ each(series.points, function (point) {
1707
+ if (point.y > 0 && !point.color) {
1708
+ point.pointAttr = seriesDownPointAttr;
1709
+ point.color = upColor;
1710
+ }
1711
+ });
1712
+ },
1713
+
1714
+ /**
1715
+ * Draw columns' connector lines
1716
+ */
1717
+ getGraphPath: function () {
1718
+
1719
+ var data = this.data,
1720
+ length = data.length,
1721
+ lineWidth = this.options.lineWidth + this.options.borderWidth,
1722
+ normalizer = mathRound(lineWidth) % 2 / 2,
1723
+ path = [],
1724
+ M = 'M',
1725
+ L = 'L',
1726
+ prevArgs,
1727
+ pointArgs,
1728
+ i,
1729
+ d;
1730
+
1731
+ for (i = 1; i < length; i++) {
1732
+ pointArgs = data[i].shapeArgs;
1733
+ prevArgs = data[i - 1].shapeArgs;
1734
+
1735
+ d = [
1736
+ M,
1737
+ prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
1738
+ L,
1739
+ pointArgs.x, prevArgs.y + normalizer
1740
+ ];
1741
+
1742
+ if (data[i - 1].y < 0) {
1743
+ d[2] += prevArgs.height;
1744
+ d[5] += prevArgs.height;
1745
+ }
1746
+
1747
+ path = path.concat(d);
1748
+ }
1749
+
1750
+ return path;
1751
+ },
1752
+
1753
+ getStack: function (i) {
1754
+ var axis = this.yAxis,
1755
+ stacks = axis.stacks,
1756
+ key = this.stackKey;
1757
+
1758
+ if (this.processedYData[i] < this.options.threshold) {
1759
+ key = '-' + key;
1760
+ }
1761
+
1762
+ return stacks[key][i];
1763
+ },
1764
+
1765
+ getStackY: function (stack) {
1766
+ return mathCeil(this.yAxis.translate(stack.total, null, true));
1767
+ },
1768
+
1769
+ /**
1770
+ * Return array of top and bottom position for sum column based on given edge points
1771
+ */
1772
+ getSumEdges: function (pointA, pointB) {
1773
+ var valueA,
1774
+ valueB,
1775
+ tmp,
1776
+ threshold = this.options.threshold;
1777
+
1778
+ valueA = pointA.y >= threshold ? pointA.shapeArgs.y + pointA.shapeArgs.height : pointA.shapeArgs.y;
1779
+ valueB = pointB.y >= threshold ? pointB.shapeArgs.y : pointB.shapeArgs.y + pointB.shapeArgs.height;
1780
+
1781
+ if (valueB > valueA) {
1782
+ tmp = valueA;
1783
+ valueA = valueB;
1784
+ valueB = tmp;
1785
+ }
1786
+
1787
+ return [valueA, valueB];
1788
+ },
1789
+
1790
+ drawGraph: Series.prototype.drawGraph
1791
+ });
1792
+
1793
+ /* ****************************************************************************
1794
+ * End Waterfall series code *
1795
+ *****************************************************************************/
1796
+ /* ****************************************************************************
1797
+ * Start Bubble series code *
1798
+ *****************************************************************************/
1799
+
1800
+ // 1 - set default options
1801
+ defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
1802
+ dataLabels: {
1803
+ inside: true,
1804
+ style: {
1805
+ color: 'white',
1806
+ textShadow: '0px 0px 3px black'
1807
+ },
1808
+ verticalAlign: 'middle'
1809
+ },
1810
+ // displayNegative: true,
1811
+ marker: {
1812
+ // fillOpacity: 0.5,
1813
+ lineColor: null, // inherit from series.color
1814
+ lineWidth: 1
1815
+ },
1816
+ minSize: 8,
1817
+ maxSize: '20%',
1818
+ // negativeColor: null,
1819
+ tooltip: {
1820
+ pointFormat: '({point.x}, {point.y}), Size: {point.z}'
1821
+ },
1822
+ zThreshold: 0
1823
+ });
1824
+
1825
+ // 2 - Create the series object
1826
+ seriesTypes.bubble = extendClass(seriesTypes.scatter, {
1827
+ type: 'bubble',
1828
+ pointArrayMap: ['y', 'z'],
1829
+ trackerGroups: ['group', 'dataLabelsGroup'],
1830
+
1831
+ /**
1832
+ * Mapping between SVG attributes and the corresponding options
1833
+ */
1834
+ pointAttrToOptions: {
1835
+ stroke: 'lineColor',
1836
+ 'stroke-width': 'lineWidth',
1837
+ fill: 'fillColor'
1838
+ },
1839
+
1840
+ /**
1841
+ * Apply the fillOpacity to all fill positions
1842
+ */
1843
+ applyOpacity: function (fill) {
1844
+ var markerOptions = this.options.marker,
1845
+ fillOpacity = pick(markerOptions.fillOpacity, 0.5);
1846
+
1847
+ // When called from Legend.colorizeItem, the fill isn't predefined
1848
+ fill = fill || markerOptions.fillColor || this.color;
1849
+
1850
+ if (fillOpacity !== 1) {
1851
+ fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba');
1852
+ }
1853
+ return fill;
1854
+ },
1855
+
1856
+ /**
1857
+ * Extend the convertAttribs method by applying opacity to the fill
1858
+ */
1859
+ convertAttribs: function () {
1860
+ var obj = Series.prototype.convertAttribs.apply(this, arguments);
1861
+
1862
+ obj.fill = this.applyOpacity(obj.fill);
1863
+
1864
+ return obj;
1865
+ },
1866
+
1867
+ /**
1868
+ * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
1869
+ * must be done prior to Series.translate because the axis needs to add padding in
1870
+ * accordance with the point sizes.
1871
+ */
1872
+ getRadii: function (zMin, zMax, minSize, maxSize) {
1873
+ var len,
1874
+ i,
1875
+ pos,
1876
+ zData = this.zData,
1877
+ radii = [],
1878
+ zRange;
1879
+
1880
+ // Set the shape type and arguments to be picked up in drawPoints
1881
+ for (i = 0, len = zData.length; i < len; i++) {
1882
+ zRange = zMax - zMin;
1883
+ pos = zRange > 0 ? // relative size, a number between 0 and 1
1884
+ (zData[i] - zMin) / (zMax - zMin) :
1885
+ 0.5;
1886
+ radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
1887
+ }
1888
+ this.radii = radii;
1889
+ },
1890
+
1891
+ /**
1892
+ * Perform animation on the bubbles
1893
+ */
1894
+ animate: function (init) {
1895
+ var animation = this.options.animation;
1896
+
1897
+ if (!init) { // run the animation
1898
+ each(this.points, function (point) {
1899
+ var graphic = point.graphic,
1900
+ shapeArgs = point.shapeArgs;
1901
+
1902
+ if (graphic && shapeArgs) {
1903
+ // start values
1904
+ graphic.attr('r', 1);
1905
+
1906
+ // animate
1907
+ graphic.animate({
1908
+ r: shapeArgs.r
1909
+ }, animation);
1910
+ }
1911
+ });
1912
+
1913
+ // delete this function to allow it only once
1914
+ this.animate = null;
1915
+ }
1916
+ },
1917
+
1918
+ /**
1919
+ * Extend the base translate method to handle bubble size
1920
+ */
1921
+ translate: function () {
1922
+
1923
+ var i,
1924
+ data = this.data,
1925
+ point,
1926
+ radius,
1927
+ radii = this.radii;
1928
+
1929
+ // Run the parent method
1930
+ seriesTypes.scatter.prototype.translate.call(this);
1931
+
1932
+ // Set the shape type and arguments to be picked up in drawPoints
1933
+ i = data.length;
1934
+
1935
+ while (i--) {
1936
+ point = data[i];
1937
+ radius = radii ? radii[i] : 0; // #1737
1938
+
1939
+ // Flag for negativeColor to be applied in Series.js
1940
+ point.negative = point.z < (this.options.zThreshold || 0);
1941
+
1942
+ if (radius >= this.minPxSize / 2) {
1943
+ // Shape arguments
1944
+ point.shapeType = 'circle';
1945
+ point.shapeArgs = {
1946
+ x: point.plotX,
1947
+ y: point.plotY,
1948
+ r: radius
1949
+ };
1950
+
1951
+ // Alignment box for the data label
1952
+ point.dlBox = {
1953
+ x: point.plotX - radius,
1954
+ y: point.plotY - radius,
1955
+ width: 2 * radius,
1956
+ height: 2 * radius
1957
+ };
1958
+ } else { // below zThreshold
1959
+ point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691
1960
+ }
1961
+ }
1962
+ },
1963
+
1964
+ /**
1965
+ * Get the series' symbol in the legend
1966
+ *
1967
+ * @param {Object} legend The legend object
1968
+ * @param {Object} item The series (this) or point
1969
+ */
1970
+ drawLegendSymbol: function (legend, item) {
1971
+ var radius = pInt(legend.itemStyle.fontSize) / 2;
1972
+
1973
+ item.legendSymbol = this.chart.renderer.circle(
1974
+ radius,
1975
+ legend.baseline - radius,
1976
+ radius
1977
+ ).attr({
1978
+ zIndex: 3
1979
+ }).add(item.legendGroup);
1980
+
1981
+ },
1982
+
1983
+ drawPoints: seriesTypes.column.prototype.drawPoints,
1984
+ alignDataLabel: seriesTypes.column.prototype.alignDataLabel
1985
+ });
1986
+
1987
+ /**
1988
+ * Add logic to pad each axis with the amount of pixels
1989
+ * necessary to avoid the bubbles to overflow.
1990
+ */
1991
+ Axis.prototype.beforePadding = function () {
1992
+ var axis = this,
1993
+ axisLength = this.len,
1994
+ chart = this.chart,
1995
+ pxMin = 0,
1996
+ pxMax = axisLength,
1997
+ isXAxis = this.isXAxis,
1998
+ dataKey = isXAxis ? 'xData' : 'yData',
1999
+ min = this.min,
2000
+ extremes = {},
2001
+ smallestSize = math.min(chart.plotWidth, chart.plotHeight),
2002
+ zMin = Number.MAX_VALUE,
2003
+ zMax = -Number.MAX_VALUE,
2004
+ range = this.max - min,
2005
+ transA = axisLength / range,
2006
+ activeSeries = [];
2007
+
2008
+ // Handle padding on the second pass, or on redraw
2009
+ if (this.tickPositions) {
2010
+ each(this.series, function (series) {
2011
+
2012
+ var seriesOptions = series.options,
2013
+ zData;
2014
+
2015
+ if (series.type === 'bubble' && series.visible) {
2016
+
2017
+ // Correction for #1673
2018
+ axis.allowZoomOutside = true;
2019
+
2020
+ // Cache it
2021
+ activeSeries.push(series);
2022
+
2023
+ if (isXAxis) { // because X axis is evaluated first
2024
+
2025
+ // For each series, translate the size extremes to pixel values
2026
+ each(['minSize', 'maxSize'], function (prop) {
2027
+ var length = seriesOptions[prop],
2028
+ isPercent = /%$/.test(length);
2029
+
2030
+ length = pInt(length);
2031
+ extremes[prop] = isPercent ?
2032
+ smallestSize * length / 100 :
2033
+ length;
2034
+
2035
+ });
2036
+ series.minPxSize = extremes.minSize;
2037
+
2038
+ // Find the min and max Z
2039
+ zData = series.zData;
2040
+ if (zData.length) { // #1735
2041
+ zMin = math.min(
2042
+ zMin,
2043
+ math.max(
2044
+ arrayMin(zData),
2045
+ seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
2046
+ )
2047
+ );
2048
+ zMax = math.max(zMax, arrayMax(zData));
2049
+ }
2050
+ }
2051
+ }
2052
+ });
2053
+
2054
+ each(activeSeries, function (series) {
2055
+
2056
+ var data = series[dataKey],
2057
+ i = data.length,
2058
+ radius;
2059
+
2060
+ if (isXAxis) {
2061
+ series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
2062
+ }
2063
+
2064
+ if (range > 0) {
2065
+ while (i--) {
2066
+ radius = series.radii[i];
2067
+ pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
2068
+ pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
2069
+ }
2070
+ }
2071
+ });
2072
+
2073
+ if (range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
2074
+ pxMax -= axisLength;
2075
+ transA *= (axisLength + pxMin - pxMax) / axisLength;
2076
+ this.min += pxMin / transA;
2077
+ this.max += pxMax / transA;
2078
+ }
2079
+ }
2080
+ };
2081
+
2082
+ /* ****************************************************************************
2083
+ * End Bubble series code *
2084
+ *****************************************************************************/
2085
+ /**
2086
+ * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
2087
+ * gathered in RadialAxes.js.
2088
+ *
2089
+ */
2090
+
2091
+ var seriesProto = Series.prototype,
2092
+ pointerProto = Highcharts.Pointer.prototype;
2093
+
2094
+
2095
+
2096
+ /**
2097
+ * Translate a point's plotX and plotY from the internal angle and radius measures to
2098
+ * true plotX, plotY coordinates
2099
+ */
2100
+ seriesProto.toXY = function (point) {
2101
+ var xy,
2102
+ chart = this.chart,
2103
+ plotX = point.plotX,
2104
+ plotY = point.plotY;
2105
+
2106
+ // Save rectangular plotX, plotY for later computation
2107
+ point.rectPlotX = plotX;
2108
+ point.rectPlotY = plotY;
2109
+
2110
+ // Record the angle in degrees for use in tooltip
2111
+ point.clientX = plotX / Math.PI * 180;
2112
+
2113
+ // Find the polar plotX and plotY
2114
+ xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
2115
+ point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
2116
+ point.plotY = point.polarPlotY = xy.y - chart.plotTop;
2117
+ };
2118
+
2119
+
2120
+ /**
2121
+ * Add some special init logic to areas and areasplines
2122
+ */
2123
+ function initArea(proceed, chart, options) {
2124
+ proceed.call(this, chart, options);
2125
+ if (this.chart.polar) {
2126
+
2127
+ /**
2128
+ * Overridden method to close a segment path. While in a cartesian plane the area
2129
+ * goes down to the threshold, in the polar chart it goes to the center.
2130
+ */
2131
+ this.closeSegment = function (path) {
2132
+ var center = this.xAxis.center;
2133
+ path.push(
2134
+ 'L',
2135
+ center[0],
2136
+ center[1]
2137
+ );
2138
+ };
2139
+
2140
+ // Instead of complicated logic to draw an area around the inner area in a stack,
2141
+ // just draw it behind
2142
+ this.closedStacks = true;
2143
+ }
2144
+ }
2145
+ wrap(seriesTypes.area.prototype, 'init', initArea);
2146
+ wrap(seriesTypes.areaspline.prototype, 'init', initArea);
2147
+
2148
+
2149
+ /**
2150
+ * Overridden method for calculating a spline from one point to the next
2151
+ */
2152
+ wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
2153
+
2154
+ var ret,
2155
+ smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
2156
+ denom = smoothing + 1,
2157
+ plotX,
2158
+ plotY,
2159
+ lastPoint,
2160
+ nextPoint,
2161
+ lastX,
2162
+ lastY,
2163
+ nextX,
2164
+ nextY,
2165
+ leftContX,
2166
+ leftContY,
2167
+ rightContX,
2168
+ rightContY,
2169
+ distanceLeftControlPoint,
2170
+ distanceRightControlPoint,
2171
+ leftContAngle,
2172
+ rightContAngle,
2173
+ jointAngle;
2174
+
2175
+
2176
+ if (this.chart.polar) {
2177
+
2178
+ plotX = point.plotX;
2179
+ plotY = point.plotY;
2180
+ lastPoint = segment[i - 1];
2181
+ nextPoint = segment[i + 1];
2182
+
2183
+ // Connect ends
2184
+ if (this.connectEnds) {
2185
+ if (!lastPoint) {
2186
+ lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
2187
+ }
2188
+ if (!nextPoint) {
2189
+ nextPoint = segment[1];
2190
+ }
2191
+ }
2192
+
2193
+ // find control points
2194
+ if (lastPoint && nextPoint) {
2195
+
2196
+ lastX = lastPoint.plotX;
2197
+ lastY = lastPoint.plotY;
2198
+ nextX = nextPoint.plotX;
2199
+ nextY = nextPoint.plotY;
2200
+ leftContX = (smoothing * plotX + lastX) / denom;
2201
+ leftContY = (smoothing * plotY + lastY) / denom;
2202
+ rightContX = (smoothing * plotX + nextX) / denom;
2203
+ rightContY = (smoothing * plotY + nextY) / denom;
2204
+ distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
2205
+ distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
2206
+ leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
2207
+ rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
2208
+ jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
2209
+
2210
+
2211
+ // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
2212
+ if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
2213
+ jointAngle -= Math.PI;
2214
+ }
2215
+
2216
+ // Find the corrected control points for a spline straight through the point
2217
+ leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
2218
+ leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
2219
+ rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
2220
+ rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
2221
+
2222
+ // Record for drawing in next point
2223
+ point.rightContX = rightContX;
2224
+ point.rightContY = rightContY;
2225
+
2226
+ }
2227
+
2228
+
2229
+ // moveTo or lineTo
2230
+ if (!i) {
2231
+ ret = ['M', plotX, plotY];
2232
+ } else { // curve from last point to this
2233
+ ret = [
2234
+ 'C',
2235
+ lastPoint.rightContX || lastPoint.plotX,
2236
+ lastPoint.rightContY || lastPoint.plotY,
2237
+ leftContX || plotX,
2238
+ leftContY || plotY,
2239
+ plotX,
2240
+ plotY
2241
+ ];
2242
+ lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
2243
+ }
2244
+
2245
+
2246
+ } else {
2247
+ ret = proceed.call(this, segment, point, i);
2248
+ }
2249
+ return ret;
2250
+ });
2251
+
2252
+ /**
2253
+ * Extend translate. The plotX and plotY values are computed as if the polar chart were a
2254
+ * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
2255
+ * center.
2256
+ */
2257
+ wrap(seriesProto, 'translate', function (proceed) {
2258
+
2259
+ // Run uber method
2260
+ proceed.call(this);
2261
+
2262
+ // Postprocess plot coordinates
2263
+ if (this.chart.polar && !this.preventPostTranslate) {
2264
+ var points = this.points,
2265
+ i = points.length;
2266
+ while (i--) {
2267
+ // Translate plotX, plotY from angle and radius to true plot coordinates
2268
+ this.toXY(points[i]);
2269
+ }
2270
+ }
2271
+ });
2272
+
2273
+ /**
2274
+ * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
2275
+ * line-like series.
2276
+ */
2277
+ wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
2278
+
2279
+ var points = this.points;
2280
+
2281
+ // Connect the path
2282
+ if (this.chart.polar && this.options.connectEnds !== false &&
2283
+ segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
2284
+ this.connectEnds = true; // re-used in splines
2285
+ segment = [].concat(segment, [points[0]]);
2286
+ }
2287
+
2288
+ // Run uber method
2289
+ return proceed.call(this, segment);
2290
+
2291
+ });
2292
+
2293
+
2294
+ function polarAnimate(proceed, init) {
2295
+ var chart = this.chart,
2296
+ animation = this.options.animation,
2297
+ group = this.group,
2298
+ markerGroup = this.markerGroup,
2299
+ center = this.xAxis.center,
2300
+ plotLeft = chart.plotLeft,
2301
+ plotTop = chart.plotTop,
2302
+ attribs;
2303
+
2304
+ // Specific animation for polar charts
2305
+ if (chart.polar) {
2306
+
2307
+ // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
2308
+ // would be so slow it would't matter.
2309
+ if (chart.renderer.isSVG) {
2310
+
2311
+ if (animation === true) {
2312
+ animation = {};
2313
+ }
2314
+
2315
+ // Initialize the animation
2316
+ if (init) {
2317
+
2318
+ // Scale down the group and place it in the center
2319
+ attribs = {
2320
+ translateX: center[0] + plotLeft,
2321
+ translateY: center[1] + plotTop,
2322
+ scaleX: 0.001, // #1499
2323
+ scaleY: 0.001
2324
+ };
2325
+
2326
+ group.attr(attribs);
2327
+ if (markerGroup) {
2328
+ markerGroup.attrSetters = group.attrSetters;
2329
+ markerGroup.attr(attribs);
2330
+ }
2331
+
2332
+ // Run the animation
2333
+ } else {
2334
+ attribs = {
2335
+ translateX: plotLeft,
2336
+ translateY: plotTop,
2337
+ scaleX: 1,
2338
+ scaleY: 1
2339
+ };
2340
+ group.animate(attribs, animation);
2341
+ if (markerGroup) {
2342
+ markerGroup.animate(attribs, animation);
2343
+ }
2344
+
2345
+ // Delete this function to allow it only once
2346
+ this.animate = null;
2347
+ }
2348
+ }
2349
+
2350
+ // For non-polar charts, revert to the basic animation
2351
+ } else {
2352
+ proceed.call(this, init);
2353
+ }
2354
+ }
2355
+
2356
+ // Define the animate method for both regular series and column series and their derivatives
2357
+ wrap(seriesProto, 'animate', polarAnimate);
2358
+ wrap(colProto, 'animate', polarAnimate);
2359
+
2360
+
2361
+ /**
2362
+ * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
2363
+ * in degrees (0-360), not plot pixel width.
2364
+ */
2365
+ wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
2366
+
2367
+ if (this.chart.polar) {
2368
+ extend(this.xAxis, {
2369
+ tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array
2370
+ });
2371
+ }
2372
+
2373
+ // Run uber method
2374
+ return proceed.call(this, renew);
2375
+ });
2376
+
2377
+
2378
+ /**
2379
+ * Extend the column prototype's translate method
2380
+ */
2381
+ wrap(colProto, 'translate', function (proceed) {
2382
+
2383
+ var xAxis = this.xAxis,
2384
+ len = this.yAxis.len,
2385
+ center = xAxis.center,
2386
+ startAngleRad = xAxis.startAngleRad,
2387
+ renderer = this.chart.renderer,
2388
+ start,
2389
+ points,
2390
+ point,
2391
+ i;
2392
+
2393
+ this.preventPostTranslate = true;
2394
+
2395
+ // Run uber method
2396
+ proceed.call(this);
2397
+
2398
+ // Postprocess plot coordinates
2399
+ if (xAxis.isRadial) {
2400
+ points = this.points;
2401
+ i = points.length;
2402
+ while (i--) {
2403
+ point = points[i];
2404
+ start = point.barX + startAngleRad;
2405
+ point.shapeType = 'path';
2406
+ point.shapeArgs = {
2407
+ d: renderer.symbols.arc(
2408
+ center[0],
2409
+ center[1],
2410
+ len - point.plotY,
2411
+ null,
2412
+ {
2413
+ start: start,
2414
+ end: start + point.pointWidth,
2415
+ innerR: len - pick(point.yBottom, len)
2416
+ }
2417
+ )
2418
+ };
2419
+ this.toXY(point); // provide correct plotX, plotY for tooltip
2420
+ }
2421
+ }
2422
+ });
2423
+
2424
+
2425
+ /**
2426
+ * Align column data labels outside the columns. #1199.
2427
+ */
2428
+ wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
2429
+
2430
+ if (this.chart.polar) {
2431
+ var angle = point.rectPlotX / Math.PI * 180,
2432
+ align,
2433
+ verticalAlign;
2434
+
2435
+ // Align nicely outside the perimeter of the columns
2436
+ if (options.align === null) {
2437
+ if (angle > 20 && angle < 160) {
2438
+ align = 'left'; // right hemisphere
2439
+ } else if (angle > 200 && angle < 340) {
2440
+ align = 'right'; // left hemisphere
2441
+ } else {
2442
+ align = 'center'; // top or bottom
2443
+ }
2444
+ options.align = align;
2445
+ }
2446
+ if (options.verticalAlign === null) {
2447
+ if (angle < 45 || angle > 315) {
2448
+ verticalAlign = 'bottom'; // top part
2449
+ } else if (angle > 135 && angle < 225) {
2450
+ verticalAlign = 'top'; // bottom part
2451
+ } else {
2452
+ verticalAlign = 'middle'; // left or right
2453
+ }
2454
+ options.verticalAlign = verticalAlign;
2455
+ }
2456
+
2457
+ seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
2458
+ } else {
2459
+ proceed.call(this, point, dataLabel, options, alignTo, isNew);
2460
+ }
2461
+
2462
+ });
2463
+
2464
+ /**
2465
+ * Extend the mouse tracker to return the tooltip position index in terms of
2466
+ * degrees rather than pixels
2467
+ */
2468
+ wrap(pointerProto, 'getIndex', function (proceed, e) {
2469
+ var ret,
2470
+ chart = this.chart,
2471
+ center,
2472
+ x,
2473
+ y;
2474
+
2475
+ if (chart.polar) {
2476
+ center = chart.xAxis[0].center;
2477
+ x = e.chartX - center[0] - chart.plotLeft;
2478
+ y = e.chartY - center[1] - chart.plotTop;
2479
+
2480
+ ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
2481
+
2482
+ } else {
2483
+
2484
+ // Run uber method
2485
+ ret = proceed.call(this, e);
2486
+ }
2487
+ return ret;
2488
+ });
2489
+
2490
+ /**
2491
+ * Extend getCoordinates to prepare for polar axis values
2492
+ */
2493
+ wrap(pointerProto, 'getCoordinates', function (proceed, e) {
2494
+ var chart = this.chart,
2495
+ ret = {
2496
+ xAxis: [],
2497
+ yAxis: []
2498
+ };
2499
+
2500
+ if (chart.polar) {
2501
+
2502
+ each(chart.axes, function (axis) {
2503
+ var isXAxis = axis.isXAxis,
2504
+ center = axis.center,
2505
+ x = e.chartX - center[0] - chart.plotLeft,
2506
+ y = e.chartY - center[1] - chart.plotTop;
2507
+
2508
+ ret[isXAxis ? 'xAxis' : 'yAxis'].push({
2509
+ axis: axis,
2510
+ value: axis.translate(
2511
+ isXAxis ?
2512
+ Math.PI - Math.atan2(x, y) : // angle
2513
+ Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
2514
+ true
2515
+ )
2516
+ });
2517
+ });
2518
+
2519
+ } else {
2520
+ ret = proceed.call(this, e);
2521
+ }
2522
+
2523
+ return ret;
2524
+ });
2525
+ }(Highcharts));
js/highcharts/highcharts.js CHANGED
@@ -1,272 +1,272 @@
1
- /*
2
- Highcharts JS v3.0.2 (2013-06-05)
3
-
4
- (c) 2009-2013 Torstein Hønsi
5
-
6
- License: www.highcharts.com/license
7
- */
8
- (function(){function v(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function x(){var a,b=arguments.length,c={},d=function(a,b){var c,h;for(h in b)b.hasOwnProperty(h)&&(c=b[h],typeof a!=="object"&&(a={}),a[h]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&typeof c.nodeType!=="number"?d(a[h]||{},c):b[h]);return a};for(a=0;a<b;a++)c=d(c,arguments[a]);return c}function u(a,b){return parseInt(a,b||10)}function fa(a){return typeof a==="string"}function V(a){return typeof a===
9
- "object"}function Da(a){return Object.prototype.toString.call(a)==="[object Array]"}function Ea(a){return typeof a==="number"}function ka(a){return I.log(a)/I.LN10}function da(a){return I.pow(10,a)}function ga(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function r(a){return a!==y&&a!==null}function A(a,b,c){var d,e;if(fa(b))r(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(r(b)&&V(b))for(d in b)a.setAttribute(d,b[d]);return e}function ha(a){return Da(a)?
10
- a:[a]}function o(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function L(a,b){if(Fa&&b&&b.opacity!==y)b.filter="alpha(opacity="+b.opacity*100+")";v(a.style,b)}function U(a,b,c,d,e){a=z.createElement(a);b&&v(a,b);e&&L(a,{padding:0,border:S,margin:0});c&&L(a,c);d&&d.appendChild(a);return a}function ea(a,b){var c=function(){};c.prototype=new a;v(c.prototype,b);return c}function ua(a,b,c,d){var e=N.lang,f=b===-1?((a||0).toString().split(".")[1]||
11
- "").length:isNaN(b=Q(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(u(a=Q(+a||0).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+Q(a-c).toFixed(f).slice(2):"")}function va(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function zb(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments);a.unshift(d);return c.apply(this,a)}}function wa(a,b){for(var c="{",
12
- d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h<i;h++)e=e[g[h]];if(f.length)f=f.join(":"),g=/\.([0-9])/,h=N.lang,i=void 0,/f$/.test(f)?(i=(i=f.match(g))?i[1]:-1,e=ua(e,i,h.decimalPoint,f.indexOf(",")>-1?h.thousandsSep:"")):e=Ua(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function ib(a,b,c,d){var e,c=o(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=
13
- [1/c])));for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function Ab(a,b){var c=b||[[Bb,[1,2,5,10,20,25,50,100,200,500]],[jb,[1,2,5,10,15,30]],[Va,[1,2,5,10,15,30]],[Oa,[1,2,3,4,6,8,12]],[oa,[1,2]],[Wa,[1,2]],[Pa,[1,2,3,4,6]],[xa,null]],d=c[c.length-1],e=E[d[0]],f=d[1],g;for(g=0;g<c.length;g++)if(d=c[g],e=E[d[0]],f=d[1],c[g+1]&&a<=(e*f[f.length-1]+E[c[g+1][0]])/2)break;e===E[xa]&&a<5*e&&(f=[1,2,5]);e===E[xa]&&a<5*e&&(f=[1,2,5]);c=ib(a/e,f);return{unitRange:e,count:c,
14
- unitName:d[0]}}function Cb(a,b,c,d){var e=[],f={},g=N.global.useUTC,h,i=new Date(b),j=a.unitRange,k=a.count;if(r(b)){j>=E[jb]&&(i.setMilliseconds(0),i.setSeconds(j>=E[Va]?0:k*T(i.getSeconds()/k)));if(j>=E[Va])i[Db](j>=E[Oa]?0:k*T(i[kb]()/k));if(j>=E[Oa])i[Eb](j>=E[oa]?0:k*T(i[lb]()/k));if(j>=E[oa])i[mb](j>=E[Pa]?1:k*T(i[Qa]()/k));j>=E[Pa]&&(i[Fb](j>=E[xa]?0:k*T(i[Xa]()/k)),h=i[Ya]());j>=E[xa]&&(h-=h%k,i[Gb](h));if(j===E[Wa])i[mb](i[Qa]()-i[nb]()+o(d,1));b=1;h=i[Ya]();for(var d=i.getTime(),m=i[Xa](),
15
- l=i[Qa](),p=g?0:(864E5+i.getTimezoneOffset()*6E4)%864E5;d<c;)e.push(d),j===E[xa]?d=Za(h+b*k,0):j===E[Pa]?d=Za(h,m+b*k):!g&&(j===E[oa]||j===E[Wa])?d=Za(h,m,l+b*k*(j===E[oa]?1:7)):d+=j*k,b++;e.push(d);n(ob(e,function(a){return j<=E[Oa]&&a%E[oa]===p}),function(a){f[a]=oa})}e.info=v(a,{higherRanks:f,totalRange:j*k});return e}function Hb(){this.symbol=this.color=0}function Ib(a,b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}
16
- function Ga(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function pa(a){for(var b=a.length,c=a[0];b--;)a[b]>c&&(c=a[b]);return c}function Ha(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Ra(a){$a||($a=U(ya));a&&$a.appendChild(a);$a.innerHTML=""}function qa(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else O.console&&console.log(c)}function ia(a){return parseFloat(a.toPrecision(14))}function Ia(a,b){za=o(a,b.animation)}
17
- function Jb(){var a=N.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Za=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,o(c,1),o(g,0),o(h,0),o(i,0))).getTime()};kb=b+"Minutes";lb=b+"Hours";nb=b+"Day";Qa=b+"Date";Xa=b+"Month";Ya=b+"FullYear";Db=c+"Minutes";Eb=c+"Hours";mb=c+"Date";Fb=c+"Month";Gb=c+"FullYear"}function ra(){}function Ja(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function pb(a,b){this.axis=a;if(b)this.options=b,this.id=b.id}function Kb(a,
18
- b,c,d,e,f){var g=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.percent=f==="percent";this.alignOptions={align:b.align||(g?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(g?"middle":c?"bottom":"top"),y:o(b.y,g?4:c?14:-6),x:o(b.x,g?c?-6:6:0)};this.textAlign=b.textAlign||(g?c?"right":"left":"center")}function ab(){this.init.apply(this,arguments)}function qb(){this.init.apply(this,arguments)}function rb(a,b){this.init(a,b)}function sb(a,b){this.init(a,
19
- b)}function tb(){this.init.apply(this,arguments)}var y,z=document,O=window,I=Math,t=I.round,T=I.floor,ja=I.ceil,q=I.max,K=I.min,Q=I.abs,Y=I.cos,ca=I.sin,Ka=I.PI,bb=Ka*2/360,Aa=navigator.userAgent,Lb=O.opera,Fa=/msie/i.test(Aa)&&!Lb,cb=z.documentMode===8,db=/AppleWebKit/.test(Aa),eb=/Firefox/.test(Aa),Mb=/(Mobile|Android|Windows Phone)/.test(Aa),sa="http://www.w3.org/2000/svg",Z=!!z.createElementNS&&!!z.createElementNS(sa,"svg").createSVGRect,Sb=eb&&parseInt(Aa.split("Firefox/")[1],10)<4,$=!Z&&!Fa&&
20
- !!z.createElement("canvas").getContext,Sa,fb=z.documentElement.ontouchstart!==y,Nb={},ub=0,$a,N,Ua,za,vb,E,ta=function(){},Ba=[],ya="div",S="none",Ob="rgba(192,192,192,"+(Z?1.0E-4:0.002)+")",Bb="millisecond",jb="second",Va="minute",Oa="hour",oa="day",Wa="week",Pa="month",xa="year",Pb="stroke-width",Za,kb,lb,nb,Qa,Xa,Ya,Db,Eb,mb,Fb,Gb,aa={};O.Highcharts=O.Highcharts?qa(16,!0):{};Ua=function(a,b,c){if(!r(b)||isNaN(b))return"Invalid date";var a=o(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[lb](),g=d[nb](),
21
- h=d[Qa](),i=d[Xa](),j=d[Ya](),k=N.lang,m=k.weekdays,d=v({a:m[g].substr(0,3),A:m[g],d:va(h),e:h,b:k.shortMonths[i],B:k.months[i],m:va(i+1),y:j.toString().substr(2,2),Y:j,H:va(f),I:va(f%12||12),l:f%12||12,M:va(d[kb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:va(d.getSeconds()),L:va(t(b%1E3),3)},Highcharts.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]==="function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Hb.prototype={wrapColor:function(a){if(this.color>=
22
- a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};E=function(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}(Bb,1,jb,1E3,Va,6E4,Oa,36E5,oa,864E5,Wa,6048E5,Pa,26784E5,xa,31556952E3);vb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-
23
- 6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length<a;)d=[].concat(b).splice(b.length-f,f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(i));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};(function(a){O.HighchartsAdapter=O.HighchartsAdapter||a&&{init:function(b){var c=
24
- a.fx,d=c.step,e,f=a.Tween,g=f&&f.propHooks;e=a.cssHooks.opacity;a.extend(a.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});a.each(["cur","_default","width","height","opacity"],function(a,b){var e=d,k,m;b==="cur"?e=c.prototype:b==="_default"&&f&&(e=g[b],b="set");(k=e[b])&&(e[b]=function(c){c=a?c:this;m=c.elem;return m.attr?m.attr(c.prop,b==="cur"?y:c.now):k.apply(this,arguments)})});zb(e,"get",function(a,b,c){return b.attr?b.opacity||0:a.call(this,b,c)});e=function(a){var c=a.elem,
25
- d;if(!a.started)d=b.init(c,c.d,c.toD),a.start=d[0],a.end=d[1],a.started=!0;c.attr("d",b.step(a.start,a.end,a.pos,c.toD))};f?g.d={set:e}:d.d=e;this.each=Array.prototype.forEach?function(a,b){return Array.prototype.forEach.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],a[c],c,a)===!1)return c};a.fn.highcharts=function(){var a="Chart",b=arguments,c,d;fa(b[0])&&(a=b[0],b=Array.prototype.slice.call(b,1));c=b[0];if(c!==y)c.chart=c.chart||{},c.chart.renderTo=this[0],new Highcharts[a](c,
26
- b[1]),d=this;c===y&&(d=Ba[A(this[0],"data-highcharts-chart")]);return d}},getScript:a.getScript,inArray:a.inArray,adapterRun:function(b,c){return a(b)[c]()},grep:a.grep,map:function(a,c){for(var d=[],e=0,f=a.length;e<f;e++)d[e]=c.call(a[e],a[e],e,a);return d},offset:function(b){return a(b).offset()},addEvent:function(b,c,d){a(b).bind(c,d)},removeEvent:function(b,c,d){var e=z.removeEventListener?"removeEventListener":"detachEvent";z[e]&&b&&!b[e]&&(b[e]=function(){});a(b).unbind(c,d)},fireEvent:function(b,
27
- c,d,e){var f=a.Event(c),g="detached"+c,h;!Fa&&d&&(delete d.layerX,delete d.layerY);v(f,d);b[c]&&(b[g]=b[c],b[c]=null);a.each(["preventDefault","stopPropagation"],function(a,b){var c=f[b];f[b]=function(){try{c.call(f)}catch(a){b==="preventDefault"&&(h=!0)}}});a(b).trigger(f);b[g]&&(b[c]=b[g],b[g]=null);e&&!f.isDefaultPrevented()&&!h&&e(f)},washMouseEvent:function(a){var c=a.originalEvent||a;if(c.pageX===y)c.pageX=a.pageX,c.pageY=a.pageY;return c},animate:function(b,c,d){var e=a(b);if(!b.style)b.style=
28
- {};if(c.d)b.toD=c.d,c.d=1;e.stop();e.animate(c,d)},stop:function(b){a(b).stop()}}})(O.jQuery);var W=O.HighchartsAdapter,M=W||{};W&&W.init.call(W,vb);var gb=M.adapterRun,Tb=M.getScript,la=M.inArray,n=M.each,ob=M.grep,Ub=M.offset,La=M.map,J=M.addEvent,ba=M.removeEvent,D=M.fireEvent,Qb=M.washMouseEvent,wb=M.animate,Ta=M.stop,M={enabled:!0,align:"center",x:0,y:15,style:{color:"#666",cursor:"default",fontSize:"11px",lineHeight:"14px"}};N={colors:"#2f7ed8,#0d233a,#8bbc21,#910000,#1aadce,#492970,#f28f43,#77a1e5,#c42525,#a6c96a".split(","),
29
- symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),shortMonths:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",numericSymbols:"k,M,G,T,P,E".split(","),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0,
30
- canvasToolsURL:"http://code.highcharts.com/3.0.2/modules/canvas-tools.js",VMLRadialGradientURL:"http://code.highcharts.com/3.0.2/gfx/vml-radial-gradient.png"},chart:{borderColor:"#4572A7",borderRadius:5,defaultSeriesType:"line",ignoreHiddenSeries:!0,spacingTop:10,spacingRight:10,spacingBottom:15,spacingLeft:10,style:{fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif',fontSize:"12px"},backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0",resetZoomButton:{theme:{zIndex:20},
31
- position:{align:"right",x:-10,y:10}}},title:{text:"Chart title",align:"center",y:15,style:{color:"#274b6d",fontSize:"16px"}},subtitle:{text:"",align:"center",y:30,style:{color:"#4d759e"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},lineWidth:2,marker:{enabled:!0,lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{enabled:!0},select:{fillColor:"#FFFFFF",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:x(M,{enabled:!1,formatter:function(){return ua(this.y,
32
- -1)},verticalAlign:"bottom",y:0}),cropThreshold:300,pointRange:0,showInLegend:!0,states:{hover:{marker:{}},select:{marker:{}}},stickyTracking:!0}},labels:{style:{position:"absolute",color:"#3E576F"}},legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderWidth:1,borderColor:"#909090",borderRadius:5,navigation:{activeColor:"#274b6d",inactiveColor:"#CCC"},shadow:!1,itemStyle:{cursor:"pointer",color:"#274b6d",fontSize:"12px"},itemHoverStyle:{color:"#000"},
33
- itemHiddenStyle:{color:"#CCC"},itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},symbolWidth:16,symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold",position:"relative",top:"1em"},style:{position:"absolute",backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,animation:Z,backgroundColor:"rgba(255, 255, 255, .85)",borderWidth:1,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",
34
- second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},headerFormat:'<span style="font-size: 10px">{point.key}</span><br/>',pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',shadow:!0,snap:Mb?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",
35
- position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var X=N.plotOptions,W=X.line;Jb();var ma=function(a){var b=[],c,d;(function(a){a&&a.stops?d=La(a.stops,function(a){return ma(a[1])}):(c=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(a))?b=[u(c[1]),u(c[2]),u(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))?b=[u(c[1],16),u(c[2],16),u(c[3],
36
- 16),1]:(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(a))&&(b=[u(c[1]),u(c[2]),u(c[3]),1])})(a);return{get:function(c){var f;d?(f=x(a),f.stops=[].concat(f.stops),n(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})):f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)n(d,function(b){b.brighten(a)});else if(Ea(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=u(a*255),b[c]<0&&(b[c]=0),b[c]>255&&
37
- (b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};ra.prototype={init:function(a,b){this.element=b==="span"?U(b):z.createElementNS(sa,b);this.renderer=a;this.attrSetters={}},opacity:1,animate:function(a,b,c){b=o(b,za,!0);Ta(this);if(b){b=x(b);if(c)b.complete=c;wb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName.toLowerCase(),i=this.renderer,j,k=this.attrSetters,m=this.shadows,l,p,s=this;fa(a)&&r(b)&&(c=a,a={},a[c]=b);if(fa(a))c=
38
- a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),s=A(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&(s=parseFloat(s));else{for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==y&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text")for(e=0;e<g.childNodes.length;e++)f=g.childNodes[e],A(f,"x")===A(g,"x")&&A(f,"x",d);else if(this.rotation&&(c==="x"||c==="y"))p=!0;else if(c==="fill")d=i.color(d,g,c);else if(h===
39
- "circle"&&(c==="x"||c==="y"))c={x:"cx",y:"cy"}[c]||c;else if(h==="rect"&&c==="r")A(g,{rx:d,ry:d}),j=!0;else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="verticalAlign"||c==="scaleX"||c==="scaleY")j=p=!0;else if(c==="stroke")d=i.color(d,g,c);else if(c==="dashstyle")if(c="stroke-dasharray",d=d&&d.toLowerCase(),d==="solid")d=S;else{if(d){d=d.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash",
40
- "8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(e=d.length;e--;)d[e]=u(d[e])*a["stroke-width"];d=d.join(",")}}else if(c==="width")d=u(d);else if(c==="align")c="text-anchor",d={left:"start",center:"middle",right:"end"}[d];else if(c==="title")e=g.getElementsByTagName("title")[0],e||(e=z.createElementNS(sa,"title"),g.appendChild(e)),e.textContent=d;c==="strokeWidth"&&(c="stroke-width");if(c==="stroke-width"||c==="stroke"){this[c]=d;if(this.stroke&&this["stroke-width"])A(g,
41
- "stroke",this.stroke),A(g,"stroke-width",this["stroke-width"]),this.hasStroke=!0;else if(c==="stroke-width"&&d===0&&this.hasStroke)g.removeAttribute("stroke"),this.hasStroke=!1;j=!0}this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(c)&&(l||(this.symbolAttr(a),l=!0),j=!0);if(m&&/^(width|height|visibility|x|y|d|transform)$/.test(c))for(e=m.length;e--;)A(m[e],c,c==="height"?q(d-(m[e].cutHeight||0),0):d);if((c==="width"||c==="height")&&h==="rect"&&d<0)d=0;this[c]=d;c==="text"?
42
- (d!==this.textStr&&delete this.bBox,this.textStr=d,this.added&&i.buildText(this)):j||A(g,c,d)}p&&this.updateTransform()}return s},addClass:function(a){A(this.element,"class",A(this.element,"class")+" "+a);return this},symbolAttr:function(a){var b=this;n("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=o(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+
43
- a.id+")":S)},crisp:function(a,b,c,d,e){var f,g={},h={},i,a=a||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;i=t(a)%2/2;h.x=T(b||this.x||0)+i;h.y=T(c||this.y||0)+i;h.width=T((d||this.width||0)-2*i);h.height=T((e||this.height||0)-2*i);h.strokeWidth=a;for(f in h)this[f]!==h[f]&&(this[f]=g[f]=h[f]);return g},css:function(a){var b=this.element,c=a&&a.width&&b.nodeName.toLowerCase()==="text",d,e="",f=function(a,b){return"-"+b.toLowerCase()};if(a&&a.color)a.fill=a.color;this.styles=a=v(this.styles,
44
- a);$&&c&&delete a.width;if(Fa&&!Z)c&&delete a.width,L(this.element,a);else{for(d in a)e+=d.replace(/([A-Z])/g,f)+":"+a[d]+";";A(b,"style",e)}c&&this.added&&this.renderer.buildText(this);return this},on:function(a,b){if(fb&&a==="click")this.element.ontouchstart=function(a){a.preventDefault();b()};this.element["on"+a]=b;return this},setRadialReference:function(a){this.element.radialReference=a;return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=
45
- !0;this.updateTransform();return this},htmlCss:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=v(this.styles,a);L(this.element,a);return this},htmlGetBBox:function(){var a=this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position="absolute";b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b},htmlUpdateTransform:function(){if(this.added){var a=this.renderer,
46
- b=this.element,c=this.translateX||0,d=this.translateY||0,e=this.x||0,f=this.y||0,g=this.textAlign||"left",h={left:0,center:0.5,right:1}[g],i=g&&g!=="left",j=this.shadows;L(b,{marginLeft:c,marginTop:d});j&&n(j,function(a){L(a,{marginLeft:c+1,marginTop:d+1})});this.inverted&&n(b.childNodes,function(c){a.invertChild(c,b)});if(b.tagName==="SPAN"){var k,m,j=this.rotation,l,p=0,s=1,p=0,xb;l=u(this.textWidth);var B=this.xCorr||0,w=this.yCorr||0,G=[j,g,b.innerHTML,this.textWidth].join(",");k={};if(G!==this.cTT){if(r(j))a.isSVG?
47
- (B=Fa?"-ms-transform":db?"-webkit-transform":eb?"MozTransform":Lb?"-o-transform":"",k[B]=k.transform="rotate("+j+"deg)"):(p=j*bb,s=Y(p),p=ca(p),k.filter=j?["progid:DXImageTransform.Microsoft.Matrix(M11=",s,", M12=",-p,", M21=",p,", M22=",s,", sizingMethod='auto expand')"].join(""):S),L(b,k);k=o(this.elemWidth,b.offsetWidth);m=o(this.elemHeight,b.offsetHeight);if(k>l&&/[ \-]/.test(b.textContent||b.innerText))L(b,{width:l+"px",display:"block",whiteSpace:"normal"}),k=l;l=a.fontMetrics(b.style.fontSize).b;
48
- B=s<0&&-k;w=p<0&&-m;xb=s*p<0;B+=p*l*(xb?1-h:h);w-=s*l*(j?xb?h:1-h:1);i&&(B-=k*h*(s<0?-1:1),j&&(w-=m*h*(p<0?-1:1)),L(b,{textAlign:g}));this.xCorr=B;this.yCorr=w}L(b,{left:e+B+"px",top:f+w+"px"});if(db)m=b.offsetHeight;this.cTT=G}}else this.alignOnAdd=!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):
49
- f&&a.push("rotate("+f+" "+(this.x||0)+" "+(this.y||0)+")");(r(c)||r(d))&&a.push("scale("+o(c,1)+" "+o(d,1)+")");a.length&&A(this.element,"transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||fa(c))this.alignTo=d=c||"renderer",ga(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;
50
- c=o(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=t(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=t(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d=this.rotation;c=this.element;var e=this.styles,f=d*bb;if(!a){if(c.namespaceURI===
51
- sa||b.forExport){try{a=c.getBBox?v({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(g){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){b=a.width;c=a.height;if(Fa&&e&&e.fontSize==="11px"&&c.toPrecision(3)==="22.7")a.height=c=14;if(d)a.width=Q(c*ca(f))+Q(b*Y(f)),a.height=Q(c*Y(f))+Q(b*ca(f))}this.bBox=a}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;
52
- b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=A(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=u(g);if(c.handleZ)for(c=0;c<e.length;c++)if(a=e[c],b=A(a,"zIndex"),a!==f&&(u(b)>g||!r(g)&&r(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;D(this,"add");return this},safeRemoveChild:function(a){var b=
53
- a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d,e;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;Ta(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(e=0;e<a.stops.length;e++)a.stops[e]=a.stops[e].destroy();a.stops=null}a.safeRemoveChild(b);c&&n(c,function(b){a.safeRemoveChild(b)});a.alignTo&&ga(a.renderer.alignedObjects,a);for(d in a)delete a[d];return null},shadow:function(a,b,c){var d=[],e,f,g=this.element,h,i,j,k;if(a){i=
54
- o(a.width,3);j=(a.opacity||0.15)/i;k=this.parentInverted?"(-1,-1)":"("+o(a.offsetX,1)+", "+o(a.offsetY,1)+")";for(e=1;e<=i;e++){f=g.cloneNode(0);h=i*2+1-2*e;A(f,{isShadow:"true",stroke:a.color||"black","stroke-opacity":j*e,"stroke-width":h,transform:"translate"+k,fill:S});if(c)A(f,"height",q(A(f,"height")-h,0)),f.cutHeight=h;b?b.element.appendChild(f):g.parentNode.insertBefore(f,g);d.push(f)}this.shadows=d}return this}};var Ca=function(){this.init.apply(this,arguments)};Ca.prototype={Element:ra,init:function(a,
55
- b,c,d){var e=location,f;f=this.createElement("svg").attr({xmlns:sa,version:"1.1"});a.appendChild(f.element);this.isSVG=!0;this.box=f.element;this.boxWrapper=f;this.alignedObjects=[];this.url=(eb||db)&&z.getElementsByTagName("base").length?e.href.replace(/#.*?$/,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(z.createTextNode("Created with Highcharts 3.0.2"));this.defs=this.createElement("defs").add();this.forExport=d;this.gradients={};this.setSize(b,
56
- c,!1);var g;if(eb&&a.getBoundingClientRect)this.subPixelFix=b=function(){L(a,{left:0,top:0});g=a.getBoundingClientRect();L(a,{left:ja(g.left)-g.left+"px",top:ja(g.top)-g.top+"px"})},b(),J(O,"resize",b)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Ha(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&ba(O,"resize",this.subPixelFix);return this.alignedObjects=
57
- null},createElement:function(a){var b=new this.Element;b.init(this,a);return b},draw:function(){},buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=o(a.textStr,"").toString().replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g),f=b.childNodes,g=/style="([^"]+)"/,h=/href="([^"]+)"/,i=A(b,"x"),j=a.styles,k=j&&j.width&&u(j.width),m=j&&j.lineHeight,
58
- l=f.length;l--;)b.removeChild(f[l]);k&&!a.added&&this.box.appendChild(b);e[e.length-1]===""&&e.pop();n(e,function(e,f){var l,o=0,e=e.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");l=e.split("|||");n(l,function(e){if(e!==""||l.length===1){var p={},n=z.createElementNS(sa,"tspan"),q;g.test(e)&&(q=e.match(g)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),A(n,"style",q));h.test(e)&&!d&&(A(n,"onclick",'location.href="'+e.match(h)[1]+'"'),L(n,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,
59
- "")||" ").replace(/&lt;/g,"<").replace(/&gt;/g,">");n.appendChild(z.createTextNode(e));o?p.dx=0:p.x=i;A(n,p);!o&&f&&(!Z&&d&&L(n,{display:"block"}),A(n,"dy",m||c.fontMetrics(/px$/.test(n.style.fontSize)?n.style.fontSize:j.fontSize).h,db&&n.offsetHeight));b.appendChild(n);o++;if(k)for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),r,t=[];e.length||t.length;)delete a.bBox,r=a.getBBox().width,p=r>k,!p||e.length===1?(e=t,t=[],e.length&&(n=z.createElementNS(sa,"tspan"),A(n,{dy:m||16,x:i}),q&&A(n,"style",
60
- q),b.appendChild(n),r>k&&(k=r))):(n.removeChild(n.firstChild),t.unshift(e.pop())),e.length&&n.appendChild(z.createTextNode(e.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c,null,null,null,null,null,"button"),i=0,j,k,m,l,p,a={x1:0,y1:0,x2:0,y2:1},e=x({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);m=e.style;delete e.style;f=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,
61
- "#FFF"],[1,"#ACF"]]}},f);l=f.style;delete f.style;g=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);p=g.style;delete g.style;J(h.element,"mouseenter",function(){h.attr(f).css(l)});J(h.element,"mouseleave",function(){j=[e,f,g][i];k=[m,l,p][i];h.attr(j).css(k)});h.setState=function(a){(i=a)?a===2&&h.attr(g).css(p):h.attr(e).css(m)};return h.on("click",function(){d.call(h)}).attr(e).css(v({cursor:"default"},m))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=t(a[1])-b%2/
62
- 2);a[2]===a[5]&&(a[2]=a[5]=t(a[2])+b%2/2);return a},path:function(a){var b={fill:S};Da(a)?b.d=a:V(a)&&v(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=V(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(V(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e=V(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:S});return e.attr(V(a)?
63
- a:e.crisp(f,a,b,q(c,0),q(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[o(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return r(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:S};arguments.length>1&&v(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink",
64
- "href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(t(b),t(c),d,e,f),i=/^url\((.*?)\)$/,j,k;if(h)g=this.path(h),v(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&v(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(t((d-b[0])/2),t((e-b[1])/2)))},j=a.match(i)[1],a=Nb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),U("img",{onload:function(){k(g,
65
- Nb[j]=[this.width,this.height])},src:j}));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,
66
- e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=Y(f),j=ca(f),k=Y(g),g=ca(g),e=e.end-f<Ka?0:1;return["M",a+c*i,b+c*j,"A",c,c,0,e,1,a+c*k,b+c*g,h?"M":"L",a+d*k,b+d*g,"A",d,d,0,e,0,a+d*i,b+d*j,h?"":"Z"]}},clipRect:function(a,b,c,d){var e="highcharts-"+ub++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},color:function(a,b,c){var d=this,e,f=/^rgba/,g,h,i,j,k,m,l,p=[];a&&a.linearGradient?g="linearGradient":a&&a.radialGradient&&
67
- (g="radialGradient");if(g){c=a[g];h=d.gradients;j=a.stops;b=b.radialReference;Da(c)&&(a[g]=c={x1:c[0],y1:c[1],x2:c[2],y2:c[3],gradientUnits:"userSpaceOnUse"});g==="radialGradient"&&b&&!r(c.gradientUnits)&&(c=x(c,{cx:b[0]-b[2]/2+c.cx*b[2],cy:b[1]-b[2]/2+c.cy*b[2],r:c.r*b[2],gradientUnits:"userSpaceOnUse"}));for(l in c)l!=="id"&&p.push(l,c[l]);for(l in j)p.push(j[l]);p=p.join(",");h[p]?a=h[p].id:(c.id=a="highcharts-"+ub++,h[p]=i=d.createElement(g).attr(c).add(d.defs),i.stops=[],n(j,function(a){f.test(a[1])?
68
- (e=ma(a[1]),k=e.get("rgb"),m=e.get("a")):(k=a[1],m=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":m}).add(i);i.stops.push(a)}));return"url("+d.url+"#"+a+")"}else return f.test(a)?(e=ma(a),A(b,c+"-opacity",e.get("a")),e.get("rgb")):(b.removeAttribute(c+"-opacity"),a)},text:function(a,b,c,d){var e=N.chart.style,f=$||!Z&&this.forExport;if(d&&!this.forExport)return this.html(a,b,c);b=t(o(b,0));c=t(o(c,0));a=this.createElement("text").attr({x:b,y:c,text:a}).css({fontFamily:e.fontFamily,
69
- fontSize:e.fontSize});f&&a.css({position:"absolute"});a.x=b;a.y=c;return a},html:function(a,b,c){var d=N.chart.style,e=this.createElement("span"),f=e.attrSetters,g=e.element,h=e.renderer;f.text=function(a){a!==g.innerHTML&&delete this.bBox;g.innerHTML=a;return!1};f.x=f.y=f.align=function(a,b){b==="align"&&(b="textAlign");e[b]=a;e.htmlUpdateTransform();return!1};e.attr({text:a,x:t(b),y:t(c)}).css({position:"absolute",whiteSpace:"nowrap",fontFamily:d.fontFamily,fontSize:d.fontSize});e.css=e.htmlCss;
70
- if(h.isSVG)e.add=function(a){var b,c=h.box.parentNode,d=[];if(a){if(b=a.div,!b){for(;a;)d.push(a),a=a.parentGroup;n(d.reverse(),function(a){var d;b=a.div=a.div||U(ya,{className:A(a.element,"class")},{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;v(a.attrSetters,{translateX:function(a){d.left=a+"px"},translateY:function(a){d.top=a+"px"},visibility:function(a,b){d[b]=a}})})}}else b=c;b.appendChild(g);e.added=!0;e.alignOnAdd&&e.htmlUpdateTransform();return e};
71
- return e},fontMetrics:function(a){var a=u(a||11),a=a<24?a+4:t(a*1.2),b=t(a*0.8);return{h:a,b:b}},label:function(a,b,c,d,e,f,g,h,i){function j(){var a,b;a=o.element.style;w=(Ma===void 0||yb===void 0||s.styles.textAlign)&&o.getBBox();s.width=(Ma||w.width||0)+2*q+hb;s.height=(yb||w.height||0)+2*q;A=q+p.fontMetrics(a&&a.fontSize).b;if(z){if(!B)a=t(-G*q),b=h?-A:0,s.box=B=d?p.symbol(d,a,b,s.width,s.height):p.rect(a,b,s.width,s.height,0,u[Pb]),B.add(s);B.isImg||B.attr(x({width:s.width,height:s.height},u));
72
- u=null}}function k(){var a=s.styles,a=a&&a.textAlign,b=hb+q*(1-G),c;c=h?0:A;if(r(Ma)&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(Ma-w.width);(b!==o.x||c!==o.y)&&o.attr({x:b,y:c});o.x=b;o.y=c}function m(a,b){B?B.attr(a,b):u[a]=b}function l(){o.add(s);s.attr({text:a,x:b,y:c});B&&r(e)&&s.attr({anchorX:e,anchorY:f})}var p=this,s=p.g(i),o=p.text("",0,0,g).attr({zIndex:1}),B,w,G=0,q=3,hb=0,Ma,yb,P,H,C=0,u={},A,g=s.attrSetters,z;J(s,"add",l);g.width=function(a){Ma=a;return!1};g.height=function(a){yb=
73
- a;return!1};g.padding=function(a){r(a)&&a!==q&&(q=a,k());return!1};g.paddingLeft=function(a){r(a)&&a!==hb&&(hb=a,k());return!1};g.align=function(a){G={left:0,center:0.5,right:1}[a];return!1};g.text=function(a,b){o.attr(b,a);j();k();return!1};g[Pb]=function(a,b){z=!0;C=a%2/2;m(b,a);return!1};g.stroke=g.fill=g.r=function(a,b){b==="fill"&&(z=!0);m(b,a);return!1};g.anchorX=function(a,b){e=a;m(b,a+C-P);return!1};g.anchorY=function(a,b){f=a;m(b,a-H);return!1};g.x=function(a){s.x=a;a-=G*((Ma||w.width)+q);
74
- P=t(a);s.attr("translateX",P);return!1};g.y=function(a){H=s.y=t(a);s.attr("translateY",H);return!1};var E=s.css;return v(s,{css:function(a){if(a){var b={},a=x(a);n("fontSize,fontWeight,fontFamily,color,lineHeight,width,textDecoration".split(","),function(c){a[c]!==y&&(b[c]=a[c],delete a[c])});o.css(b)}return E.call(s,a)},getBBox:function(){return{width:w.width+2*q,height:w.height+2*q,x:w.x-q,y:w.y-q}},shadow:function(a){B&&B.shadow(a);return s},destroy:function(){ba(s,"add",l);ba(s.element,"mouseenter");
75
- ba(s.element,"mouseleave");o&&(o=o.destroy());B&&(B=B.destroy());ra.prototype.destroy.call(s);s=p=j=k=m=l=null}})}};Sa=Ca;var F;if(!Z&&!$){Highcharts.VMLElement=F={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",";"],e=b===ya;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=U(c);this.renderer=a;this.attrSetters=
76
- {}},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();D(this,"add");return this},updateTransform:ra.prototype.htmlUpdateTransform,attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,m=this.shadows,l,p=this.attrSetters,s=this;fa(a)&&r(b)&&(c=a,a={},a[c]=b);if(fa(a))c=a,s=c==="strokeWidth"||
77
- c==="stroke-width"?this.strokeweight:this[c];else for(c in a)if(d=a[c],l=!1,e=p[c]&&p[c].call(this,d,c),e!==!1&&d!==null){e!==y&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k||(this.symbolAttr(a),k=!0),l=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;l=[];for(var o;e--;)if(Ea(d[e]))l[e]=t(d[e]*10)-5;else if(d[e]==="Z")l[e]="x";else if(l[e]=d[e],d.isArc&&(d[e]==="wa"||d[e]==="at"))o=d[e]==="wa"?1:-1,l[e+5]===l[e+7]&&(l[e+7]-=o),l[e+6]===l[e+8]&&(l[e+8]-=
78
- o);d=l.join(" ")||"x";f.path=d;if(m)for(e=m.length;e--;)m[e].path=m[e].cutOff?this.cutOffPath(d,m[e].cutOff):d;l=!0}else if(c==="visibility"){if(m)for(e=m.length;e--;)m[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,cb||(g[c]=d?"visible":"hidden"),c="top");g[c]=d;l=!0}else if(c==="zIndex")d&&(g[c]=d),l=!0;else if(la(c,["x","y","width","height"])!==-1)this[c]=d,c==="x"||c==="y"?c={x:"left",y:"top"}[c]:d=q(0,d),this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,l=!0;else if(c==="class"&&
79
- h==="DIV")f.className=d;else if(c==="stroke")d=i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ea(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||U(i.prepVML(["<stroke/>"]),null,null,f))[c]=d||"solid",this.dashstyle=d,l=!0;else if(c==="fill")if(h==="SPAN")g.color=d;else{if(h!=="IMG")f.filled=d!==S?!0:!1,d=i.color(d,f,c,this),c="fillcolor"}else if(c==="opacity")l=!0;else if(h==="shape"&&c==="rotation")this[c]=
80
- d,f.style.left=-t(ca(d*bb)+1)+"px",f.style.top=t(Y(d*bb))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),l=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,l=!0;l||(cb?f[c]=d:A(f,c,d))}return s},clip:function(a){var b=this,c;a?(c=a.members,ga(c,b),c.push(b),b.destroyClip=function(){ga(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:cb?"inherit":"rect(auto)"});return b.css(a)},css:ra.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&
81
- Ra(a)},destroy:function(){this.destroyClip&&this.destroyClip();return ra.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=O.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=u(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,m,l,p,s;k&&typeof k.value!=="string"&&(k="x");l=k;if(a){p=o(a.width,3);s=(a.opacity||
82
- 0.15)/p;for(e=1;e<=3;e++){m=p*2+1-2*e;c&&(l=this.cutOffPath(k.value,m+0.5));j=['<shape isShadow="true" strokeweight="',m,'" filled="false" path="',l,'" coordsize="10 10" style="',f.style.cssText,'" />'];h=U(g.prepVML(j),null,{left:u(i.left)+o(a.offsetX,1),top:u(i.top)+o(a.offsetY,1)});if(c)h.cutOff=m+1;j=['<stroke color="',a.color||"black",'" opacity="',s*e,'"/>'];U(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}};F=ea(ra,F);
83
- var na={Element:F,isIE8:Aa.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(ya);e=d.element;e.style.position="relative";a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!z.namespaces.hcv)z.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),z.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},isHidden:function(){return!this.box.offsetWidth},
84
- clipRect:function(a,b,c,d){var e=this.createElement(),f=V(a);return v(e,{members:[],left:f?a.x:a,top:f?a.y:b,width:f?a.width:c,height:f?a.height:d,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+t(a?e:d)+"px,"+t(a?f:b)+"px,"+t(a?b:f)+"px,"+t(a?d:e)+"px)"};!a&&cb&&c==="DIV"&&v(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},
85
- color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=S;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,m,l=a.linearGradient||a.radialGradient,p,s,o,B,w,q="",a=a.stops,r,t=[],y=function(){h=['<fill colors="'+t.join(",")+'" opacity="',o,'" o:opacity2="',s,'" type="',i,'" ',q,'focus="100%" method="any" />'];U(e.prepVML(h),null,null,b)};p=a[0];r=a[a.length-1];p[0]>0&&a.unshift([0,p[1]]);r[0]<1&&a.push([1,r[1]]);n(a,function(a,b){g.test(a[1])?(f=ma(a[1]),k=f.get("rgb"),
86
- m=f.get("a")):(k=a[1],m=1);t.push(a[0]*100+"% "+k);b?(o=m,B=k):(s=m,w=k)});if(c==="fill")if(i==="gradient")c=l.x1||l[0]||0,a=l.y1||l[1]||0,p=l.x2||l[2]||0,l=l.y2||l[3]||0,q='angle="'+(90-I.atan((l-a)/(p-c))*180/Ka)+'"',y();else{var j=l.r,v=j*2,P=j*2,H=l.cx,C=l.cy,x=b.radialReference,u,j=function(){x&&(u=d.getBBox(),H+=(x[0]-u.x)/u.width-0.5,C+=(x[1]-u.y)/u.height-0.5,v*=x[2]/u.width,P*=x[2]/u.height);q='src="'+N.global.VMLRadialGradientURL+'" size="'+v+","+P+'" origin="0.5,0.5" position="'+H+","+
87
- C+'" color2="'+w+'" ';y()};d.added?j():J(d,"add",j);j=B}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=ma(a),h=["<",c,' opacity="',f.get("a"),'"/>'],U(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):
88
- a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:Ca.prototype.html,path:function(a){var b={coordsize:"10 10"};Da(a)?b.d=a:V(a)&&v(b,a);return this.createElement("shape").attr(b)},circle:function(a,b,c){var d=this.symbol("circle");if(V(a))c=a.r,b=a.y,a=a.x;d.isCircle=!0;return d.attr({x:a,y:b,width:2*c,height:2*c})},g:function(a){var b;a&&(b={className:"highcharts-"+a,"class":"highcharts-"+a});return this.createElement(ya).attr(b)},
89
- image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.attr({x:b,y:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(V(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,q(c,0),q(d,0)))},invertChild:function(a,b){var c=b.style;L(a,{flip:"x",left:u(c.width)-1,top:u(c.height)-1,rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=Y(f),i=ca(f),j=Y(g),
90
- k=ca(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){var f=a+c,g=b+d,h;!r(e)||!e.r?f=Ca.prototype.symbols.square.apply(0,arguments):(h=K(e.r,c,d),f=["M",a+h,b,"L",f-h,b,"wa",f-2*h,b,f,b+2*h,f-h,b,f,b+h,"L",f,g-h,"wa",f-2*h,g-2*
91
- h,f,g,f,g-h,f-h,g,"L",a+h,g,"wa",a,g-2*h,a+2*h,g,a+h,g,a,g-h,"L",a,b+h,"wa",a,b,a+2*h,b+2*h,a,b+h,a+h,b,"x","e"]);return f}}};Highcharts.VMLRenderer=F=function(){this.init.apply(this,arguments)};F.prototype=x(Ca.prototype,na);Sa=F}var Rb;if($)Highcharts.CanVGRenderer=F=function(){sa="http://www.w3.org/1999/xhtml"},F.prototype.symbols={},Rb=function(){function a(){var a=b.length,d;for(d=0;d<a;d++)b[d]();b=[]}var b=[];return{push:function(c,d){b.length===0&&Tb(d,a);b.push(c)}}}(),Sa=F;Ja.prototype=
92
- {addLabel:function(){var a=this.axis,b=a.options,c=a.chart,d=a.horiz,e=a.categories,f=a.series[0]&&a.series[0].names,g=this.pos,h=b.labels,i=a.tickPositions,d=d&&e&&!h.step&&!h.staggerLines&&!h.rotation&&c.plotWidth/i.length||!d&&(c.optionsMarginLeft||c.plotWidth/2),j=g===i[0],k=g===i[i.length-1],f=e?o(e[g],f&&f[g],g):g,e=this.label,i=i.info,m;a.isDatetimeAxis&&i&&(m=b.dateTimeLabelFormats[i.higherRanks[g]||i.unitName]);this.isFirst=j;this.isLast=k;b=a.labelFormatter.call({axis:a,chart:c,isFirst:j,
93
- isLast:k,dateTimeLabelFormat:m,value:a.isLog?ia(da(f)):f});g=d&&{width:q(1,t(d-2*(h.padding||10)))+"px"};g=v(g,h.style);if(r(e))e&&e.attr({text:b}).css(g);else{d={align:h.align};if(Ea(h.rotation))d.rotation=h.rotation;this.label=r(b)&&h.enabled?c.renderer.text(b,0,0,h.useHTML).attr(d).css(g).add(a.labelGroup):null}},getLabelSize:function(){var a=this.label,b=this.axis;return a?(this.labelBBox=a.getBBox())[b.horiz?"height":"width"]:0},getLabelSides:function(){var a=this.axis.options.labels,b=this.labelBBox.width,
94
- a=b*{left:0,center:0.5,right:1}[a.align]-a.x;return[-a,b-a]},handleOverflow:function(a,b){var c=!0,d=this.axis,e=d.chart,f=this.isFirst,g=this.isLast,h=b.x,i=d.reversed,j=d.tickPositions;if(f||g){var k=this.getLabelSides(),m=k[0],k=k[1],e=e.plotLeft,l=e+d.len,j=(d=d.ticks[j[a+(f?1:-1)]])&&d.label.xy&&d.label.xy.x+d.getLabelSides()[f?0:1];f&&!i||g&&i?h+m<e&&(h=e-m,d&&h+k>j&&(c=!1)):h+k>l&&(h=l-k,d&&h+m<j&&(c=!1));b.x=h}return c},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||
95
- f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,j=i.transA,k=i.reversed,i=i.staggerLines,a=a+e.x-(f&&d?f*j*(k?-1:1):0),b=b+e.y-(f&&!d?f*j*(k?1:-1):0);r(e.y)||(b+=u(c.styles.lineHeight)*0.9-c.getBBox().height/2);i&&(b+=g/(h||1)%i*16);return{x:a,y:b}},getMarkPath:function(a,
96
- b,c,d,e,f){return f.crispLine(["M",a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,j=this.pos,k=e.labels,m=this.gridLine,l=h?h+"Grid":"grid",p=h?h+"Tick":"tick",s=e[l+"LineWidth"],n=e[l+"LineColor"],B=e[l+"LineDashStyle"],w=e[p+"Length"],l=e[p+"Width"]||0,q=e[p+"Color"],r=e[p+"Position"],p=this.mark,t=k.step,v=!0,u=d.tickmarkOffset,P=this.getPosition(g,j,u,b),H=P.x,P=P.y,C=g&&H===d.pos||!g&&P===d.pos+d.len?
97
- -1:1,x=d.staggerLines;this.isActive=!0;if(s){j=d.getPlotLinePath(j+u,s*C,b,!0);if(m===y){m={stroke:n,"stroke-width":s};if(B)m.dashstyle=B;if(!h)m.zIndex=1;if(b)m.opacity=0;this.gridLine=m=s?f.path(j).attr(m).add(d.gridGroup):null}if(!b&&m&&j)m[this.isNew?"attr":"animate"]({d:j,opacity:c})}if(l&&w)r==="inside"&&(w=-w),d.opposite&&(w=-w),b=this.getMarkPath(H,P,w,l*C,g,f),p?p.animate({d:b,opacity:c}):this.mark=f.path(b).attr({stroke:q,"stroke-width":l,opacity:c}).add(d.axisGroup);if(i&&!isNaN(H))i.xy=
98
- P=this.getLabelPosition(H,P,i,g,k,u,a,t),this.isFirst&&!o(e.showFirstLabel,1)||this.isLast&&!o(e.showLastLabel,1)?v=!1:!x&&g&&k.overflow==="justify"&&!this.handleOverflow(a,P)&&(v=!1),t&&a%t&&(v=!1),v&&!isNaN(P.y)?(P.opacity=c,i[this.isNew?"attr":"animate"](P),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Ha(this,this.axis)}};pb.prototype={render:function(){var a=this,b=a.axis,c=b.horiz,d=(b.pointRange||0)/2,e=a.options,f=e.label,g=a.label,h=e.width,i=e.to,j=e.from,k=r(j)&&r(i),m=e.value,l=
99
- e.dashStyle,p=a.svgElem,s=[],n,B=e.color,w=e.zIndex,G=e.events,t=b.chart.renderer;b.isLog&&(j=ka(j),i=ka(i),m=ka(m));if(h){if(s=b.getPlotLinePath(m,h),d={stroke:B,"stroke-width":h},l)d.dashstyle=l}else if(k){if(j=q(j,b.min-d),i=K(i,b.max+d),s=b.getPlotBandPath(j,i,e),d={fill:B},e.borderWidth)d.stroke=e.borderColor,d["stroke-width"]=e.borderWidth}else return;if(r(w))d.zIndex=w;if(p)s?p.animate({d:s},null,p.onGetPath):(p.hide(),p.onGetPath=function(){p.show()});else if(s&&s.length&&(a.svgElem=p=t.path(s).attr(d).add(),
100
- G))for(n in e=function(b){p.on(b,function(c){G[b].apply(a,[c])})},G)e(n);if(f&&r(f.text)&&s&&s.length&&b.width>0&&b.height>0){f=x({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=t.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:w}).css(f.style).add();b=[s[1],s[4],o(s[6],s[1])];s=[s[2],s[5],o(s[7],s[2])];c=Ga(b);k=Ga(s);g.align(f,!1,{x:c,y:k,width:pa(b)-c,height:pa(s)-k});g.show()}else g&&g.hide();
101
- return a},destroy:function(){ga(this.axis.plotLinesAndBands,this);Ha(this,this.axis)}};Kb.prototype={destroy:function(){Ha(this,this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options,c=b.format,c=c?wa(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,0,0,b.useHTML).css(b.style).attr({align:this.textAlign,rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=
102
- this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.percent?100:this.total,0,0,0,1),c=c.translate(0),c=Q(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e?c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e.attr({visibility:this.options.crop===!1||d.isInsidePlot(f.x,f.y)?Z?"inherit":"visible":"hidden"})}};ab.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",
103
- minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:M,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#4d759e",
104
- fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return ua(this.total,-1)},style:M.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}},
105
- defaultBottomAxisOptions:{labels:{align:"center",x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x":"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions=
106
- b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=r(d.linkedTo);this.tickmarkOffset=this.categories&&d.tickmarkPlacement==="between"?0.5:0;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;
107
- this.stacks={};this._stacksTouched=0;this.min=this.max=null;var f,d=this.options.events;la(this,a.axes)===-1&&(a.axes.push(this),a[c?"xAxis":"yAxis"].push(this));this.series=this.series||[];if(a.inverted&&c&&this.reversed===y)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)J(this,f,d[f]);if(this.isLog)this.val2lin=ka,this.lin2val=da},setOptions:function(a){this.options=x(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,
108
- this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],x(N[this.isXAxis?"xAxis":"yAxis"],a))},update:function(a,b){var c=this.chart,a=c.options[this.xOrY+"Axis"][this.options.index]=x(this.userOptions,a);this.destroy();this._addedPlotLB=!1;this.init(c,a);c.isDirtyBox=!0;o(b,!0)&&c.redraw()},remove:function(a){var b=this.chart,c=this.xOrY+"Axis";n(this.series,function(a){a.remove(!1)});ga(b.axes,this);ga(b[c],this);b.options[c].splice(this.options.index,
109
- 1);n(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;o(a,!0)&&b.redraw()},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=N.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=wa(h,this);else if(c)g=b;else if(d)g=Ua(d,b);else if(f&&a>=1E3)for(;f--&&g===y;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=ua(b/c,-1)+e[f]);g===y&&(g=b>=1E3?ua(b,0):ua(b,-1));return g},getSeriesExtremes:function(){var a=
110
- this,b=a.chart,c=a.stacks,d=[],e=[],f=a._stacksTouched+=1,g,h;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var j=g.options,k,m,l,p,s,n,B,w,G,t=j.threshold,v,u=[],x=0;a.hasVisibleSeries=!0;if(a.isLog&&t<=0)t=j.threshold=null;if(a.isXAxis){if(j=g.xData,j.length)a.dataMin=K(o(a.dataMin,j[0]),Ga(j)),a.dataMax=q(o(a.dataMax,j[0]),pa(j))}else{var P,H,C,A=g.cropped,z=g.xAxis.getExtremes(),E=!!g.modifyValue;k=j.stacking;a.usePercentage=
111
- k==="percent";if(k)s=j.stack,p=g.type+o(s,""),n="-"+p,g.stackKey=p,m=d[p]||[],d[p]=m,l=e[n]||[],e[n]=l;if(a.usePercentage)a.dataMin=0,a.dataMax=99;j=g.processedXData;B=g.processedYData;v=B.length;for(h=0;h<v;h++){w=j[h];G=B[h];if(k)H=(P=G<t)?l:m,C=P?n:p,r(H[w])?(H[w]=ia(H[w]+G),G=[G,H[w]]):H[w]=G,c[C]||(c[C]={}),c[C][w]||(c[C][w]=new Kb(a,a.options.stackLabels,P,w,s,k)),c[C][w].setTotal(H[w]),c[C][w].touched=f;if(G!==null&&G!==y&&(!a.isLog||G.length||G>0))if(E&&(G=g.modifyValue(G)),g.getExtremesFromAll||
112
- A||(j[h+1]||w)>=z.min&&(j[h-1]||w)<=z.max)if(w=G.length)for(;w--;)G[w]!==null&&(u[x++]=G[w]);else u[x++]=G}if(!a.usePercentage&&u.length)g.dataMin=k=Ga(u),g.dataMax=g=pa(u),a.dataMin=K(o(a.dataMin,k),k),a.dataMax=q(o(a.dataMax,g),g);if(r(t))if(a.dataMin>=t)a.dataMin=t,a.ignoreMinPadding=!0;else if(a.dataMax<t)a.dataMax=t,a.ignoreMaxPadding=!0}}});for(g in c)for(h in c[g])c[g][h].touched<f&&(c[g][h].destroy(),delete c[g][h])},translate:function(a,b,c,d,e,f){var g=this.len,h=1,i=0,j=d?this.oldTransA:
113
- this.transA,d=d?this.oldMin:this.min,k=this.minPixelPadding,e=(this.options.ordinal||this.isLog&&e)&&this.lin2val;if(!j)j=this.transA;c&&(h*=-1,i=g);this.reversed&&(h*=-1,i-=h*g);b?(a=a*h+i,a-=k,a=a/j+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),a=h*(a-d)*j+i+h*k+(f?j*this.pointRange/2:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,
114
- b,c,d){var e=this.chart,f=this.left,g=this.top,h,i,j,a=this.translate(a,null,null,c),k=c&&e.oldChartHeight||e.chartHeight,m=c&&e.oldChartWidth||e.chartWidth,l;h=this.transB;c=i=t(a+h);h=j=t(k-a-h);if(isNaN(a))l=!0;else if(this.horiz){if(h=g,j=k-this.bottom,c<f||c>f+this.width)l=!0}else if(c=f,i=m-this.right,h<g||h>g+this.height)l=!0;return l&&!d?null:e.renderer.crispLine(["M",c,h,"L",i,j],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],
115
- c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d,b=ia(T(b/a)*a),c=ia(ja(c/a)*a),e=[];b<=c;){e.push(b);b=ia(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=t(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=T(b),h,i,j,k,m,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];f<c+1&&!m;f++){i=e.length;for(h=0;h<i&&!m;h++)j=ka(da(f)*e[h]),j>b&&(!d||
116
- k<=c)&&g.push(k),k>c&&(m=!0),k=j}else if(b=da(b),c=da(c),a=e[d?"minorTickInterval":"tickInterval"],a=o(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=ib(a,null,I.pow(10,T(I.log(a)/I.LN10))),g=La(this.getLinearTickPositions(a,b,c),ka),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length;
117
- for(a=1;a<e;a++)d=d.concat(this.getLogTickPositions(c,b[a-1],b[a],!0))}else if(this.isDatetimeAxis&&a.minorTickInterval==="auto")d=d.concat(Cb(Ab(c),this.min,this.max,a.startOfWeek)),d[0]<this.min&&d.shift();else for(b=this.min+(b[0]-this.min)%c;b<=this.max;b+=c)d.push(b);return d},adjustForMinRange:function(){var a=this.options,b=this.min,c=this.max,d,e=this.dataMax-this.dataMin>=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===y&&!this.isLog)r(a.min)||r(a.max)?this.minRange=null:(n(this.series,
118
- function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===y||h<f)f=h}),this.minRange=K(f*5,this.dataMax-this.dataMin));if(c-b<this.minRange){var k=this.minRange;d=(k-c+b)/2;d=[b-d,o(a.min,b-d)];if(e)d[2]=this.dataMin;b=pa(d);c=[b+k,o(a.max,b+k)];if(e)c[2]=this.dataMax;c=Ga(c);c-b<k&&(d[0]=c-k,d[1]=o(a.min,c-k),b=pa(d))}this.min=b;this.max=c},setAxisTranslation:function(a){var b=this.max-this.min,c=0,d,e=0,f=0,g=this.linkedParent,h=this.transA;if(this.isXAxis)g?(e=g.minPointOffset,
119
- f=g.pointRangePadding):n(this.series,function(a){var g=a.pointRange,h=a.options.pointPlacement,m=a.closestPointRange;g>b&&(g=0);c=q(c,g);e=q(e,h?0:g/2);f=q(f,h==="on"?0:g);!a.noSharedTooltip&&r(m)&&(d=r(d)?K(d,m):m)}),g=this.ordinalSlope&&d?this.ordinalSlope/d:1,this.minPointOffset=e*=g,this.pointRangePadding=f*=g,this.pointRange=K(c,b),this.closestPointRange=d;if(a)this.oldTransA=h;this.translationSlope=this.transA=h=this.len/(b+f||1);this.transB=this.horiz?this.left:this.bottom;this.minPixelPadding=
120
- h*e},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,k=d.minPadding,m=d.tickInterval,l=d.minTickInterval,p=d.tickPixelInterval,s=b.categories;h?(b.linkedParent=c[g?"xAxis":"yAxis"][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=o(c.min,c.dataMin),b.max=o(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&qa(11,1)):(b.min=o(b.userMin,d.min,b.dataMin),b.max=o(b.userMax,d.max,b.dataMax));
121
- if(e)!a&&K(b.min,o(b.dataMin,b.min))<=0&&qa(10,1),b.min=ia(ka(b.min)),b.max=ia(ka(b.max));if(b.range&&(b.userMin=b.min=q(b.min,b.max-b.range),b.userMax=b.max,a))b.range=null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!s&&!b.usePercentage&&!h&&r(b.min)&&r(b.max)&&(c=b.max-b.min)){if(!r(d.min)&&!r(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!r(d.max)&&!r(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}b.tickInterval=b.min===b.max||b.min===void 0||b.max===
122
- void 0?1:h&&!m&&p===b.linkedParent.options.tickPixelInterval?b.linkedParent.tickInterval:o(m,s?1:(b.max-b.min)*p/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!m&&b.tickInterval<l)b.tickInterval=l;if(!f&&!e&&(a=I.pow(10,T(I.log(b.tickInterval)/I.LN10)),!m))b.tickInterval=ib(b.tickInterval,
123
- null,a,d);b.minorTickInterval=d.minorTickInterval==="auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=i=d.tickPositions?[].concat(d.tickPositions):i&&i.apply(b,[b.min,b.max]);if(!i)i=f?(b.getNonLinearTimeTicks||Cb)(Ab(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),b.tickPositions=i;if(!h)e=i[0],f=i[i.length-1],h=b.minPointOffset||
124
- 0,d.startOnTick?b.min=e:b.min-h>e&&i.shift(),d.endOnTick?b.max=f:b.max+h<f&&i.pop(),i.length===1&&(b.min-=0.001,b.max+=0.001)},setMaxTicks:function(){var a=this.chart,b=a.maxTicks||{},c=this.tickPositions,d=this._maxTicksKey=[this.xOrY,this.pos,this.len].join("-");if(!this.isLinked&&!this.isDatetimeAxis&&c&&c.length>(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&
125
- !this.categories&&!this.isLinked&&this.options.alignTicks!==!1){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e<a){for(;b.length<a;)b.push(ia(b[b.length-1]+this.tickInterval));this.transA*=(e-1)/(a-1);this.max=b[b.length-1]}if(r(d)&&a!==d)this.isDirty=!0}},setScale:function(){var a=this.stacks,b,c,d,e;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();e=this.len!==this.oldAxisLength;n(this.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)d=
126
- !0});if(e||d||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax)if(this.forceRedraw=!1,this.getSeriesExtremes(),this.setTickPositions(),this.oldUserMin=this.userMin,this.oldUserMax=this.userMax,!this.isDirty)this.isDirty=e||this.min!==this.oldMin||this.max!==this.oldMax;if(!this.isXAxis)for(b in a)for(c in a[b])a[b][c].cum=a[b][c].total;this.setMaxTicks()},setExtremes:function(a,b,c,d,e){var f=this,g=f.chart,c=o(c,!0),e=v(e,{min:a,max:b});D(f,"setExtremes",
127
- e,function(){f.userMin=a;f.userMax=b;f.isDirtyExtremes=!0;c&&g.redraw(d)})},zoom:function(a,b){this.allowZoomOutside||(a<=this.dataMin&&(a=y),b>=this.dataMax&&(b=y));this.displayBtn=a!==y||b!==y;this.setExtremes(a,b,!1,y,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=b.offsetRight||0,e=this.horiz,f,g;this.left=g=o(b.left,a.plotLeft+c);this.top=f=o(b.top,a.plotTop);this.width=c=o(b.width,a.plotWidth-c+d);this.height=b=o(b.height,a.plotHeight);
128
- this.bottom=a.chartHeight-b-f;this.right=a.chartWidth-c-g;this.len=q(e?c:b,0);this.pos=e?g:f},getExtremes:function(){var a=this.isLog;return{min:a?ia(da(this.min)):this.min,max:a?ia(da(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?da(this.min):this.min,b=b?da(this.max):this.max;c>a||a===null?a=c:b<a&&(a=b);return this.translate(a,0,1,0,1)},addPlotBand:function(a){this.addPlotBandOrLine(a,"plotBands")},
129
- addPlotLine:function(a){this.addPlotBandOrLine(a,"plotLines")},addPlotBandOrLine:function(a,b){var c=(new pb(this,a)).render(),d=this.userOptions;b&&(d[b]=d[b]||[],d[b].push(a));this.plotLinesAndBands.push(c);return c},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,j,k=0,m,l=0,p=d.title,s=d.labels,t=0,B=b.axisOffset,w=b.clipOffset,G=[-1,1,1,-1][h],v;a.hasData=b=a.hasVisibleSeries||r(a.min)&&r(a.max)&&!!e;
130
- a.showAxis=j=b||o(d.showEmpty,!0);if(!a.axisGroup)a.gridGroup=c.g("grid").attr({zIndex:d.gridZIndex||1}).add(),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(),a.labelGroup=c.g("axis-labels").attr({zIndex:s.zIndex||7}).add();if(b||a.isLinked)n(e,function(b){f[b]?f[b].addLabel():f[b]=new Ja(a,b)}),n(e,function(a){if(h===0||h===2||{1:"left",3:"right"}[h]===s.align)t=q(f[a].getLabelSize(),t)}),a.staggerLines&&(t+=(a.staggerLines-1)*16);else for(v in f)f[v].destroy(),delete f[v];if(p&&p.text&&
131
- p.enabled!==!1){if(!a.axisTitle)a.axisTitle=c.text(p.text,0,0,p.useHTML).attr({zIndex:7,rotation:p.rotation||0,align:p.textAlign||{low:"left",middle:"center",high:"right"}[p.align]}).css(p.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(j)k=a.axisTitle.getBBox()[g?"height":"width"],l=o(p.margin,g?5:10),m=p.offset;a.axisTitle[j?"show":"hide"]()}a.offset=G*o(d.offset,B[h]);a.axisTitleMargin=o(m,t+l+(h!==2&&t&&G*d.labels[g?"y":"x"]));B[h]=q(B[h],a.axisTitleMargin+k+G*a.offset);w[i]=q(w[i],d.lineWidth)},
132
- getLinePath:function(a){var b=this.chart,c=this.opposite,d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d;this.lineTop=d=b.chartHeight-this.bottom-(c?this.height:0)+d;c||(a*=-1);return b.renderer.crispLine(["M",e?this.left:f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=u(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+
133
- d/2,high:f+(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*this.axisTitleMargin+(this.side===2?i:0);return{x:a?d:b+(g?this.width:0)+h+(e.x||0),y:a?b-(g?this.height:0)+h:d+(e.y||0)}},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.isLog,f=a.isLinked,g=a.tickPositions,h=a.axisTitle,i=a.stacks,j=a.ticks,k=a.minorTicks,m=a.alternateBands,l=d.stackLabels,p=d.alternateGridColor,s=a.tickmarkOffset,o=d.lineWidth,B,w=b.hasRendered&&r(a.oldMin)&&!isNaN(a.oldMin);B=a.hasData;var q=
134
- a.showAxis,t,v;n([j,k,m],function(a){for(var b in a)a[b].isActive=!1});if(B||f)if(a.minorTickInterval&&!a.categories&&n(a.getMinorTickPositions(),function(b){k[b]||(k[b]=new Ja(a,b,"minor"));w&&k[b].isNew&&k[b].render(null,!0);k[b].render(null,!1,1)}),g.length&&(n(g.slice(1).concat([g[0]]),function(b,c){c=c===g.length-1?0:c+1;if(!f||b>=a.min&&b<=a.max)j[b]||(j[b]=new Ja(a,b)),w&&j[b].isNew&&j[b].render(c,!0),j[b].render(c,!1,1)}),s&&a.min===0&&(j[-1]||(j[-1]=new Ja(a,-1,null,!0)),j[-1].render(-1))),
135
- p&&n(g,function(b,c){if(c%2===0&&b<a.max)m[b]||(m[b]=new pb(a)),t=b+s,v=g[c+1]!==y?g[c+1]+s:a.max,m[b].options={from:e?da(t):t,to:e?da(v):v,color:p},m[b].render(),m[b].isActive=!0}),!a._addedPlotLB)n((d.plotLines||[]).concat(d.plotBands||[]),function(b){a.addPlotBandOrLine(b)}),a._addedPlotLB=!0;n([j,k,m],function(a){var c,d,e=[],f=za?za.duration||500:0,g=function(){for(d=e.length;d--;)a[e[d]]&&!a[e[d]].isActive&&(a[e[d]].destroy(),delete a[e[d]])};for(c in a)if(!a[c].isActive)a[c].render(c,!1,0),
136
- a[c].isActive=!1,e.push(c);a===m||!b.hasRendered||!f?g():f&&setTimeout(g,f)});if(o)B=a.getLinePath(o),a.axisLine?a.axisLine.animate({d:B}):a.axisLine=c.path(B).attr({stroke:d.lineColor,"stroke-width":o,zIndex:7}).add(a.axisGroup),a.axisLine[q?"show":"hide"]();if(h&&q)h[h.isNew?"attr":"animate"](a.getTitlePosition()),h.isNew=!1;if(l&&l.enabled){var u,x,d=a.stackTotalGroup;if(!d)a.stackTotalGroup=d=c.g("stack-labels").attr({visibility:"visible",zIndex:6}).add();d.translate(b.plotLeft,b.plotTop);for(u in i)for(x in c=
137
- i[u],c)c[x].render(d)}a.isDirty=!1},removePlotBandOrLine:function(a){for(var b=this.plotLinesAndBands,c=b.length;c--;)b[c].id===a&&b[c].destroy()},setTitle:function(a,b){this.update({title:a},b)},redraw:function(){var a=this.chart.pointer;a.reset&&a.reset(!0);this.render();n(this.plotLinesAndBands,function(a){a.render()});n(this.series,function(a){a.isDirty=!0})},setCategories:function(a,b){this.update({categories:a},b)},destroy:function(){var a=this,b=a.stacks,c;ba(a);for(c in b)Ha(b[c]),b[c]=null;
138
- n([a.ticks,a.minorTicks,a.alternateBands,a.plotLinesAndBands],function(a){Ha(a)});n("stackTotalGroup,axisLine,axisGroup,gridGroup,labelGroup,axisTitle".split(","),function(b){a[b]&&(a[b]=a[b].destroy())})}};qb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=u(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape,null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,
139
- r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).hide().add();$||this.label.shadow(b.shadow);this.shared=b.shared},destroy:function(){n(this.crosshairs,function(a){a&&a.destroy()});if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden;v(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:g?(2*f.anchorX+c)/3:c,anchorY:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g&&(Q(a-
140
- f.x)>1||Q(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(){var a=this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},o(this.options.hideDelay,500)),b&&n(b,function(a){a.setState()}),this.chart.hoverPoints=null},hideCrosshairs:function(){n(this.crosshairs,function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e=
141
- d.inverted,f=d.plotTop,g=0,h=0,i,a=ha(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===y&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(n(a,function(a){i=a.series.yAxis;g+=a.plotX;h+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return La(c,t)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i=
142
- o(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,m;d<7&&(d=e+q(j,0)+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,m=!0);k<f+5&&(k=f+5,m&&c>=k&&c<=k+b&&(k=c+f+i));k+b>f+h&&(k=q(f,f+h-b-i));return{x:d,y:k}},defaultFormatter:function(a){var b=this.points||ha(this),c=b[0].series,d;d=[c.tooltipHeaderFormatter(b[0])];n(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});d.push(a.options.footerFormat||
143
- "");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h,i={},j,k=[];j=e.formatter||this.defaultFormatter;var i=c.hoverPoints,m,l=e.crosshairs;h=this.shared;clearTimeout(this.hideTimer);this.followPointer=ha(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];h&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,i&&n(i,function(a){a.setState()}),n(a,function(a){a.setState("hover");k.push(a.getLabelConfig())}),i={x:a[0].category,
144
- y:a[0].y},i.points=k,a=a[0]):i=a.getLabelConfig();j=j.call(i,this);i=a.series;h=h||!i.isCartesian||i.tooltipOutsidePlot||c.isInsidePlot(f,g);j===!1||!h?this.hide():(this.isHidden&&(Ta(d),d.attr("opacity",1).show()),d.attr({text:j}),m=e.borderColor||a.color||i.color||"#606060",d.attr({stroke:m}),this.updatePosition({plotX:f,plotY:g}),this.isHidden=!1);if(l){l=ha(l);for(d=l.length;d--;)if(e=a.series[d?"yAxis":"xAxis"],l[d]&&e)if(h=d?o(a.stackY,a.y):a.x,e.isLog&&(h=ka(h)),e=e.getPlotLinePath(h,1),this.crosshairs[d])this.crosshairs[d].attr({d:e,
145
- visibility:"visible"});else{h={"stroke-width":l[d].width||1,stroke:l[d].color||"#C0C0C0",zIndex:l[d].zIndex||2};if(l[d].dashStyle)h.dashstyle=l[d].dashStyle;this.crosshairs[d]=c.renderer.path(e).attr(h).add()}}D(c,"tooltipRefresh",{text:j,x:f+c.plotLeft,y:g+c.plotTop,borderColor:m})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(t(c.x),t(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)}};rb.prototype={init:function(a,
146
- b){var c=$?"":b.chart.zoomType,d=a.inverted,e;this.options=b;this.chart=a;this.zoomX=e=/x/.test(c);this.zoomY=c=/y/.test(c);this.zoomHor=e&&!d||c&&d;this.zoomVert=c&&!d||e&&d;this.pinchDown=[];this.lastValidTouch={};if(b.tooltip.enabled)a.tooltip=new qb(a,b.tooltip);this.setDOMEvents()},normalize:function(a){var b,c,d,a=a||O.event;if(!a.target)a.target=a.srcElement;a=Qb(a);d=a.touches?a.touches.item(0):a;this.chartPosition=b=Ub(this.chart.container);d.pageX===y?(c=a.x,b=a.y):(c=d.pageX-b.left,b=d.pageY-
147
- b.top);return v(a,{chartX:t(c),chartY:t(b)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};n(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f=b.hoverPoint,g=b.hoverSeries,h,i,j=b.chartWidth,k=this.getIndex(a);if(d&&this.options.tooltip.shared&&
148
- (!g||!g.noSharedTooltip)){e=[];h=c.length;for(i=0;i<h;i++)if(c[i].visible&&c[i].options.enableMouseTracking!==!1&&!c[i].noSharedTooltip&&c[i].tooltipPoints.length&&(b=c[i].tooltipPoints[k],b.series))b._dist=Q(k-b.clientX),j=K(j,b._dist),e.push(b);for(h=e.length;h--;)e[h]._dist>j&&e.splice(h,1);if(e.length&&e[0].clientX!==this.hoverX)d.refresh(e,a),this.hoverX=e[0].clientX}if(g&&g.tracker){if((b=g.tooltipPoints[k])&&b!==f)b.onMouseOver(a)}else d&&d.followPointer&&!d.isHidden&&(a=d.getAnchor([{}],a),
149
- d.updatePosition({plotX:a[0],plotY:a[1]}))},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,b=e&&e.shared?b.hoverPoints:d;(a=a&&e&&b)&&ha(b)[0].plotX===y&&(a=!1);if(a)e.refresh(b);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&(e.hide(),e.hideCrosshairs());this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart;n(c.series,function(d){d.xAxis&&d.xAxis.zoomEnabled&&(d.group.attr(a),d.markerGroup&&(d.markerGroup.attr(a),d.markerGroup.clip(b?c.clipRect:null)),d.dataLabelsGroup&&
150
- d.dataLabelsGroup.attr(a))});c.clipRect.attr(b||c.clipBox)},pinchTranslateDirection:function(a,b,c,d,e,f,g){var h=this.chart,i=a?"x":"y",j=a?"X":"Y",k="chart"+j,m=a?"width":"height",l=h["plot"+(a?"Left":"Top")],p,s,o=1,n=h.inverted,w=h.bounds[a?"h":"v"],q=b.length===1,t=b[0][k],r=c[0][k],v=!q&&b[1][k],u=!q&&c[1][k],x,c=function(){!q&&Q(t-v)>20&&(o=Q(r-u)/Q(t-v));s=(l-r)/o+t;p=h["plot"+(a?"Width":"Height")]/o};c();b=s;b<w.min?(b=w.min,x=!0):b+p>w.max&&(b=w.max-p,x=!0);x?(r-=0.8*(r-g[i][0]),q||(u-=
151
- 0.8*(u-g[i][1])),c()):g[i]=[r,u];n||(f[i]=s-l,f[m]=p);f=n?1/o:o;e[m]=p;e[i]=b;d[n?a?"scaleY":"scaleX":"scale"+j]=o;d["translate"+j]=f*l+(r-f*t)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=c.tooltip&&c.tooltip.options.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.zoomHor||b.pinchHor,j=b.zoomVert||b.pinchVert,k=i||j,m=b.selectionMarker,l={},p={};a.type==="touchstart"&&(e||k)&&a.preventDefault();La(f,function(a){return b.normalize(a)});if(a.type==="touchstart")n(f,function(a,
152
- b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],n(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=K(e,f),e=q(e,f);b.min=K(a.pos,g-d);b.max=q(a.pos+a.len,e+d)}});else if(d.length){if(!m)b.selectionMarker=m=v({destroy:ta},c.plotBox);i&&b.pinchTranslateDirection(!0,d,f,l,m,p,h);j&&b.pinchTranslateDirection(!1,d,f,l,m,p,h);b.hasPinched=k;b.scaleGroups(l,
153
- p);!k&&e&&g===1&&this.runPointActions(b.normalize(a))}},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,a=a.chartY,e=this.zoomHor,f=this.zoomVert,g=b.plotLeft,h=b.plotTop,i=b.plotWidth,j=b.plotHeight,k,m=this.mouseDownX,l=this.mouseDownY;d<g?d=g:d>g+i&&(d=g+i);a<h?a=h:a>h+j&&(a=h+j);this.hasDragged=Math.sqrt(Math.pow(m-d,2)+Math.pow(l-a,2));if(this.hasDragged>
154
- 10){k=b.isInsidePlot(m-g,l-h);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&k&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(g,h,e?1:i,f?1:j,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&e&&(e=d-m,this.selectionMarker.attr({width:Q(e),x:(e>0?0:e)+m}));this.selectionMarker&&f&&(e=a-l,this.selectionMarker.attr({height:Q(e),y:(e>0?0:e)+l}));k&&!this.selectionMarker&&c.panning&&b.pan(d)}},drop:function(a){var b=this.chart,c=this.hasPinched;
155
- if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},e=this.selectionMarker,f=e.x,g=e.y,h;if(this.hasDragged||c)n(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?f:g),b=a.toValue(b?f+e.width:g+e.height);!isNaN(c)&&!isNaN(b)&&(d[a.xOrY+"Axis"].push({axis:a,min:K(c,b),max:q(c,b)}),h=!0)}}),h&&D(b,"selection",d,function(a){b.zoom(v(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();c&&this.scaleGroups({translateX:b.plotLeft,
156
- translateY:b.plotTop,scaleX:1,scaleY:1})}if(b)L(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){this.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=Qb(a);c&&d&&d.isCartesian&&!b.isInsidePlot(a.pageX-c.left-b.plotLeft,a.pageY-c.top-
157
- b.plotTop)&&this.reset()},onContainerMouseLeave:function(){this.reset();this.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart,a=this.normalize(a);a.returnValue=!1;b.mouseIsDown==="mousedown"&&this.drag(a);b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=A(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a=a.parentNode}},onTrackerMouseOut:function(a){var b=
158
- this.chart.hoverSeries;if(b&&!b.options.stickyTracking&&!this.inClass(a.toElement||a.relatedTarget,"highcharts-tooltip"))b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,f=b.inverted,g,h,i,a=this.normalize(a);a.cancelBubble=!0;if(!b.cancelClick)c&&this.inClass(a.target,"highcharts-tracker")?(g=this.chartPosition,h=c.plotX,i=c.plotY,v(c,{pageX:g.left+d+(f?b.plotWidth-i:h),pageY:g.top+e+(f?b.plotHeight-h:i)}),D(c.series,"click",v(a,{point:c})),b.hoverPoint&&
159
- c.firePointEvent("click",a)):(v(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&D(b,"click",a))},onContainerTouchStart:function(a){var b=this.chart;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&(this.runPointActions(a),this.pinch(a))):a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length===1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){this.drop(a)},setDOMEvents:function(){var a=
160
- this,b=a.chart.container,c;this._events=c=[[b,"onmousedown","onContainerMouseDown"],[b,"onmousemove","onContainerMouseMove"],[b,"onclick","onContainerClick"],[b,"mouseleave","onContainerMouseLeave"],[z,"mousemove","onDocumentMouseMove"],[z,"mouseup","onDocumentMouseUp"]];fb&&c.push([b,"ontouchstart","onContainerTouchStart"],[b,"ontouchmove","onContainerTouchMove"],[z,"touchend","onDocumentTouchEnd"]);n(c,function(b){a["_"+b[2]]=function(c){a[b[2]](c)};b[1].indexOf("on")===0?b[0][b[1]]=a["_"+b[2]]:
161
- J(b[0],b[1],a["_"+b[2]])})},destroy:function(){var a=this;n(a._events,function(b){b[1].indexOf("on")===0?b[0][b[1]]=null:ba(b[0],b[1],a["_"+b[2]])});delete a._events;clearInterval(a.tooltipTimeout)}};sb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=o(b.padding,8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=u(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=x(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=e-5,c.maxItemWidth=0,c.chart=a,
162
- c.itemHeight=0,c.lastLineHeight=0,c.render(),J(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.color:g,g=a.options&&a.options.marker,i={stroke:h,fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g)for(j in g=a.convertAttribs(g),g)d=g[j],d!==y&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,
163
- b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Ra(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=
164
- b.translateY,n(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,L(f,{left:b.translateX+e.legendItemWidth+f.x-20+"px",top:g+"px",display:g>c-6&&g<c+d-6?"":S}))})},renderTitle:function(){var a=this.padding,b=this.options.title,c=0;if(b.text){if(!this.title)this.title=this.chart.renderer.label(b.text,a-3,a-4,null,null,null,null,null,"legend-title").attr({zIndex:1}).css(b.style).add(this.group);c=this.title.getBBox().height;this.contentGroup.attr({translateY:c})}this.titleHeight=c},renderItem:function(a){var w;
165
- var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=e.symbolWidth,h=e.symbolPadding,i=b.itemStyle,j=b.itemHiddenStyle,k=b.padding,m=!e.rtl,l=e.width,p=e.itemMarginBottom||0,s=b.itemMarginTop,o=b.initialItemX,n=a.legendItem,t=a.series||a,r=t.options,v=r.showCheckbox,u=e.useHTML;if(!n&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup),t.drawLegendSymbol(b,a),a.legendItem=n=d.text(e.labelFormat?wa(e.labelFormat,a):e.labelFormatter.call(a),m?g+h:-h,b.baseline,
166
- u).css(x(a.visible?i:j)).attr({align:m?"left":"right",zIndex:2}).add(a.legendGroup),(u?n:a.legendGroup).on("mouseover",function(){a.setState("hover");n.css(b.options.itemHoverStyle)}).on("mouseout",function(){n.css(a.visible?i:j);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):D(a,"legendItemClick",b,c)}),b.colorizeItem(a,a.visible),r&&v))a.checkbox=U("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},
167
- e.itemCheckboxStyle,c.container),J(a.checkbox,"click",function(b){D(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})});d=n.getBBox();w=a.legendItemWidth=e.itemWidth||g+h+d.width+k+(v?20:0),e=w;b.itemHeight=g=d.height;if(f&&b.itemX-o+e>(l||c.chartWidth-2*k-o))b.itemX=o,b.itemY+=s+b.lastLineHeight+p,b.lastLineHeight=0;b.maxItemWidth=q(b.maxItemWidth,e);b.lastItemY=s+b.itemY+p;b.lastLineHeight=q(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=s+g+p,b.lastLineHeight=
168
- g);b.offsetWidth=l||q(f?b.itemX-o:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,m=j.borderWidth,l=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&!r(b.linkedTo)&&(e=e.concat(a.legendItems||
169
- (b.legendType==="point"?a.data:a)))});Ib(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(m||l){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,m||0).attr({stroke:j.borderColor,
170
- "stroke-width":m||0,fill:l||S}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;n(e,function(b){a.positionItem(b)});f&&d.align(v({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,i=e.navigation,j=o(i.animation,!0),k=i.arrowSize||12,m=this.nav;e.layout===
171
- "horizontal"&&(f/=2);g&&(f=K(f,g));if(a>f&&!e.useHTML){this.clipHeight=c=f-20-this.titleHeight;this.pageCount=ja(a/c);this.currentPage=o(this.currentPage,1);this.fullHeight=a;if(!h)h=b.clipRect=d.clipRect(0,0,9999,0),b.contentGroup.clip(h);h.attr({height:c});if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,k,k).on("click",function(){b.scroll(-1,j)}).add(m),this.pager=d.text("",15,10).css(i.style).add(m),this.down=d.symbol("triangle-down",0,0,k,k).on("click",
172
- function(){b.scroll(1,j)}).add(m);b.scroll(0);a=f}else if(m)h.attr({height:c.chartHeight}),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+a,e=this.clipHeight,f=this.options.navigation,g=f.activeColor,h=f.inactiveColor,f=this.pager,i=this.padding;d>c&&(d=c);if(d>0)b!==y&&Ia(b,this.chart),this.nav.attr({translateX:i,translateY:e+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:d===1?h:g}).css({cursor:d===
173
- 1?"default":"pointer"}),f.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?h:g}).css({cursor:d===c?"default":"pointer"}),e=-K(e*(d-1),this.fullHeight-e+i)+1,this.scrollGroup.animate({translateY:e}),f.attr({text:d+"/"+c}),this.currentPage=d,this.positionCheckboxes(e)}};tb.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=x(N,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=V(e)?e:[e,e,e,e];this.optionsMarginTop=o(d.marginTop,e[0]);this.optionsMarginRight=
174
- o(d.marginRight,e[1]);this.optionsMarginBottom=o(d.marginBottom,e[2]);this.optionsMarginLeft=o(d.marginLeft,e[3]);this.runChartClick=(e=d.events)&&!!e.click;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=Ba.length;Ba.push(f);d.reflow!==!1&&J(f,"load",function(){f.initReflow()});if(e)for(g in e)J(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=$?!1:o(d.animation,!0);f.pointCount=0;f.counters=new Hb;
175
- f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=aa[a.type||b.type||b.defaultSeriesType])||qa(17,!0);b=new b;b.init(this,a);return b},addSeries:function(a,b,c){var d,e=this;a&&(b=o(b,!0),D(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var b=b?"xAxis":"yAxis",e=this.options;new ab(this,x(a,{index:this[b].length}));e[b]=ha(e[b]||{});e[b].push(a);o(c,!0)&&this.redraw(d)},isInsidePlot:function(a,b,c){var d=
176
- c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&n(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,i=c.length,j=i,k=this.renderer,m=k.isHidden(),l=[];Ia(a,this);for(m&&this.cloneRenderTo();j--;)if(a=c[j],a.isDirty&&a.options.stacking){g=!0;break}if(g)for(j=i;j--;)if(a=c[j],a.options.stacking)a.isDirty=
177
- !0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,l.push(function(){D(a,"afterSetExtremes",a.getExtremes())});if(a.isDirty||h||g)a.redraw(),h=!0})}h&&this.drawChartBox();n(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||
178
- a.xAxis)&&a.redraw()});d&&d.reset&&d.reset(!0);k.draw();D(this,"redraw");m&&this.cloneRenderTo(!0);n(l,function(a){a.call()})},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=U(ya,{className:"highcharts-loading"},v(d.style,{zIndex:10,display:S}),this.container),this.loadingSpan=U("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)L(c,{opacity:0,display:"",left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+
179
- "px",height:this.plotHeight+"px"}),wb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&wb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){L(b,{display:S})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d<b.length;d++)if(b[d].options.id===a)return b[d];for(d=0;d<c.length;d++)if(c[d].options.id===a)return c[d];for(d=0;d<c.length;d++){e=c[d].points||
180
- [];for(b=0;b<e.length;b++)if(e[b].id===a)return e[b]}return null},getAxes:function(){var a=this,b=this.options,c=b.xAxis=ha(b.xAxis||{}),b=b.yAxis=ha(b.yAxis||{});n(c,function(a,b){a.index=b;a.isX=!0});n(b,function(a,b){a.index=b});c=c.concat(b);n(c,function(b){new ab(a,b)});a.adjustTickAmounts()},getSelectedPoints:function(){var a=[];n(this.series,function(b){a=a.concat(ob(b.points||[],function(a){return a.selected}))});return a},getSelectedSeries:function(){return ob(this.series,function(a){return a.selected})},
181
- showResetZoom:function(){var a=this,b=N.lang,c=a.options.chart.resetZoomButton,d=c.theme,e=d.states,f=c.relativeTo==="chart"?null:"plotBox";this.resetZoomButton=a.renderer.button(b.resetZoom,null,null,function(){a.zoomOut()},d,e&&e.hover).attr({align:c.position.align,title:b.resetZoomTitle}).add().align(c.position,!1,f)},zoomOut:function(){var a=this;D(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var b,c=this.pointer,d=!1,e;!a||a.resetSelection?n(this.axes,function(a){b=
182
- a.zoom()}):n(a.xAxis.concat(a.yAxis),function(a){var e=a.axis,h=e.isXAxis;if(c[h?"zoomX":"zoomY"]||c[h?"pinchX":"pinchY"])b=e.zoom(a.min,a.max),e.displayBtn&&(d=!0)});e=this.resetZoomButton;if(d&&!e)this.showResetZoom();else if(!d&&V(e))this.resetZoomButton=e.destroy();b&&this.redraw(o(this.options.chart.animation,a&&a.animation,this.pointCount<100))},pan:function(a){var b=this.xAxis[0],c=this.mouseDownX,d=b.pointRange/2,e=b.getExtremes(),f=b.translate(c-a,!0)+d,c=b.translate(c+this.plotWidth-a,!0)-
183
- d;(d=this.hoverPoints)&&n(d,function(a){a.setState()});b.series.length&&f>K(e.dataMin,e.min)&&c<q(e.dataMax,e.max)&&b.setExtremes(f,c,!0,!1,{trigger:"pan"});this.mouseDownX=a;L(this.container,{cursor:"move"})},setTitle:function(a,b){var f;var c=this,d=c.options,e;e=d.title=x(d.title,a);f=d.subtitle=x(d.subtitle,b),d=f;n([["title",a,e],["subtitle",b,d]],function(a){var b=a[0],d=c[b],e=a[1],a=a[2];d&&e&&(c[b]=d=d.destroy());a&&a.text&&!d&&(c[b]=c.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align,
184
- "class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add().align(a,!1,"spacingBox"))})},getChartSize:function(){var a=this.options.chart,b=this.renderToClone||this.renderTo;this.containerWidth=gb(b,"width");this.containerHeight=gb(b,"height");this.chartWidth=q(0,a.width||this.containerWidth||600);this.chartHeight=q(0,o(a.height,this.containerHeight>19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Ra(b),delete this.renderToClone):
185
- (c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),L(b,{position:"absolute",top:"-9999px",display:"block"}),z.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+ub++;if(fa(a))this.renderTo=a=z.getElementById(a);a||qa(13,!0);c=u(A(a,"data-highcharts-chart"));!isNaN(c)&&Ba[c]&&Ba[c].destroy();A(a,"data-highcharts-chart",this.index);a.innerHTML="";a.offsetWidth||this.cloneRenderTo();
186
- this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=U(ya,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},v({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new Ca(a,c,d,!0):new Sa(a,c,d);$&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart,
187
- b=a.spacingTop,c=a.spacingRight,d=a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,i=this.optionsMarginRight,j=this.optionsMarginBottom,k=this.options.title,m=this.options.subtitle,l=this.options.legend,p=o(l.margin,10),s=l.x,t=l.y,B=l.align,w=l.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&&!r(this.optionsMarginTop))if(m=q(this.title&&!k.floating&&!k.verticalAlign&&k.y||0,this.subtitle&&!m.floating&&!m.verticalAlign&&
188
- m.y||0))this.plotTop=q(this.plotTop,m+o(k.margin,15)+b);if(f.display&&!l.floating)if(B==="right"){if(!r(i))this.marginRight=q(this.marginRight,f.legendWidth-s+p+c)}else if(B==="left"){if(!r(h))this.plotLeft=q(this.plotLeft,f.legendWidth+s+p+a)}else if(w==="top"){if(!r(g))this.plotTop=q(this.plotTop,f.legendHeight+t+p+b)}else if(w==="bottom"&&!r(j))this.marginBottom=q(this.marginBottom,f.legendHeight-t+p+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=
189
- this.extraTopMargin);this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});r(h)||(this.plotLeft+=e[3]);r(g)||(this.plotTop+=e[0]);r(j)||(this.marginBottom+=e[2]);r(i)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g=c.width||gb(d,"width"),h=c.height||gb(d,"height"),a=a?a.target:O;if(!b.hasUserSize&&g&&h&&(a===O||a===z)){if(g!==b.containerWidth||h!==b.containerHeight)clearTimeout(e),b.reflowTimeout=e=setTimeout(function(){if(b.container)b.setSize(g,
190
- h,!1),b.hasUserSize=null},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart,d=b.renderTo,e;J(O,"resize",a);J(b,"destroy",function(){ba(O,"resize",a)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&D(d,"endResize",null,function(){d.isResizing-=1})};Ia(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(r(a))d.chartWidth=e=q(0,t(a)),d.hasUserSize=!!e;if(r(b))d.chartHeight=f=q(0,t(b));L(d.container,{width:e+"px",height:f+"px"});d.setChartSize(!0);
191
- d.renderer.setSize(e,f,c);d.maxTicks=null;n(d.axes,function(a){a.isDirty=!0;a.setScale()});n(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();d.redraw(c);d.oldChartHeight=null;D(d,"resize");za===!1?g():setTimeout(g,za&&za.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=f.spacingTop,h=f.spacingRight,i=f.spacingBottom,j=f.spacingLeft,k=this.clipOffset,m,l,p,o;this.plotLeft=m=
192
- t(this.plotLeft);this.plotTop=l=t(this.plotTop);this.plotWidth=p=q(0,t(d-m-this.marginRight));this.plotHeight=o=q(0,t(e-l-this.marginBottom));this.plotSizeX=b?o:p;this.plotSizeY=b?p:o;this.plotBorderWidth=b=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:j,y:g,width:d-j-h,height:e-g-i};this.plotBox=c.plotBox={x:m,y:l,width:p,height:o};c=ja(q(b,k[3])/2);d=ja(q(b,k[0])/2);this.clipBox={x:c,y:d,width:T(this.plotSizeX-q(b,k[1])/2-c),height:T(this.plotSizeY-q(b,k[2])/2-d)};a||n(this.axes,function(a){a.setAxisSize();
193
- a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart,b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=o(this.optionsMarginTop,a.spacingTop);this.marginRight=o(this.optionsMarginRight,b);this.marginBottom=o(this.optionsMarginBottom,c);this.plotLeft=o(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,
194
- g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,m=a.plotBackgroundImage,l=a.plotBorderWidth||0,p,o=this.plotLeft,n=this.plotTop,t=this.plotWidth,q=this.plotHeight,r=this.plotBox,v=this.clipRect,u=this.clipBox;p=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-p,d-p));else{e={fill:j||S};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(p/2,p/2,c-p,d-p,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?
195
- f.animate(r):this.plotBackground=b.rect(o,n,t,q,0).attr({fill:k}).add().shadow(a.plotShadow);if(m)h?h.animate(r):this.plotBGImage=b.image(m,o,n,t,q).add();v?v.animate({width:u.width,height:u.height}):this.clipRect=b.clipRect(u);if(l)g?g.animate(g.crisp(null,o,n,t,q)):this.plotBorder=b.rect(o,n,t,q,0,l).attr({stroke:a.plotBorderColor,"stroke-width":l,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"],
196
- function(g){c=aa[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=aa[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new sb(a,d.legend);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()});
197
- if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(b){var d=v(e.style,b.style),f=u(d.left)+a.plotLeft,g=u(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);
198
- a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;D(a,"destroy");Ba[a.index]=y;a.renderTo.removeAttribute("data-highcharts-chart");ba(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});
199
- if(d)d.innerHTML="",ba(d),f&&Ra(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!Z&&O==O.top&&z.readyState!=="complete"||$&&!O.canvg?($?Rb.push(function(){a.firstRender()},a.options.global.canvasToolsURL):z.attachEvent("onreadystatechange",function(){z.detachEvent("onreadystatechange",a.firstRender);z.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),D(a,"init"),a.resetMargins(),
200
- a.setChartSize(),a.propFromSeries(),a.getAxes(),n(b.series||[],function(b){a.initSeries(b)}),D(a,"beforeRender"),a.pointer=new rb(a,b),a.render(),a.renderer.draw(),c&&c.apply(a,[a]),n(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),D(a,"load")}};tb.prototype.callbacks=[];var Na=function(){};Na.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++],
201
- a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Na.prototype.optionsToObject.call(this,a);v(this,a);this.options=this.options?v(this.options,a):a;if(d)this.y=this[d];if(this.x===y&&c)this.x=b===y?c.autoIncrement():b;return this},optionsToObject:function(a){var b,c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b={y:a};else if(Da(a)){b={};if(a.length>e){c=typeof a[0];
202
- if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;g<e;)b[d[g++]]=a[f++]}else if(typeof a==="object"){b=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers=!0}return b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;if(b&&(this.setState(),ga(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)ba(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=
203
- null},destroyElements:function(){for(var a="graphic,dataLabel,dataLabelUpper,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c=this,d=c.series,e=d.chart,a=o(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=
204
- c.options.selected=a;d.options.data[la(c,d.data)]=c.options;c.setState(a&&"select");b||n(e.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=a.options.selected=!1,d.options.data[la(a,d.data)]=a.options,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(a){var b=this.series,c=b.chart,d=c.tooltip,e=c.hoverPoint;if(e&&e!==this)e.onMouseOut();this.firePointEvent("mouseOver");d&&(!d.shared||b.noSharedTooltip)&&d.refresh(this,a);this.setState("hover");c.hoverPoint=this},
205
- onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||la(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=o(c.valueDecimals,""),e=c.valuePrefix||"",f=c.valueSuffix||"";n(b.pointArrayMap||["y"],function(b){b="{point."+b;if(e||f)a=a.replace(b+"}",e+b+"}"+f);a=a.replace(b+"}",b+":,."+d+"f}")});return wa(a,{point:this,series:this.series})},update:function(a,b,c){var d=this,e=d.series,f=d.graphic,
206
- g,h=e.data,i=e.chart,b=o(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);V(a)&&(e.getAttribs(),f&&f.attr(d.pointAttr[e.state]));g=la(d,h);e.xData[g]=d.x;e.yData[g]=e.toYData?e.toYData(d):d.y;e.zData[g]=d.z;e.options.data[g]=d.options;e.isDirty=!0;e.isDirtyData=!0;b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.chart,f,g=d.data;Ia(b,e);a=o(a,!0);c.firePointEvent("remove",null,function(){f=la(c,g);g.splice(f,1);d.options.data.splice(f,1);d.xData.splice(f,1);
207
- d.yData.splice(f,1);d.zData.splice(f,1);c.destroy();d.isDirty=!0;d.isDirtyData=!0;a&&e.redraw()})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});D(this,a,b,c)},importEvents:function(){if(!this.hasImportedEvents){var a=x(this.series.options.point,this.options).events,b;this.events=a;for(b in a)J(this,
208
- b,a[b]);this.hasImportedEvents=!0}},setState:function(a){var b=this.plotX,c=this.plotY,d=this.series,e=d.options.states,f=X[d.type].marker&&d.options.marker,g=f&&!f.enabled,h=f&&f.states[a],i=h&&h.enabled===!1,j=d.stateMarkerGraphic,k=this.marker||{},m=d.chart,l=this.pointAttr,a=a||"";if(!(a===this.state||this.selected&&a!=="select"||e[a]&&e[a].enabled===!1||a&&(i||g&&!h.enabled))){if(this.graphic)e=f&&this.graphic.symbolName&&l[a].r,this.graphic.attr(x(l[a],e?{x:b-e,y:c-e,width:2*e,height:2*e}:{}));
209
- else{if(a&&h)e=h.radius,k=k.symbol||d.symbol,j&&j.currentSymbol!==k&&(j=j.destroy()),j?j.attr({x:b-e,y:c-e}):(d.stateMarkerGraphic=j=m.renderer.symbol(k,b-e,c-e,2*e,2*e).attr(l[a]).add(d.markerGroup),j.currentSymbol=k);if(j)j[a&&m.isInsidePlot(b,c)?"show":"hide"]()}this.state=a}}};var R=function(){};R.prototype={isCartesian:!0,type:"line",pointClass:Na,sorted:!0,requireSorting:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},colorCounter:0,init:function(a,
210
- b){var c,d,e=a.series;this.chart=a;this.options=b=this.setOptions(b);this.bindAxes();v(this,{name:b.name,state:"",pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});if($)b.animation=!1;d=b.events;for(c in d)J(this,c,d[c]);if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;this.getColor();this.getSymbol();this.setData(b.data,!1);if(this.isCartesian)a.hasCartesianSeries=!0;e.push(this);this._i=e.length-1;Ib(e,function(a,b){return o(a.options.index,
211
- a._i)-o(b.options.index,a._i)});n(e,function(a,b){a.index=b;a.name=a.name||"Series "+(b+1)});c=b.linkedTo;this.linkedSeries=[];if(fa(c)&&(c=c===":previous"?e[this.index-1]:a.get(c)))c.linkedSeries.push(this),this.linkedParent=c},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;a.isCartesian&&n(["xAxis","yAxis"],function(e){n(c[e],function(c){d=c.options;if(b[e]===d.index||b[e]!==y&&b[e]===d.id||b[e]===y&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0});a[e]||qa(18,!0)})},autoIncrement:function(){var a=
212
- this.options,b=this.xIncrement,b=o(b,a.pointStart,0);this.pointInterval=o(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);d.length&&(b=[d])}else n(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions,
213
- d=c[this.type];this.userOptions=a;a=x(d,c.series,a);this.tooltipOptions=x(b.tooltip,a.tooltip);d.marker===null&&delete a.marker;return a},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,e;e=a.color||X[this.type].color;if(!e&&!a.colorByPoint)r(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a=this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols,
214
- c=c.counters;this.symbol=b.symbol;if(!this.symbol)r(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&&c.enabled)b=
215
- c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).add(f)},addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xData,k=this.yData,m=this.zData,l=this.names,p=g&&g.shift||0,n=e.data;Ia(d,i);if(g&&c)g.shift=p+1;if(h){if(c)h.shift=p+1;h.isArea=!0}b=o(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);j.push(d.x);k.push(this.toYData?this.toYData(d):d.y);m.push(d.z);if(l)l[d.x]=d.name;n.push(a);e.legendType==="point"&&
216
- this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1):(f.shift(),j.shift(),k.shift(),m.shift(),n.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&i.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.chart,f=null,g=this.xAxis,h=g&&g.categories&&!g.categories.length?[]:null,i;this.xIncrement=null;this.pointRange=g&&g.categories?1:d.pointRange;this.colorCounter=0;var j=[],k=[],m=[],l=a?a.length:[],p=(i=this.pointArrayMap)&&i.length,n=!!this.toYData;if(l>(d.turboThreshold||
217
- 1E3)){for(i=0;f===null&&i<l;)f=a[i],i++;if(Ea(f)){f=o(d.pointStart,0);d=o(d.pointInterval,1);for(i=0;i<l;i++)j[i]=f,k[i]=a[i],f+=d;this.xIncrement=f}else if(Da(f))if(p)for(i=0;i<l;i++)d=a[i],j[i]=d[0],k[i]=d.slice(1,p+1);else for(i=0;i<l;i++)d=a[i],j[i]=d[0],k[i]=d[1]}else for(i=0;i<l;i++)if(a[i]!==y&&(d={series:this},this.pointClass.prototype.applyOptions.apply(d,[a[i]]),j[i]=d.x,k[i]=n?this.toYData(d):d.y,m[i]=d.z,h&&d.name))h[i]=d.name;this.requireSorting&&j.length>1&&j[1]<j[0]&&qa(15);fa(k[0])&&
218
- qa(14,!0);this.data=[];this.options.data=a;this.xData=j;this.yData=k;this.zData=m;this.names=h;for(i=c&&c.length||0;i--;)c[i]&&c[i].destroy&&c[i].destroy();if(g)g.minRange=g.userMinRange;this.isDirty=this.isDirtyData=e.isDirtyBox=!0;o(b,!0)&&e.redraw(!1)},remove:function(a,b){var c=this,d=c.chart,a=o(a,!0);if(!c.isRemoving)c.isRemoving=!0,D(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;a&&d.redraw(b)});c.isRemoving=!1},processData:function(a){var b=this.xData,c=this.yData,
219
- d=b.length,e=0,f=d,g,h,i=this.xAxis,j=this.options,k=j.cropThreshold,m=this.isCartesian;if(m&&!this.isDirty&&!i.isDirty&&!this.yAxis.isDirty&&!a)return!1;if(m&&this.sorted&&(!k||d>k||this.forceCrop))if(a=i.getExtremes(),i=a.min,k=a.max,b[d-1]<i||b[0]>k)b=[],c=[];else if(b[0]<i||b[d-1]>k){for(a=0;a<d;a++)if(b[a]>=i){e=q(0,a-1);break}for(;a<d;a++)if(b[a]>k){f=a+1;break}b=b.slice(e,f);c=c.slice(e,f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===y||d<h))h=d;this.cropped=g;this.cropStart=e;
220
- this.processedXData=b;this.processedYData=c;if(j.pointRange===null)this.pointRange=h||1;this.closestPointRange=h},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,j=this.hasGroupedData,k,m=[],l;if(!b&&!j)b=[],b.length=a.length,b=this.data=b;for(l=0;l<g;l++)i=h+l,j?m[l]=(new f).init(this,[d[l]].concat(ha(e[l]))):(b[i]?k=b[i]:a[i]!==y&&(b[i]=k=(new f).init(this,a[i],d[l])),m[l]=k);if(b&&(g!==
221
- (c=b.length)||j))for(l=0;l<c;l++)if(l===h&&!j&&(l+=g),b[l])b[l].destroyElements(),b[l].plotX=y;this.data=b;this.points=m},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i,j,k=a.pointPlacement==="between",m=a.threshold;j=e.series.sort(function(a,b){return a.index-b.index});for(a=j.length;a--;)if(j[a].visible){j[a]===this&&(i=!0);break}for(a=0;a<
222
- g;a++){j=f[a];var l=j.x,p=j.y,n=j.low,q=e.stacks[(p<m?"-":"")+this.stackKey];if(e.isLog&&p<=0)j.y=p=null;j.plotX=c.translate(l,0,0,0,1,k);if(b&&this.visible&&q&&q[l])n=q[l],q=n.total,n.cum=n=n.cum-p,p=n+p,i&&(n=o(m,e.min)),e.isLog&&n<=0&&(n=null),b==="percent"&&(n=q?n*100/q:0,p=q?p*100/q:0),j.percentage=q?j.y*100/q:0,j.total=j.stackTotal=q,j.stackY=p;j.yBottom=r(n)?e.translate(n,0,1,0,1):null;h&&(p=this.modifyValue(p,j));j.plotY=typeof p==="number"&&p!==Infinity?t(e.translate(p,0,1,0,1)*10)/10:y;
223
- j.clientX=k?c.translate(l,0,0,0,1):j.plotX;j.negative=j.y<(m||0);j.category=d&&d[j.x]!==y?d[j.x]:j.x}this.getSegments()},setTooltipPoints:function(a){var b=[],c,d,e=(c=this.xAxis)?c.tooltipLen||c.len:this.chart.plotSizeX,f,g,h=[];if(this.options.enableMouseTracking!==!1){if(a)this.tooltipPoints=null;n(this.segments||this.points,function(a){b=b.concat(a)});c&&c.reversed&&(b=b.reverse());a=b.length;for(g=0;g<a;g++){f=b[g];c=b[g-1]?d+1:0;for(d=b[g+1]?q(0,T((f.clientX+(b[g+1]?b[g+1].clientX:e))/2)):e;c>=
224
- 0&&c<=d;)h[c++]=f}this.tooltipPoints=h}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=b.dateTimeLabelFormats,e=this.xAxis,f=e&&e.options.type==="datetime",b=b.headerFormat,e=e&&e.closestPointRange,g;if(f&&!c)if(e)for(g in E){if(E[g]>=e){c=d[g];break}}else c=d.day;f&&c&&Ea(a.key)&&(b=b.replace("{point.key}","{point.key:"+c+"}"));return wa(b,{point:a,series:this})},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&
225
- D(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&D(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!V(e))e=X[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h],
226
- e=c[h+"m"],a||(c[h]=a=d.clipRect(v(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()},e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip());
227
- setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,m=this.options.marker,l,n=this.markerGroup;if(m.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,i=g.marker||{},a=m.enabled&&i.enabled===y||i.enabled,l=c.isInsidePlot(t(d),e,c.inverted),a&&e!==y&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=o(i.symbol,this.symbol),j=i.indexOf("url")===
228
- 0,k)k.attr({visibility:l?Z?"inherit":"visible":"hidden"}).animate(v({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(l&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(n)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=o(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=X[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color,
229
- h={stroke:g,fill:g},i=a.points||[],j=[],k,m=a.pointAttrToOptions,l=b.negativeColor,p;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=e.color||ma(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,h);n(["hover","select"],function(b){j[b]=a.convertAttribs(d[b],j[""])});a.pointAttr=j;for(g=i.length;g--;){h=i[g];if((c=h.options&&h.options.marker||h.options)&&c.enabled===!1)c.radius=0;if(h.negative&&l)h.color=h.fillColor=l;f=b.colorByPoint||h.color;
230
- if(h.options)for(p in m)r(c[m[p]])&&(f=!0);if(f){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=ma(f.color||h.color).brighten(f.brightness||e.brightness).get();k[""]=a.convertAttribs(v({color:h.color},c),j[""]);k.hover=a.convertAttribs(d.hover,j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""]);if(h.negative&&b.marker&&l)k[""].fill=k.hover.fill=k.select.fill=a.convertAttribs({fillColor:l}).fill}else k=j;h.pointAttr=k}},update:function(a,b){var c=this.chart,d=
231
- this.type,a=x(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},a);this.remove(!1);v(this,aa[a.type||d].prototype);this.init(c,a);o(b,!0)&&c.redraw(!1)},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(Aa),d,e,f=a.data||[],g,h,i;D(a,"destroy");ba(a);n(["xAxis","yAxis"],function(b){if(i=a[b])ga(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);
232
- n("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;ga(b.series,a);for(h in a)delete a[h]},drawDataLabels:function(){var a=this,b=a.options.dataLabels,c=a.points,d,e,f,g;if(b.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(b),g=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",b.zIndex||6),e=b,n(c,function(c){var i,
233
- j=c.dataLabel,k,m,l=c.connector,n=!0;d=c.options&&c.options.dataLabels;i=e.enabled||d&&d.enabled;if(j&&!i)c.dataLabel=j.destroy();else if(i){i=b.rotation;b=x(e,d);k=c.getLabelConfig();f=b.format?wa(b.format,k):b.formatter.call(k,b);b.style.color=o(b.color,b.style.color,a.color,"black");if(j)if(r(f))j.attr({text:f}),n=!1;else{if(c.dataLabel=j=j.destroy(),l)c.connector=l.destroy()}else if(r(f)){j={fill:b.backgroundColor,stroke:b.borderColor,"stroke-width":b.borderWidth,r:b.borderRadius||0,rotation:i,
234
- padding:b.padding,zIndex:1};for(m in j)j[m]===y&&delete j[m];j=c.dataLabel=a.chart.renderer[i?"text":"label"](f,0,-999,null,null,null,b.useHTML).attr(j).css(b.style).add(g).shadow(b.shadow)}j&&a.alignDataLabel(c,j,b,null,n)}})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=o(a.plotX,-999),a=o(a.plotY,-999),i=b.getBBox(),d=v({x:g?f.plotWidth-a:h,y:t(g?f.plotHeight-h:a),width:0,height:0},d);v(c,{width:i.width,height:i.height});c.rotation?(d={align:c.align,x:d.x+c.x+d.width/2,y:d.y+
235
- c.y+d.height/2},b[e?"attr":"animate"](d)):b.align(c,null,d);b.attr({visibility:c.crop===!1||f.isInsidePlot(h,a,g)?f.renderer.isSVG?"inherit":"visible":"hidden"})},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;n(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c},
236
- getGraphPath:function(){var a=this,b=[],c,d=[];n(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=this.getGraphPath(),g=b.negativeColor;g&&c.push(["graphNeg",g]);n(c,function(c,g){var j=c[0],k=a[j];if(k)Ta(k),k.animate({d:f});else if(d&&f.length){k={stroke:c[1],"stroke-width":d,zIndex:1};if(e)k.dashstyle=e;a[j]=
237
- a.chart.renderer.path(f).attr(k).add(a.group).shadow(!g&&b.shadow)}})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=b.chartHeight,k=q(e,j);if(d&&(f||g))d=ja(this.yAxis.len-this.yAxis.translate(a.threshold||0)),a={x:0,y:0,width:k,height:d},k={x:0,y:d,width:k,height:k-d},b.inverted&&c.isVML&&(a={x:b.plotWidth-d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d,
238
- height:e}),this.yAxis.reversed?(b=k,e=a):(b=a,e=k),h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};n(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)J(c,"resize",a),J(b,"destroy",function(){ba(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b,
239
- c,d,e){var f=this[a],g=!f,h=this.chart,i=this.xAxis,j=this.yAxis;g&&(this[a]=f=h.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"]({translateX:i?i.left:h.plotLeft,translateY:j?j.top:h.plotTop,scaleX:1,scaleY:1});return f},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate&&a.renderer.isSVG,e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup",
240
- "markers",e,f,h);d&&this.animate(!0);this.getAttribs();b.inverted=this.isCartesian?a.inverted:!1;this.drawGraph&&(this.drawGraph(),this.clipNeg());this.drawDataLabels();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&&!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis,
241
- e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:o(d&&d.left,a.plotLeft),translateY:o(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints(!0);this.render();b&&D(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg,e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},setVisible:function(a,
242
- b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===y?!h:a)?"show":"hide";n(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&n(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});n(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;b!==!1&&d.redraw();D(c,
243
- f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===y?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;D(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.pointer,h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,k=k&&{cursor:k},m=a.singlePoints,l,n=function(){if(f.hoverSeries!==a)a.onMouseOver()};if(e&&!c)for(l=
244
- e+1;l--;)d[l]==="M"&&d.splice(l+1,0,d[l+1]-i,d[l+2],"L"),(l&&d[l]==="M"||l===e)&&d.splice(l,0,"L",d[l-2]+i,d[l-1]);for(l=0;l<m.length;l++)e=m[l],d.push("M",e.plotX-i,e.plotY,"L",e.plotX+i,e.plotY);if(j)j.attr({d:d});else if(a.tracker=j=h.path(d).attr({"class":"highcharts-tracker","stroke-linejoin":"round",visibility:a.visible?"visible":"hidden",stroke:Ob,fill:c?Ob:S,"stroke-width":b.lineWidth+(c?0:2*i),zIndex:2}).addClass("highcharts-tracker").on("mouseover",n).on("mouseout",function(a){g.onTrackerMouseOut(a)}).css(k).add(a.markerGroup),
245
- fb)j.on("touchstart",n)}};M=ea(R);aa.line=M;X.area=x(W,{threshold:0});M=ea(R,{type:"area",getSegments:function(){var a=[],b=[],c=[],d=this.xAxis,e=this.yAxis,f=e.stacks[this.stackKey],g={},h,i,j=this.points,k,m;if(this.options.stacking&&!this.cropped){for(k=0;k<j.length;k++)g[j[k].x]=j[k];for(m in f)c.push(+m);c.sort(function(a,b){return a-b});n(c,function(a){g[a]?b.push(g[a]):(h=d.translate(a),i=e.toPixels(f[a].cum,!0),b.push({y:null,plotX:h,clientX:h,plotY:i,yBottom:i,onMouseOver:ta}))});b.length&&
246
- a.push(b)}else R.prototype.getSegments.call(this),a=this.segments;this.segments=a},getSegmentPath:function(a){var b=R.prototype.getSegmentPath.call(this,a),c=[].concat(b),d,e=this.options;b.length===3&&c.push("L",b[1],b[2]);if(e.stacking&&!this.closedStacks)for(d=a.length-1;d>=0;d--)d<a.length-1&&e.step&&c.push(a[d+1].plotX,a[d].yBottom),c.push(a[d].plotX,a[d].yBottom);else this.closeSegment(c,a);this.areaPath=this.areaPath.concat(c);return b},closeSegment:function(a,b){var c=this.yAxis.getThreshold(this.options.threshold);
247
- a.push("L",b[b.length-1].plotX,c,"L",b[0].plotX,c)},drawGraph:function(){this.areaPath=[];R.prototype.drawGraph.apply(this);var a=this,b=this.areaPath,c=this.options,d=[["area",this.color,c.fillColor]];c.negativeColor&&d.push(["areaNeg",c.negativeColor,c.negativeFillColor]);n(d,function(d){var f=d[0],g=a[f];g?g.animate({d:b}):a[f]=a.chart.renderer.path(b).attr({fill:o(d[2],ma(d[1]).setOpacity(c.fillOpacity||0.75).get()),zIndex:0}).add(a.group)})},drawLegendSymbol:function(a,b){b.legendSymbol=this.chart.renderer.rect(0,
248
- a.baseline-11,a.options.symbolWidth,12,2).attr({zIndex:3}).add(b.legendGroup)}});aa.area=M;X.spline=x(W);F=ea(R,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,i,j,k;if(f&&g){a=f.plotY;j=g.plotX;var g=g.plotY,m;h=(1.5*d+f.plotX)/2.5;i=(1.5*e+a)/2.5;j=(1.5*d+j)/2.5;k=(1.5*e+g)/2.5;m=(k-i)*(j-d)/(j-h)+e-k;i+=m;k+=m;i>a&&i>e?(i=q(a,e),k=2*e-i):i<a&&i<e&&(i=K(a,e),k=2*e-i);k>g&&k>e?(k=q(g,e),i=2*e-k):k<g&&k<e&&(k=K(g,e),i=2*e-k);b.rightContX=j;b.rightContY=k}c?
249
- (b=["C",f.rightContX||f.plotX,f.rightContY||f.plotY,h||d,i||e,d,e],f.rightContX=f.rightContY=null):b=["M",d,e];return b}});aa.spline=F;X.areaspline=x(X.area);na=M.prototype;F=ea(F,{type:"areaspline",closedStacks:!0,getSegmentPath:na.getSegmentPath,closeSegment:na.closeSegment,drawGraph:na.drawGraph});aa.areaspline=F;X.column=x(W,{borderColor:"#FFFFFF",borderWidth:1,borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,
250
- shadow:!1},select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}},dataLabels:{align:null,verticalAlign:null,y:null},stickyTracking:!1,threshold:0});F=ea(R,{type:"column",tooltipOutsidePlot:!0,requireSorting:!1,pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color",r:"borderRadius"},trackerGroups:["group","dataLabelsGroup"],init:function(){R.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},
251
- getColumnMetrics:function(){var a=this,b=a.chart,c=a.options,d=this.xAxis,e=d.reversed,f,g={},h,i=0;c.grouping===!1?i=1:n(b.series,function(b){var c=b.options;if(b.type===a.type&&b.visible&&a.options.group===c.group)c.stacking?(f=b.stackKey,g[f]===y&&(g[f]=i++),h=g[f]):c.grouping!==!1&&(h=i++),b.columnIndex=h});var b=K(Q(d.transA)*(d.ordinalSlope||c.pointRange||d.closestPointRange||1),d.len),d=b*c.groupPadding,j=(b-2*d)/i,k=c.pointWidth,c=r(k)?(j-k)/2:j*c.pointPadding,k=o(k,j-2*c);return a.columnMetrics=
252
- {width:k,offset:c+(d+((e?i-(a.columnIndex||0):a.columnIndex)||0)*j-b/2)*(e?-1:1)}},translate:function(){var a=this,b=a.chart,c=a.options,d=c.stacking,e=c.borderWidth,f=a.yAxis,g=a.translatedThreshold=f.getThreshold(c.threshold),h=o(c.minPointLength,5),c=a.getColumnMetrics(),i=c.width,j=ja(q(i,1+2*e)),k=c.offset;R.prototype.translate.apply(a);n(a.points,function(c){var l=K(q(-999,c.plotY),f.len+999),n=o(c.yBottom,g),s=c.plotX+k,t=ja(K(l,n)),l=ja(q(l,n)-t),r=f.stacks[(c.y<0?"-":"")+a.stackKey];d&&a.visible&&
253
- r&&r[c.x]&&r[c.x].setOffset(k,j);Q(l)<h&&h&&(l=h,t=Q(t-g)>h?n-h:g-(f.translate(c.y,0,1,0,1)<=g?h:0));c.barX=s;c.pointWidth=i;c.shapeType="rect";c.shapeArgs=c=b.renderer.Element.prototype.crisp.call(0,e,s,t,j,l);e%2&&(c.y-=1,c.height+=1)})},getSymbol:ta,drawLegendSymbol:M.prototype.drawLegendSymbol,drawGraph:ta,drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d;n(a.points,function(e){var f=e.plotY,g=e.graphic;if(f!==y&&!isNaN(f)&&e.y!==null)d=e.shapeArgs,g?(Ta(g),g.animate(x(d))):e.graphic=
254
- c[e.shapeType](d).attr(e.pointAttr[e.selected?"select":""]).add(a.group).shadow(b.shadow,null,b.stacking&&!b.borderRadius);else if(g)e.graphic=g.destroy()})},drawTracker:function(){var a=this,b=a.chart.pointer,c=a.options.cursor,d=c&&{cursor:c},e=function(b){var c=b.target,d;for(a.onMouseOver();c&&!d;)d=c.point,c=c.parentNode;if(d!==y)d.onMouseOver(b)};n(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});a._hasTracking?a._hasTracking=!0:n(a.trackerGroups,
255
- function(c){if(a[c]&&(a[c].addClass("highcharts-tracker").on("mouseover",e).on("mouseout",function(a){b.onTrackerMouseOut(a)}).css(d),fb))a[c].on("touchstart",e)})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>o(this.translatedThreshold,f.plotSizeY),j=o(c.inside,!!this.options.stacking);if(h&&(d=x(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:
256
- 0,d.height=0);c.align=o(c.align,!g||j?"center":i?"right":"left");c.verticalAlign=o(c.verticalAlign,g||j?"middle":i?"top":"bottom");R.prototype.alignDataLabel.call(this,a,b,c,d,e)},animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(Z)a?(e.scaleY=0.001,a=K(b.pos+b.len,q(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)},
257
- remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});R.prototype.remove.apply(a,arguments)}});aa.column=F;X.bar=x(X.column);na=ea(F,{type:"bar",inverted:!0});aa.bar=na;X.scatter=x(W,{lineWidth:0,tooltip:{headerFormat:'<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>",followPointer:!0},stickyTracking:!1});na=ea(R,{type:"scatter",sorted:!1,requireSorting:!1,
258
- noSharedTooltip:!0,trackerGroups:["markerGroup"],drawTracker:F.prototype.drawTracker,setTooltipPoints:ta});aa.scatter=na;X.pie=x(W,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});W={type:"pie",isCartesian:!1,
259
- pointClass:ea(Na,{init:function(){Na.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;v(a,{visible:a.visible!==!1,name:o(a.name,"Slice")});b=function(){a.slice()};J(a,"select",b);J(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart,e;b.visible=b.options.visible=a=a===y?!b.visible:a;c.options.data[la(b,c.data)]=b.options;e=a?"show":"hide";n(["graphic","dataLabel","connector","shadowGroup"],function(a){if(b[a])b[a][e]()});b.legendItem&&d.legend.colorizeItem(b,
260
- a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;Ia(c,d.chart);o(b,!0);this.sliced=this.options.sliced=a=r(a)?a:!this.sliced;d.options.data[la(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",
261
- fill:"color"},getColor:ta,animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b){R.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();o(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-
262
- 2*c,b=a.center,a=[o(b[0],"50%"),o(b[1],"50%"),a.size||"100%",a.innerSize||0],g=K(e,f),h;return La(a,function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*u(a)/100:a)+(d?c:0)})},translate:function(a){this.generatePoints();var b=0,c=0,d=this.options,e=d.slicedOffset,f=e+d.borderWidth,g,h,i,j=this.startAngleRad=Ka/180*((d.startAngle||0)%360-90),k=this.points,m=2*Ka,l=d.dataLabels.distance,n=d.ignoreHiddenPoint,o,q=k.length,r;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){i=
263
- I.asin((b-a[1])/(a[2]/2+l));return a[0]+(c?-1:1)*Y(i)*(a[2]/2+l)};for(o=0;o<q;o++)r=k[o],b+=n&&!r.visible?0:r.y;for(o=0;o<q;o++){r=k[o];d=b?r.y/b:0;g=t((j+c*m)*1E3)/1E3;if(!n||r.visible)c+=d;h=t((j+c*m)*1E3)/1E3;r.shapeType="arc";r.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:g,end:h};i=(h+g)/2;i>0.75*m&&(i-=2*Ka);r.slicedTranslation={translateX:t(Y(i)*e),translateY:t(ca(i)*e)};g=Y(i)*a[2]/2;h=ca(i)*a[2]/2;r.tooltipPos=[a[0]+g*0.7,a[1]+h*0.7];r.half=i<m/4?0:1;r.angle=i;f=K(f,l/2);r.labelPos=
264
- [a[0]+g+Y(i)*l,a[1]+h+ca(i)*l,a[0]+g+Y(i)*f,a[1]+h+ca(i)*f,a[0]+g,a[1]+h,l<0?"center":r.half?"right":"left",i];r.percentage=d*100;r.total=b}this.setTooltipPoints()},drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);n(a.points,function(h){d=h.graphic;g=h.shapeArgs;f=h.shadowGroup;if(e&&!f)f=h.shadowGroup=b.g("shadow").add(a.shadowGroup);c=h.sliced?h.slicedTranslation:{translateX:0,translateY:0};
265
- f&&f.attr(c);d?d.animate(v(g,c)):h.graphic=d=b.arc(g).setRadialReference(a.center).attr(h.pointAttr[h.selected?"select":""]).attr({"stroke-linejoin":"round"}).attr(c).add(a.group).shadow(e,f);h.visible===!1&&h.setVisible(!1)})},drawDataLabels:function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=o(e.connectorPadding,10),g=o(e.connectorWidth,1),h=d.plotWidth,d=d.plotHeight,i,j,k=o(e.softConnector,!0),m=e.distance,l=a.center,p=l[2]/2,s=l[1],r=m>0,v,w,u,x,y=[[],[]],A,z,E,H,C,D=[0,0,0,0],
266
- K=function(a,b){return b.y-a.y},M=function(a,b){a.sort(function(a,c){return a.angle!==void 0&&(c.angle-a.angle)*b})};if(e.enabled||a._hasPointLabels){R.prototype.drawDataLabels.apply(a);n(b,function(a){a.dataLabel&&y[a.half].push(a)});for(H=0;!x&&b[H];)x=b[H]&&b[H].dataLabel&&(b[H].dataLabel.getBBox().height||21),H++;for(H=2;H--;){var b=[],L=[],I=y[H],J=I.length,F;M(I,H-0.5);if(m>0){for(C=s-p-m;C<=s+p+m;C+=x)b.push(C);w=b.length;if(J>w){c=[].concat(I);c.sort(K);for(C=J;C--;)c[C].rank=C;for(C=J;C--;)I[C].rank>=
267
- w&&I.splice(C,1);J=I.length}for(C=0;C<J;C++){c=I[C];u=c.labelPos;c=9999;var O,N;for(N=0;N<w;N++)O=Q(b[N]-u[1]),O<c&&(c=O,F=N);if(F<C&&b[C]!==null)F=C;else for(w<J-C+F&&b[C]!==null&&(F=w-J+C);b[F]===null;)F++;L.push({i:F,y:b[F]});b[F]=null}L.sort(K)}for(C=0;C<J;C++){c=I[C];u=c.labelPos;v=c.dataLabel;E=c.visible===!1?"hidden":"visible";c=u[1];if(m>0){if(w=L.pop(),F=w.i,z=w.y,c>z&&b[F+1]!==null||c<z&&b[F-1]!==null)z=c}else z=c;A=e.justify?l[0]+(H?-1:1)*(p+m):a.getX(F===0||F===b.length-1?c:z,H);v._attr=
268
- {visibility:E,align:u[6]};v._pos={x:A+e.x+({left:f,right:-f}[u[6]]||0),y:z+e.y-10};v.connX=A;v.connY=z;if(this.options.size===null)w=v.width,A-w<f?D[3]=q(t(w-A+f),D[3]):A+w>h-f&&(D[1]=q(t(A+w-h+f),D[1])),z-x/2<0?D[0]=q(t(-z+x/2),D[0]):z+x/2>d&&(D[2]=q(t(z+x/2-d),D[2]))}}if(pa(D)===0||this.verifyDataLabelOverflow(D))this.placeDataLabels(),r&&g&&n(this.points,function(b){i=b.connector;u=b.labelPos;if((v=b.dataLabel)&&v._pos)E=v._attr.visibility,A=v.connX,z=v.connY,j=k?["M",A+(u[6]==="left"?5:-5),z,
269
- "C",A,z,2*u[2]-u[4],2*u[3]-u[5],u[2],u[3],"L",u[4],u[5]]:["M",A+(u[6]==="left"?5:-5),z,"L",u[2],u[3],"L",u[4],u[5]],i?(i.animate({d:j}),i.attr("visibility",E)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:E}).add(a.group);else if(i)b.connector=i.destroy()})}},verifyDataLabelOverflow:function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=q(b[2]-q(a[1],a[3]),c):(e=q(b[2]-a[1]-a[3],c),b[0]+=(a[3]-
270
- a[1])/2);d[1]!==null?e=q(K(e,b[2]-q(a[0],a[2])),c):(e=q(K(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e<b[2]?(b[2]=e,this.translate(b),n(this.points,function(a){if(a.dataLabel)a.dataLabel._pos=null}),this.drawDataLabels()):f=!0;return f},placeDataLabels:function(){n(this.points,function(a){var a=a.dataLabel,b;if(a)(b=a._pos)?(a.attr(a._attr),a[a.moved?"animate":"attr"](b),a.moved=!0):a&&a.attr({y:-999})})},alignDataLabel:ta,drawTracker:F.prototype.drawTracker,drawLegendSymbol:M.prototype.drawLegendSymbol,
271
- getSymbol:ta};W=ea(R,W);aa.pie=W;v(Highcharts,{Axis:ab,Chart:tb,Color:ma,Legend:sb,Pointer:rb,Point:Na,Tick:Ja,Tooltip:qb,Renderer:Sa,Series:R,SVGElement:ra,SVGRenderer:Ca,arrayMin:Ga,arrayMax:pa,charts:Ba,dateFormat:Ua,format:wa,pathAnim:vb,getOptions:function(){return N},hasBidiBug:Sb,isTouchDevice:Mb,numberFormat:ua,seriesTypes:aa,setOptions:function(a){N=x(N,a);Jb();return N},addEvent:J,removeEvent:ba,createElement:U,discardElement:Ra,css:L,each:n,extend:v,map:La,merge:x,pick:o,splat:ha,extendClass:ea,
272
- pInt:u,wrap:zb,svg:Z,canvas:$,vml:!Z&&!$,product:"Highcharts",version:"3.0.2"})})();
1
+ /*
2
+ Highcharts JS v3.0.2 (2013-06-05)
3
+
4
+ (c) 2009-2013 Torstein Hønsi
5
+
6
+ License: www.highcharts.com/license
7
+ */
8
+ (function(){function v(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function x(){var a,b=arguments.length,c={},d=function(a,b){var c,h;for(h in b)b.hasOwnProperty(h)&&(c=b[h],typeof a!=="object"&&(a={}),a[h]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&typeof c.nodeType!=="number"?d(a[h]||{},c):b[h]);return a};for(a=0;a<b;a++)c=d(c,arguments[a]);return c}function u(a,b){return parseInt(a,b||10)}function fa(a){return typeof a==="string"}function V(a){return typeof a===
9
+ "object"}function Da(a){return Object.prototype.toString.call(a)==="[object Array]"}function Ea(a){return typeof a==="number"}function ka(a){return I.log(a)/I.LN10}function da(a){return I.pow(10,a)}function ga(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function r(a){return a!==y&&a!==null}function A(a,b,c){var d,e;if(fa(b))r(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(r(b)&&V(b))for(d in b)a.setAttribute(d,b[d]);return e}function ha(a){return Da(a)?
10
+ a:[a]}function o(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function L(a,b){if(Fa&&b&&b.opacity!==y)b.filter="alpha(opacity="+b.opacity*100+")";v(a.style,b)}function U(a,b,c,d,e){a=z.createElement(a);b&&v(a,b);e&&L(a,{padding:0,border:S,margin:0});c&&L(a,c);d&&d.appendChild(a);return a}function ea(a,b){var c=function(){};c.prototype=new a;v(c.prototype,b);return c}function ua(a,b,c,d){var e=N.lang,f=b===-1?((a||0).toString().split(".")[1]||
11
+ "").length:isNaN(b=Q(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(u(a=Q(+a||0).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+Q(a-c).toFixed(f).slice(2):"")}function va(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function zb(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments);a.unshift(d);return c.apply(this,a)}}function wa(a,b){for(var c="{",
12
+ d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h<i;h++)e=e[g[h]];if(f.length)f=f.join(":"),g=/\.([0-9])/,h=N.lang,i=void 0,/f$/.test(f)?(i=(i=f.match(g))?i[1]:-1,e=ua(e,i,h.decimalPoint,f.indexOf(",")>-1?h.thousandsSep:"")):e=Ua(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function ib(a,b,c,d){var e,c=o(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=
13
+ [1/c])));for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function Ab(a,b){var c=b||[[Bb,[1,2,5,10,20,25,50,100,200,500]],[jb,[1,2,5,10,15,30]],[Va,[1,2,5,10,15,30]],[Oa,[1,2,3,4,6,8,12]],[oa,[1,2]],[Wa,[1,2]],[Pa,[1,2,3,4,6]],[xa,null]],d=c[c.length-1],e=E[d[0]],f=d[1],g;for(g=0;g<c.length;g++)if(d=c[g],e=E[d[0]],f=d[1],c[g+1]&&a<=(e*f[f.length-1]+E[c[g+1][0]])/2)break;e===E[xa]&&a<5*e&&(f=[1,2,5]);e===E[xa]&&a<5*e&&(f=[1,2,5]);c=ib(a/e,f);return{unitRange:e,count:c,
14
+ unitName:d[0]}}function Cb(a,b,c,d){var e=[],f={},g=N.global.useUTC,h,i=new Date(b),j=a.unitRange,k=a.count;if(r(b)){j>=E[jb]&&(i.setMilliseconds(0),i.setSeconds(j>=E[Va]?0:k*T(i.getSeconds()/k)));if(j>=E[Va])i[Db](j>=E[Oa]?0:k*T(i[kb]()/k));if(j>=E[Oa])i[Eb](j>=E[oa]?0:k*T(i[lb]()/k));if(j>=E[oa])i[mb](j>=E[Pa]?1:k*T(i[Qa]()/k));j>=E[Pa]&&(i[Fb](j>=E[xa]?0:k*T(i[Xa]()/k)),h=i[Ya]());j>=E[xa]&&(h-=h%k,i[Gb](h));if(j===E[Wa])i[mb](i[Qa]()-i[nb]()+o(d,1));b=1;h=i[Ya]();for(var d=i.getTime(),m=i[Xa](),
15
+ l=i[Qa](),p=g?0:(864E5+i.getTimezoneOffset()*6E4)%864E5;d<c;)e.push(d),j===E[xa]?d=Za(h+b*k,0):j===E[Pa]?d=Za(h,m+b*k):!g&&(j===E[oa]||j===E[Wa])?d=Za(h,m,l+b*k*(j===E[oa]?1:7)):d+=j*k,b++;e.push(d);n(ob(e,function(a){return j<=E[Oa]&&a%E[oa]===p}),function(a){f[a]=oa})}e.info=v(a,{higherRanks:f,totalRange:j*k});return e}function Hb(){this.symbol=this.color=0}function Ib(a,b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}
16
+ function Ga(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function pa(a){for(var b=a.length,c=a[0];b--;)a[b]>c&&(c=a[b]);return c}function Ha(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Ra(a){$a||($a=U(ya));a&&$a.appendChild(a);$a.innerHTML=""}function qa(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else O.console&&console.log(c)}function ia(a){return parseFloat(a.toPrecision(14))}function Ia(a,b){za=o(a,b.animation)}
17
+ function Jb(){var a=N.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Za=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,o(c,1),o(g,0),o(h,0),o(i,0))).getTime()};kb=b+"Minutes";lb=b+"Hours";nb=b+"Day";Qa=b+"Date";Xa=b+"Month";Ya=b+"FullYear";Db=c+"Minutes";Eb=c+"Hours";mb=c+"Date";Fb=c+"Month";Gb=c+"FullYear"}function ra(){}function Ja(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function pb(a,b){this.axis=a;if(b)this.options=b,this.id=b.id}function Kb(a,
18
+ b,c,d,e,f){var g=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.percent=f==="percent";this.alignOptions={align:b.align||(g?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(g?"middle":c?"bottom":"top"),y:o(b.y,g?4:c?14:-6),x:o(b.x,g?c?-6:6:0)};this.textAlign=b.textAlign||(g?c?"right":"left":"center")}function ab(){this.init.apply(this,arguments)}function qb(){this.init.apply(this,arguments)}function rb(a,b){this.init(a,b)}function sb(a,b){this.init(a,
19
+ b)}function tb(){this.init.apply(this,arguments)}var y,z=document,O=window,I=Math,t=I.round,T=I.floor,ja=I.ceil,q=I.max,K=I.min,Q=I.abs,Y=I.cos,ca=I.sin,Ka=I.PI,bb=Ka*2/360,Aa=navigator.userAgent,Lb=O.opera,Fa=/msie/i.test(Aa)&&!Lb,cb=z.documentMode===8,db=/AppleWebKit/.test(Aa),eb=/Firefox/.test(Aa),Mb=/(Mobile|Android|Windows Phone)/.test(Aa),sa="http://www.w3.org/2000/svg",Z=!!z.createElementNS&&!!z.createElementNS(sa,"svg").createSVGRect,Sb=eb&&parseInt(Aa.split("Firefox/")[1],10)<4,$=!Z&&!Fa&&
20
+ !!z.createElement("canvas").getContext,Sa,fb=z.documentElement.ontouchstart!==y,Nb={},ub=0,$a,N,Ua,za,vb,E,ta=function(){},Ba=[],ya="div",S="none",Ob="rgba(192,192,192,"+(Z?1.0E-4:0.002)+")",Bb="millisecond",jb="second",Va="minute",Oa="hour",oa="day",Wa="week",Pa="month",xa="year",Pb="stroke-width",Za,kb,lb,nb,Qa,Xa,Ya,Db,Eb,mb,Fb,Gb,aa={};O.Highcharts=O.Highcharts?qa(16,!0):{};Ua=function(a,b,c){if(!r(b)||isNaN(b))return"Invalid date";var a=o(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[lb](),g=d[nb](),
21
+ h=d[Qa](),i=d[Xa](),j=d[Ya](),k=N.lang,m=k.weekdays,d=v({a:m[g].substr(0,3),A:m[g],d:va(h),e:h,b:k.shortMonths[i],B:k.months[i],m:va(i+1),y:j.toString().substr(2,2),Y:j,H:va(f),I:va(f%12||12),l:f%12||12,M:va(d[kb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:va(d.getSeconds()),L:va(t(b%1E3),3)},Highcharts.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]==="function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Hb.prototype={wrapColor:function(a){if(this.color>=
22
+ a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};E=function(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}(Bb,1,jb,1E3,Va,6E4,Oa,36E5,oa,864E5,Wa,6048E5,Pa,26784E5,xa,31556952E3);vb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-
23
+ 6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length<a;)d=[].concat(b).splice(b.length-f,f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(i));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};(function(a){O.HighchartsAdapter=O.HighchartsAdapter||a&&{init:function(b){var c=
24
+ a.fx,d=c.step,e,f=a.Tween,g=f&&f.propHooks;e=a.cssHooks.opacity;a.extend(a.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});a.each(["cur","_default","width","height","opacity"],function(a,b){var e=d,k,m;b==="cur"?e=c.prototype:b==="_default"&&f&&(e=g[b],b="set");(k=e[b])&&(e[b]=function(c){c=a?c:this;m=c.elem;return m.attr?m.attr(c.prop,b==="cur"?y:c.now):k.apply(this,arguments)})});zb(e,"get",function(a,b,c){return b.attr?b.opacity||0:a.call(this,b,c)});e=function(a){var c=a.elem,
25
+ d;if(!a.started)d=b.init(c,c.d,c.toD),a.start=d[0],a.end=d[1],a.started=!0;c.attr("d",b.step(a.start,a.end,a.pos,c.toD))};f?g.d={set:e}:d.d=e;this.each=Array.prototype.forEach?function(a,b){return Array.prototype.forEach.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],a[c],c,a)===!1)return c};a.fn.highcharts=function(){var a="Chart",b=arguments,c,d;fa(b[0])&&(a=b[0],b=Array.prototype.slice.call(b,1));c=b[0];if(c!==y)c.chart=c.chart||{},c.chart.renderTo=this[0],new Highcharts[a](c,
26
+ b[1]),d=this;c===y&&(d=Ba[A(this[0],"data-highcharts-chart")]);return d}},getScript:a.getScript,inArray:a.inArray,adapterRun:function(b,c){return a(b)[c]()},grep:a.grep,map:function(a,c){for(var d=[],e=0,f=a.length;e<f;e++)d[e]=c.call(a[e],a[e],e,a);return d},offset:function(b){return a(b).offset()},addEvent:function(b,c,d){a(b).bind(c,d)},removeEvent:function(b,c,d){var e=z.removeEventListener?"removeEventListener":"detachEvent";z[e]&&b&&!b[e]&&(b[e]=function(){});a(b).unbind(c,d)},fireEvent:function(b,
27
+ c,d,e){var f=a.Event(c),g="detached"+c,h;!Fa&&d&&(delete d.layerX,delete d.layerY);v(f,d);b[c]&&(b[g]=b[c],b[c]=null);a.each(["preventDefault","stopPropagation"],function(a,b){var c=f[b];f[b]=function(){try{c.call(f)}catch(a){b==="preventDefault"&&(h=!0)}}});a(b).trigger(f);b[g]&&(b[c]=b[g],b[g]=null);e&&!f.isDefaultPrevented()&&!h&&e(f)},washMouseEvent:function(a){var c=a.originalEvent||a;if(c.pageX===y)c.pageX=a.pageX,c.pageY=a.pageY;return c},animate:function(b,c,d){var e=a(b);if(!b.style)b.style=
28
+ {};if(c.d)b.toD=c.d,c.d=1;e.stop();e.animate(c,d)},stop:function(b){a(b).stop()}}})(O.jQuery);var W=O.HighchartsAdapter,M=W||{};W&&W.init.call(W,vb);var gb=M.adapterRun,Tb=M.getScript,la=M.inArray,n=M.each,ob=M.grep,Ub=M.offset,La=M.map,J=M.addEvent,ba=M.removeEvent,D=M.fireEvent,Qb=M.washMouseEvent,wb=M.animate,Ta=M.stop,M={enabled:!0,align:"center",x:0,y:15,style:{color:"#666",cursor:"default",fontSize:"11px",lineHeight:"14px"}};N={colors:"#2f7ed8,#0d233a,#8bbc21,#910000,#1aadce,#492970,#f28f43,#77a1e5,#c42525,#a6c96a".split(","),
29
+ symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),shortMonths:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",numericSymbols:"k,M,G,T,P,E".split(","),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0,
30
+ canvasToolsURL:"http://code.highcharts.com/3.0.2/modules/canvas-tools.js",VMLRadialGradientURL:"http://code.highcharts.com/3.0.2/gfx/vml-radial-gradient.png"},chart:{borderColor:"#4572A7",borderRadius:5,defaultSeriesType:"line",ignoreHiddenSeries:!0,spacingTop:10,spacingRight:10,spacingBottom:15,spacingLeft:10,style:{fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif',fontSize:"12px"},backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0",resetZoomButton:{theme:{zIndex:20},
31
+ position:{align:"right",x:-10,y:10}}},title:{text:"Chart title",align:"center",y:15,style:{color:"#274b6d",fontSize:"16px"}},subtitle:{text:"",align:"center",y:30,style:{color:"#4d759e"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},lineWidth:2,marker:{enabled:!0,lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{enabled:!0},select:{fillColor:"#FFFFFF",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:x(M,{enabled:!1,formatter:function(){return ua(this.y,
32
+ -1)},verticalAlign:"bottom",y:0}),cropThreshold:300,pointRange:0,showInLegend:!0,states:{hover:{marker:{}},select:{marker:{}}},stickyTracking:!0}},labels:{style:{position:"absolute",color:"#3E576F"}},legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderWidth:1,borderColor:"#909090",borderRadius:5,navigation:{activeColor:"#274b6d",inactiveColor:"#CCC"},shadow:!1,itemStyle:{cursor:"pointer",color:"#274b6d",fontSize:"12px"},itemHoverStyle:{color:"#000"},
33
+ itemHiddenStyle:{color:"#CCC"},itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},symbolWidth:16,symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold",position:"relative",top:"1em"},style:{position:"absolute",backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,animation:Z,backgroundColor:"rgba(255, 255, 255, .85)",borderWidth:1,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",
34
+ second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},headerFormat:'<span style="font-size: 10px">{point.key}</span><br/>',pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',shadow:!0,snap:Mb?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",
35
+ position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var X=N.plotOptions,W=X.line;Jb();var ma=function(a){var b=[],c,d;(function(a){a&&a.stops?d=La(a.stops,function(a){return ma(a[1])}):(c=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(a))?b=[u(c[1]),u(c[2]),u(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))?b=[u(c[1],16),u(c[2],16),u(c[3],
36
+ 16),1]:(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(a))&&(b=[u(c[1]),u(c[2]),u(c[3]),1])})(a);return{get:function(c){var f;d?(f=x(a),f.stops=[].concat(f.stops),n(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})):f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)n(d,function(b){b.brighten(a)});else if(Ea(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=u(a*255),b[c]<0&&(b[c]=0),b[c]>255&&
37
+ (b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};ra.prototype={init:function(a,b){this.element=b==="span"?U(b):z.createElementNS(sa,b);this.renderer=a;this.attrSetters={}},opacity:1,animate:function(a,b,c){b=o(b,za,!0);Ta(this);if(b){b=x(b);if(c)b.complete=c;wb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName.toLowerCase(),i=this.renderer,j,k=this.attrSetters,m=this.shadows,l,p,s=this;fa(a)&&r(b)&&(c=a,a={},a[c]=b);if(fa(a))c=
38
+ a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),s=A(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&(s=parseFloat(s));else{for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==y&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text")for(e=0;e<g.childNodes.length;e++)f=g.childNodes[e],A(f,"x")===A(g,"x")&&A(f,"x",d);else if(this.rotation&&(c==="x"||c==="y"))p=!0;else if(c==="fill")d=i.color(d,g,c);else if(h===
39
+ "circle"&&(c==="x"||c==="y"))c={x:"cx",y:"cy"}[c]||c;else if(h==="rect"&&c==="r")A(g,{rx:d,ry:d}),j=!0;else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="verticalAlign"||c==="scaleX"||c==="scaleY")j=p=!0;else if(c==="stroke")d=i.color(d,g,c);else if(c==="dashstyle")if(c="stroke-dasharray",d=d&&d.toLowerCase(),d==="solid")d=S;else{if(d){d=d.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash",
40
+ "8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(e=d.length;e--;)d[e]=u(d[e])*a["stroke-width"];d=d.join(",")}}else if(c==="width")d=u(d);else if(c==="align")c="text-anchor",d={left:"start",center:"middle",right:"end"}[d];else if(c==="title")e=g.getElementsByTagName("title")[0],e||(e=z.createElementNS(sa,"title"),g.appendChild(e)),e.textContent=d;c==="strokeWidth"&&(c="stroke-width");if(c==="stroke-width"||c==="stroke"){this[c]=d;if(this.stroke&&this["stroke-width"])A(g,
41
+ "stroke",this.stroke),A(g,"stroke-width",this["stroke-width"]),this.hasStroke=!0;else if(c==="stroke-width"&&d===0&&this.hasStroke)g.removeAttribute("stroke"),this.hasStroke=!1;j=!0}this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(c)&&(l||(this.symbolAttr(a),l=!0),j=!0);if(m&&/^(width|height|visibility|x|y|d|transform)$/.test(c))for(e=m.length;e--;)A(m[e],c,c==="height"?q(d-(m[e].cutHeight||0),0):d);if((c==="width"||c==="height")&&h==="rect"&&d<0)d=0;this[c]=d;c==="text"?
42
+ (d!==this.textStr&&delete this.bBox,this.textStr=d,this.added&&i.buildText(this)):j||A(g,c,d)}p&&this.updateTransform()}return s},addClass:function(a){A(this.element,"class",A(this.element,"class")+" "+a);return this},symbolAttr:function(a){var b=this;n("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=o(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+
43
+ a.id+")":S)},crisp:function(a,b,c,d,e){var f,g={},h={},i,a=a||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;i=t(a)%2/2;h.x=T(b||this.x||0)+i;h.y=T(c||this.y||0)+i;h.width=T((d||this.width||0)-2*i);h.height=T((e||this.height||0)-2*i);h.strokeWidth=a;for(f in h)this[f]!==h[f]&&(this[f]=g[f]=h[f]);return g},css:function(a){var b=this.element,c=a&&a.width&&b.nodeName.toLowerCase()==="text",d,e="",f=function(a,b){return"-"+b.toLowerCase()};if(a&&a.color)a.fill=a.color;this.styles=a=v(this.styles,
44
+ a);$&&c&&delete a.width;if(Fa&&!Z)c&&delete a.width,L(this.element,a);else{for(d in a)e+=d.replace(/([A-Z])/g,f)+":"+a[d]+";";A(b,"style",e)}c&&this.added&&this.renderer.buildText(this);return this},on:function(a,b){if(fb&&a==="click")this.element.ontouchstart=function(a){a.preventDefault();b()};this.element["on"+a]=b;return this},setRadialReference:function(a){this.element.radialReference=a;return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=
45
+ !0;this.updateTransform();return this},htmlCss:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=v(this.styles,a);L(this.element,a);return this},htmlGetBBox:function(){var a=this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position="absolute";b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b},htmlUpdateTransform:function(){if(this.added){var a=this.renderer,
46
+ b=this.element,c=this.translateX||0,d=this.translateY||0,e=this.x||0,f=this.y||0,g=this.textAlign||"left",h={left:0,center:0.5,right:1}[g],i=g&&g!=="left",j=this.shadows;L(b,{marginLeft:c,marginTop:d});j&&n(j,function(a){L(a,{marginLeft:c+1,marginTop:d+1})});this.inverted&&n(b.childNodes,function(c){a.invertChild(c,b)});if(b.tagName==="SPAN"){var k,m,j=this.rotation,l,p=0,s=1,p=0,xb;l=u(this.textWidth);var B=this.xCorr||0,w=this.yCorr||0,G=[j,g,b.innerHTML,this.textWidth].join(",");k={};if(G!==this.cTT){if(r(j))a.isSVG?
47
+ (B=Fa?"-ms-transform":db?"-webkit-transform":eb?"MozTransform":Lb?"-o-transform":"",k[B]=k.transform="rotate("+j+"deg)"):(p=j*bb,s=Y(p),p=ca(p),k.filter=j?["progid:DXImageTransform.Microsoft.Matrix(M11=",s,", M12=",-p,", M21=",p,", M22=",s,", sizingMethod='auto expand')"].join(""):S),L(b,k);k=o(this.elemWidth,b.offsetWidth);m=o(this.elemHeight,b.offsetHeight);if(k>l&&/[ \-]/.test(b.textContent||b.innerText))L(b,{width:l+"px",display:"block",whiteSpace:"normal"}),k=l;l=a.fontMetrics(b.style.fontSize).b;
48
+ B=s<0&&-k;w=p<0&&-m;xb=s*p<0;B+=p*l*(xb?1-h:h);w-=s*l*(j?xb?h:1-h:1);i&&(B-=k*h*(s<0?-1:1),j&&(w-=m*h*(p<0?-1:1)),L(b,{textAlign:g}));this.xCorr=B;this.yCorr=w}L(b,{left:e+B+"px",top:f+w+"px"});if(db)m=b.offsetHeight;this.cTT=G}}else this.alignOnAdd=!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):
49
+ f&&a.push("rotate("+f+" "+(this.x||0)+" "+(this.y||0)+")");(r(c)||r(d))&&a.push("scale("+o(c,1)+" "+o(d,1)+")");a.length&&A(this.element,"transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||fa(c))this.alignTo=d=c||"renderer",ga(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;
50
+ c=o(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=t(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=t(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d=this.rotation;c=this.element;var e=this.styles,f=d*bb;if(!a){if(c.namespaceURI===
51
+ sa||b.forExport){try{a=c.getBBox?v({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(g){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){b=a.width;c=a.height;if(Fa&&e&&e.fontSize==="11px"&&c.toPrecision(3)==="22.7")a.height=c=14;if(d)a.width=Q(c*ca(f))+Q(b*Y(f)),a.height=Q(c*Y(f))+Q(b*ca(f))}this.bBox=a}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;
52
+ b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=A(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=u(g);if(c.handleZ)for(c=0;c<e.length;c++)if(a=e[c],b=A(a,"zIndex"),a!==f&&(u(b)>g||!r(g)&&r(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;D(this,"add");return this},safeRemoveChild:function(a){var b=
53
+ a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d,e;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;Ta(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(e=0;e<a.stops.length;e++)a.stops[e]=a.stops[e].destroy();a.stops=null}a.safeRemoveChild(b);c&&n(c,function(b){a.safeRemoveChild(b)});a.alignTo&&ga(a.renderer.alignedObjects,a);for(d in a)delete a[d];return null},shadow:function(a,b,c){var d=[],e,f,g=this.element,h,i,j,k;if(a){i=
54
+ o(a.width,3);j=(a.opacity||0.15)/i;k=this.parentInverted?"(-1,-1)":"("+o(a.offsetX,1)+", "+o(a.offsetY,1)+")";for(e=1;e<=i;e++){f=g.cloneNode(0);h=i*2+1-2*e;A(f,{isShadow:"true",stroke:a.color||"black","stroke-opacity":j*e,"stroke-width":h,transform:"translate"+k,fill:S});if(c)A(f,"height",q(A(f,"height")-h,0)),f.cutHeight=h;b?b.element.appendChild(f):g.parentNode.insertBefore(f,g);d.push(f)}this.shadows=d}return this}};var Ca=function(){this.init.apply(this,arguments)};Ca.prototype={Element:ra,init:function(a,
55
+ b,c,d){var e=location,f;f=this.createElement("svg").attr({xmlns:sa,version:"1.1"});a.appendChild(f.element);this.isSVG=!0;this.box=f.element;this.boxWrapper=f;this.alignedObjects=[];this.url=(eb||db)&&z.getElementsByTagName("base").length?e.href.replace(/#.*?$/,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(z.createTextNode("Created with Highcharts 3.0.2"));this.defs=this.createElement("defs").add();this.forExport=d;this.gradients={};this.setSize(b,
56
+ c,!1);var g;if(eb&&a.getBoundingClientRect)this.subPixelFix=b=function(){L(a,{left:0,top:0});g=a.getBoundingClientRect();L(a,{left:ja(g.left)-g.left+"px",top:ja(g.top)-g.top+"px"})},b(),J(O,"resize",b)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Ha(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&ba(O,"resize",this.subPixelFix);return this.alignedObjects=
57
+ null},createElement:function(a){var b=new this.Element;b.init(this,a);return b},draw:function(){},buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=o(a.textStr,"").toString().replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g),f=b.childNodes,g=/style="([^"]+)"/,h=/href="([^"]+)"/,i=A(b,"x"),j=a.styles,k=j&&j.width&&u(j.width),m=j&&j.lineHeight,
58
+ l=f.length;l--;)b.removeChild(f[l]);k&&!a.added&&this.box.appendChild(b);e[e.length-1]===""&&e.pop();n(e,function(e,f){var l,o=0,e=e.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");l=e.split("|||");n(l,function(e){if(e!==""||l.length===1){var p={},n=z.createElementNS(sa,"tspan"),q;g.test(e)&&(q=e.match(g)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),A(n,"style",q));h.test(e)&&!d&&(A(n,"onclick",'location.href="'+e.match(h)[1]+'"'),L(n,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,
59
+ "")||" ").replace(/&lt;/g,"<").replace(/&gt;/g,">");n.appendChild(z.createTextNode(e));o?p.dx=0:p.x=i;A(n,p);!o&&f&&(!Z&&d&&L(n,{display:"block"}),A(n,"dy",m||c.fontMetrics(/px$/.test(n.style.fontSize)?n.style.fontSize:j.fontSize).h,db&&n.offsetHeight));b.appendChild(n);o++;if(k)for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),r,t=[];e.length||t.length;)delete a.bBox,r=a.getBBox().width,p=r>k,!p||e.length===1?(e=t,t=[],e.length&&(n=z.createElementNS(sa,"tspan"),A(n,{dy:m||16,x:i}),q&&A(n,"style",
60
+ q),b.appendChild(n),r>k&&(k=r))):(n.removeChild(n.firstChild),t.unshift(e.pop())),e.length&&n.appendChild(z.createTextNode(e.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c,null,null,null,null,null,"button"),i=0,j,k,m,l,p,a={x1:0,y1:0,x2:0,y2:1},e=x({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);m=e.style;delete e.style;f=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,
61
+ "#FFF"],[1,"#ACF"]]}},f);l=f.style;delete f.style;g=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);p=g.style;delete g.style;J(h.element,"mouseenter",function(){h.attr(f).css(l)});J(h.element,"mouseleave",function(){j=[e,f,g][i];k=[m,l,p][i];h.attr(j).css(k)});h.setState=function(a){(i=a)?a===2&&h.attr(g).css(p):h.attr(e).css(m)};return h.on("click",function(){d.call(h)}).attr(e).css(v({cursor:"default"},m))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=t(a[1])-b%2/
62
+ 2);a[2]===a[5]&&(a[2]=a[5]=t(a[2])+b%2/2);return a},path:function(a){var b={fill:S};Da(a)?b.d=a:V(a)&&v(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=V(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(V(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e=V(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:S});return e.attr(V(a)?
63
+ a:e.crisp(f,a,b,q(c,0),q(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[o(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return r(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:S};arguments.length>1&&v(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink",
64
+ "href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(t(b),t(c),d,e,f),i=/^url\((.*?)\)$/,j,k;if(h)g=this.path(h),v(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&v(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(t((d-b[0])/2),t((e-b[1])/2)))},j=a.match(i)[1],a=Nb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),U("img",{onload:function(){k(g,
65
+ Nb[j]=[this.width,this.height])},src:j}));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,
66
+ e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=Y(f),j=ca(f),k=Y(g),g=ca(g),e=e.end-f<Ka?0:1;return["M",a+c*i,b+c*j,"A",c,c,0,e,1,a+c*k,b+c*g,h?"M":"L",a+d*k,b+d*g,"A",d,d,0,e,0,a+d*i,b+d*j,h?"":"Z"]}},clipRect:function(a,b,c,d){var e="highcharts-"+ub++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},color:function(a,b,c){var d=this,e,f=/^rgba/,g,h,i,j,k,m,l,p=[];a&&a.linearGradient?g="linearGradient":a&&a.radialGradient&&
67
+ (g="radialGradient");if(g){c=a[g];h=d.gradients;j=a.stops;b=b.radialReference;Da(c)&&(a[g]=c={x1:c[0],y1:c[1],x2:c[2],y2:c[3],gradientUnits:"userSpaceOnUse"});g==="radialGradient"&&b&&!r(c.gradientUnits)&&(c=x(c,{cx:b[0]-b[2]/2+c.cx*b[2],cy:b[1]-b[2]/2+c.cy*b[2],r:c.r*b[2],gradientUnits:"userSpaceOnUse"}));for(l in c)l!=="id"&&p.push(l,c[l]);for(l in j)p.push(j[l]);p=p.join(",");h[p]?a=h[p].id:(c.id=a="highcharts-"+ub++,h[p]=i=d.createElement(g).attr(c).add(d.defs),i.stops=[],n(j,function(a){f.test(a[1])?
68
+ (e=ma(a[1]),k=e.get("rgb"),m=e.get("a")):(k=a[1],m=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":m}).add(i);i.stops.push(a)}));return"url("+d.url+"#"+a+")"}else return f.test(a)?(e=ma(a),A(b,c+"-opacity",e.get("a")),e.get("rgb")):(b.removeAttribute(c+"-opacity"),a)},text:function(a,b,c,d){var e=N.chart.style,f=$||!Z&&this.forExport;if(d&&!this.forExport)return this.html(a,b,c);b=t(o(b,0));c=t(o(c,0));a=this.createElement("text").attr({x:b,y:c,text:a}).css({fontFamily:e.fontFamily,
69
+ fontSize:e.fontSize});f&&a.css({position:"absolute"});a.x=b;a.y=c;return a},html:function(a,b,c){var d=N.chart.style,e=this.createElement("span"),f=e.attrSetters,g=e.element,h=e.renderer;f.text=function(a){a!==g.innerHTML&&delete this.bBox;g.innerHTML=a;return!1};f.x=f.y=f.align=function(a,b){b==="align"&&(b="textAlign");e[b]=a;e.htmlUpdateTransform();return!1};e.attr({text:a,x:t(b),y:t(c)}).css({position:"absolute",whiteSpace:"nowrap",fontFamily:d.fontFamily,fontSize:d.fontSize});e.css=e.htmlCss;
70
+ if(h.isSVG)e.add=function(a){var b,c=h.box.parentNode,d=[];if(a){if(b=a.div,!b){for(;a;)d.push(a),a=a.parentGroup;n(d.reverse(),function(a){var d;b=a.div=a.div||U(ya,{className:A(a.element,"class")},{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;v(a.attrSetters,{translateX:function(a){d.left=a+"px"},translateY:function(a){d.top=a+"px"},visibility:function(a,b){d[b]=a}})})}}else b=c;b.appendChild(g);e.added=!0;e.alignOnAdd&&e.htmlUpdateTransform();return e};
71
+ return e},fontMetrics:function(a){var a=u(a||11),a=a<24?a+4:t(a*1.2),b=t(a*0.8);return{h:a,b:b}},label:function(a,b,c,d,e,f,g,h,i){function j(){var a,b;a=o.element.style;w=(Ma===void 0||yb===void 0||s.styles.textAlign)&&o.getBBox();s.width=(Ma||w.width||0)+2*q+hb;s.height=(yb||w.height||0)+2*q;A=q+p.fontMetrics(a&&a.fontSize).b;if(z){if(!B)a=t(-G*q),b=h?-A:0,s.box=B=d?p.symbol(d,a,b,s.width,s.height):p.rect(a,b,s.width,s.height,0,u[Pb]),B.add(s);B.isImg||B.attr(x({width:s.width,height:s.height},u));
72
+ u=null}}function k(){var a=s.styles,a=a&&a.textAlign,b=hb+q*(1-G),c;c=h?0:A;if(r(Ma)&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(Ma-w.width);(b!==o.x||c!==o.y)&&o.attr({x:b,y:c});o.x=b;o.y=c}function m(a,b){B?B.attr(a,b):u[a]=b}function l(){o.add(s);s.attr({text:a,x:b,y:c});B&&r(e)&&s.attr({anchorX:e,anchorY:f})}var p=this,s=p.g(i),o=p.text("",0,0,g).attr({zIndex:1}),B,w,G=0,q=3,hb=0,Ma,yb,P,H,C=0,u={},A,g=s.attrSetters,z;J(s,"add",l);g.width=function(a){Ma=a;return!1};g.height=function(a){yb=
73
+ a;return!1};g.padding=function(a){r(a)&&a!==q&&(q=a,k());return!1};g.paddingLeft=function(a){r(a)&&a!==hb&&(hb=a,k());return!1};g.align=function(a){G={left:0,center:0.5,right:1}[a];return!1};g.text=function(a,b){o.attr(b,a);j();k();return!1};g[Pb]=function(a,b){z=!0;C=a%2/2;m(b,a);return!1};g.stroke=g.fill=g.r=function(a,b){b==="fill"&&(z=!0);m(b,a);return!1};g.anchorX=function(a,b){e=a;m(b,a+C-P);return!1};g.anchorY=function(a,b){f=a;m(b,a-H);return!1};g.x=function(a){s.x=a;a-=G*((Ma||w.width)+q);
74
+ P=t(a);s.attr("translateX",P);return!1};g.y=function(a){H=s.y=t(a);s.attr("translateY",H);return!1};var E=s.css;return v(s,{css:function(a){if(a){var b={},a=x(a);n("fontSize,fontWeight,fontFamily,color,lineHeight,width,textDecoration".split(","),function(c){a[c]!==y&&(b[c]=a[c],delete a[c])});o.css(b)}return E.call(s,a)},getBBox:function(){return{width:w.width+2*q,height:w.height+2*q,x:w.x-q,y:w.y-q}},shadow:function(a){B&&B.shadow(a);return s},destroy:function(){ba(s,"add",l);ba(s.element,"mouseenter");
75
+ ba(s.element,"mouseleave");o&&(o=o.destroy());B&&(B=B.destroy());ra.prototype.destroy.call(s);s=p=j=k=m=l=null}})}};Sa=Ca;var F;if(!Z&&!$){Highcharts.VMLElement=F={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",";"],e=b===ya;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=U(c);this.renderer=a;this.attrSetters=
76
+ {}},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();D(this,"add");return this},updateTransform:ra.prototype.htmlUpdateTransform,attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,m=this.shadows,l,p=this.attrSetters,s=this;fa(a)&&r(b)&&(c=a,a={},a[c]=b);if(fa(a))c=a,s=c==="strokeWidth"||
77
+ c==="stroke-width"?this.strokeweight:this[c];else for(c in a)if(d=a[c],l=!1,e=p[c]&&p[c].call(this,d,c),e!==!1&&d!==null){e!==y&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k||(this.symbolAttr(a),k=!0),l=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;l=[];for(var o;e--;)if(Ea(d[e]))l[e]=t(d[e]*10)-5;else if(d[e]==="Z")l[e]="x";else if(l[e]=d[e],d.isArc&&(d[e]==="wa"||d[e]==="at"))o=d[e]==="wa"?1:-1,l[e+5]===l[e+7]&&(l[e+7]-=o),l[e+6]===l[e+8]&&(l[e+8]-=
78
+ o);d=l.join(" ")||"x";f.path=d;if(m)for(e=m.length;e--;)m[e].path=m[e].cutOff?this.cutOffPath(d,m[e].cutOff):d;l=!0}else if(c==="visibility"){if(m)for(e=m.length;e--;)m[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,cb||(g[c]=d?"visible":"hidden"),c="top");g[c]=d;l=!0}else if(c==="zIndex")d&&(g[c]=d),l=!0;else if(la(c,["x","y","width","height"])!==-1)this[c]=d,c==="x"||c==="y"?c={x:"left",y:"top"}[c]:d=q(0,d),this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,l=!0;else if(c==="class"&&
79
+ h==="DIV")f.className=d;else if(c==="stroke")d=i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ea(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||U(i.prepVML(["<stroke/>"]),null,null,f))[c]=d||"solid",this.dashstyle=d,l=!0;else if(c==="fill")if(h==="SPAN")g.color=d;else{if(h!=="IMG")f.filled=d!==S?!0:!1,d=i.color(d,f,c,this),c="fillcolor"}else if(c==="opacity")l=!0;else if(h==="shape"&&c==="rotation")this[c]=
80
+ d,f.style.left=-t(ca(d*bb)+1)+"px",f.style.top=t(Y(d*bb))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),l=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,l=!0;l||(cb?f[c]=d:A(f,c,d))}return s},clip:function(a){var b=this,c;a?(c=a.members,ga(c,b),c.push(b),b.destroyClip=function(){ga(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:cb?"inherit":"rect(auto)"});return b.css(a)},css:ra.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&
81
+ Ra(a)},destroy:function(){this.destroyClip&&this.destroyClip();return ra.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=O.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=u(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,m,l,p,s;k&&typeof k.value!=="string"&&(k="x");l=k;if(a){p=o(a.width,3);s=(a.opacity||
82
+ 0.15)/p;for(e=1;e<=3;e++){m=p*2+1-2*e;c&&(l=this.cutOffPath(k.value,m+0.5));j=['<shape isShadow="true" strokeweight="',m,'" filled="false" path="',l,'" coordsize="10 10" style="',f.style.cssText,'" />'];h=U(g.prepVML(j),null,{left:u(i.left)+o(a.offsetX,1),top:u(i.top)+o(a.offsetY,1)});if(c)h.cutOff=m+1;j=['<stroke color="',a.color||"black",'" opacity="',s*e,'"/>'];U(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}};F=ea(ra,F);
83
+ var na={Element:F,isIE8:Aa.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(ya);e=d.element;e.style.position="relative";a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!z.namespaces.hcv)z.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),z.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},isHidden:function(){return!this.box.offsetWidth},
84
+ clipRect:function(a,b,c,d){var e=this.createElement(),f=V(a);return v(e,{members:[],left:f?a.x:a,top:f?a.y:b,width:f?a.width:c,height:f?a.height:d,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+t(a?e:d)+"px,"+t(a?f:b)+"px,"+t(a?b:f)+"px,"+t(a?d:e)+"px)"};!a&&cb&&c==="DIV"&&v(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},
85
+ color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=S;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,m,l=a.linearGradient||a.radialGradient,p,s,o,B,w,q="",a=a.stops,r,t=[],y=function(){h=['<fill colors="'+t.join(",")+'" opacity="',o,'" o:opacity2="',s,'" type="',i,'" ',q,'focus="100%" method="any" />'];U(e.prepVML(h),null,null,b)};p=a[0];r=a[a.length-1];p[0]>0&&a.unshift([0,p[1]]);r[0]<1&&a.push([1,r[1]]);n(a,function(a,b){g.test(a[1])?(f=ma(a[1]),k=f.get("rgb"),
86
+ m=f.get("a")):(k=a[1],m=1);t.push(a[0]*100+"% "+k);b?(o=m,B=k):(s=m,w=k)});if(c==="fill")if(i==="gradient")c=l.x1||l[0]||0,a=l.y1||l[1]||0,p=l.x2||l[2]||0,l=l.y2||l[3]||0,q='angle="'+(90-I.atan((l-a)/(p-c))*180/Ka)+'"',y();else{var j=l.r,v=j*2,P=j*2,H=l.cx,C=l.cy,x=b.radialReference,u,j=function(){x&&(u=d.getBBox(),H+=(x[0]-u.x)/u.width-0.5,C+=(x[1]-u.y)/u.height-0.5,v*=x[2]/u.width,P*=x[2]/u.height);q='src="'+N.global.VMLRadialGradientURL+'" size="'+v+","+P+'" origin="0.5,0.5" position="'+H+","+
87
+ C+'" color2="'+w+'" ';y()};d.added?j():J(d,"add",j);j=B}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=ma(a),h=["<",c,' opacity="',f.get("a"),'"/>'],U(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):
88
+ a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:Ca.prototype.html,path:function(a){var b={coordsize:"10 10"};Da(a)?b.d=a:V(a)&&v(b,a);return this.createElement("shape").attr(b)},circle:function(a,b,c){var d=this.symbol("circle");if(V(a))c=a.r,b=a.y,a=a.x;d.isCircle=!0;return d.attr({x:a,y:b,width:2*c,height:2*c})},g:function(a){var b;a&&(b={className:"highcharts-"+a,"class":"highcharts-"+a});return this.createElement(ya).attr(b)},
89
+ image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.attr({x:b,y:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(V(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,q(c,0),q(d,0)))},invertChild:function(a,b){var c=b.style;L(a,{flip:"x",left:u(c.width)-1,top:u(c.height)-1,rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=Y(f),i=ca(f),j=Y(g),
90
+ k=ca(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){var f=a+c,g=b+d,h;!r(e)||!e.r?f=Ca.prototype.symbols.square.apply(0,arguments):(h=K(e.r,c,d),f=["M",a+h,b,"L",f-h,b,"wa",f-2*h,b,f,b+2*h,f-h,b,f,b+h,"L",f,g-h,"wa",f-2*h,g-2*
91
+ h,f,g,f,g-h,f-h,g,"L",a+h,g,"wa",a,g-2*h,a+2*h,g,a+h,g,a,g-h,"L",a,b+h,"wa",a,b,a+2*h,b+2*h,a,b+h,a+h,b,"x","e"]);return f}}};Highcharts.VMLRenderer=F=function(){this.init.apply(this,arguments)};F.prototype=x(Ca.prototype,na);Sa=F}var Rb;if($)Highcharts.CanVGRenderer=F=function(){sa="http://www.w3.org/1999/xhtml"},F.prototype.symbols={},Rb=function(){function a(){var a=b.length,d;for(d=0;d<a;d++)b[d]();b=[]}var b=[];return{push:function(c,d){b.length===0&&Tb(d,a);b.push(c)}}}(),Sa=F;Ja.prototype=
92
+ {addLabel:function(){var a=this.axis,b=a.options,c=a.chart,d=a.horiz,e=a.categories,f=a.series[0]&&a.series[0].names,g=this.pos,h=b.labels,i=a.tickPositions,d=d&&e&&!h.step&&!h.staggerLines&&!h.rotation&&c.plotWidth/i.length||!d&&(c.optionsMarginLeft||c.plotWidth/2),j=g===i[0],k=g===i[i.length-1],f=e?o(e[g],f&&f[g],g):g,e=this.label,i=i.info,m;a.isDatetimeAxis&&i&&(m=b.dateTimeLabelFormats[i.higherRanks[g]||i.unitName]);this.isFirst=j;this.isLast=k;b=a.labelFormatter.call({axis:a,chart:c,isFirst:j,
93
+ isLast:k,dateTimeLabelFormat:m,value:a.isLog?ia(da(f)):f});g=d&&{width:q(1,t(d-2*(h.padding||10)))+"px"};g=v(g,h.style);if(r(e))e&&e.attr({text:b}).css(g);else{d={align:h.align};if(Ea(h.rotation))d.rotation=h.rotation;this.label=r(b)&&h.enabled?c.renderer.text(b,0,0,h.useHTML).attr(d).css(g).add(a.labelGroup):null}},getLabelSize:function(){var a=this.label,b=this.axis;return a?(this.labelBBox=a.getBBox())[b.horiz?"height":"width"]:0},getLabelSides:function(){var a=this.axis.options.labels,b=this.labelBBox.width,
94
+ a=b*{left:0,center:0.5,right:1}[a.align]-a.x;return[-a,b-a]},handleOverflow:function(a,b){var c=!0,d=this.axis,e=d.chart,f=this.isFirst,g=this.isLast,h=b.x,i=d.reversed,j=d.tickPositions;if(f||g){var k=this.getLabelSides(),m=k[0],k=k[1],e=e.plotLeft,l=e+d.len,j=(d=d.ticks[j[a+(f?1:-1)]])&&d.label.xy&&d.label.xy.x+d.getLabelSides()[f?0:1];f&&!i||g&&i?h+m<e&&(h=e-m,d&&h+k>j&&(c=!1)):h+k>l&&(h=l-k,d&&h+m<j&&(c=!1));b.x=h}return c},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||
95
+ f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,j=i.transA,k=i.reversed,i=i.staggerLines,a=a+e.x-(f&&d?f*j*(k?-1:1):0),b=b+e.y-(f&&!d?f*j*(k?1:-1):0);r(e.y)||(b+=u(c.styles.lineHeight)*0.9-c.getBBox().height/2);i&&(b+=g/(h||1)%i*16);return{x:a,y:b}},getMarkPath:function(a,
96
+ b,c,d,e,f){return f.crispLine(["M",a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,j=this.pos,k=e.labels,m=this.gridLine,l=h?h+"Grid":"grid",p=h?h+"Tick":"tick",s=e[l+"LineWidth"],n=e[l+"LineColor"],B=e[l+"LineDashStyle"],w=e[p+"Length"],l=e[p+"Width"]||0,q=e[p+"Color"],r=e[p+"Position"],p=this.mark,t=k.step,v=!0,u=d.tickmarkOffset,P=this.getPosition(g,j,u,b),H=P.x,P=P.y,C=g&&H===d.pos||!g&&P===d.pos+d.len?
97
+ -1:1,x=d.staggerLines;this.isActive=!0;if(s){j=d.getPlotLinePath(j+u,s*C,b,!0);if(m===y){m={stroke:n,"stroke-width":s};if(B)m.dashstyle=B;if(!h)m.zIndex=1;if(b)m.opacity=0;this.gridLine=m=s?f.path(j).attr(m).add(d.gridGroup):null}if(!b&&m&&j)m[this.isNew?"attr":"animate"]({d:j,opacity:c})}if(l&&w)r==="inside"&&(w=-w),d.opposite&&(w=-w),b=this.getMarkPath(H,P,w,l*C,g,f),p?p.animate({d:b,opacity:c}):this.mark=f.path(b).attr({stroke:q,"stroke-width":l,opacity:c}).add(d.axisGroup);if(i&&!isNaN(H))i.xy=
98
+ P=this.getLabelPosition(H,P,i,g,k,u,a,t),this.isFirst&&!o(e.showFirstLabel,1)||this.isLast&&!o(e.showLastLabel,1)?v=!1:!x&&g&&k.overflow==="justify"&&!this.handleOverflow(a,P)&&(v=!1),t&&a%t&&(v=!1),v&&!isNaN(P.y)?(P.opacity=c,i[this.isNew?"attr":"animate"](P),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Ha(this,this.axis)}};pb.prototype={render:function(){var a=this,b=a.axis,c=b.horiz,d=(b.pointRange||0)/2,e=a.options,f=e.label,g=a.label,h=e.width,i=e.to,j=e.from,k=r(j)&&r(i),m=e.value,l=
99
+ e.dashStyle,p=a.svgElem,s=[],n,B=e.color,w=e.zIndex,G=e.events,t=b.chart.renderer;b.isLog&&(j=ka(j),i=ka(i),m=ka(m));if(h){if(s=b.getPlotLinePath(m,h),d={stroke:B,"stroke-width":h},l)d.dashstyle=l}else if(k){if(j=q(j,b.min-d),i=K(i,b.max+d),s=b.getPlotBandPath(j,i,e),d={fill:B},e.borderWidth)d.stroke=e.borderColor,d["stroke-width"]=e.borderWidth}else return;if(r(w))d.zIndex=w;if(p)s?p.animate({d:s},null,p.onGetPath):(p.hide(),p.onGetPath=function(){p.show()});else if(s&&s.length&&(a.svgElem=p=t.path(s).attr(d).add(),
100
+ G))for(n in e=function(b){p.on(b,function(c){G[b].apply(a,[c])})},G)e(n);if(f&&r(f.text)&&s&&s.length&&b.width>0&&b.height>0){f=x({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=t.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:w}).css(f.style).add();b=[s[1],s[4],o(s[6],s[1])];s=[s[2],s[5],o(s[7],s[2])];c=Ga(b);k=Ga(s);g.align(f,!1,{x:c,y:k,width:pa(b)-c,height:pa(s)-k});g.show()}else g&&g.hide();
101
+ return a},destroy:function(){ga(this.axis.plotLinesAndBands,this);Ha(this,this.axis)}};Kb.prototype={destroy:function(){Ha(this,this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options,c=b.format,c=c?wa(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,0,0,b.useHTML).css(b.style).attr({align:this.textAlign,rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=
102
+ this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.percent?100:this.total,0,0,0,1),c=c.translate(0),c=Q(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e?c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e.attr({visibility:this.options.crop===!1||d.isInsidePlot(f.x,f.y)?Z?"inherit":"visible":"hidden"})}};ab.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",
103
+ minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:M,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#4d759e",
104
+ fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return ua(this.total,-1)},style:M.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}},
105
+ defaultBottomAxisOptions:{labels:{align:"center",x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x":"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions=
106
+ b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=r(d.linkedTo);this.tickmarkOffset=this.categories&&d.tickmarkPlacement==="between"?0.5:0;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;
107
+ this.stacks={};this._stacksTouched=0;this.min=this.max=null;var f,d=this.options.events;la(this,a.axes)===-1&&(a.axes.push(this),a[c?"xAxis":"yAxis"].push(this));this.series=this.series||[];if(a.inverted&&c&&this.reversed===y)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)J(this,f,d[f]);if(this.isLog)this.val2lin=ka,this.lin2val=da},setOptions:function(a){this.options=x(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,
108
+ this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],x(N[this.isXAxis?"xAxis":"yAxis"],a))},update:function(a,b){var c=this.chart,a=c.options[this.xOrY+"Axis"][this.options.index]=x(this.userOptions,a);this.destroy();this._addedPlotLB=!1;this.init(c,a);c.isDirtyBox=!0;o(b,!0)&&c.redraw()},remove:function(a){var b=this.chart,c=this.xOrY+"Axis";n(this.series,function(a){a.remove(!1)});ga(b.axes,this);ga(b[c],this);b.options[c].splice(this.options.index,
109
+ 1);n(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;o(a,!0)&&b.redraw()},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=N.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=wa(h,this);else if(c)g=b;else if(d)g=Ua(d,b);else if(f&&a>=1E3)for(;f--&&g===y;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=ua(b/c,-1)+e[f]);g===y&&(g=b>=1E3?ua(b,0):ua(b,-1));return g},getSeriesExtremes:function(){var a=
110
+ this,b=a.chart,c=a.stacks,d=[],e=[],f=a._stacksTouched+=1,g,h;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var j=g.options,k,m,l,p,s,n,B,w,G,t=j.threshold,v,u=[],x=0;a.hasVisibleSeries=!0;if(a.isLog&&t<=0)t=j.threshold=null;if(a.isXAxis){if(j=g.xData,j.length)a.dataMin=K(o(a.dataMin,j[0]),Ga(j)),a.dataMax=q(o(a.dataMax,j[0]),pa(j))}else{var P,H,C,A=g.cropped,z=g.xAxis.getExtremes(),E=!!g.modifyValue;k=j.stacking;a.usePercentage=
111
+ k==="percent";if(k)s=j.stack,p=g.type+o(s,""),n="-"+p,g.stackKey=p,m=d[p]||[],d[p]=m,l=e[n]||[],e[n]=l;if(a.usePercentage)a.dataMin=0,a.dataMax=99;j=g.processedXData;B=g.processedYData;v=B.length;for(h=0;h<v;h++){w=j[h];G=B[h];if(k)H=(P=G<t)?l:m,C=P?n:p,r(H[w])?(H[w]=ia(H[w]+G),G=[G,H[w]]):H[w]=G,c[C]||(c[C]={}),c[C][w]||(c[C][w]=new Kb(a,a.options.stackLabels,P,w,s,k)),c[C][w].setTotal(H[w]),c[C][w].touched=f;if(G!==null&&G!==y&&(!a.isLog||G.length||G>0))if(E&&(G=g.modifyValue(G)),g.getExtremesFromAll||
112
+ A||(j[h+1]||w)>=z.min&&(j[h-1]||w)<=z.max)if(w=G.length)for(;w--;)G[w]!==null&&(u[x++]=G[w]);else u[x++]=G}if(!a.usePercentage&&u.length)g.dataMin=k=Ga(u),g.dataMax=g=pa(u),a.dataMin=K(o(a.dataMin,k),k),a.dataMax=q(o(a.dataMax,g),g);if(r(t))if(a.dataMin>=t)a.dataMin=t,a.ignoreMinPadding=!0;else if(a.dataMax<t)a.dataMax=t,a.ignoreMaxPadding=!0}}});for(g in c)for(h in c[g])c[g][h].touched<f&&(c[g][h].destroy(),delete c[g][h])},translate:function(a,b,c,d,e,f){var g=this.len,h=1,i=0,j=d?this.oldTransA:
113
+ this.transA,d=d?this.oldMin:this.min,k=this.minPixelPadding,e=(this.options.ordinal||this.isLog&&e)&&this.lin2val;if(!j)j=this.transA;c&&(h*=-1,i=g);this.reversed&&(h*=-1,i-=h*g);b?(a=a*h+i,a-=k,a=a/j+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),a=h*(a-d)*j+i+h*k+(f?j*this.pointRange/2:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,
114
+ b,c,d){var e=this.chart,f=this.left,g=this.top,h,i,j,a=this.translate(a,null,null,c),k=c&&e.oldChartHeight||e.chartHeight,m=c&&e.oldChartWidth||e.chartWidth,l;h=this.transB;c=i=t(a+h);h=j=t(k-a-h);if(isNaN(a))l=!0;else if(this.horiz){if(h=g,j=k-this.bottom,c<f||c>f+this.width)l=!0}else if(c=f,i=m-this.right,h<g||h>g+this.height)l=!0;return l&&!d?null:e.renderer.crispLine(["M",c,h,"L",i,j],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],
115
+ c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d,b=ia(T(b/a)*a),c=ia(ja(c/a)*a),e=[];b<=c;){e.push(b);b=ia(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=t(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=T(b),h,i,j,k,m,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];f<c+1&&!m;f++){i=e.length;for(h=0;h<i&&!m;h++)j=ka(da(f)*e[h]),j>b&&(!d||
116
+ k<=c)&&g.push(k),k>c&&(m=!0),k=j}else if(b=da(b),c=da(c),a=e[d?"minorTickInterval":"tickInterval"],a=o(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=ib(a,null,I.pow(10,T(I.log(a)/I.LN10))),g=La(this.getLinearTickPositions(a,b,c),ka),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length;
117
+ for(a=1;a<e;a++)d=d.concat(this.getLogTickPositions(c,b[a-1],b[a],!0))}else if(this.isDatetimeAxis&&a.minorTickInterval==="auto")d=d.concat(Cb(Ab(c),this.min,this.max,a.startOfWeek)),d[0]<this.min&&d.shift();else for(b=this.min+(b[0]-this.min)%c;b<=this.max;b+=c)d.push(b);return d},adjustForMinRange:function(){var a=this.options,b=this.min,c=this.max,d,e=this.dataMax-this.dataMin>=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===y&&!this.isLog)r(a.min)||r(a.max)?this.minRange=null:(n(this.series,
118
+ function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===y||h<f)f=h}),this.minRange=K(f*5,this.dataMax-this.dataMin));if(c-b<this.minRange){var k=this.minRange;d=(k-c+b)/2;d=[b-d,o(a.min,b-d)];if(e)d[2]=this.dataMin;b=pa(d);c=[b+k,o(a.max,b+k)];if(e)c[2]=this.dataMax;c=Ga(c);c-b<k&&(d[0]=c-k,d[1]=o(a.min,c-k),b=pa(d))}this.min=b;this.max=c},setAxisTranslation:function(a){var b=this.max-this.min,c=0,d,e=0,f=0,g=this.linkedParent,h=this.transA;if(this.isXAxis)g?(e=g.minPointOffset,
119
+ f=g.pointRangePadding):n(this.series,function(a){var g=a.pointRange,h=a.options.pointPlacement,m=a.closestPointRange;g>b&&(g=0);c=q(c,g);e=q(e,h?0:g/2);f=q(f,h==="on"?0:g);!a.noSharedTooltip&&r(m)&&(d=r(d)?K(d,m):m)}),g=this.ordinalSlope&&d?this.ordinalSlope/d:1,this.minPointOffset=e*=g,this.pointRangePadding=f*=g,this.pointRange=K(c,b),this.closestPointRange=d;if(a)this.oldTransA=h;this.translationSlope=this.transA=h=this.len/(b+f||1);this.transB=this.horiz?this.left:this.bottom;this.minPixelPadding=
120
+ h*e},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,k=d.minPadding,m=d.tickInterval,l=d.minTickInterval,p=d.tickPixelInterval,s=b.categories;h?(b.linkedParent=c[g?"xAxis":"yAxis"][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=o(c.min,c.dataMin),b.max=o(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&qa(11,1)):(b.min=o(b.userMin,d.min,b.dataMin),b.max=o(b.userMax,d.max,b.dataMax));
121
+ if(e)!a&&K(b.min,o(b.dataMin,b.min))<=0&&qa(10,1),b.min=ia(ka(b.min)),b.max=ia(ka(b.max));if(b.range&&(b.userMin=b.min=q(b.min,b.max-b.range),b.userMax=b.max,a))b.range=null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!s&&!b.usePercentage&&!h&&r(b.min)&&r(b.max)&&(c=b.max-b.min)){if(!r(d.min)&&!r(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!r(d.max)&&!r(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}b.tickInterval=b.min===b.max||b.min===void 0||b.max===
122
+ void 0?1:h&&!m&&p===b.linkedParent.options.tickPixelInterval?b.linkedParent.tickInterval:o(m,s?1:(b.max-b.min)*p/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!m&&b.tickInterval<l)b.tickInterval=l;if(!f&&!e&&(a=I.pow(10,T(I.log(b.tickInterval)/I.LN10)),!m))b.tickInterval=ib(b.tickInterval,
123
+ null,a,d);b.minorTickInterval=d.minorTickInterval==="auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=i=d.tickPositions?[].concat(d.tickPositions):i&&i.apply(b,[b.min,b.max]);if(!i)i=f?(b.getNonLinearTimeTicks||Cb)(Ab(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),b.tickPositions=i;if(!h)e=i[0],f=i[i.length-1],h=b.minPointOffset||
124
+ 0,d.startOnTick?b.min=e:b.min-h>e&&i.shift(),d.endOnTick?b.max=f:b.max+h<f&&i.pop(),i.length===1&&(b.min-=0.001,b.max+=0.001)},setMaxTicks:function(){var a=this.chart,b=a.maxTicks||{},c=this.tickPositions,d=this._maxTicksKey=[this.xOrY,this.pos,this.len].join("-");if(!this.isLinked&&!this.isDatetimeAxis&&c&&c.length>(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&
125
+ !this.categories&&!this.isLinked&&this.options.alignTicks!==!1){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e<a){for(;b.length<a;)b.push(ia(b[b.length-1]+this.tickInterval));this.transA*=(e-1)/(a-1);this.max=b[b.length-1]}if(r(d)&&a!==d)this.isDirty=!0}},setScale:function(){var a=this.stacks,b,c,d,e;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();e=this.len!==this.oldAxisLength;n(this.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)d=
126
+ !0});if(e||d||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax)if(this.forceRedraw=!1,this.getSeriesExtremes(),this.setTickPositions(),this.oldUserMin=this.userMin,this.oldUserMax=this.userMax,!this.isDirty)this.isDirty=e||this.min!==this.oldMin||this.max!==this.oldMax;if(!this.isXAxis)for(b in a)for(c in a[b])a[b][c].cum=a[b][c].total;this.setMaxTicks()},setExtremes:function(a,b,c,d,e){var f=this,g=f.chart,c=o(c,!0),e=v(e,{min:a,max:b});D(f,"setExtremes",
127
+ e,function(){f.userMin=a;f.userMax=b;f.isDirtyExtremes=!0;c&&g.redraw(d)})},zoom:function(a,b){this.allowZoomOutside||(a<=this.dataMin&&(a=y),b>=this.dataMax&&(b=y));this.displayBtn=a!==y||b!==y;this.setExtremes(a,b,!1,y,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=b.offsetRight||0,e=this.horiz,f,g;this.left=g=o(b.left,a.plotLeft+c);this.top=f=o(b.top,a.plotTop);this.width=c=o(b.width,a.plotWidth-c+d);this.height=b=o(b.height,a.plotHeight);
128
+ this.bottom=a.chartHeight-b-f;this.right=a.chartWidth-c-g;this.len=q(e?c:b,0);this.pos=e?g:f},getExtremes:function(){var a=this.isLog;return{min:a?ia(da(this.min)):this.min,max:a?ia(da(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?da(this.min):this.min,b=b?da(this.max):this.max;c>a||a===null?a=c:b<a&&(a=b);return this.translate(a,0,1,0,1)},addPlotBand:function(a){this.addPlotBandOrLine(a,"plotBands")},
129
+ addPlotLine:function(a){this.addPlotBandOrLine(a,"plotLines")},addPlotBandOrLine:function(a,b){var c=(new pb(this,a)).render(),d=this.userOptions;b&&(d[b]=d[b]||[],d[b].push(a));this.plotLinesAndBands.push(c);return c},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,j,k=0,m,l=0,p=d.title,s=d.labels,t=0,B=b.axisOffset,w=b.clipOffset,G=[-1,1,1,-1][h],v;a.hasData=b=a.hasVisibleSeries||r(a.min)&&r(a.max)&&!!e;
130
+ a.showAxis=j=b||o(d.showEmpty,!0);if(!a.axisGroup)a.gridGroup=c.g("grid").attr({zIndex:d.gridZIndex||1}).add(),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(),a.labelGroup=c.g("axis-labels").attr({zIndex:s.zIndex||7}).add();if(b||a.isLinked)n(e,function(b){f[b]?f[b].addLabel():f[b]=new Ja(a,b)}),n(e,function(a){if(h===0||h===2||{1:"left",3:"right"}[h]===s.align)t=q(f[a].getLabelSize(),t)}),a.staggerLines&&(t+=(a.staggerLines-1)*16);else for(v in f)f[v].destroy(),delete f[v];if(p&&p.text&&
131
+ p.enabled!==!1){if(!a.axisTitle)a.axisTitle=c.text(p.text,0,0,p.useHTML).attr({zIndex:7,rotation:p.rotation||0,align:p.textAlign||{low:"left",middle:"center",high:"right"}[p.align]}).css(p.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(j)k=a.axisTitle.getBBox()[g?"height":"width"],l=o(p.margin,g?5:10),m=p.offset;a.axisTitle[j?"show":"hide"]()}a.offset=G*o(d.offset,B[h]);a.axisTitleMargin=o(m,t+l+(h!==2&&t&&G*d.labels[g?"y":"x"]));B[h]=q(B[h],a.axisTitleMargin+k+G*a.offset);w[i]=q(w[i],d.lineWidth)},
132
+ getLinePath:function(a){var b=this.chart,c=this.opposite,d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d;this.lineTop=d=b.chartHeight-this.bottom-(c?this.height:0)+d;c||(a*=-1);return b.renderer.crispLine(["M",e?this.left:f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=u(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+
133
+ d/2,high:f+(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*this.axisTitleMargin+(this.side===2?i:0);return{x:a?d:b+(g?this.width:0)+h+(e.x||0),y:a?b-(g?this.height:0)+h:d+(e.y||0)}},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.isLog,f=a.isLinked,g=a.tickPositions,h=a.axisTitle,i=a.stacks,j=a.ticks,k=a.minorTicks,m=a.alternateBands,l=d.stackLabels,p=d.alternateGridColor,s=a.tickmarkOffset,o=d.lineWidth,B,w=b.hasRendered&&r(a.oldMin)&&!isNaN(a.oldMin);B=a.hasData;var q=
134
+ a.showAxis,t,v;n([j,k,m],function(a){for(var b in a)a[b].isActive=!1});if(B||f)if(a.minorTickInterval&&!a.categories&&n(a.getMinorTickPositions(),function(b){k[b]||(k[b]=new Ja(a,b,"minor"));w&&k[b].isNew&&k[b].render(null,!0);k[b].render(null,!1,1)}),g.length&&(n(g.slice(1).concat([g[0]]),function(b,c){c=c===g.length-1?0:c+1;if(!f||b>=a.min&&b<=a.max)j[b]||(j[b]=new Ja(a,b)),w&&j[b].isNew&&j[b].render(c,!0),j[b].render(c,!1,1)}),s&&a.min===0&&(j[-1]||(j[-1]=new Ja(a,-1,null,!0)),j[-1].render(-1))),
135
+ p&&n(g,function(b,c){if(c%2===0&&b<a.max)m[b]||(m[b]=new pb(a)),t=b+s,v=g[c+1]!==y?g[c+1]+s:a.max,m[b].options={from:e?da(t):t,to:e?da(v):v,color:p},m[b].render(),m[b].isActive=!0}),!a._addedPlotLB)n((d.plotLines||[]).concat(d.plotBands||[]),function(b){a.addPlotBandOrLine(b)}),a._addedPlotLB=!0;n([j,k,m],function(a){var c,d,e=[],f=za?za.duration||500:0,g=function(){for(d=e.length;d--;)a[e[d]]&&!a[e[d]].isActive&&(a[e[d]].destroy(),delete a[e[d]])};for(c in a)if(!a[c].isActive)a[c].render(c,!1,0),
136
+ a[c].isActive=!1,e.push(c);a===m||!b.hasRendered||!f?g():f&&setTimeout(g,f)});if(o)B=a.getLinePath(o),a.axisLine?a.axisLine.animate({d:B}):a.axisLine=c.path(B).attr({stroke:d.lineColor,"stroke-width":o,zIndex:7}).add(a.axisGroup),a.axisLine[q?"show":"hide"]();if(h&&q)h[h.isNew?"attr":"animate"](a.getTitlePosition()),h.isNew=!1;if(l&&l.enabled){var u,x,d=a.stackTotalGroup;if(!d)a.stackTotalGroup=d=c.g("stack-labels").attr({visibility:"visible",zIndex:6}).add();d.translate(b.plotLeft,b.plotTop);for(u in i)for(x in c=
137
+ i[u],c)c[x].render(d)}a.isDirty=!1},removePlotBandOrLine:function(a){for(var b=this.plotLinesAndBands,c=b.length;c--;)b[c].id===a&&b[c].destroy()},setTitle:function(a,b){this.update({title:a},b)},redraw:function(){var a=this.chart.pointer;a.reset&&a.reset(!0);this.render();n(this.plotLinesAndBands,function(a){a.render()});n(this.series,function(a){a.isDirty=!0})},setCategories:function(a,b){this.update({categories:a},b)},destroy:function(){var a=this,b=a.stacks,c;ba(a);for(c in b)Ha(b[c]),b[c]=null;
138
+ n([a.ticks,a.minorTicks,a.alternateBands,a.plotLinesAndBands],function(a){Ha(a)});n("stackTotalGroup,axisLine,axisGroup,gridGroup,labelGroup,axisTitle".split(","),function(b){a[b]&&(a[b]=a[b].destroy())})}};qb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=u(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape,null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,
139
+ r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).hide().add();$||this.label.shadow(b.shadow);this.shared=b.shared},destroy:function(){n(this.crosshairs,function(a){a&&a.destroy()});if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden;v(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:g?(2*f.anchorX+c)/3:c,anchorY:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g&&(Q(a-
140
+ f.x)>1||Q(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(){var a=this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},o(this.options.hideDelay,500)),b&&n(b,function(a){a.setState()}),this.chart.hoverPoints=null},hideCrosshairs:function(){n(this.crosshairs,function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e=
141
+ d.inverted,f=d.plotTop,g=0,h=0,i,a=ha(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===y&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(n(a,function(a){i=a.series.yAxis;g+=a.plotX;h+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return La(c,t)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i=
142
+ o(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,m;d<7&&(d=e+q(j,0)+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,m=!0);k<f+5&&(k=f+5,m&&c>=k&&c<=k+b&&(k=c+f+i));k+b>f+h&&(k=q(f,f+h-b-i));return{x:d,y:k}},defaultFormatter:function(a){var b=this.points||ha(this),c=b[0].series,d;d=[c.tooltipHeaderFormatter(b[0])];n(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});d.push(a.options.footerFormat||
143
+ "");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h,i={},j,k=[];j=e.formatter||this.defaultFormatter;var i=c.hoverPoints,m,l=e.crosshairs;h=this.shared;clearTimeout(this.hideTimer);this.followPointer=ha(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];h&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,i&&n(i,function(a){a.setState()}),n(a,function(a){a.setState("hover");k.push(a.getLabelConfig())}),i={x:a[0].category,
144
+ y:a[0].y},i.points=k,a=a[0]):i=a.getLabelConfig();j=j.call(i,this);i=a.series;h=h||!i.isCartesian||i.tooltipOutsidePlot||c.isInsidePlot(f,g);j===!1||!h?this.hide():(this.isHidden&&(Ta(d),d.attr("opacity",1).show()),d.attr({text:j}),m=e.borderColor||a.color||i.color||"#606060",d.attr({stroke:m}),this.updatePosition({plotX:f,plotY:g}),this.isHidden=!1);if(l){l=ha(l);for(d=l.length;d--;)if(e=a.series[d?"yAxis":"xAxis"],l[d]&&e)if(h=d?o(a.stackY,a.y):a.x,e.isLog&&(h=ka(h)),e=e.getPlotLinePath(h,1),this.crosshairs[d])this.crosshairs[d].attr({d:e,
145
+ visibility:"visible"});else{h={"stroke-width":l[d].width||1,stroke:l[d].color||"#C0C0C0",zIndex:l[d].zIndex||2};if(l[d].dashStyle)h.dashstyle=l[d].dashStyle;this.crosshairs[d]=c.renderer.path(e).attr(h).add()}}D(c,"tooltipRefresh",{text:j,x:f+c.plotLeft,y:g+c.plotTop,borderColor:m})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(t(c.x),t(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)}};rb.prototype={init:function(a,
146
+ b){var c=$?"":b.chart.zoomType,d=a.inverted,e;this.options=b;this.chart=a;this.zoomX=e=/x/.test(c);this.zoomY=c=/y/.test(c);this.zoomHor=e&&!d||c&&d;this.zoomVert=c&&!d||e&&d;this.pinchDown=[];this.lastValidTouch={};if(b.tooltip.enabled)a.tooltip=new qb(a,b.tooltip);this.setDOMEvents()},normalize:function(a){var b,c,d,a=a||O.event;if(!a.target)a.target=a.srcElement;a=Qb(a);d=a.touches?a.touches.item(0):a;this.chartPosition=b=Ub(this.chart.container);d.pageX===y?(c=a.x,b=a.y):(c=d.pageX-b.left,b=d.pageY-
147
+ b.top);return v(a,{chartX:t(c),chartY:t(b)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};n(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f=b.hoverPoint,g=b.hoverSeries,h,i,j=b.chartWidth,k=this.getIndex(a);if(d&&this.options.tooltip.shared&&
148
+ (!g||!g.noSharedTooltip)){e=[];h=c.length;for(i=0;i<h;i++)if(c[i].visible&&c[i].options.enableMouseTracking!==!1&&!c[i].noSharedTooltip&&c[i].tooltipPoints.length&&(b=c[i].tooltipPoints[k],b.series))b._dist=Q(k-b.clientX),j=K(j,b._dist),e.push(b);for(h=e.length;h--;)e[h]._dist>j&&e.splice(h,1);if(e.length&&e[0].clientX!==this.hoverX)d.refresh(e,a),this.hoverX=e[0].clientX}if(g&&g.tracker){if((b=g.tooltipPoints[k])&&b!==f)b.onMouseOver(a)}else d&&d.followPointer&&!d.isHidden&&(a=d.getAnchor([{}],a),
149
+ d.updatePosition({plotX:a[0],plotY:a[1]}))},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,b=e&&e.shared?b.hoverPoints:d;(a=a&&e&&b)&&ha(b)[0].plotX===y&&(a=!1);if(a)e.refresh(b);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&(e.hide(),e.hideCrosshairs());this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart;n(c.series,function(d){d.xAxis&&d.xAxis.zoomEnabled&&(d.group.attr(a),d.markerGroup&&(d.markerGroup.attr(a),d.markerGroup.clip(b?c.clipRect:null)),d.dataLabelsGroup&&
150
+ d.dataLabelsGroup.attr(a))});c.clipRect.attr(b||c.clipBox)},pinchTranslateDirection:function(a,b,c,d,e,f,g){var h=this.chart,i=a?"x":"y",j=a?"X":"Y",k="chart"+j,m=a?"width":"height",l=h["plot"+(a?"Left":"Top")],p,s,o=1,n=h.inverted,w=h.bounds[a?"h":"v"],q=b.length===1,t=b[0][k],r=c[0][k],v=!q&&b[1][k],u=!q&&c[1][k],x,c=function(){!q&&Q(t-v)>20&&(o=Q(r-u)/Q(t-v));s=(l-r)/o+t;p=h["plot"+(a?"Width":"Height")]/o};c();b=s;b<w.min?(b=w.min,x=!0):b+p>w.max&&(b=w.max-p,x=!0);x?(r-=0.8*(r-g[i][0]),q||(u-=
151
+ 0.8*(u-g[i][1])),c()):g[i]=[r,u];n||(f[i]=s-l,f[m]=p);f=n?1/o:o;e[m]=p;e[i]=b;d[n?a?"scaleY":"scaleX":"scale"+j]=o;d["translate"+j]=f*l+(r-f*t)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=c.tooltip&&c.tooltip.options.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.zoomHor||b.pinchHor,j=b.zoomVert||b.pinchVert,k=i||j,m=b.selectionMarker,l={},p={};a.type==="touchstart"&&(e||k)&&a.preventDefault();La(f,function(a){return b.normalize(a)});if(a.type==="touchstart")n(f,function(a,
152
+ b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],n(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=K(e,f),e=q(e,f);b.min=K(a.pos,g-d);b.max=q(a.pos+a.len,e+d)}});else if(d.length){if(!m)b.selectionMarker=m=v({destroy:ta},c.plotBox);i&&b.pinchTranslateDirection(!0,d,f,l,m,p,h);j&&b.pinchTranslateDirection(!1,d,f,l,m,p,h);b.hasPinched=k;b.scaleGroups(l,
153
+ p);!k&&e&&g===1&&this.runPointActions(b.normalize(a))}},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,a=a.chartY,e=this.zoomHor,f=this.zoomVert,g=b.plotLeft,h=b.plotTop,i=b.plotWidth,j=b.plotHeight,k,m=this.mouseDownX,l=this.mouseDownY;d<g?d=g:d>g+i&&(d=g+i);a<h?a=h:a>h+j&&(a=h+j);this.hasDragged=Math.sqrt(Math.pow(m-d,2)+Math.pow(l-a,2));if(this.hasDragged>
154
+ 10){k=b.isInsidePlot(m-g,l-h);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&k&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(g,h,e?1:i,f?1:j,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&e&&(e=d-m,this.selectionMarker.attr({width:Q(e),x:(e>0?0:e)+m}));this.selectionMarker&&f&&(e=a-l,this.selectionMarker.attr({height:Q(e),y:(e>0?0:e)+l}));k&&!this.selectionMarker&&c.panning&&b.pan(d)}},drop:function(a){var b=this.chart,c=this.hasPinched;
155
+ if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},e=this.selectionMarker,f=e.x,g=e.y,h;if(this.hasDragged||c)n(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?f:g),b=a.toValue(b?f+e.width:g+e.height);!isNaN(c)&&!isNaN(b)&&(d[a.xOrY+"Axis"].push({axis:a,min:K(c,b),max:q(c,b)}),h=!0)}}),h&&D(b,"selection",d,function(a){b.zoom(v(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();c&&this.scaleGroups({translateX:b.plotLeft,
156
+ translateY:b.plotTop,scaleX:1,scaleY:1})}if(b)L(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){this.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=Qb(a);c&&d&&d.isCartesian&&!b.isInsidePlot(a.pageX-c.left-b.plotLeft,a.pageY-c.top-
157
+ b.plotTop)&&this.reset()},onContainerMouseLeave:function(){this.reset();this.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart,a=this.normalize(a);a.returnValue=!1;b.mouseIsDown==="mousedown"&&this.drag(a);b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=A(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a=a.parentNode}},onTrackerMouseOut:function(a){var b=
158
+ this.chart.hoverSeries;if(b&&!b.options.stickyTracking&&!this.inClass(a.toElement||a.relatedTarget,"highcharts-tooltip"))b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,f=b.inverted,g,h,i,a=this.normalize(a);a.cancelBubble=!0;if(!b.cancelClick)c&&this.inClass(a.target,"highcharts-tracker")?(g=this.chartPosition,h=c.plotX,i=c.plotY,v(c,{pageX:g.left+d+(f?b.plotWidth-i:h),pageY:g.top+e+(f?b.plotHeight-h:i)}),D(c.series,"click",v(a,{point:c})),b.hoverPoint&&
159
+ c.firePointEvent("click",a)):(v(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&D(b,"click",a))},onContainerTouchStart:function(a){var b=this.chart;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&(this.runPointActions(a),this.pinch(a))):a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length===1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){this.drop(a)},setDOMEvents:function(){var a=
160
+ this,b=a.chart.container,c;this._events=c=[[b,"onmousedown","onContainerMouseDown"],[b,"onmousemove","onContainerMouseMove"],[b,"onclick","onContainerClick"],[b,"mouseleave","onContainerMouseLeave"],[z,"mousemove","onDocumentMouseMove"],[z,"mouseup","onDocumentMouseUp"]];fb&&c.push([b,"ontouchstart","onContainerTouchStart"],[b,"ontouchmove","onContainerTouchMove"],[z,"touchend","onDocumentTouchEnd"]);n(c,function(b){a["_"+b[2]]=function(c){a[b[2]](c)};b[1].indexOf("on")===0?b[0][b[1]]=a["_"+b[2]]:
161
+ J(b[0],b[1],a["_"+b[2]])})},destroy:function(){var a=this;n(a._events,function(b){b[1].indexOf("on")===0?b[0][b[1]]=null:ba(b[0],b[1],a["_"+b[2]])});delete a._events;clearInterval(a.tooltipTimeout)}};sb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=o(b.padding,8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=u(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=x(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=e-5,c.maxItemWidth=0,c.chart=a,
162
+ c.itemHeight=0,c.lastLineHeight=0,c.render(),J(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.color:g,g=a.options&&a.options.marker,i={stroke:h,fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g)for(j in g=a.convertAttribs(g),g)d=g[j],d!==y&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,
163
+ b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Ra(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=
164
+ b.translateY,n(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,L(f,{left:b.translateX+e.legendItemWidth+f.x-20+"px",top:g+"px",display:g>c-6&&g<c+d-6?"":S}))})},renderTitle:function(){var a=this.padding,b=this.options.title,c=0;if(b.text){if(!this.title)this.title=this.chart.renderer.label(b.text,a-3,a-4,null,null,null,null,null,"legend-title").attr({zIndex:1}).css(b.style).add(this.group);c=this.title.getBBox().height;this.contentGroup.attr({translateY:c})}this.titleHeight=c},renderItem:function(a){var w;
165
+ var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=e.symbolWidth,h=e.symbolPadding,i=b.itemStyle,j=b.itemHiddenStyle,k=b.padding,m=!e.rtl,l=e.width,p=e.itemMarginBottom||0,s=b.itemMarginTop,o=b.initialItemX,n=a.legendItem,t=a.series||a,r=t.options,v=r.showCheckbox,u=e.useHTML;if(!n&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup),t.drawLegendSymbol(b,a),a.legendItem=n=d.text(e.labelFormat?wa(e.labelFormat,a):e.labelFormatter.call(a),m?g+h:-h,b.baseline,
166
+ u).css(x(a.visible?i:j)).attr({align:m?"left":"right",zIndex:2}).add(a.legendGroup),(u?n:a.legendGroup).on("mouseover",function(){a.setState("hover");n.css(b.options.itemHoverStyle)}).on("mouseout",function(){n.css(a.visible?i:j);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):D(a,"legendItemClick",b,c)}),b.colorizeItem(a,a.visible),r&&v))a.checkbox=U("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},
167
+ e.itemCheckboxStyle,c.container),J(a.checkbox,"click",function(b){D(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})});d=n.getBBox();w=a.legendItemWidth=e.itemWidth||g+h+d.width+k+(v?20:0),e=w;b.itemHeight=g=d.height;if(f&&b.itemX-o+e>(l||c.chartWidth-2*k-o))b.itemX=o,b.itemY+=s+b.lastLineHeight+p,b.lastLineHeight=0;b.maxItemWidth=q(b.maxItemWidth,e);b.lastItemY=s+b.itemY+p;b.lastLineHeight=q(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=s+g+p,b.lastLineHeight=
168
+ g);b.offsetWidth=l||q(f?b.itemX-o:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,m=j.borderWidth,l=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&!r(b.linkedTo)&&(e=e.concat(a.legendItems||
169
+ (b.legendType==="point"?a.data:a)))});Ib(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(m||l){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,m||0).attr({stroke:j.borderColor,
170
+ "stroke-width":m||0,fill:l||S}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;n(e,function(b){a.positionItem(b)});f&&d.align(v({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,i=e.navigation,j=o(i.animation,!0),k=i.arrowSize||12,m=this.nav;e.layout===
171
+ "horizontal"&&(f/=2);g&&(f=K(f,g));if(a>f&&!e.useHTML){this.clipHeight=c=f-20-this.titleHeight;this.pageCount=ja(a/c);this.currentPage=o(this.currentPage,1);this.fullHeight=a;if(!h)h=b.clipRect=d.clipRect(0,0,9999,0),b.contentGroup.clip(h);h.attr({height:c});if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,k,k).on("click",function(){b.scroll(-1,j)}).add(m),this.pager=d.text("",15,10).css(i.style).add(m),this.down=d.symbol("triangle-down",0,0,k,k).on("click",
172
+ function(){b.scroll(1,j)}).add(m);b.scroll(0);a=f}else if(m)h.attr({height:c.chartHeight}),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+a,e=this.clipHeight,f=this.options.navigation,g=f.activeColor,h=f.inactiveColor,f=this.pager,i=this.padding;d>c&&(d=c);if(d>0)b!==y&&Ia(b,this.chart),this.nav.attr({translateX:i,translateY:e+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:d===1?h:g}).css({cursor:d===
173
+ 1?"default":"pointer"}),f.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?h:g}).css({cursor:d===c?"default":"pointer"}),e=-K(e*(d-1),this.fullHeight-e+i)+1,this.scrollGroup.animate({translateY:e}),f.attr({text:d+"/"+c}),this.currentPage=d,this.positionCheckboxes(e)}};tb.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=x(N,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=V(e)?e:[e,e,e,e];this.optionsMarginTop=o(d.marginTop,e[0]);this.optionsMarginRight=
174
+ o(d.marginRight,e[1]);this.optionsMarginBottom=o(d.marginBottom,e[2]);this.optionsMarginLeft=o(d.marginLeft,e[3]);this.runChartClick=(e=d.events)&&!!e.click;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=Ba.length;Ba.push(f);d.reflow!==!1&&J(f,"load",function(){f.initReflow()});if(e)for(g in e)J(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=$?!1:o(d.animation,!0);f.pointCount=0;f.counters=new Hb;
175
+ f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=aa[a.type||b.type||b.defaultSeriesType])||qa(17,!0);b=new b;b.init(this,a);return b},addSeries:function(a,b,c){var d,e=this;a&&(b=o(b,!0),D(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var b=b?"xAxis":"yAxis",e=this.options;new ab(this,x(a,{index:this[b].length}));e[b]=ha(e[b]||{});e[b].push(a);o(c,!0)&&this.redraw(d)},isInsidePlot:function(a,b,c){var d=
176
+ c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&n(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,i=c.length,j=i,k=this.renderer,m=k.isHidden(),l=[];Ia(a,this);for(m&&this.cloneRenderTo();j--;)if(a=c[j],a.isDirty&&a.options.stacking){g=!0;break}if(g)for(j=i;j--;)if(a=c[j],a.options.stacking)a.isDirty=
177
+ !0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,l.push(function(){D(a,"afterSetExtremes",a.getExtremes())});if(a.isDirty||h||g)a.redraw(),h=!0})}h&&this.drawChartBox();n(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||
178
+ a.xAxis)&&a.redraw()});d&&d.reset&&d.reset(!0);k.draw();D(this,"redraw");m&&this.cloneRenderTo(!0);n(l,function(a){a.call()})},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=U(ya,{className:"highcharts-loading"},v(d.style,{zIndex:10,display:S}),this.container),this.loadingSpan=U("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)L(c,{opacity:0,display:"",left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+
179
+ "px",height:this.plotHeight+"px"}),wb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&wb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){L(b,{display:S})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d<b.length;d++)if(b[d].options.id===a)return b[d];for(d=0;d<c.length;d++)if(c[d].options.id===a)return c[d];for(d=0;d<c.length;d++){e=c[d].points||
180
+ [];for(b=0;b<e.length;b++)if(e[b].id===a)return e[b]}return null},getAxes:function(){var a=this,b=this.options,c=b.xAxis=ha(b.xAxis||{}),b=b.yAxis=ha(b.yAxis||{});n(c,function(a,b){a.index=b;a.isX=!0});n(b,function(a,b){a.index=b});c=c.concat(b);n(c,function(b){new ab(a,b)});a.adjustTickAmounts()},getSelectedPoints:function(){var a=[];n(this.series,function(b){a=a.concat(ob(b.points||[],function(a){return a.selected}))});return a},getSelectedSeries:function(){return ob(this.series,function(a){return a.selected})},
181
+ showResetZoom:function(){var a=this,b=N.lang,c=a.options.chart.resetZoomButton,d=c.theme,e=d.states,f=c.relativeTo==="chart"?null:"plotBox";this.resetZoomButton=a.renderer.button(b.resetZoom,null,null,function(){a.zoomOut()},d,e&&e.hover).attr({align:c.position.align,title:b.resetZoomTitle}).add().align(c.position,!1,f)},zoomOut:function(){var a=this;D(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var b,c=this.pointer,d=!1,e;!a||a.resetSelection?n(this.axes,function(a){b=
182
+ a.zoom()}):n(a.xAxis.concat(a.yAxis),function(a){var e=a.axis,h=e.isXAxis;if(c[h?"zoomX":"zoomY"]||c[h?"pinchX":"pinchY"])b=e.zoom(a.min,a.max),e.displayBtn&&(d=!0)});e=this.resetZoomButton;if(d&&!e)this.showResetZoom();else if(!d&&V(e))this.resetZoomButton=e.destroy();b&&this.redraw(o(this.options.chart.animation,a&&a.animation,this.pointCount<100))},pan:function(a){var b=this.xAxis[0],c=this.mouseDownX,d=b.pointRange/2,e=b.getExtremes(),f=b.translate(c-a,!0)+d,c=b.translate(c+this.plotWidth-a,!0)-
183
+ d;(d=this.hoverPoints)&&n(d,function(a){a.setState()});b.series.length&&f>K(e.dataMin,e.min)&&c<q(e.dataMax,e.max)&&b.setExtremes(f,c,!0,!1,{trigger:"pan"});this.mouseDownX=a;L(this.container,{cursor:"move"})},setTitle:function(a,b){var f;var c=this,d=c.options,e;e=d.title=x(d.title,a);f=d.subtitle=x(d.subtitle,b),d=f;n([["title",a,e],["subtitle",b,d]],function(a){var b=a[0],d=c[b],e=a[1],a=a[2];d&&e&&(c[b]=d=d.destroy());a&&a.text&&!d&&(c[b]=c.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align,
184
+ "class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add().align(a,!1,"spacingBox"))})},getChartSize:function(){var a=this.options.chart,b=this.renderToClone||this.renderTo;this.containerWidth=gb(b,"width");this.containerHeight=gb(b,"height");this.chartWidth=q(0,a.width||this.containerWidth||600);this.chartHeight=q(0,o(a.height,this.containerHeight>19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Ra(b),delete this.renderToClone):
185
+ (c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),L(b,{position:"absolute",top:"-9999px",display:"block"}),z.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+ub++;if(fa(a))this.renderTo=a=z.getElementById(a);a||qa(13,!0);c=u(A(a,"data-highcharts-chart"));!isNaN(c)&&Ba[c]&&Ba[c].destroy();A(a,"data-highcharts-chart",this.index);a.innerHTML="";a.offsetWidth||this.cloneRenderTo();
186
+ this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=U(ya,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},v({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new Ca(a,c,d,!0):new Sa(a,c,d);$&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart,
187
+ b=a.spacingTop,c=a.spacingRight,d=a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,i=this.optionsMarginRight,j=this.optionsMarginBottom,k=this.options.title,m=this.options.subtitle,l=this.options.legend,p=o(l.margin,10),s=l.x,t=l.y,B=l.align,w=l.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&&!r(this.optionsMarginTop))if(m=q(this.title&&!k.floating&&!k.verticalAlign&&k.y||0,this.subtitle&&!m.floating&&!m.verticalAlign&&
188
+ m.y||0))this.plotTop=q(this.plotTop,m+o(k.margin,15)+b);if(f.display&&!l.floating)if(B==="right"){if(!r(i))this.marginRight=q(this.marginRight,f.legendWidth-s+p+c)}else if(B==="left"){if(!r(h))this.plotLeft=q(this.plotLeft,f.legendWidth+s+p+a)}else if(w==="top"){if(!r(g))this.plotTop=q(this.plotTop,f.legendHeight+t+p+b)}else if(w==="bottom"&&!r(j))this.marginBottom=q(this.marginBottom,f.legendHeight-t+p+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=
189
+ this.extraTopMargin);this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});r(h)||(this.plotLeft+=e[3]);r(g)||(this.plotTop+=e[0]);r(j)||(this.marginBottom+=e[2]);r(i)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g=c.width||gb(d,"width"),h=c.height||gb(d,"height"),a=a?a.target:O;if(!b.hasUserSize&&g&&h&&(a===O||a===z)){if(g!==b.containerWidth||h!==b.containerHeight)clearTimeout(e),b.reflowTimeout=e=setTimeout(function(){if(b.container)b.setSize(g,
190
+ h,!1),b.hasUserSize=null},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart,d=b.renderTo,e;J(O,"resize",a);J(b,"destroy",function(){ba(O,"resize",a)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&D(d,"endResize",null,function(){d.isResizing-=1})};Ia(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(r(a))d.chartWidth=e=q(0,t(a)),d.hasUserSize=!!e;if(r(b))d.chartHeight=f=q(0,t(b));L(d.container,{width:e+"px",height:f+"px"});d.setChartSize(!0);
191
+ d.renderer.setSize(e,f,c);d.maxTicks=null;n(d.axes,function(a){a.isDirty=!0;a.setScale()});n(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();d.redraw(c);d.oldChartHeight=null;D(d,"resize");za===!1?g():setTimeout(g,za&&za.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=f.spacingTop,h=f.spacingRight,i=f.spacingBottom,j=f.spacingLeft,k=this.clipOffset,m,l,p,o;this.plotLeft=m=
192
+ t(this.plotLeft);this.plotTop=l=t(this.plotTop);this.plotWidth=p=q(0,t(d-m-this.marginRight));this.plotHeight=o=q(0,t(e-l-this.marginBottom));this.plotSizeX=b?o:p;this.plotSizeY=b?p:o;this.plotBorderWidth=b=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:j,y:g,width:d-j-h,height:e-g-i};this.plotBox=c.plotBox={x:m,y:l,width:p,height:o};c=ja(q(b,k[3])/2);d=ja(q(b,k[0])/2);this.clipBox={x:c,y:d,width:T(this.plotSizeX-q(b,k[1])/2-c),height:T(this.plotSizeY-q(b,k[2])/2-d)};a||n(this.axes,function(a){a.setAxisSize();
193
+ a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart,b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=o(this.optionsMarginTop,a.spacingTop);this.marginRight=o(this.optionsMarginRight,b);this.marginBottom=o(this.optionsMarginBottom,c);this.plotLeft=o(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,
194
+ g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,m=a.plotBackgroundImage,l=a.plotBorderWidth||0,p,o=this.plotLeft,n=this.plotTop,t=this.plotWidth,q=this.plotHeight,r=this.plotBox,v=this.clipRect,u=this.clipBox;p=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-p,d-p));else{e={fill:j||S};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(p/2,p/2,c-p,d-p,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?
195
+ f.animate(r):this.plotBackground=b.rect(o,n,t,q,0).attr({fill:k}).add().shadow(a.plotShadow);if(m)h?h.animate(r):this.plotBGImage=b.image(m,o,n,t,q).add();v?v.animate({width:u.width,height:u.height}):this.clipRect=b.clipRect(u);if(l)g?g.animate(g.crisp(null,o,n,t,q)):this.plotBorder=b.rect(o,n,t,q,0,l).attr({stroke:a.plotBorderColor,"stroke-width":l,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"],
196
+ function(g){c=aa[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=aa[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new sb(a,d.legend);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()});
197
+ if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(b){var d=v(e.style,b.style),f=u(d.left)+a.plotLeft,g=u(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);
198
+ a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;D(a,"destroy");Ba[a.index]=y;a.renderTo.removeAttribute("data-highcharts-chart");ba(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});
199
+ if(d)d.innerHTML="",ba(d),f&&Ra(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!Z&&O==O.top&&z.readyState!=="complete"||$&&!O.canvg?($?Rb.push(function(){a.firstRender()},a.options.global.canvasToolsURL):z.attachEvent("onreadystatechange",function(){z.detachEvent("onreadystatechange",a.firstRender);z.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),D(a,"init"),a.resetMargins(),
200
+ a.setChartSize(),a.propFromSeries(),a.getAxes(),n(b.series||[],function(b){a.initSeries(b)}),D(a,"beforeRender"),a.pointer=new rb(a,b),a.render(),a.renderer.draw(),c&&c.apply(a,[a]),n(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),D(a,"load")}};tb.prototype.callbacks=[];var Na=function(){};Na.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++],
201
+ a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Na.prototype.optionsToObject.call(this,a);v(this,a);this.options=this.options?v(this.options,a):a;if(d)this.y=this[d];if(this.x===y&&c)this.x=b===y?c.autoIncrement():b;return this},optionsToObject:function(a){var b,c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b={y:a};else if(Da(a)){b={};if(a.length>e){c=typeof a[0];
202
+ if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;g<e;)b[d[g++]]=a[f++]}else if(typeof a==="object"){b=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers=!0}return b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;if(b&&(this.setState(),ga(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)ba(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=
203
+ null},destroyElements:function(){for(var a="graphic,dataLabel,dataLabelUpper,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c=this,d=c.series,e=d.chart,a=o(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=
204
+ c.options.selected=a;d.options.data[la(c,d.data)]=c.options;c.setState(a&&"select");b||n(e.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=a.options.selected=!1,d.options.data[la(a,d.data)]=a.options,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(a){var b=this.series,c=b.chart,d=c.tooltip,e=c.hoverPoint;if(e&&e!==this)e.onMouseOut();this.firePointEvent("mouseOver");d&&(!d.shared||b.noSharedTooltip)&&d.refresh(this,a);this.setState("hover");c.hoverPoint=this},
205
+ onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||la(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=o(c.valueDecimals,""),e=c.valuePrefix||"",f=c.valueSuffix||"";n(b.pointArrayMap||["y"],function(b){b="{point."+b;if(e||f)a=a.replace(b+"}",e+b+"}"+f);a=a.replace(b+"}",b+":,."+d+"f}")});return wa(a,{point:this,series:this.series})},update:function(a,b,c){var d=this,e=d.series,f=d.graphic,
206
+ g,h=e.data,i=e.chart,b=o(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);V(a)&&(e.getAttribs(),f&&f.attr(d.pointAttr[e.state]));g=la(d,h);e.xData[g]=d.x;e.yData[g]=e.toYData?e.toYData(d):d.y;e.zData[g]=d.z;e.options.data[g]=d.options;e.isDirty=!0;e.isDirtyData=!0;b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.chart,f,g=d.data;Ia(b,e);a=o(a,!0);c.firePointEvent("remove",null,function(){f=la(c,g);g.splice(f,1);d.options.data.splice(f,1);d.xData.splice(f,1);
207
+ d.yData.splice(f,1);d.zData.splice(f,1);c.destroy();d.isDirty=!0;d.isDirtyData=!0;a&&e.redraw()})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});D(this,a,b,c)},importEvents:function(){if(!this.hasImportedEvents){var a=x(this.series.options.point,this.options).events,b;this.events=a;for(b in a)J(this,
208
+ b,a[b]);this.hasImportedEvents=!0}},setState:function(a){var b=this.plotX,c=this.plotY,d=this.series,e=d.options.states,f=X[d.type].marker&&d.options.marker,g=f&&!f.enabled,h=f&&f.states[a],i=h&&h.enabled===!1,j=d.stateMarkerGraphic,k=this.marker||{},m=d.chart,l=this.pointAttr,a=a||"";if(!(a===this.state||this.selected&&a!=="select"||e[a]&&e[a].enabled===!1||a&&(i||g&&!h.enabled))){if(this.graphic)e=f&&this.graphic.symbolName&&l[a].r,this.graphic.attr(x(l[a],e?{x:b-e,y:c-e,width:2*e,height:2*e}:{}));
209
+ else{if(a&&h)e=h.radius,k=k.symbol||d.symbol,j&&j.currentSymbol!==k&&(j=j.destroy()),j?j.attr({x:b-e,y:c-e}):(d.stateMarkerGraphic=j=m.renderer.symbol(k,b-e,c-e,2*e,2*e).attr(l[a]).add(d.markerGroup),j.currentSymbol=k);if(j)j[a&&m.isInsidePlot(b,c)?"show":"hide"]()}this.state=a}}};var R=function(){};R.prototype={isCartesian:!0,type:"line",pointClass:Na,sorted:!0,requireSorting:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},colorCounter:0,init:function(a,
210
+ b){var c,d,e=a.series;this.chart=a;this.options=b=this.setOptions(b);this.bindAxes();v(this,{name:b.name,state:"",pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});if($)b.animation=!1;d=b.events;for(c in d)J(this,c,d[c]);if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;this.getColor();this.getSymbol();this.setData(b.data,!1);if(this.isCartesian)a.hasCartesianSeries=!0;e.push(this);this._i=e.length-1;Ib(e,function(a,b){return o(a.options.index,
211
+ a._i)-o(b.options.index,a._i)});n(e,function(a,b){a.index=b;a.name=a.name||"Series "+(b+1)});c=b.linkedTo;this.linkedSeries=[];if(fa(c)&&(c=c===":previous"?e[this.index-1]:a.get(c)))c.linkedSeries.push(this),this.linkedParent=c},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;a.isCartesian&&n(["xAxis","yAxis"],function(e){n(c[e],function(c){d=c.options;if(b[e]===d.index||b[e]!==y&&b[e]===d.id||b[e]===y&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0});a[e]||qa(18,!0)})},autoIncrement:function(){var a=
212
+ this.options,b=this.xIncrement,b=o(b,a.pointStart,0);this.pointInterval=o(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);d.length&&(b=[d])}else n(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions,
213
+ d=c[this.type];this.userOptions=a;a=x(d,c.series,a);this.tooltipOptions=x(b.tooltip,a.tooltip);d.marker===null&&delete a.marker;return a},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,e;e=a.color||X[this.type].color;if(!e&&!a.colorByPoint)r(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a=this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols,
214
+ c=c.counters;this.symbol=b.symbol;if(!this.symbol)r(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&&c.enabled)b=
215
+ c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).add(f)},addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xData,k=this.yData,m=this.zData,l=this.names,p=g&&g.shift||0,n=e.data;Ia(d,i);if(g&&c)g.shift=p+1;if(h){if(c)h.shift=p+1;h.isArea=!0}b=o(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);j.push(d.x);k.push(this.toYData?this.toYData(d):d.y);m.push(d.z);if(l)l[d.x]=d.name;n.push(a);e.legendType==="point"&&
216
+ this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1):(f.shift(),j.shift(),k.shift(),m.shift(),n.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&i.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.chart,f=null,g=this.xAxis,h=g&&g.categories&&!g.categories.length?[]:null,i;this.xIncrement=null;this.pointRange=g&&g.categories?1:d.pointRange;this.colorCounter=0;var j=[],k=[],m=[],l=a?a.length:[],p=(i=this.pointArrayMap)&&i.length,n=!!this.toYData;if(l>(d.turboThreshold||
217
+ 1E3)){for(i=0;f===null&&i<l;)f=a[i],i++;if(Ea(f)){f=o(d.pointStart,0);d=o(d.pointInterval,1);for(i=0;i<l;i++)j[i]=f,k[i]=a[i],f+=d;this.xIncrement=f}else if(Da(f))if(p)for(i=0;i<l;i++)d=a[i],j[i]=d[0],k[i]=d.slice(1,p+1);else for(i=0;i<l;i++)d=a[i],j[i]=d[0],k[i]=d[1]}else for(i=0;i<l;i++)if(a[i]!==y&&(d={series:this},this.pointClass.prototype.applyOptions.apply(d,[a[i]]),j[i]=d.x,k[i]=n?this.toYData(d):d.y,m[i]=d.z,h&&d.name))h[i]=d.name;this.requireSorting&&j.length>1&&j[1]<j[0]&&qa(15);fa(k[0])&&
218
+ qa(14,!0);this.data=[];this.options.data=a;this.xData=j;this.yData=k;this.zData=m;this.names=h;for(i=c&&c.length||0;i--;)c[i]&&c[i].destroy&&c[i].destroy();if(g)g.minRange=g.userMinRange;this.isDirty=this.isDirtyData=e.isDirtyBox=!0;o(b,!0)&&e.redraw(!1)},remove:function(a,b){var c=this,d=c.chart,a=o(a,!0);if(!c.isRemoving)c.isRemoving=!0,D(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;a&&d.redraw(b)});c.isRemoving=!1},processData:function(a){var b=this.xData,c=this.yData,
219
+ d=b.length,e=0,f=d,g,h,i=this.xAxis,j=this.options,k=j.cropThreshold,m=this.isCartesian;if(m&&!this.isDirty&&!i.isDirty&&!this.yAxis.isDirty&&!a)return!1;if(m&&this.sorted&&(!k||d>k||this.forceCrop))if(a=i.getExtremes(),i=a.min,k=a.max,b[d-1]<i||b[0]>k)b=[],c=[];else if(b[0]<i||b[d-1]>k){for(a=0;a<d;a++)if(b[a]>=i){e=q(0,a-1);break}for(;a<d;a++)if(b[a]>k){f=a+1;break}b=b.slice(e,f);c=c.slice(e,f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===y||d<h))h=d;this.cropped=g;this.cropStart=e;
220
+ this.processedXData=b;this.processedYData=c;if(j.pointRange===null)this.pointRange=h||1;this.closestPointRange=h},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,j=this.hasGroupedData,k,m=[],l;if(!b&&!j)b=[],b.length=a.length,b=this.data=b;for(l=0;l<g;l++)i=h+l,j?m[l]=(new f).init(this,[d[l]].concat(ha(e[l]))):(b[i]?k=b[i]:a[i]!==y&&(b[i]=k=(new f).init(this,a[i],d[l])),m[l]=k);if(b&&(g!==
221
+ (c=b.length)||j))for(l=0;l<c;l++)if(l===h&&!j&&(l+=g),b[l])b[l].destroyElements(),b[l].plotX=y;this.data=b;this.points=m},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i,j,k=a.pointPlacement==="between",m=a.threshold;j=e.series.sort(function(a,b){return a.index-b.index});for(a=j.length;a--;)if(j[a].visible){j[a]===this&&(i=!0);break}for(a=0;a<
222
+ g;a++){j=f[a];var l=j.x,p=j.y,n=j.low,q=e.stacks[(p<m?"-":"")+this.stackKey];if(e.isLog&&p<=0)j.y=p=null;j.plotX=c.translate(l,0,0,0,1,k);if(b&&this.visible&&q&&q[l])n=q[l],q=n.total,n.cum=n=n.cum-p,p=n+p,i&&(n=o(m,e.min)),e.isLog&&n<=0&&(n=null),b==="percent"&&(n=q?n*100/q:0,p=q?p*100/q:0),j.percentage=q?j.y*100/q:0,j.total=j.stackTotal=q,j.stackY=p;j.yBottom=r(n)?e.translate(n,0,1,0,1):null;h&&(p=this.modifyValue(p,j));j.plotY=typeof p==="number"&&p!==Infinity?t(e.translate(p,0,1,0,1)*10)/10:y;
223
+ j.clientX=k?c.translate(l,0,0,0,1):j.plotX;j.negative=j.y<(m||0);j.category=d&&d[j.x]!==y?d[j.x]:j.x}this.getSegments()},setTooltipPoints:function(a){var b=[],c,d,e=(c=this.xAxis)?c.tooltipLen||c.len:this.chart.plotSizeX,f,g,h=[];if(this.options.enableMouseTracking!==!1){if(a)this.tooltipPoints=null;n(this.segments||this.points,function(a){b=b.concat(a)});c&&c.reversed&&(b=b.reverse());a=b.length;for(g=0;g<a;g++){f=b[g];c=b[g-1]?d+1:0;for(d=b[g+1]?q(0,T((f.clientX+(b[g+1]?b[g+1].clientX:e))/2)):e;c>=
224
+ 0&&c<=d;)h[c++]=f}this.tooltipPoints=h}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=b.dateTimeLabelFormats,e=this.xAxis,f=e&&e.options.type==="datetime",b=b.headerFormat,e=e&&e.closestPointRange,g;if(f&&!c)if(e)for(g in E){if(E[g]>=e){c=d[g];break}}else c=d.day;f&&c&&Ea(a.key)&&(b=b.replace("{point.key}","{point.key:"+c+"}"));return wa(b,{point:a,series:this})},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&
225
+ D(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&D(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!V(e))e=X[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h],
226
+ e=c[h+"m"],a||(c[h]=a=d.clipRect(v(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()},e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip());
227
+ setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,m=this.options.marker,l,n=this.markerGroup;if(m.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,i=g.marker||{},a=m.enabled&&i.enabled===y||i.enabled,l=c.isInsidePlot(t(d),e,c.inverted),a&&e!==y&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=o(i.symbol,this.symbol),j=i.indexOf("url")===
228
+ 0,k)k.attr({visibility:l?Z?"inherit":"visible":"hidden"}).animate(v({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(l&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(n)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=o(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=X[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color,
229
+ h={stroke:g,fill:g},i=a.points||[],j=[],k,m=a.pointAttrToOptions,l=b.negativeColor,p;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=e.color||ma(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,h);n(["hover","select"],function(b){j[b]=a.convertAttribs(d[b],j[""])});a.pointAttr=j;for(g=i.length;g--;){h=i[g];if((c=h.options&&h.options.marker||h.options)&&c.enabled===!1)c.radius=0;if(h.negative&&l)h.color=h.fillColor=l;f=b.colorByPoint||h.color;
230
+ if(h.options)for(p in m)r(c[m[p]])&&(f=!0);if(f){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=ma(f.color||h.color).brighten(f.brightness||e.brightness).get();k[""]=a.convertAttribs(v({color:h.color},c),j[""]);k.hover=a.convertAttribs(d.hover,j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""]);if(h.negative&&b.marker&&l)k[""].fill=k.hover.fill=k.select.fill=a.convertAttribs({fillColor:l}).fill}else k=j;h.pointAttr=k}},update:function(a,b){var c=this.chart,d=
231
+ this.type,a=x(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},a);this.remove(!1);v(this,aa[a.type||d].prototype);this.init(c,a);o(b,!0)&&c.redraw(!1)},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(Aa),d,e,f=a.data||[],g,h,i;D(a,"destroy");ba(a);n(["xAxis","yAxis"],function(b){if(i=a[b])ga(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);
232
+ n("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;ga(b.series,a);for(h in a)delete a[h]},drawDataLabels:function(){var a=this,b=a.options.dataLabels,c=a.points,d,e,f,g;if(b.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(b),g=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",b.zIndex||6),e=b,n(c,function(c){var i,
233
+ j=c.dataLabel,k,m,l=c.connector,n=!0;d=c.options&&c.options.dataLabels;i=e.enabled||d&&d.enabled;if(j&&!i)c.dataLabel=j.destroy();else if(i){i=b.rotation;b=x(e,d);k=c.getLabelConfig();f=b.format?wa(b.format,k):b.formatter.call(k,b);b.style.color=o(b.color,b.style.color,a.color,"black");if(j)if(r(f))j.attr({text:f}),n=!1;else{if(c.dataLabel=j=j.destroy(),l)c.connector=l.destroy()}else if(r(f)){j={fill:b.backgroundColor,stroke:b.borderColor,"stroke-width":b.borderWidth,r:b.borderRadius||0,rotation:i,
234
+ padding:b.padding,zIndex:1};for(m in j)j[m]===y&&delete j[m];j=c.dataLabel=a.chart.renderer[i?"text":"label"](f,0,-999,null,null,null,b.useHTML).attr(j).css(b.style).add(g).shadow(b.shadow)}j&&a.alignDataLabel(c,j,b,null,n)}})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=o(a.plotX,-999),a=o(a.plotY,-999),i=b.getBBox(),d=v({x:g?f.plotWidth-a:h,y:t(g?f.plotHeight-h:a),width:0,height:0},d);v(c,{width:i.width,height:i.height});c.rotation?(d={align:c.align,x:d.x+c.x+d.width/2,y:d.y+
235
+ c.y+d.height/2},b[e?"attr":"animate"](d)):b.align(c,null,d);b.attr({visibility:c.crop===!1||f.isInsidePlot(h,a,g)?f.renderer.isSVG?"inherit":"visible":"hidden"})},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;n(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c},
236
+ getGraphPath:function(){var a=this,b=[],c,d=[];n(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=this.getGraphPath(),g=b.negativeColor;g&&c.push(["graphNeg",g]);n(c,function(c,g){var j=c[0],k=a[j];if(k)Ta(k),k.animate({d:f});else if(d&&f.length){k={stroke:c[1],"stroke-width":d,zIndex:1};if(e)k.dashstyle=e;a[j]=
237
+ a.chart.renderer.path(f).attr(k).add(a.group).shadow(!g&&b.shadow)}})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=b.chartHeight,k=q(e,j);if(d&&(f||g))d=ja(this.yAxis.len-this.yAxis.translate(a.threshold||0)),a={x:0,y:0,width:k,height:d},k={x:0,y:d,width:k,height:k-d},b.inverted&&c.isVML&&(a={x:b.plotWidth-d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d,
238
+ height:e}),this.yAxis.reversed?(b=k,e=a):(b=a,e=k),h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};n(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)J(c,"resize",a),J(b,"destroy",function(){ba(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b,
239
+ c,d,e){var f=this[a],g=!f,h=this.chart,i=this.xAxis,j=this.yAxis;g&&(this[a]=f=h.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"]({translateX:i?i.left:h.plotLeft,translateY:j?j.top:h.plotTop,scaleX:1,scaleY:1});return f},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate&&a.renderer.isSVG,e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup",
240
+ "markers",e,f,h);d&&this.animate(!0);this.getAttribs();b.inverted=this.isCartesian?a.inverted:!1;this.drawGraph&&(this.drawGraph(),this.clipNeg());this.drawDataLabels();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&&!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis,
241
+ e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:o(d&&d.left,a.plotLeft),translateY:o(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints(!0);this.render();b&&D(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg,e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},setVisible:function(a,
242
+ b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===y?!h:a)?"show":"hide";n(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&n(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});n(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;b!==!1&&d.redraw();D(c,
243
+ f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===y?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;D(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.pointer,h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,k=k&&{cursor:k},m=a.singlePoints,l,n=function(){if(f.hoverSeries!==a)a.onMouseOver()};if(e&&!c)for(l=
244
+ e+1;l--;)d[l]==="M"&&d.splice(l+1,0,d[l+1]-i,d[l+2],"L"),(l&&d[l]==="M"||l===e)&&d.splice(l,0,"L",d[l-2]+i,d[l-1]);for(l=0;l<m.length;l++)e=m[l],d.push("M",e.plotX-i,e.plotY,"L",e.plotX+i,e.plotY);if(j)j.attr({d:d});else if(a.tracker=j=h.path(d).attr({"class":"highcharts-tracker","stroke-linejoin":"round",visibility:a.visible?"visible":"hidden",stroke:Ob,fill:c?Ob:S,"stroke-width":b.lineWidth+(c?0:2*i),zIndex:2}).addClass("highcharts-tracker").on("mouseover",n).on("mouseout",function(a){g.onTrackerMouseOut(a)}).css(k).add(a.markerGroup),
245
+ fb)j.on("touchstart",n)}};M=ea(R);aa.line=M;X.area=x(W,{threshold:0});M=ea(R,{type:"area",getSegments:function(){var a=[],b=[],c=[],d=this.xAxis,e=this.yAxis,f=e.stacks[this.stackKey],g={},h,i,j=this.points,k,m;if(this.options.stacking&&!this.cropped){for(k=0;k<j.length;k++)g[j[k].x]=j[k];for(m in f)c.push(+m);c.sort(function(a,b){return a-b});n(c,function(a){g[a]?b.push(g[a]):(h=d.translate(a),i=e.toPixels(f[a].cum,!0),b.push({y:null,plotX:h,clientX:h,plotY:i,yBottom:i,onMouseOver:ta}))});b.length&&
246
+ a.push(b)}else R.prototype.getSegments.call(this),a=this.segments;this.segments=a},getSegmentPath:function(a){var b=R.prototype.getSegmentPath.call(this,a),c=[].concat(b),d,e=this.options;b.length===3&&c.push("L",b[1],b[2]);if(e.stacking&&!this.closedStacks)for(d=a.length-1;d>=0;d--)d<a.length-1&&e.step&&c.push(a[d+1].plotX,a[d].yBottom),c.push(a[d].plotX,a[d].yBottom);else this.closeSegment(c,a);this.areaPath=this.areaPath.concat(c);return b},closeSegment:function(a,b){var c=this.yAxis.getThreshold(this.options.threshold);
247
+ a.push("L",b[b.length-1].plotX,c,"L",b[0].plotX,c)},drawGraph:function(){this.areaPath=[];R.prototype.drawGraph.apply(this);var a=this,b=this.areaPath,c=this.options,d=[["area",this.color,c.fillColor]];c.negativeColor&&d.push(["areaNeg",c.negativeColor,c.negativeFillColor]);n(d,function(d){var f=d[0],g=a[f];g?g.animate({d:b}):a[f]=a.chart.renderer.path(b).attr({fill:o(d[2],ma(d[1]).setOpacity(c.fillOpacity||0.75).get()),zIndex:0}).add(a.group)})},drawLegendSymbol:function(a,b){b.legendSymbol=this.chart.renderer.rect(0,
248
+ a.baseline-11,a.options.symbolWidth,12,2).attr({zIndex:3}).add(b.legendGroup)}});aa.area=M;X.spline=x(W);F=ea(R,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,i,j,k;if(f&&g){a=f.plotY;j=g.plotX;var g=g.plotY,m;h=(1.5*d+f.plotX)/2.5;i=(1.5*e+a)/2.5;j=(1.5*d+j)/2.5;k=(1.5*e+g)/2.5;m=(k-i)*(j-d)/(j-h)+e-k;i+=m;k+=m;i>a&&i>e?(i=q(a,e),k=2*e-i):i<a&&i<e&&(i=K(a,e),k=2*e-i);k>g&&k>e?(k=q(g,e),i=2*e-k):k<g&&k<e&&(k=K(g,e),i=2*e-k);b.rightContX=j;b.rightContY=k}c?
249
+ (b=["C",f.rightContX||f.plotX,f.rightContY||f.plotY,h||d,i||e,d,e],f.rightContX=f.rightContY=null):b=["M",d,e];return b}});aa.spline=F;X.areaspline=x(X.area);na=M.prototype;F=ea(F,{type:"areaspline",closedStacks:!0,getSegmentPath:na.getSegmentPath,closeSegment:na.closeSegment,drawGraph:na.drawGraph});aa.areaspline=F;X.column=x(W,{borderColor:"#FFFFFF",borderWidth:1,borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,
250
+ shadow:!1},select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}},dataLabels:{align:null,verticalAlign:null,y:null},stickyTracking:!1,threshold:0});F=ea(R,{type:"column",tooltipOutsidePlot:!0,requireSorting:!1,pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color",r:"borderRadius"},trackerGroups:["group","dataLabelsGroup"],init:function(){R.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},
251
+ getColumnMetrics:function(){var a=this,b=a.chart,c=a.options,d=this.xAxis,e=d.reversed,f,g={},h,i=0;c.grouping===!1?i=1:n(b.series,function(b){var c=b.options;if(b.type===a.type&&b.visible&&a.options.group===c.group)c.stacking?(f=b.stackKey,g[f]===y&&(g[f]=i++),h=g[f]):c.grouping!==!1&&(h=i++),b.columnIndex=h});var b=K(Q(d.transA)*(d.ordinalSlope||c.pointRange||d.closestPointRange||1),d.len),d=b*c.groupPadding,j=(b-2*d)/i,k=c.pointWidth,c=r(k)?(j-k)/2:j*c.pointPadding,k=o(k,j-2*c);return a.columnMetrics=
252
+ {width:k,offset:c+(d+((e?i-(a.columnIndex||0):a.columnIndex)||0)*j-b/2)*(e?-1:1)}},translate:function(){var a=this,b=a.chart,c=a.options,d=c.stacking,e=c.borderWidth,f=a.yAxis,g=a.translatedThreshold=f.getThreshold(c.threshold),h=o(c.minPointLength,5),c=a.getColumnMetrics(),i=c.width,j=ja(q(i,1+2*e)),k=c.offset;R.prototype.translate.apply(a);n(a.points,function(c){var l=K(q(-999,c.plotY),f.len+999),n=o(c.yBottom,g),s=c.plotX+k,t=ja(K(l,n)),l=ja(q(l,n)-t),r=f.stacks[(c.y<0?"-":"")+a.stackKey];d&&a.visible&&
253
+ r&&r[c.x]&&r[c.x].setOffset(k,j);Q(l)<h&&h&&(l=h,t=Q(t-g)>h?n-h:g-(f.translate(c.y,0,1,0,1)<=g?h:0));c.barX=s;c.pointWidth=i;c.shapeType="rect";c.shapeArgs=c=b.renderer.Element.prototype.crisp.call(0,e,s,t,j,l);e%2&&(c.y-=1,c.height+=1)})},getSymbol:ta,drawLegendSymbol:M.prototype.drawLegendSymbol,drawGraph:ta,drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d;n(a.points,function(e){var f=e.plotY,g=e.graphic;if(f!==y&&!isNaN(f)&&e.y!==null)d=e.shapeArgs,g?(Ta(g),g.animate(x(d))):e.graphic=
254
+ c[e.shapeType](d).attr(e.pointAttr[e.selected?"select":""]).add(a.group).shadow(b.shadow,null,b.stacking&&!b.borderRadius);else if(g)e.graphic=g.destroy()})},drawTracker:function(){var a=this,b=a.chart.pointer,c=a.options.cursor,d=c&&{cursor:c},e=function(b){var c=b.target,d;for(a.onMouseOver();c&&!d;)d=c.point,c=c.parentNode;if(d!==y)d.onMouseOver(b)};n(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});a._hasTracking?a._hasTracking=!0:n(a.trackerGroups,
255
+ function(c){if(a[c]&&(a[c].addClass("highcharts-tracker").on("mouseover",e).on("mouseout",function(a){b.onTrackerMouseOut(a)}).css(d),fb))a[c].on("touchstart",e)})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>o(this.translatedThreshold,f.plotSizeY),j=o(c.inside,!!this.options.stacking);if(h&&(d=x(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:
256
+ 0,d.height=0);c.align=o(c.align,!g||j?"center":i?"right":"left");c.verticalAlign=o(c.verticalAlign,g||j?"middle":i?"top":"bottom");R.prototype.alignDataLabel.call(this,a,b,c,d,e)},animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(Z)a?(e.scaleY=0.001,a=K(b.pos+b.len,q(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)},
257
+ remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});R.prototype.remove.apply(a,arguments)}});aa.column=F;X.bar=x(X.column);na=ea(F,{type:"bar",inverted:!0});aa.bar=na;X.scatter=x(W,{lineWidth:0,tooltip:{headerFormat:'<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>",followPointer:!0},stickyTracking:!1});na=ea(R,{type:"scatter",sorted:!1,requireSorting:!1,
258
+ noSharedTooltip:!0,trackerGroups:["markerGroup"],drawTracker:F.prototype.drawTracker,setTooltipPoints:ta});aa.scatter=na;X.pie=x(W,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});W={type:"pie",isCartesian:!1,
259
+ pointClass:ea(Na,{init:function(){Na.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;v(a,{visible:a.visible!==!1,name:o(a.name,"Slice")});b=function(){a.slice()};J(a,"select",b);J(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart,e;b.visible=b.options.visible=a=a===y?!b.visible:a;c.options.data[la(b,c.data)]=b.options;e=a?"show":"hide";n(["graphic","dataLabel","connector","shadowGroup"],function(a){if(b[a])b[a][e]()});b.legendItem&&d.legend.colorizeItem(b,
260
+ a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;Ia(c,d.chart);o(b,!0);this.sliced=this.options.sliced=a=r(a)?a:!this.sliced;d.options.data[la(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",
261
+ fill:"color"},getColor:ta,animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b){R.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();o(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-
262
+ 2*c,b=a.center,a=[o(b[0],"50%"),o(b[1],"50%"),a.size||"100%",a.innerSize||0],g=K(e,f),h;return La(a,function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*u(a)/100:a)+(d?c:0)})},translate:function(a){this.generatePoints();var b=0,c=0,d=this.options,e=d.slicedOffset,f=e+d.borderWidth,g,h,i,j=this.startAngleRad=Ka/180*((d.startAngle||0)%360-90),k=this.points,m=2*Ka,l=d.dataLabels.distance,n=d.ignoreHiddenPoint,o,q=k.length,r;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){i=
263
+ I.asin((b-a[1])/(a[2]/2+l));return a[0]+(c?-1:1)*Y(i)*(a[2]/2+l)};for(o=0;o<q;o++)r=k[o],b+=n&&!r.visible?0:r.y;for(o=0;o<q;o++){r=k[o];d=b?r.y/b:0;g=t((j+c*m)*1E3)/1E3;if(!n||r.visible)c+=d;h=t((j+c*m)*1E3)/1E3;r.shapeType="arc";r.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:g,end:h};i=(h+g)/2;i>0.75*m&&(i-=2*Ka);r.slicedTranslation={translateX:t(Y(i)*e),translateY:t(ca(i)*e)};g=Y(i)*a[2]/2;h=ca(i)*a[2]/2;r.tooltipPos=[a[0]+g*0.7,a[1]+h*0.7];r.half=i<m/4?0:1;r.angle=i;f=K(f,l/2);r.labelPos=
264
+ [a[0]+g+Y(i)*l,a[1]+h+ca(i)*l,a[0]+g+Y(i)*f,a[1]+h+ca(i)*f,a[0]+g,a[1]+h,l<0?"center":r.half?"right":"left",i];r.percentage=d*100;r.total=b}this.setTooltipPoints()},drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);n(a.points,function(h){d=h.graphic;g=h.shapeArgs;f=h.shadowGroup;if(e&&!f)f=h.shadowGroup=b.g("shadow").add(a.shadowGroup);c=h.sliced?h.slicedTranslation:{translateX:0,translateY:0};
265
+ f&&f.attr(c);d?d.animate(v(g,c)):h.graphic=d=b.arc(g).setRadialReference(a.center).attr(h.pointAttr[h.selected?"select":""]).attr({"stroke-linejoin":"round"}).attr(c).add(a.group).shadow(e,f);h.visible===!1&&h.setVisible(!1)})},drawDataLabels:function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=o(e.connectorPadding,10),g=o(e.connectorWidth,1),h=d.plotWidth,d=d.plotHeight,i,j,k=o(e.softConnector,!0),m=e.distance,l=a.center,p=l[2]/2,s=l[1],r=m>0,v,w,u,x,y=[[],[]],A,z,E,H,C,D=[0,0,0,0],
266
+ K=function(a,b){return b.y-a.y},M=function(a,b){a.sort(function(a,c){return a.angle!==void 0&&(c.angle-a.angle)*b})};if(e.enabled||a._hasPointLabels){R.prototype.drawDataLabels.apply(a);n(b,function(a){a.dataLabel&&y[a.half].push(a)});for(H=0;!x&&b[H];)x=b[H]&&b[H].dataLabel&&(b[H].dataLabel.getBBox().height||21),H++;for(H=2;H--;){var b=[],L=[],I=y[H],J=I.length,F;M(I,H-0.5);if(m>0){for(C=s-p-m;C<=s+p+m;C+=x)b.push(C);w=b.length;if(J>w){c=[].concat(I);c.sort(K);for(C=J;C--;)c[C].rank=C;for(C=J;C--;)I[C].rank>=
267
+ w&&I.splice(C,1);J=I.length}for(C=0;C<J;C++){c=I[C];u=c.labelPos;c=9999;var O,N;for(N=0;N<w;N++)O=Q(b[N]-u[1]),O<c&&(c=O,F=N);if(F<C&&b[C]!==null)F=C;else for(w<J-C+F&&b[C]!==null&&(F=w-J+C);b[F]===null;)F++;L.push({i:F,y:b[F]});b[F]=null}L.sort(K)}for(C=0;C<J;C++){c=I[C];u=c.labelPos;v=c.dataLabel;E=c.visible===!1?"hidden":"visible";c=u[1];if(m>0){if(w=L.pop(),F=w.i,z=w.y,c>z&&b[F+1]!==null||c<z&&b[F-1]!==null)z=c}else z=c;A=e.justify?l[0]+(H?-1:1)*(p+m):a.getX(F===0||F===b.length-1?c:z,H);v._attr=
268
+ {visibility:E,align:u[6]};v._pos={x:A+e.x+({left:f,right:-f}[u[6]]||0),y:z+e.y-10};v.connX=A;v.connY=z;if(this.options.size===null)w=v.width,A-w<f?D[3]=q(t(w-A+f),D[3]):A+w>h-f&&(D[1]=q(t(A+w-h+f),D[1])),z-x/2<0?D[0]=q(t(-z+x/2),D[0]):z+x/2>d&&(D[2]=q(t(z+x/2-d),D[2]))}}if(pa(D)===0||this.verifyDataLabelOverflow(D))this.placeDataLabels(),r&&g&&n(this.points,function(b){i=b.connector;u=b.labelPos;if((v=b.dataLabel)&&v._pos)E=v._attr.visibility,A=v.connX,z=v.connY,j=k?["M",A+(u[6]==="left"?5:-5),z,
269
+ "C",A,z,2*u[2]-u[4],2*u[3]-u[5],u[2],u[3],"L",u[4],u[5]]:["M",A+(u[6]==="left"?5:-5),z,"L",u[2],u[3],"L",u[4],u[5]],i?(i.animate({d:j}),i.attr("visibility",E)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:E}).add(a.group);else if(i)b.connector=i.destroy()})}},verifyDataLabelOverflow:function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=q(b[2]-q(a[1],a[3]),c):(e=q(b[2]-a[1]-a[3],c),b[0]+=(a[3]-
270
+ a[1])/2);d[1]!==null?e=q(K(e,b[2]-q(a[0],a[2])),c):(e=q(K(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e<b[2]?(b[2]=e,this.translate(b),n(this.points,function(a){if(a.dataLabel)a.dataLabel._pos=null}),this.drawDataLabels()):f=!0;return f},placeDataLabels:function(){n(this.points,function(a){var a=a.dataLabel,b;if(a)(b=a._pos)?(a.attr(a._attr),a[a.moved?"animate":"attr"](b),a.moved=!0):a&&a.attr({y:-999})})},alignDataLabel:ta,drawTracker:F.prototype.drawTracker,drawLegendSymbol:M.prototype.drawLegendSymbol,
271
+ getSymbol:ta};W=ea(R,W);aa.pie=W;v(Highcharts,{Axis:ab,Chart:tb,Color:ma,Legend:sb,Pointer:rb,Point:Na,Tick:Ja,Tooltip:qb,Renderer:Sa,Series:R,SVGElement:ra,SVGRenderer:Ca,arrayMin:Ga,arrayMax:pa,charts:Ba,dateFormat:Ua,format:wa,pathAnim:vb,getOptions:function(){return N},hasBidiBug:Sb,isTouchDevice:Mb,numberFormat:ua,seriesTypes:aa,setOptions:function(a){N=x(N,a);Jb();return N},addEvent:J,removeEvent:ba,createElement:U,discardElement:Ra,css:L,each:n,extend:v,map:La,merge:x,pick:o,splat:ha,extendClass:ea,
272
+ pInt:u,wrap:zb,svg:Z,canvas:$,vml:!Z&&!$,product:"Highcharts",version:"3.0.2"})})();
js/highcharts/highcharts.src.js CHANGED
@@ -1,16320 +1,16320 @@
1
- // ==ClosureCompiler==
2
- // @compilation_level SIMPLE_OPTIMIZATIONS
3
-
4
- /**
5
- * @license Highcharts JS v3.0.2 (2013-06-05)
6
- *
7
- * (c) 2009-2013 Torstein Hønsi
8
- *
9
- * License: www.highcharts.com/license
10
- */
11
-
12
- // JSLint options:
13
- /*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */
14
-
15
- (function () {
16
- // encapsulated variables
17
- var UNDEFINED,
18
- doc = document,
19
- win = window,
20
- math = Math,
21
- mathRound = math.round,
22
- mathFloor = math.floor,
23
- mathCeil = math.ceil,
24
- mathMax = math.max,
25
- mathMin = math.min,
26
- mathAbs = math.abs,
27
- mathCos = math.cos,
28
- mathSin = math.sin,
29
- mathPI = math.PI,
30
- deg2rad = mathPI * 2 / 360,
31
-
32
-
33
- // some variables
34
- userAgent = navigator.userAgent,
35
- isOpera = win.opera,
36
- isIE = /msie/i.test(userAgent) && !isOpera,
37
- docMode8 = doc.documentMode === 8,
38
- isWebKit = /AppleWebKit/.test(userAgent),
39
- isFirefox = /Firefox/.test(userAgent),
40
- isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent),
41
- SVG_NS = 'http://www.w3.org/2000/svg',
42
- hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect,
43
- hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38
44
- useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext,
45
- Renderer,
46
- hasTouch = doc.documentElement.ontouchstart !== UNDEFINED,
47
- symbolSizes = {},
48
- idCounter = 0,
49
- garbageBin,
50
- defaultOptions,
51
- dateFormat, // function
52
- globalAnimation,
53
- pathAnim,
54
- timeUnits,
55
- noop = function () {},
56
- charts = [],
57
- PRODUCT = 'Highcharts',
58
- VERSION = '3.0.2',
59
-
60
- // some constants for frequently used strings
61
- DIV = 'div',
62
- ABSOLUTE = 'absolute',
63
- RELATIVE = 'relative',
64
- HIDDEN = 'hidden',
65
- PREFIX = 'highcharts-',
66
- VISIBLE = 'visible',
67
- PX = 'px',
68
- NONE = 'none',
69
- M = 'M',
70
- L = 'L',
71
- /*
72
- * Empirical lowest possible opacities for TRACKER_FILL
73
- * IE6: 0.002
74
- * IE7: 0.002
75
- * IE8: 0.002
76
- * IE9: 0.00000000001 (unlimited)
77
- * IE10: 0.0001 (exporting only)
78
- * FF: 0.00000000001 (unlimited)
79
- * Chrome: 0.000001
80
- * Safari: 0.000001
81
- * Opera: 0.00000000001 (unlimited)
82
- */
83
- TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')', // invisible but clickable
84
- //TRACKER_FILL = 'rgba(192,192,192,0.5)',
85
- NORMAL_STATE = '',
86
- HOVER_STATE = 'hover',
87
- SELECT_STATE = 'select',
88
- MILLISECOND = 'millisecond',
89
- SECOND = 'second',
90
- MINUTE = 'minute',
91
- HOUR = 'hour',
92
- DAY = 'day',
93
- WEEK = 'week',
94
- MONTH = 'month',
95
- YEAR = 'year',
96
-
97
- // constants for attributes
98
- LINEAR_GRADIENT = 'linearGradient',
99
- STOPS = 'stops',
100
- STROKE_WIDTH = 'stroke-width',
101
-
102
- // time methods, changed based on whether or not UTC is used
103
- makeTime,
104
- getMinutes,
105
- getHours,
106
- getDay,
107
- getDate,
108
- getMonth,
109
- getFullYear,
110
- setMinutes,
111
- setHours,
112
- setDate,
113
- setMonth,
114
- setFullYear,
115
-
116
-
117
- // lookup over the types and the associated classes
118
- seriesTypes = {};
119
-
120
- // The Highcharts namespace
121
- win.Highcharts = win.Highcharts ? error(16, true) : {};
122
-
123
- /**
124
- * Extend an object with the members of another
125
- * @param {Object} a The object to be extended
126
- * @param {Object} b The object to add to the first one
127
- */
128
- function extend(a, b) {
129
- var n;
130
- if (!a) {
131
- a = {};
132
- }
133
- for (n in b) {
134
- a[n] = b[n];
135
- }
136
- return a;
137
- }
138
-
139
- /**
140
- * Deep merge two or more objects and return a third object.
141
- * Previously this function redirected to jQuery.extend(true), but this had two limitations.
142
- * First, it deep merged arrays, which lead to workarounds in Highcharts. Second,
143
- * it copied properties from extended prototypes.
144
- */
145
- function merge() {
146
- var i,
147
- len = arguments.length,
148
- ret = {},
149
- doCopy = function (copy, original) {
150
- var value, key;
151
-
152
- for (key in original) {
153
- if (original.hasOwnProperty(key)) {
154
- value = original[key];
155
-
156
- // An object is replacing a primitive
157
- if (typeof copy !== 'object') {
158
- copy = {};
159
- }
160
-
161
- // Copy the contents of objects, but not arrays or DOM nodes
162
- if (value && typeof value === 'object' && Object.prototype.toString.call(value) !== '[object Array]'
163
- && typeof value.nodeType !== 'number') {
164
- copy[key] = doCopy(copy[key] || {}, value);
165
-
166
- // Primitives and arrays are copied over directly
167
- } else {
168
- copy[key] = original[key];
169
- }
170
- }
171
- }
172
- return copy;
173
- };
174
-
175
- // For each argument, extend the return
176
- for (i = 0; i < len; i++) {
177
- ret = doCopy(ret, arguments[i]);
178
- }
179
-
180
- return ret;
181
- }
182
-
183
- /**
184
- * Take an array and turn into a hash with even number arguments as keys and odd numbers as
185
- * values. Allows creating constants for commonly used style properties, attributes etc.
186
- * Avoid it in performance critical situations like looping
187
- */
188
- function hash() {
189
- var i = 0,
190
- args = arguments,
191
- length = args.length,
192
- obj = {};
193
- for (; i < length; i++) {
194
- obj[args[i++]] = args[i];
195
- }
196
- return obj;
197
- }
198
-
199
- /**
200
- * Shortcut for parseInt
201
- * @param {Object} s
202
- * @param {Number} mag Magnitude
203
- */
204
- function pInt(s, mag) {
205
- return parseInt(s, mag || 10);
206
- }
207
-
208
- /**
209
- * Check for string
210
- * @param {Object} s
211
- */
212
- function isString(s) {
213
- return typeof s === 'string';
214
- }
215
-
216
- /**
217
- * Check for object
218
- * @param {Object} obj
219
- */
220
- function isObject(obj) {
221
- return typeof obj === 'object';
222
- }
223
-
224
- /**
225
- * Check for array
226
- * @param {Object} obj
227
- */
228
- function isArray(obj) {
229
- return Object.prototype.toString.call(obj) === '[object Array]';
230
- }
231
-
232
- /**
233
- * Check for number
234
- * @param {Object} n
235
- */
236
- function isNumber(n) {
237
- return typeof n === 'number';
238
- }
239
-
240
- function log2lin(num) {
241
- return math.log(num) / math.LN10;
242
- }
243
- function lin2log(num) {
244
- return math.pow(10, num);
245
- }
246
-
247
- /**
248
- * Remove last occurence of an item from an array
249
- * @param {Array} arr
250
- * @param {Mixed} item
251
- */
252
- function erase(arr, item) {
253
- var i = arr.length;
254
- while (i--) {
255
- if (arr[i] === item) {
256
- arr.splice(i, 1);
257
- break;
258
- }
259
- }
260
- //return arr;
261
- }
262
-
263
- /**
264
- * Returns true if the object is not null or undefined. Like MooTools' $.defined.
265
- * @param {Object} obj
266
- */
267
- function defined(obj) {
268
- return obj !== UNDEFINED && obj !== null;
269
- }
270
-
271
- /**
272
- * Set or get an attribute or an object of attributes. Can't use jQuery attr because
273
- * it attempts to set expando properties on the SVG element, which is not allowed.
274
- *
275
- * @param {Object} elem The DOM element to receive the attribute(s)
276
- * @param {String|Object} prop The property or an abject of key-value pairs
277
- * @param {String} value The value if a single property is set
278
- */
279
- function attr(elem, prop, value) {
280
- var key,
281
- setAttribute = 'setAttribute',
282
- ret;
283
-
284
- // if the prop is a string
285
- if (isString(prop)) {
286
- // set the value
287
- if (defined(value)) {
288
-
289
- elem[setAttribute](prop, value);
290
-
291
- // get the value
292
- } else if (elem && elem.getAttribute) { // elem not defined when printing pie demo...
293
- ret = elem.getAttribute(prop);
294
- }
295
-
296
- // else if prop is defined, it is a hash of key/value pairs
297
- } else if (defined(prop) && isObject(prop)) {
298
- for (key in prop) {
299
- elem[setAttribute](key, prop[key]);
300
- }
301
- }
302
- return ret;
303
- }
304
- /**
305
- * Check if an element is an array, and if not, make it into an array. Like
306
- * MooTools' $.splat.
307
- */
308
- function splat(obj) {
309
- return isArray(obj) ? obj : [obj];
310
- }
311
-
312
-
313
- /**
314
- * Return the first value that is defined. Like MooTools' $.pick.
315
- */
316
- function pick() {
317
- var args = arguments,
318
- i,
319
- arg,
320
- length = args.length;
321
- for (i = 0; i < length; i++) {
322
- arg = args[i];
323
- if (typeof arg !== 'undefined' && arg !== null) {
324
- return arg;
325
- }
326
- }
327
- }
328
-
329
- /**
330
- * Set CSS on a given element
331
- * @param {Object} el
332
- * @param {Object} styles Style object with camel case property names
333
- */
334
- function css(el, styles) {
335
- if (isIE) {
336
- if (styles && styles.opacity !== UNDEFINED) {
337
- styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
338
- }
339
- }
340
- extend(el.style, styles);
341
- }
342
-
343
- /**
344
- * Utility function to create element with attributes and styles
345
- * @param {Object} tag
346
- * @param {Object} attribs
347
- * @param {Object} styles
348
- * @param {Object} parent
349
- * @param {Object} nopad
350
- */
351
- function createElement(tag, attribs, styles, parent, nopad) {
352
- var el = doc.createElement(tag);
353
- if (attribs) {
354
- extend(el, attribs);
355
- }
356
- if (nopad) {
357
- css(el, {padding: 0, border: NONE, margin: 0});
358
- }
359
- if (styles) {
360
- css(el, styles);
361
- }
362
- if (parent) {
363
- parent.appendChild(el);
364
- }
365
- return el;
366
- }
367
-
368
- /**
369
- * Extend a prototyped class by new members
370
- * @param {Object} parent
371
- * @param {Object} members
372
- */
373
- function extendClass(parent, members) {
374
- var object = function () {};
375
- object.prototype = new parent();
376
- extend(object.prototype, members);
377
- return object;
378
- }
379
-
380
- /**
381
- * Format a number and return a string based on input settings
382
- * @param {Number} number The input number to format
383
- * @param {Number} decimals The amount of decimals
384
- * @param {String} decPoint The decimal point, defaults to the one given in the lang options
385
- * @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options
386
- */
387
- function numberFormat(number, decimals, decPoint, thousandsSep) {
388
- var lang = defaultOptions.lang,
389
- // http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/
390
- n = number,
391
- c = decimals === -1 ?
392
- ((n || 0).toString().split('.')[1] || '').length : // preserve decimals
393
- (isNaN(decimals = mathAbs(decimals)) ? 2 : decimals),
394
- d = decPoint === undefined ? lang.decimalPoint : decPoint,
395
- t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep,
396
- s = n < 0 ? "-" : "",
397
- i = String(pInt(n = mathAbs(+n || 0).toFixed(c))),
398
- j = i.length > 3 ? i.length % 3 : 0;
399
-
400
- return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
401
- (c ? d + mathAbs(n - i).toFixed(c).slice(2) : "");
402
- }
403
-
404
- /**
405
- * Pad a string to a given length by adding 0 to the beginning
406
- * @param {Number} number
407
- * @param {Number} length
408
- */
409
- function pad(number, length) {
410
- // Create an array of the remaining length +1 and join it with 0's
411
- return new Array((length || 2) + 1 - String(number).length).join(0) + number;
412
- }
413
-
414
- /**
415
- * Wrap a method with extended functionality, preserving the original function
416
- * @param {Object} obj The context object that the method belongs to
417
- * @param {String} method The name of the method to extend
418
- * @param {Function} func A wrapper function callback. This function is called with the same arguments
419
- * as the original function, except that the original function is unshifted and passed as the first
420
- * argument.
421
- */
422
- function wrap(obj, method, func) {
423
- var proceed = obj[method];
424
- obj[method] = function () {
425
- var args = Array.prototype.slice.call(arguments);
426
- args.unshift(proceed);
427
- return func.apply(this, args);
428
- };
429
- }
430
-
431
- /**
432
- * Based on http://www.php.net/manual/en/function.strftime.php
433
- * @param {String} format
434
- * @param {Number} timestamp
435
- * @param {Boolean} capitalize
436
- */
437
- dateFormat = function (format, timestamp, capitalize) {
438
- if (!defined(timestamp) || isNaN(timestamp)) {
439
- return 'Invalid date';
440
- }
441
- format = pick(format, '%Y-%m-%d %H:%M:%S');
442
-
443
- var date = new Date(timestamp),
444
- key, // used in for constuct below
445
- // get the basic time values
446
- hours = date[getHours](),
447
- day = date[getDay](),
448
- dayOfMonth = date[getDate](),
449
- month = date[getMonth](),
450
- fullYear = date[getFullYear](),
451
- lang = defaultOptions.lang,
452
- langWeekdays = lang.weekdays,
453
-
454
- // List all format keys. Custom formats can be added from the outside.
455
- replacements = extend({
456
-
457
- // Day
458
- 'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon'
459
- 'A': langWeekdays[day], // Long weekday, like 'Monday'
460
- 'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31
461
- 'e': dayOfMonth, // Day of the month, 1 through 31
462
-
463
- // Week (none implemented)
464
- //'W': weekNumber(),
465
-
466
- // Month
467
- 'b': lang.shortMonths[month], // Short month, like 'Jan'
468
- 'B': lang.months[month], // Long month, like 'January'
469
- 'm': pad(month + 1), // Two digit month number, 01 through 12
470
-
471
- // Year
472
- 'y': fullYear.toString().substr(2, 2), // Two digits year, like 09 for 2009
473
- 'Y': fullYear, // Four digits year, like 2009
474
-
475
- // Time
476
- 'H': pad(hours), // Two digits hours in 24h format, 00 through 23
477
- 'I': pad((hours % 12) || 12), // Two digits hours in 12h format, 00 through 11
478
- 'l': (hours % 12) || 12, // Hours in 12h format, 1 through 12
479
- 'M': pad(date[getMinutes]()), // Two digits minutes, 00 through 59
480
- 'p': hours < 12 ? 'AM' : 'PM', // Upper case AM or PM
481
- 'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM
482
- 'S': pad(date.getSeconds()), // Two digits seconds, 00 through 59
483
- 'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby)
484
- }, Highcharts.dateFormats);
485
-
486
-
487
- // do the replaces
488
- for (key in replacements) {
489
- while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster
490
- format = format.replace('%' + key, typeof replacements[key] === 'function' ? replacements[key](timestamp) : replacements[key]);
491
- }
492
- }
493
-
494
- // Optionally capitalize the string and return
495
- return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format;
496
- };
497
-
498
- /**
499
- * Format a single variable. Similar to sprintf, without the % prefix.
500
- */
501
- function formatSingle(format, val) {
502
- var floatRegex = /f$/,
503
- decRegex = /\.([0-9])/,
504
- lang = defaultOptions.lang,
505
- decimals;
506
-
507
- if (floatRegex.test(format)) { // float
508
- decimals = format.match(decRegex);
509
- decimals = decimals ? decimals[1] : -1;
510
- val = numberFormat(
511
- val,
512
- decimals,
513
- lang.decimalPoint,
514
- format.indexOf(',') > -1 ? lang.thousandsSep : ''
515
- );
516
- } else {
517
- val = dateFormat(format, val);
518
- }
519
- return val;
520
- }
521
-
522
- /**
523
- * Format a string according to a subset of the rules of Python's String.format method.
524
- */
525
- function format(str, ctx) {
526
- var splitter = '{',
527
- isInside = false,
528
- segment,
529
- valueAndFormat,
530
- path,
531
- i,
532
- len,
533
- ret = [],
534
- val,
535
- index;
536
-
537
- while ((index = str.indexOf(splitter)) !== -1) {
538
-
539
- segment = str.slice(0, index);
540
- if (isInside) { // we're on the closing bracket looking back
541
-
542
- valueAndFormat = segment.split(':');
543
- path = valueAndFormat.shift().split('.'); // get first and leave format
544
- len = path.length;
545
- val = ctx;
546
-
547
- // Assign deeper paths
548
- for (i = 0; i < len; i++) {
549
- val = val[path[i]];
550
- }
551
-
552
- // Format the replacement
553
- if (valueAndFormat.length) {
554
- val = formatSingle(valueAndFormat.join(':'), val);
555
- }
556
-
557
- // Push the result and advance the cursor
558
- ret.push(val);
559
-
560
- } else {
561
- ret.push(segment);
562
-
563
- }
564
- str = str.slice(index + 1); // the rest
565
- isInside = !isInside; // toggle
566
- splitter = isInside ? '}' : '{'; // now look for next matching bracket
567
- }
568
- ret.push(str);
569
- return ret.join('');
570
- }
571
-
572
- /**
573
- * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5
574
- * @param {Number} interval
575
- * @param {Array} multiples
576
- * @param {Number} magnitude
577
- * @param {Object} options
578
- */
579
- function normalizeTickInterval(interval, multiples, magnitude, options) {
580
- var normalized, i;
581
-
582
- // round to a tenfold of 1, 2, 2.5 or 5
583
- magnitude = pick(magnitude, 1);
584
- normalized = interval / magnitude;
585
-
586
- // multiples for a linear scale
587
- if (!multiples) {
588
- multiples = [1, 2, 2.5, 5, 10];
589
-
590
- // the allowDecimals option
591
- if (options && options.allowDecimals === false) {
592
- if (magnitude === 1) {
593
- multiples = [1, 2, 5, 10];
594
- } else if (magnitude <= 0.1) {
595
- multiples = [1 / magnitude];
596
- }
597
- }
598
- }
599
-
600
- // normalize the interval to the nearest multiple
601
- for (i = 0; i < multiples.length; i++) {
602
- interval = multiples[i];
603
- if (normalized <= (multiples[i] + (multiples[i + 1] || multiples[i])) / 2) {
604
- break;
605
- }
606
- }
607
-
608
- // multiply back to the correct magnitude
609
- interval *= magnitude;
610
-
611
- return interval;
612
- }
613
-
614
- /**
615
- * Get a normalized tick interval for dates. Returns a configuration object with
616
- * unit range (interval), count and name. Used to prepare data for getTimeTicks.
617
- * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
618
- * of segments in stock charts, the normalizing logic was extracted in order to
619
- * prevent it for running over again for each segment having the same interval.
620
- * #662, #697.
621
- */
622
- function normalizeTimeTickInterval(tickInterval, unitsOption) {
623
- var units = unitsOption || [[
624
- MILLISECOND, // unit name
625
- [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
626
- ], [
627
- SECOND,
628
- [1, 2, 5, 10, 15, 30]
629
- ], [
630
- MINUTE,
631
- [1, 2, 5, 10, 15, 30]
632
- ], [
633
- HOUR,
634
- [1, 2, 3, 4, 6, 8, 12]
635
- ], [
636
- DAY,
637
- [1, 2]
638
- ], [
639
- WEEK,
640
- [1, 2]
641
- ], [
642
- MONTH,
643
- [1, 2, 3, 4, 6]
644
- ], [
645
- YEAR,
646
- null
647
- ]],
648
- unit = units[units.length - 1], // default unit is years
649
- interval = timeUnits[unit[0]],
650
- multiples = unit[1],
651
- count,
652
- i;
653
-
654
- // loop through the units to find the one that best fits the tickInterval
655
- for (i = 0; i < units.length; i++) {
656
- unit = units[i];
657
- interval = timeUnits[unit[0]];
658
- multiples = unit[1];
659
-
660
-
661
- if (units[i + 1]) {
662
- // lessThan is in the middle between the highest multiple and the next unit.
663
- var lessThan = (interval * multiples[multiples.length - 1] +
664
- timeUnits[units[i + 1][0]]) / 2;
665
-
666
- // break and keep the current unit
667
- if (tickInterval <= lessThan) {
668
- break;
669
- }
670
- }
671
- }
672
-
673
- // prevent 2.5 years intervals, though 25, 250 etc. are allowed
674
- if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
675
- multiples = [1, 2, 5];
676
- }
677
-
678
- // prevent 2.5 years intervals, though 25, 250 etc. are allowed
679
- if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
680
- multiples = [1, 2, 5];
681
- }
682
-
683
- // get the count
684
- count = normalizeTickInterval(tickInterval / interval, multiples);
685
-
686
- return {
687
- unitRange: interval,
688
- count: count,
689
- unitName: unit[0]
690
- };
691
- }
692
-
693
- /**
694
- * Set the tick positions to a time unit that makes sense, for example
695
- * on the first of each month or on every Monday. Return an array
696
- * with the time positions. Used in datetime axes as well as for grouping
697
- * data on a datetime axis.
698
- *
699
- * @param {Object} normalizedInterval The interval in axis values (ms) and the count
700
- * @param {Number} min The minimum in axis values
701
- * @param {Number} max The maximum in axis values
702
- * @param {Number} startOfWeek
703
- */
704
- function getTimeTicks(normalizedInterval, min, max, startOfWeek) {
705
- var tickPositions = [],
706
- i,
707
- higherRanks = {},
708
- useUTC = defaultOptions.global.useUTC,
709
- minYear, // used in months and years as a basis for Date.UTC()
710
- minDate = new Date(min),
711
- interval = normalizedInterval.unitRange,
712
- count = normalizedInterval.count;
713
-
714
- if (defined(min)) { // #1300
715
- if (interval >= timeUnits[SECOND]) { // second
716
- minDate.setMilliseconds(0);
717
- minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 :
718
- count * mathFloor(minDate.getSeconds() / count));
719
- }
720
-
721
- if (interval >= timeUnits[MINUTE]) { // minute
722
- minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 :
723
- count * mathFloor(minDate[getMinutes]() / count));
724
- }
725
-
726
- if (interval >= timeUnits[HOUR]) { // hour
727
- minDate[setHours](interval >= timeUnits[DAY] ? 0 :
728
- count * mathFloor(minDate[getHours]() / count));
729
- }
730
-
731
- if (interval >= timeUnits[DAY]) { // day
732
- minDate[setDate](interval >= timeUnits[MONTH] ? 1 :
733
- count * mathFloor(minDate[getDate]() / count));
734
- }
735
-
736
- if (interval >= timeUnits[MONTH]) { // month
737
- minDate[setMonth](interval >= timeUnits[YEAR] ? 0 :
738
- count * mathFloor(minDate[getMonth]() / count));
739
- minYear = minDate[getFullYear]();
740
- }
741
-
742
- if (interval >= timeUnits[YEAR]) { // year
743
- minYear -= minYear % count;
744
- minDate[setFullYear](minYear);
745
- }
746
-
747
- // week is a special case that runs outside the hierarchy
748
- if (interval === timeUnits[WEEK]) {
749
- // get start of current week, independent of count
750
- minDate[setDate](minDate[getDate]() - minDate[getDay]() +
751
- pick(startOfWeek, 1));
752
- }
753
-
754
-
755
- // get tick positions
756
- i = 1;
757
- minYear = minDate[getFullYear]();
758
- var time = minDate.getTime(),
759
- minMonth = minDate[getMonth](),
760
- minDateDate = minDate[getDate](),
761
- timezoneOffset = useUTC ?
762
- 0 :
763
- (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950
764
-
765
- // iterate and add tick positions at appropriate values
766
- while (time < max) {
767
- tickPositions.push(time);
768
-
769
- // if the interval is years, use Date.UTC to increase years
770
- if (interval === timeUnits[YEAR]) {
771
- time = makeTime(minYear + i * count, 0);
772
-
773
- // if the interval is months, use Date.UTC to increase months
774
- } else if (interval === timeUnits[MONTH]) {
775
- time = makeTime(minYear, minMonth + i * count);
776
-
777
- // if we're using global time, the interval is not fixed as it jumps
778
- // one hour at the DST crossover
779
- } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) {
780
- time = makeTime(minYear, minMonth, minDateDate +
781
- i * count * (interval === timeUnits[DAY] ? 1 : 7));
782
-
783
- // else, the interval is fixed and we use simple addition
784
- } else {
785
- time += interval * count;
786
- }
787
-
788
- i++;
789
- }
790
-
791
- // push the last time
792
- tickPositions.push(time);
793
-
794
-
795
- // mark new days if the time is dividible by day (#1649, #1760)
796
- each(grep(tickPositions, function (time) {
797
- return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset;
798
- }), function (time) {
799
- higherRanks[time] = DAY;
800
- });
801
- }
802
-
803
-
804
- // record information on the chosen unit - for dynamic label formatter
805
- tickPositions.info = extend(normalizedInterval, {
806
- higherRanks: higherRanks,
807
- totalRange: interval * count
808
- });
809
-
810
- return tickPositions;
811
- }
812
-
813
- /**
814
- * Helper class that contains variuos counters that are local to the chart.
815
- */
816
- function ChartCounters() {
817
- this.color = 0;
818
- this.symbol = 0;
819
- }
820
-
821
- ChartCounters.prototype = {
822
- /**
823
- * Wraps the color counter if it reaches the specified length.
824
- */
825
- wrapColor: function (length) {
826
- if (this.color >= length) {
827
- this.color = 0;
828
- }
829
- },
830
-
831
- /**
832
- * Wraps the symbol counter if it reaches the specified length.
833
- */
834
- wrapSymbol: function (length) {
835
- if (this.symbol >= length) {
836
- this.symbol = 0;
837
- }
838
- }
839
- };
840
-
841
-
842
- /**
843
- * Utility method that sorts an object array and keeping the order of equal items.
844
- * ECMA script standard does not specify the behaviour when items are equal.
845
- */
846
- function stableSort(arr, sortFunction) {
847
- var length = arr.length,
848
- sortValue,
849
- i;
850
-
851
- // Add index to each item
852
- for (i = 0; i < length; i++) {
853
- arr[i].ss_i = i; // stable sort index
854
- }
855
-
856
- arr.sort(function (a, b) {
857
- sortValue = sortFunction(a, b);
858
- return sortValue === 0 ? a.ss_i - b.ss_i : sortValue;
859
- });
860
-
861
- // Remove index from items
862
- for (i = 0; i < length; i++) {
863
- delete arr[i].ss_i; // stable sort index
864
- }
865
- }
866
-
867
- /**
868
- * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
869
- * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
870
- * method is slightly slower, but safe.
871
- */
872
- function arrayMin(data) {
873
- var i = data.length,
874
- min = data[0];
875
-
876
- while (i--) {
877
- if (data[i] < min) {
878
- min = data[i];
879
- }
880
- }
881
- return min;
882
- }
883
-
884
- /**
885
- * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
886
- * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
887
- * method is slightly slower, but safe.
888
- */
889
- function arrayMax(data) {
890
- var i = data.length,
891
- max = data[0];
892
-
893
- while (i--) {
894
- if (data[i] > max) {
895
- max = data[i];
896
- }
897
- }
898
- return max;
899
- }
900
-
901
- /**
902
- * Utility method that destroys any SVGElement or VMLElement that are properties on the given object.
903
- * It loops all properties and invokes destroy if there is a destroy method. The property is
904
- * then delete'ed.
905
- * @param {Object} The object to destroy properties on
906
- * @param {Object} Exception, do not destroy this property, only delete it.
907
- */
908
- function destroyObjectProperties(obj, except) {
909
- var n;
910
- for (n in obj) {
911
- // If the object is non-null and destroy is defined
912
- if (obj[n] && obj[n] !== except && obj[n].destroy) {
913
- // Invoke the destroy
914
- obj[n].destroy();
915
- }
916
-
917
- // Delete the property from the object.
918
- delete obj[n];
919
- }
920
- }
921
-
922
-
923
- /**
924
- * Discard an element by moving it to the bin and delete
925
- * @param {Object} The HTML node to discard
926
- */
927
- function discardElement(element) {
928
- // create a garbage bin element, not part of the DOM
929
- if (!garbageBin) {
930
- garbageBin = createElement(DIV);
931
- }
932
-
933
- // move the node and empty bin
934
- if (element) {
935
- garbageBin.appendChild(element);
936
- }
937
- garbageBin.innerHTML = '';
938
- }
939
-
940
- /**
941
- * Provide error messages for debugging, with links to online explanation
942
- */
943
- function error(code, stop) {
944
- var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code;
945
- if (stop) {
946
- throw msg;
947
- } else if (win.console) {
948
- console.log(msg);
949
- }
950
- }
951
-
952
- /**
953
- * Fix JS round off float errors
954
- * @param {Number} num
955
- */
956
- function correctFloat(num) {
957
- return parseFloat(
958
- num.toPrecision(14)
959
- );
960
- }
961
-
962
- /**
963
- * Set the global animation to either a given value, or fall back to the
964
- * given chart's animation option
965
- * @param {Object} animation
966
- * @param {Object} chart
967
- */
968
- function setAnimation(animation, chart) {
969
- globalAnimation = pick(animation, chart.animation);
970
- }
971
-
972
- /**
973
- * The time unit lookup
974
- */
975
- /*jslint white: true*/
976
- timeUnits = hash(
977
- MILLISECOND, 1,
978
- SECOND, 1000,
979
- MINUTE, 60000,
980
- HOUR, 3600000,
981
- DAY, 24 * 3600000,
982
- WEEK, 7 * 24 * 3600000,
983
- MONTH, 31 * 24 * 3600000,
984
- YEAR, 31556952000
985
- );
986
- /*jslint white: false*/
987
- /**
988
- * Path interpolation algorithm used across adapters
989
- */
990
- pathAnim = {
991
- /**
992
- * Prepare start and end values so that the path can be animated one to one
993
- */
994
- init: function (elem, fromD, toD) {
995
- fromD = fromD || '';
996
- var shift = elem.shift,
997
- bezier = fromD.indexOf('C') > -1,
998
- numParams = bezier ? 7 : 3,
999
- endLength,
1000
- slice,
1001
- i,
1002
- start = fromD.split(' '),
1003
- end = [].concat(toD), // copy
1004
- startBaseLine,
1005
- endBaseLine,
1006
- sixify = function (arr) { // in splines make move points have six parameters like bezier curves
1007
- i = arr.length;
1008
- while (i--) {
1009
- if (arr[i] === M) {
1010
- arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]);
1011
- }
1012
- }
1013
- };
1014
-
1015
- if (bezier) {
1016
- sixify(start);
1017
- sixify(end);
1018
- }
1019
-
1020
- // pull out the base lines before padding
1021
- if (elem.isArea) {
1022
- startBaseLine = start.splice(start.length - 6, 6);
1023
- endBaseLine = end.splice(end.length - 6, 6);
1024
- }
1025
-
1026
- // if shifting points, prepend a dummy point to the end path
1027
- if (shift <= end.length / numParams) {
1028
- while (shift--) {
1029
- end = [].concat(end).splice(0, numParams).concat(end);
1030
- }
1031
- }
1032
- elem.shift = 0; // reset for following animations
1033
-
1034
- // copy and append last point until the length matches the end length
1035
- if (start.length) {
1036
- endLength = end.length;
1037
- while (start.length < endLength) {
1038
-
1039
- //bezier && sixify(start);
1040
- slice = [].concat(start).splice(start.length - numParams, numParams);
1041
- if (bezier) { // disable first control point
1042
- slice[numParams - 6] = slice[numParams - 2];
1043
- slice[numParams - 5] = slice[numParams - 1];
1044
- }
1045
- start = start.concat(slice);
1046
- }
1047
- }
1048
-
1049
- if (startBaseLine) { // append the base lines for areas
1050
- start = start.concat(startBaseLine);
1051
- end = end.concat(endBaseLine);
1052
- }
1053
- return [start, end];
1054
- },
1055
-
1056
- /**
1057
- * Interpolate each value of the path and return the array
1058
- */
1059
- step: function (start, end, pos, complete) {
1060
- var ret = [],
1061
- i = start.length,
1062
- startVal;
1063
-
1064
- if (pos === 1) { // land on the final path without adjustment points appended in the ends
1065
- ret = complete;
1066
-
1067
- } else if (i === end.length && pos < 1) {
1068
- while (i--) {
1069
- startVal = parseFloat(start[i]);
1070
- ret[i] =
1071
- isNaN(startVal) ? // a letter instruction like M or L
1072
- start[i] :
1073
- pos * (parseFloat(end[i] - startVal)) + startVal;
1074
-
1075
- }
1076
- } else { // if animation is finished or length not matching, land on right value
1077
- ret = end;
1078
- }
1079
- return ret;
1080
- }
1081
- };
1082
-
1083
- (function ($) {
1084
- /**
1085
- * The default HighchartsAdapter for jQuery
1086
- */
1087
- win.HighchartsAdapter = win.HighchartsAdapter || ($ && {
1088
-
1089
- /**
1090
- * Initialize the adapter by applying some extensions to jQuery
1091
- */
1092
- init: function (pathAnim) {
1093
-
1094
- // extend the animate function to allow SVG animations
1095
- var Fx = $.fx,
1096
- Step = Fx.step,
1097
- dSetter,
1098
- Tween = $.Tween,
1099
- propHooks = Tween && Tween.propHooks,
1100
- opacityHook = $.cssHooks.opacity;
1101
-
1102
- /*jslint unparam: true*//* allow unused param x in this function */
1103
- $.extend($.easing, {
1104
- easeOutQuad: function (x, t, b, c, d) {
1105
- return -c * (t /= d) * (t - 2) + b;
1106
- }
1107
- });
1108
- /*jslint unparam: false*/
1109
-
1110
- // extend some methods to check for elem.attr, which means it is a Highcharts SVG object
1111
- $.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) {
1112
- var obj = Step,
1113
- base,
1114
- elem;
1115
-
1116
- // Handle different parent objects
1117
- if (fn === 'cur') {
1118
- obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype
1119
-
1120
- } else if (fn === '_default' && Tween) { // jQuery 1.8 model
1121
- obj = propHooks[fn];
1122
- fn = 'set';
1123
- }
1124
-
1125
- // Overwrite the method
1126
- base = obj[fn];
1127
- if (base) { // step.width and step.height don't exist in jQuery < 1.7
1128
-
1129
- // create the extended function replacement
1130
- obj[fn] = function (fx) {
1131
-
1132
- // Fx.prototype.cur does not use fx argument
1133
- fx = i ? fx : this;
1134
-
1135
- // shortcut
1136
- elem = fx.elem;
1137
-
1138
- // Fx.prototype.cur returns the current value. The other ones are setters
1139
- // and returning a value has no effect.
1140
- return elem.attr ? // is SVG element wrapper
1141
- elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method
1142
- base.apply(this, arguments); // use jQuery's built-in method
1143
- };
1144
- }
1145
- });
1146
-
1147
- // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+
1148
- wrap(opacityHook, 'get', function (proceed, elem, computed) {
1149
- return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed);
1150
- });
1151
-
1152
-
1153
- // Define the setter function for d (path definitions)
1154
- dSetter = function (fx) {
1155
- var elem = fx.elem,
1156
- ends;
1157
-
1158
- // Normally start and end should be set in state == 0, but sometimes,
1159
- // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped
1160
- // in these cases
1161
- if (!fx.started) {
1162
- ends = pathAnim.init(elem, elem.d, elem.toD);
1163
- fx.start = ends[0];
1164
- fx.end = ends[1];
1165
- fx.started = true;
1166
- }
1167
-
1168
-
1169
- // interpolate each value of the path
1170
- elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD));
1171
- };
1172
-
1173
- // jQuery 1.8 style
1174
- if (Tween) {
1175
- propHooks.d = {
1176
- set: dSetter
1177
- };
1178
- // pre 1.8
1179
- } else {
1180
- // animate paths
1181
- Step.d = dSetter;
1182
- }
1183
-
1184
- /**
1185
- * Utility for iterating over an array. Parameters are reversed compared to jQuery.
1186
- * @param {Array} arr
1187
- * @param {Function} fn
1188
- */
1189
- this.each = Array.prototype.forEach ?
1190
- function (arr, fn) { // modern browsers
1191
- return Array.prototype.forEach.call(arr, fn);
1192
-
1193
- } :
1194
- function (arr, fn) { // legacy
1195
- var i = 0,
1196
- len = arr.length;
1197
- for (; i < len; i++) {
1198
- if (fn.call(arr[i], arr[i], i, arr) === false) {
1199
- return i;
1200
- }
1201
- }
1202
- };
1203
-
1204
- /**
1205
- * Register Highcharts as a plugin in the respective framework
1206
- */
1207
- $.fn.highcharts = function () {
1208
- var constr = 'Chart', // default constructor
1209
- args = arguments,
1210
- options,
1211
- ret,
1212
- chart;
1213
-
1214
- if (isString(args[0])) {
1215
- constr = args[0];
1216
- args = Array.prototype.slice.call(args, 1);
1217
- }
1218
- options = args[0];
1219
-
1220
- // Create the chart
1221
- if (options !== UNDEFINED) {
1222
- /*jslint unused:false*/
1223
- options.chart = options.chart || {};
1224
- options.chart.renderTo = this[0];
1225
- chart = new Highcharts[constr](options, args[1]);
1226
- ret = this;
1227
- /*jslint unused:true*/
1228
- }
1229
-
1230
- // When called without parameters or with the return argument, get a predefined chart
1231
- if (options === UNDEFINED) {
1232
- ret = charts[attr(this[0], 'data-highcharts-chart')];
1233
- }
1234
-
1235
- return ret;
1236
- };
1237
-
1238
- },
1239
-
1240
-
1241
- /**
1242
- * Downloads a script and executes a callback when done.
1243
- * @param {String} scriptLocation
1244
- * @param {Function} callback
1245
- */
1246
- getScript: $.getScript,
1247
-
1248
- /**
1249
- * Return the index of an item in an array, or -1 if not found
1250
- */
1251
- inArray: $.inArray,
1252
-
1253
- /**
1254
- * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method.
1255
- * @param {Object} elem The HTML element
1256
- * @param {String} method Which method to run on the wrapped element
1257
- */
1258
- adapterRun: function (elem, method) {
1259
- return $(elem)[method]();
1260
- },
1261
-
1262
- /**
1263
- * Filter an array
1264
- */
1265
- grep: $.grep,
1266
-
1267
- /**
1268
- * Map an array
1269
- * @param {Array} arr
1270
- * @param {Function} fn
1271
- */
1272
- map: function (arr, fn) {
1273
- //return jQuery.map(arr, fn);
1274
- var results = [],
1275
- i = 0,
1276
- len = arr.length;
1277
- for (; i < len; i++) {
1278
- results[i] = fn.call(arr[i], arr[i], i, arr);
1279
- }
1280
- return results;
1281
-
1282
- },
1283
-
1284
- /**
1285
- * Get the position of an element relative to the top left of the page
1286
- */
1287
- offset: function (el) {
1288
- return $(el).offset();
1289
- },
1290
-
1291
- /**
1292
- * Add an event listener
1293
- * @param {Object} el A HTML element or custom object
1294
- * @param {String} event The event type
1295
- * @param {Function} fn The event handler
1296
- */
1297
- addEvent: function (el, event, fn) {
1298
- $(el).bind(event, fn);
1299
- },
1300
-
1301
- /**
1302
- * Remove event added with addEvent
1303
- * @param {Object} el The object
1304
- * @param {String} eventType The event type. Leave blank to remove all events.
1305
- * @param {Function} handler The function to remove
1306
- */
1307
- removeEvent: function (el, eventType, handler) {
1308
- // workaround for jQuery issue with unbinding custom events:
1309
- // http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2
1310
- var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent';
1311
- if (doc[func] && el && !el[func]) {
1312
- el[func] = function () {};
1313
- }
1314
-
1315
- $(el).unbind(eventType, handler);
1316
- },
1317
-
1318
- /**
1319
- * Fire an event on a custom object
1320
- * @param {Object} el
1321
- * @param {String} type
1322
- * @param {Object} eventArguments
1323
- * @param {Function} defaultFunction
1324
- */
1325
- fireEvent: function (el, type, eventArguments, defaultFunction) {
1326
- var event = $.Event(type),
1327
- detachedType = 'detached' + type,
1328
- defaultPrevented;
1329
-
1330
- // Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts
1331
- // never uses these properties, Chrome includes them in the default click event and
1332
- // raises the warning when they are copied over in the extend statement below.
1333
- //
1334
- // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid
1335
- // testing if they are there (warning in chrome) the only option is to test if running IE.
1336
- if (!isIE && eventArguments) {
1337
- delete eventArguments.layerX;
1338
- delete eventArguments.layerY;
1339
- }
1340
-
1341
- extend(event, eventArguments);
1342
-
1343
- // Prevent jQuery from triggering the object method that is named the
1344
- // same as the event. For example, if the event is 'select', jQuery
1345
- // attempts calling el.select and it goes into a loop.
1346
- if (el[type]) {
1347
- el[detachedType] = el[type];
1348
- el[type] = null;
1349
- }
1350
-
1351
- // Wrap preventDefault and stopPropagation in try/catch blocks in
1352
- // order to prevent JS errors when cancelling events on non-DOM
1353
- // objects. #615.
1354
- /*jslint unparam: true*/
1355
- $.each(['preventDefault', 'stopPropagation'], function (i, fn) {
1356
- var base = event[fn];
1357
- event[fn] = function () {
1358
- try {
1359
- base.call(event);
1360
- } catch (e) {
1361
- if (fn === 'preventDefault') {
1362
- defaultPrevented = true;
1363
- }
1364
- }
1365
- };
1366
- });
1367
- /*jslint unparam: false*/
1368
-
1369
- // trigger it
1370
- $(el).trigger(event);
1371
-
1372
- // attach the method
1373
- if (el[detachedType]) {
1374
- el[type] = el[detachedType];
1375
- el[detachedType] = null;
1376
- }
1377
-
1378
- if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) {
1379
- defaultFunction(event);
1380
- }
1381
- },
1382
-
1383
- /**
1384
- * Extension method needed for MooTools
1385
- */
1386
- washMouseEvent: function (e) {
1387
- var ret = e.originalEvent || e;
1388
-
1389
- // computed by jQuery, needed by IE8
1390
- if (ret.pageX === UNDEFINED) { // #1236
1391
- ret.pageX = e.pageX;
1392
- ret.pageY = e.pageY;
1393
- }
1394
-
1395
- return ret;
1396
- },
1397
-
1398
- /**
1399
- * Animate a HTML element or SVG element wrapper
1400
- * @param {Object} el
1401
- * @param {Object} params
1402
- * @param {Object} options jQuery-like animation options: duration, easing, callback
1403
- */
1404
- animate: function (el, params, options) {
1405
- var $el = $(el);
1406
- if (!el.style) {
1407
- el.style = {}; // #1881
1408
- }
1409
- if (params.d) {
1410
- el.toD = params.d; // keep the array form for paths, used in $.fx.step.d
1411
- params.d = 1; // because in jQuery, animating to an array has a different meaning
1412
- }
1413
-
1414
- $el.stop();
1415
- $el.animate(params, options);
1416
-
1417
- },
1418
- /**
1419
- * Stop running animation
1420
- */
1421
- stop: function (el) {
1422
- $(el).stop();
1423
- }
1424
- });
1425
- }(win.jQuery));
1426
-
1427
-
1428
- // check for a custom HighchartsAdapter defined prior to this file
1429
- var globalAdapter = win.HighchartsAdapter,
1430
- adapter = globalAdapter || {};
1431
-
1432
- // Initialize the adapter
1433
- if (globalAdapter) {
1434
- globalAdapter.init.call(globalAdapter, pathAnim);
1435
- }
1436
-
1437
-
1438
- // Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object
1439
- // and all the utility functions will be null. In that case they are populated by the
1440
- // default adapters below.
1441
- var adapterRun = adapter.adapterRun,
1442
- getScript = adapter.getScript,
1443
- inArray = adapter.inArray,
1444
- each = adapter.each,
1445
- grep = adapter.grep,
1446
- offset = adapter.offset,
1447
- map = adapter.map,
1448
- addEvent = adapter.addEvent,
1449
- removeEvent = adapter.removeEvent,
1450
- fireEvent = adapter.fireEvent,
1451
- washMouseEvent = adapter.washMouseEvent,
1452
- animate = adapter.animate,
1453
- stop = adapter.stop;
1454
-
1455
-
1456
-
1457
- /* ****************************************************************************
1458
- * Handle the options *
1459
- *****************************************************************************/
1460
- var
1461
-
1462
- defaultLabelOptions = {
1463
- enabled: true,
1464
- // rotation: 0,
1465
- align: 'center',
1466
- x: 0,
1467
- y: 15,
1468
- /*formatter: function () {
1469
- return this.value;
1470
- },*/
1471
- style: {
1472
- color: '#666',
1473
- cursor: 'default',
1474
- fontSize: '11px',
1475
- lineHeight: '14px'
1476
- }
1477
- };
1478
-
1479
- defaultOptions = {
1480
- colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce', '#492970',
1481
- '#f28f43', '#77a1e5', '#c42525', '#a6c96a'],
1482
- symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
1483
- lang: {
1484
- loading: 'Loading...',
1485
- months: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
1486
- 'August', 'September', 'October', 'November', 'December'],
1487
- shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
1488
- weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1489
- decimalPoint: '.',
1490
- numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels
1491
- resetZoom: 'Reset zoom',
1492
- resetZoomTitle: 'Reset zoom level 1:1',
1493
- thousandsSep: ','
1494
- },
1495
- global: {
1496
- useUTC: true,
1497
- canvasToolsURL: 'http://code.highcharts.com/3.0.2/modules/canvas-tools.js',
1498
- VMLRadialGradientURL: 'http://code.highcharts.com/3.0.2/gfx/vml-radial-gradient.png'
1499
- },
1500
- chart: {
1501
- //animation: true,
1502
- //alignTicks: false,
1503
- //reflow: true,
1504
- //className: null,
1505
- //events: { load, selection },
1506
- //margin: [null],
1507
- //marginTop: null,
1508
- //marginRight: null,
1509
- //marginBottom: null,
1510
- //marginLeft: null,
1511
- borderColor: '#4572A7',
1512
- //borderWidth: 0,
1513
- borderRadius: 5,
1514
- defaultSeriesType: 'line',
1515
- ignoreHiddenSeries: true,
1516
- //inverted: false,
1517
- //shadow: false,
1518
- spacingTop: 10,
1519
- spacingRight: 10,
1520
- spacingBottom: 15,
1521
- spacingLeft: 10,
1522
- style: {
1523
- fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font
1524
- fontSize: '12px'
1525
- },
1526
- backgroundColor: '#FFFFFF',
1527
- //plotBackgroundColor: null,
1528
- plotBorderColor: '#C0C0C0',
1529
- //plotBorderWidth: 0,
1530
- //plotShadow: false,
1531
- //zoomType: ''
1532
- resetZoomButton: {
1533
- theme: {
1534
- zIndex: 20
1535
- },
1536
- position: {
1537
- align: 'right',
1538
- x: -10,
1539
- //verticalAlign: 'top',
1540
- y: 10
1541
- }
1542
- // relativeTo: 'plot'
1543
- }
1544
- },
1545
- title: {
1546
- text: 'Chart title',
1547
- align: 'center',
1548
- // floating: false,
1549
- // margin: 15,
1550
- // x: 0,
1551
- // verticalAlign: 'top',
1552
- y: 15,
1553
- style: {
1554
- color: '#274b6d',//#3E576F',
1555
- fontSize: '16px'
1556
- }
1557
-
1558
- },
1559
- subtitle: {
1560
- text: '',
1561
- align: 'center',
1562
- // floating: false
1563
- // x: 0,
1564
- // verticalAlign: 'top',
1565
- y: 30,
1566
- style: {
1567
- color: '#4d759e'
1568
- }
1569
- },
1570
-
1571
- plotOptions: {
1572
- line: { // base series options
1573
- allowPointSelect: false,
1574
- showCheckbox: false,
1575
- animation: {
1576
- duration: 1000
1577
- },
1578
- //connectNulls: false,
1579
- //cursor: 'default',
1580
- //clip: true,
1581
- //dashStyle: null,
1582
- //enableMouseTracking: true,
1583
- events: {},
1584
- //legendIndex: 0,
1585
- lineWidth: 2,
1586
- //shadow: false,
1587
- // stacking: null,
1588
- marker: {
1589
- enabled: true,
1590
- //symbol: null,
1591
- lineWidth: 0,
1592
- radius: 4,
1593
- lineColor: '#FFFFFF',
1594
- //fillColor: null,
1595
- states: { // states for a single point
1596
- hover: {
1597
- enabled: true
1598
- //radius: base + 2
1599
- },
1600
- select: {
1601
- fillColor: '#FFFFFF',
1602
- lineColor: '#000000',
1603
- lineWidth: 2
1604
- }
1605
- }
1606
- },
1607
- point: {
1608
- events: {}
1609
- },
1610
- dataLabels: merge(defaultLabelOptions, {
1611
- enabled: false,
1612
- formatter: function () {
1613
- return numberFormat(this.y, -1);
1614
- },
1615
- verticalAlign: 'bottom', // above singular point
1616
- y: 0
1617
- // backgroundColor: undefined,
1618
- // borderColor: undefined,
1619
- // borderRadius: undefined,
1620
- // borderWidth: undefined,
1621
- // padding: 3,
1622
- // shadow: false
1623
- }),
1624
- cropThreshold: 300, // draw points outside the plot area when the number of points is less than this
1625
- pointRange: 0,
1626
- //pointStart: 0,
1627
- //pointInterval: 1,
1628
- showInLegend: true,
1629
- states: { // states for the entire series
1630
- hover: {
1631
- //enabled: false,
1632
- //lineWidth: base + 1,
1633
- marker: {
1634
- // lineWidth: base + 1,
1635
- // radius: base + 1
1636
- }
1637
- },
1638
- select: {
1639
- marker: {}
1640
- }
1641
- },
1642
- stickyTracking: true
1643
- //tooltip: {
1644
- //pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b>'
1645
- //valueDecimals: null,
1646
- //xDateFormat: '%A, %b %e, %Y',
1647
- //valuePrefix: '',
1648
- //ySuffix: ''
1649
- //}
1650
- // turboThreshold: 1000
1651
- // zIndex: null
1652
- }
1653
- },
1654
- labels: {
1655
- //items: [],
1656
- style: {
1657
- //font: defaultFont,
1658
- position: ABSOLUTE,
1659
- color: '#3E576F'
1660
- }
1661
- },
1662
- legend: {
1663
- enabled: true,
1664
- align: 'center',
1665
- //floating: false,
1666
- layout: 'horizontal',
1667
- labelFormatter: function () {
1668
- return this.name;
1669
- },
1670
- borderWidth: 1,
1671
- borderColor: '#909090',
1672
- borderRadius: 5,
1673
- navigation: {
1674
- // animation: true,
1675
- activeColor: '#274b6d',
1676
- // arrowSize: 12
1677
- inactiveColor: '#CCC'
1678
- // style: {} // text styles
1679
- },
1680
- // margin: 10,
1681
- // reversed: false,
1682
- shadow: false,
1683
- // backgroundColor: null,
1684
- /*style: {
1685
- padding: '5px'
1686
- },*/
1687
- itemStyle: {
1688
- cursor: 'pointer',
1689
- color: '#274b6d',
1690
- fontSize: '12px'
1691
- },
1692
- itemHoverStyle: {
1693
- //cursor: 'pointer', removed as of #601
1694
- color: '#000'
1695
- },
1696
- itemHiddenStyle: {
1697
- color: '#CCC'
1698
- },
1699
- itemCheckboxStyle: {
1700
- position: ABSOLUTE,
1701
- width: '13px', // for IE precision
1702
- height: '13px'
1703
- },
1704
- // itemWidth: undefined,
1705
- symbolWidth: 16,
1706
- symbolPadding: 5,
1707
- verticalAlign: 'bottom',
1708
- // width: undefined,
1709
- x: 0,
1710
- y: 0,
1711
- title: {
1712
- //text: null,
1713
- style: {
1714
- fontWeight: 'bold'
1715
- }
1716
- }
1717
- },
1718
-
1719
- loading: {
1720
- // hideDuration: 100,
1721
- labelStyle: {
1722
- fontWeight: 'bold',
1723
- position: RELATIVE,
1724
- top: '1em'
1725
- },
1726
- // showDuration: 0,
1727
- style: {
1728
- position: ABSOLUTE,
1729
- backgroundColor: 'white',
1730
- opacity: 0.5,
1731
- textAlign: 'center'
1732
- }
1733
- },
1734
-
1735
- tooltip: {
1736
- enabled: true,
1737
- animation: hasSVG,
1738
- //crosshairs: null,
1739
- backgroundColor: 'rgba(255, 255, 255, .85)',
1740
- borderWidth: 1,
1741
- borderRadius: 3,
1742
- dateTimeLabelFormats: {
1743
- millisecond: '%A, %b %e, %H:%M:%S.%L',
1744
- second: '%A, %b %e, %H:%M:%S',
1745
- minute: '%A, %b %e, %H:%M',
1746
- hour: '%A, %b %e, %H:%M',
1747
- day: '%A, %b %e, %Y',
1748
- week: 'Week from %A, %b %e, %Y',
1749
- month: '%B %Y',
1750
- year: '%Y'
1751
- },
1752
- //formatter: defaultFormatter,
1753
- headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
1754
- pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',
1755
- shadow: true,
1756
- //shared: false,
1757
- snap: isTouchDevice ? 25 : 10,
1758
- style: {
1759
- color: '#333333',
1760
- cursor: 'default',
1761
- fontSize: '12px',
1762
- padding: '8px',
1763
- whiteSpace: 'nowrap'
1764
- }
1765
- //xDateFormat: '%A, %b %e, %Y',
1766
- //valueDecimals: null,
1767
- //valuePrefix: '',
1768
- //valueSuffix: ''
1769
- },
1770
-
1771
- credits: {
1772
- enabled: true,
1773
- text: 'Highcharts.com',
1774
- href: 'http://www.highcharts.com',
1775
- position: {
1776
- align: 'right',
1777
- x: -10,
1778
- verticalAlign: 'bottom',
1779
- y: -5
1780
- },
1781
- style: {
1782
- cursor: 'pointer',
1783
- color: '#909090',
1784
- fontSize: '9px'
1785
- }
1786
- }
1787
- };
1788
-
1789
-
1790
-
1791
-
1792
- // Series defaults
1793
- var defaultPlotOptions = defaultOptions.plotOptions,
1794
- defaultSeriesOptions = defaultPlotOptions.line;
1795
-
1796
- // set the default time methods
1797
- setTimeMethods();
1798
-
1799
-
1800
-
1801
- /**
1802
- * Set the time methods globally based on the useUTC option. Time method can be either
1803
- * local time or UTC (default).
1804
- */
1805
- function setTimeMethods() {
1806
- var useUTC = defaultOptions.global.useUTC,
1807
- GET = useUTC ? 'getUTC' : 'get',
1808
- SET = useUTC ? 'setUTC' : 'set';
1809
-
1810
- makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) {
1811
- return new Date(
1812
- year,
1813
- month,
1814
- pick(date, 1),
1815
- pick(hours, 0),
1816
- pick(minutes, 0),
1817
- pick(seconds, 0)
1818
- ).getTime();
1819
- };
1820
- getMinutes = GET + 'Minutes';
1821
- getHours = GET + 'Hours';
1822
- getDay = GET + 'Day';
1823
- getDate = GET + 'Date';
1824
- getMonth = GET + 'Month';
1825
- getFullYear = GET + 'FullYear';
1826
- setMinutes = SET + 'Minutes';
1827
- setHours = SET + 'Hours';
1828
- setDate = SET + 'Date';
1829
- setMonth = SET + 'Month';
1830
- setFullYear = SET + 'FullYear';
1831
-
1832
- }
1833
-
1834
- /**
1835
- * Merge the default options with custom options and return the new options structure
1836
- * @param {Object} options The new custom options
1837
- */
1838
- function setOptions(options) {
1839
-
1840
- // Pull out axis options and apply them to the respective default axis options
1841
- /*defaultXAxisOptions = merge(defaultXAxisOptions, options.xAxis);
1842
- defaultYAxisOptions = merge(defaultYAxisOptions, options.yAxis);
1843
- options.xAxis = options.yAxis = UNDEFINED;*/
1844
-
1845
- // Merge in the default options
1846
- defaultOptions = merge(defaultOptions, options);
1847
-
1848
- // Apply UTC
1849
- setTimeMethods();
1850
-
1851
- return defaultOptions;
1852
- }
1853
-
1854
- /**
1855
- * Get the updated default options. Merely exposing defaultOptions for outside modules
1856
- * isn't enough because the setOptions method creates a new object.
1857
- */
1858
- function getOptions() {
1859
- return defaultOptions;
1860
- }
1861
-
1862
-
1863
- /**
1864
- * Handle color operations. The object methods are chainable.
1865
- * @param {String} input The input color in either rbga or hex format
1866
- */
1867
- var Color = function (input) {
1868
- // declare variables
1869
- var rgba = [], result, stops;
1870
-
1871
- /**
1872
- * Parse the input color to rgba array
1873
- * @param {String} input
1874
- */
1875
- function init(input) {
1876
-
1877
- // Gradients
1878
- if (input && input.stops) {
1879
- stops = map(input.stops, function (stop) {
1880
- return Color(stop[1]);
1881
- });
1882
-
1883
- // Solid colors
1884
- } else {
1885
- // rgba
1886
- result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(input);
1887
- if (result) {
1888
- rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)];
1889
- } else {
1890
- // hex
1891
- result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(input);
1892
- if (result) {
1893
- rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1];
1894
- } else {
1895
- // rgb
1896
- result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(input);
1897
- if (result) {
1898
- rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
1899
- }
1900
- }
1901
- }
1902
- }
1903
-
1904
- }
1905
- /**
1906
- * Return the color a specified format
1907
- * @param {String} format
1908
- */
1909
- function get(format) {
1910
- var ret;
1911
-
1912
- if (stops) {
1913
- ret = merge(input);
1914
- ret.stops = [].concat(ret.stops);
1915
- each(stops, function (stop, i) {
1916
- ret.stops[i] = [ret.stops[i][0], stop.get(format)];
1917
- });
1918
-
1919
- // it's NaN if gradient colors on a column chart
1920
- } else if (rgba && !isNaN(rgba[0])) {
1921
- if (format === 'rgb') {
1922
- ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
1923
- } else if (format === 'a') {
1924
- ret = rgba[3];
1925
- } else {
1926
- ret = 'rgba(' + rgba.join(',') + ')';
1927
- }
1928
- } else {
1929
- ret = input;
1930
- }
1931
- return ret;
1932
- }
1933
-
1934
- /**
1935
- * Brighten the color
1936
- * @param {Number} alpha
1937
- */
1938
- function brighten(alpha) {
1939
- if (stops) {
1940
- each(stops, function (stop) {
1941
- stop.brighten(alpha);
1942
- });
1943
-
1944
- } else if (isNumber(alpha) && alpha !== 0) {
1945
- var i;
1946
- for (i = 0; i < 3; i++) {
1947
- rgba[i] += pInt(alpha * 255);
1948
-
1949
- if (rgba[i] < 0) {
1950
- rgba[i] = 0;
1951
- }
1952
- if (rgba[i] > 255) {
1953
- rgba[i] = 255;
1954
- }
1955
- }
1956
- }
1957
- return this;
1958
- }
1959
- /**
1960
- * Set the color's opacity to a given alpha value
1961
- * @param {Number} alpha
1962
- */
1963
- function setOpacity(alpha) {
1964
- rgba[3] = alpha;
1965
- return this;
1966
- }
1967
-
1968
- // initialize: parse the input
1969
- init(input);
1970
-
1971
- // public methods
1972
- return {
1973
- get: get,
1974
- brighten: brighten,
1975
- rgba: rgba,
1976
- setOpacity: setOpacity
1977
- };
1978
- };
1979
-
1980
-
1981
- /**
1982
- * A wrapper object for SVG elements
1983
- */
1984
- function SVGElement() {}
1985
-
1986
- SVGElement.prototype = {
1987
- /**
1988
- * Initialize the SVG renderer
1989
- * @param {Object} renderer
1990
- * @param {String} nodeName
1991
- */
1992
- init: function (renderer, nodeName) {
1993
- var wrapper = this;
1994
- wrapper.element = nodeName === 'span' ?
1995
- createElement(nodeName) :
1996
- doc.createElementNS(SVG_NS, nodeName);
1997
- wrapper.renderer = renderer;
1998
- /**
1999
- * A collection of attribute setters. These methods, if defined, are called right before a certain
2000
- * attribute is set on an element wrapper. Returning false prevents the default attribute
2001
- * setter to run. Returning a value causes the default setter to set that value. Used in
2002
- * Renderer.label.
2003
- */
2004
- wrapper.attrSetters = {};
2005
- },
2006
- /**
2007
- * Default base for animation
2008
- */
2009
- opacity: 1,
2010
- /**
2011
- * Animate a given attribute
2012
- * @param {Object} params
2013
- * @param {Number} options The same options as in jQuery animation
2014
- * @param {Function} complete Function to perform at the end of animation
2015
- */
2016
- animate: function (params, options, complete) {
2017
- var animOptions = pick(options, globalAnimation, true);
2018
- stop(this); // stop regardless of animation actually running, or reverting to .attr (#607)
2019
- if (animOptions) {
2020
- animOptions = merge(animOptions);
2021
- if (complete) { // allows using a callback with the global animation without overwriting it
2022
- animOptions.complete = complete;
2023
- }
2024
- animate(this, params, animOptions);
2025
- } else {
2026
- this.attr(params);
2027
- if (complete) {
2028
- complete();
2029
- }
2030
- }
2031
- },
2032
- /**
2033
- * Set or get a given attribute
2034
- * @param {Object|String} hash
2035
- * @param {Mixed|Undefined} val
2036
- */
2037
- attr: function (hash, val) {
2038
- var wrapper = this,
2039
- key,
2040
- value,
2041
- result,
2042
- i,
2043
- child,
2044
- element = wrapper.element,
2045
- nodeName = element.nodeName.toLowerCase(), // Android2 requires lower for "text"
2046
- renderer = wrapper.renderer,
2047
- skipAttr,
2048
- titleNode,
2049
- attrSetters = wrapper.attrSetters,
2050
- shadows = wrapper.shadows,
2051
- hasSetSymbolSize,
2052
- doTransform,
2053
- ret = wrapper;
2054
-
2055
- // single key-value pair
2056
- if (isString(hash) && defined(val)) {
2057
- key = hash;
2058
- hash = {};
2059
- hash[key] = val;
2060
- }
2061
-
2062
- // used as a getter: first argument is a string, second is undefined
2063
- if (isString(hash)) {
2064
- key = hash;
2065
- if (nodeName === 'circle') {
2066
- key = { x: 'cx', y: 'cy' }[key] || key;
2067
- } else if (key === 'strokeWidth') {
2068
- key = 'stroke-width';
2069
- }
2070
- ret = attr(element, key) || wrapper[key] || 0;
2071
- if (key !== 'd' && key !== 'visibility') { // 'd' is string in animation step
2072
- ret = parseFloat(ret);
2073
- }
2074
-
2075
- // setter
2076
- } else {
2077
-
2078
- for (key in hash) {
2079
- skipAttr = false; // reset
2080
- value = hash[key];
2081
-
2082
- // check for a specific attribute setter
2083
- result = attrSetters[key] && attrSetters[key].call(wrapper, value, key);
2084
-
2085
- if (result !== false) {
2086
- if (result !== UNDEFINED) {
2087
- value = result; // the attribute setter has returned a new value to set
2088
- }
2089
-
2090
-
2091
- // paths
2092
- if (key === 'd') {
2093
- if (value && value.join) { // join path
2094
- value = value.join(' ');
2095
- }
2096
- if (/(NaN| {2}|^$)/.test(value)) {
2097
- value = 'M 0 0';
2098
- }
2099
- //wrapper.d = value; // shortcut for animations
2100
-
2101
- // update child tspans x values
2102
- } else if (key === 'x' && nodeName === 'text') {
2103
- for (i = 0; i < element.childNodes.length; i++) {
2104
- child = element.childNodes[i];
2105
- // if the x values are equal, the tspan represents a linebreak
2106
- if (attr(child, 'x') === attr(element, 'x')) {
2107
- //child.setAttribute('x', value);
2108
- attr(child, 'x', value);
2109
- }
2110
- }
2111
-
2112
- } else if (wrapper.rotation && (key === 'x' || key === 'y')) {
2113
- doTransform = true;
2114
-
2115
- // apply gradients
2116
- } else if (key === 'fill') {
2117
- value = renderer.color(value, element, key);
2118
-
2119
- // circle x and y
2120
- } else if (nodeName === 'circle' && (key === 'x' || key === 'y')) {
2121
- key = { x: 'cx', y: 'cy' }[key] || key;
2122
-
2123
- // rectangle border radius
2124
- } else if (nodeName === 'rect' && key === 'r') {
2125
- attr(element, {
2126
- rx: value,
2127
- ry: value
2128
- });
2129
- skipAttr = true;
2130
-
2131
- // translation and text rotation
2132
- } else if (key === 'translateX' || key === 'translateY' || key === 'rotation' ||
2133
- key === 'verticalAlign' || key === 'scaleX' || key === 'scaleY') {
2134
- doTransform = true;
2135
- skipAttr = true;
2136
-
2137
- // apply opacity as subnode (required by legacy WebKit and Batik)
2138
- } else if (key === 'stroke') {
2139
- value = renderer.color(value, element, key);
2140
-
2141
- // emulate VML's dashstyle implementation
2142
- } else if (key === 'dashstyle') {
2143
- key = 'stroke-dasharray';
2144
- value = value && value.toLowerCase();
2145
- if (value === 'solid') {
2146
- value = NONE;
2147
- } else if (value) {
2148
- value = value
2149
- .replace('shortdashdotdot', '3,1,1,1,1,1,')
2150
- .replace('shortdashdot', '3,1,1,1')
2151
- .replace('shortdot', '1,1,')
2152
- .replace('shortdash', '3,1,')
2153
- .replace('longdash', '8,3,')
2154
- .replace(/dot/g, '1,3,')
2155
- .replace('dash', '4,3,')
2156
- .replace(/,$/, '')
2157
- .split(','); // ending comma
2158
-
2159
- i = value.length;
2160
- while (i--) {
2161
- value[i] = pInt(value[i]) * hash['stroke-width'];
2162
- }
2163
- value = value.join(',');
2164
- }
2165
-
2166
- // IE9/MooTools combo: MooTools returns objects instead of numbers and IE9 Beta 2
2167
- // is unable to cast them. Test again with final IE9.
2168
- } else if (key === 'width') {
2169
- value = pInt(value);
2170
-
2171
- // Text alignment
2172
- } else if (key === 'align') {
2173
- key = 'text-anchor';
2174
- value = { left: 'start', center: 'middle', right: 'end' }[value];
2175
-
2176
- // Title requires a subnode, #431
2177
- } else if (key === 'title') {
2178
- titleNode = element.getElementsByTagName('title')[0];
2179
- if (!titleNode) {
2180
- titleNode = doc.createElementNS(SVG_NS, 'title');
2181
- element.appendChild(titleNode);
2182
- }
2183
- titleNode.textContent = value;
2184
- }
2185
-
2186
- // jQuery animate changes case
2187
- if (key === 'strokeWidth') {
2188
- key = 'stroke-width';
2189
- }
2190
-
2191
- // In Chrome/Win < 6 as well as Batik, the stroke attribute can't be set when the stroke-
2192
- // width is 0. #1369
2193
- if (key === 'stroke-width' || key === 'stroke') {
2194
- wrapper[key] = value;
2195
- // Only apply the stroke attribute if the stroke width is defined and larger than 0
2196
- if (wrapper.stroke && wrapper['stroke-width']) {
2197
- attr(element, 'stroke', wrapper.stroke);
2198
- attr(element, 'stroke-width', wrapper['stroke-width']);
2199
- wrapper.hasStroke = true;
2200
- } else if (key === 'stroke-width' && value === 0 && wrapper.hasStroke) {
2201
- element.removeAttribute('stroke');
2202
- wrapper.hasStroke = false;
2203
- }
2204
- skipAttr = true;
2205
- }
2206
-
2207
- // symbols
2208
- if (wrapper.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
2209
-
2210
-
2211
- if (!hasSetSymbolSize) {
2212
- wrapper.symbolAttr(hash);
2213
- hasSetSymbolSize = true;
2214
- }
2215
- skipAttr = true;
2216
- }
2217
-
2218
- // let the shadow follow the main element
2219
- if (shadows && /^(width|height|visibility|x|y|d|transform)$/.test(key)) {
2220
- i = shadows.length;
2221
- while (i--) {
2222
- attr(
2223
- shadows[i],
2224
- key,
2225
- key === 'height' ?
2226
- mathMax(value - (shadows[i].cutHeight || 0), 0) :
2227
- value
2228
- );
2229
- }
2230
- }
2231
-
2232
- // validate heights
2233
- if ((key === 'width' || key === 'height') && nodeName === 'rect' && value < 0) {
2234
- value = 0;
2235
- }
2236
-
2237
- // Record for animation and quick access without polling the DOM
2238
- wrapper[key] = value;
2239
-
2240
-
2241
- if (key === 'text') {
2242
- // Delete bBox memo when the text changes
2243
- if (value !== wrapper.textStr) {
2244
- delete wrapper.bBox;
2245
- }
2246
- wrapper.textStr = value;
2247
- if (wrapper.added) {
2248
- renderer.buildText(wrapper);
2249
- }
2250
- } else if (!skipAttr) {
2251
- attr(element, key, value);
2252
- }
2253
-
2254
- }
2255
-
2256
- }
2257
-
2258
- // Update transform. Do this outside the loop to prevent redundant updating for batch setting
2259
- // of attributes.
2260
- if (doTransform) {
2261
- wrapper.updateTransform();
2262
- }
2263
-
2264
- }
2265
-
2266
- return ret;
2267
- },
2268
-
2269
-
2270
- /**
2271
- * Add a class name to an element
2272
- */
2273
- addClass: function (className) {
2274
- attr(this.element, 'class', attr(this.element, 'class') + ' ' + className);
2275
- return this;
2276
- },
2277
- /* hasClass and removeClass are not (yet) needed
2278
- hasClass: function (className) {
2279
- return attr(this.element, 'class').indexOf(className) !== -1;
2280
- },
2281
- removeClass: function (className) {
2282
- attr(this.element, 'class', attr(this.element, 'class').replace(className, ''));
2283
- return this;
2284
- },
2285
- */
2286
-
2287
- /**
2288
- * If one of the symbol size affecting parameters are changed,
2289
- * check all the others only once for each call to an element's
2290
- * .attr() method
2291
- * @param {Object} hash
2292
- */
2293
- symbolAttr: function (hash) {
2294
- var wrapper = this;
2295
-
2296
- each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) {
2297
- wrapper[key] = pick(hash[key], wrapper[key]);
2298
- });
2299
-
2300
- wrapper.attr({
2301
- d: wrapper.renderer.symbols[wrapper.symbolName](
2302
- wrapper.x,
2303
- wrapper.y,
2304
- wrapper.width,
2305
- wrapper.height,
2306
- wrapper
2307
- )
2308
- });
2309
- },
2310
-
2311
- /**
2312
- * Apply a clipping path to this object
2313
- * @param {String} id
2314
- */
2315
- clip: function (clipRect) {
2316
- return this.attr('clip-path', clipRect ? 'url(' + this.renderer.url + '#' + clipRect.id + ')' : NONE);
2317
- },
2318
-
2319
- /**
2320
- * Calculate the coordinates needed for drawing a rectangle crisply and return the
2321
- * calculated attributes
2322
- * @param {Number} strokeWidth
2323
- * @param {Number} x
2324
- * @param {Number} y
2325
- * @param {Number} width
2326
- * @param {Number} height
2327
- */
2328
- crisp: function (strokeWidth, x, y, width, height) {
2329
-
2330
- var wrapper = this,
2331
- key,
2332
- attribs = {},
2333
- values = {},
2334
- normalizer;
2335
-
2336
- strokeWidth = strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0;
2337
- normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors
2338
-
2339
- // normalize for crisp edges
2340
- values.x = mathFloor(x || wrapper.x || 0) + normalizer;
2341
- values.y = mathFloor(y || wrapper.y || 0) + normalizer;
2342
- values.width = mathFloor((width || wrapper.width || 0) - 2 * normalizer);
2343
- values.height = mathFloor((height || wrapper.height || 0) - 2 * normalizer);
2344
- values.strokeWidth = strokeWidth;
2345
-
2346
- for (key in values) {
2347
- if (wrapper[key] !== values[key]) { // only set attribute if changed
2348
- wrapper[key] = attribs[key] = values[key];
2349
- }
2350
- }
2351
-
2352
- return attribs;
2353
- },
2354
-
2355
- /**
2356
- * Set styles for the element
2357
- * @param {Object} styles
2358
- */
2359
- css: function (styles) {
2360
- /*jslint unparam: true*//* allow unused param a in the regexp function below */
2361
- var elemWrapper = this,
2362
- elem = elemWrapper.element,
2363
- textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text',
2364
- n,
2365
- serializedCss = '',
2366
- hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
2367
- /*jslint unparam: false*/
2368
-
2369
- // convert legacy
2370
- if (styles && styles.color) {
2371
- styles.fill = styles.color;
2372
- }
2373
-
2374
- // Merge the new styles with the old ones
2375
- styles = extend(
2376
- elemWrapper.styles,
2377
- styles
2378
- );
2379
-
2380
- // store object
2381
- elemWrapper.styles = styles;
2382
-
2383
-
2384
- // Don't handle line wrap on canvas
2385
- if (useCanVG && textWidth) {
2386
- delete styles.width;
2387
- }
2388
-
2389
- // serialize and set style attribute
2390
- if (isIE && !hasSVG) { // legacy IE doesn't support setting style attribute
2391
- if (textWidth) {
2392
- delete styles.width;
2393
- }
2394
- css(elemWrapper.element, styles);
2395
- } else {
2396
- for (n in styles) {
2397
- serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';';
2398
- }
2399
- attr(elem, 'style', serializedCss); // #1881
2400
- }
2401
-
2402
-
2403
- // re-build text
2404
- if (textWidth && elemWrapper.added) {
2405
- elemWrapper.renderer.buildText(elemWrapper);
2406
- }
2407
-
2408
- return elemWrapper;
2409
- },
2410
-
2411
- /**
2412
- * Add an event listener
2413
- * @param {String} eventType
2414
- * @param {Function} handler
2415
- */
2416
- on: function (eventType, handler) {
2417
- // touch
2418
- if (hasTouch && eventType === 'click') {
2419
- this.element.ontouchstart = function (e) {
2420
- e.preventDefault();
2421
- handler();
2422
- };
2423
- }
2424
- // simplest possible event model for internal use
2425
- this.element['on' + eventType] = handler;
2426
- return this;
2427
- },
2428
-
2429
- /**
2430
- * Set the coordinates needed to draw a consistent radial gradient across
2431
- * pie slices regardless of positioning inside the chart. The format is
2432
- * [centerX, centerY, diameter] in pixels.
2433
- */
2434
- setRadialReference: function (coordinates) {
2435
- this.element.radialReference = coordinates;
2436
- return this;
2437
- },
2438
-
2439
- /**
2440
- * Move an object and its children by x and y values
2441
- * @param {Number} x
2442
- * @param {Number} y
2443
- */
2444
- translate: function (x, y) {
2445
- return this.attr({
2446
- translateX: x,
2447
- translateY: y
2448
- });
2449
- },
2450
-
2451
- /**
2452
- * Invert a group, rotate and flip
2453
- */
2454
- invert: function () {
2455
- var wrapper = this;
2456
- wrapper.inverted = true;
2457
- wrapper.updateTransform();
2458
- return wrapper;
2459
- },
2460
-
2461
- /**
2462
- * Apply CSS to HTML elements. This is used in text within SVG rendering and
2463
- * by the VML renderer
2464
- */
2465
- htmlCss: function (styles) {
2466
- var wrapper = this,
2467
- element = wrapper.element,
2468
- textWidth = styles && element.tagName === 'SPAN' && styles.width;
2469
-
2470
- if (textWidth) {
2471
- delete styles.width;
2472
- wrapper.textWidth = textWidth;
2473
- wrapper.updateTransform();
2474
- }
2475
-
2476
- wrapper.styles = extend(wrapper.styles, styles);
2477
- css(wrapper.element, styles);
2478
-
2479
- return wrapper;
2480
- },
2481
-
2482
-
2483
-
2484
- /**
2485
- * VML and useHTML method for calculating the bounding box based on offsets
2486
- * @param {Boolean} refresh Whether to force a fresh value from the DOM or to
2487
- * use the cached value
2488
- *
2489
- * @return {Object} A hash containing values for x, y, width and height
2490
- */
2491
-
2492
- htmlGetBBox: function () {
2493
- var wrapper = this,
2494
- element = wrapper.element,
2495
- bBox = wrapper.bBox;
2496
-
2497
- // faking getBBox in exported SVG in legacy IE
2498
- if (!bBox) {
2499
- // faking getBBox in exported SVG in legacy IE (is this a duplicate of the fix for #1079?)
2500
- if (element.nodeName === 'text') {
2501
- element.style.position = ABSOLUTE;
2502
- }
2503
-
2504
- bBox = wrapper.bBox = {
2505
- x: element.offsetLeft,
2506
- y: element.offsetTop,
2507
- width: element.offsetWidth,
2508
- height: element.offsetHeight
2509
- };
2510
- }
2511
-
2512
- return bBox;
2513
- },
2514
-
2515
- /**
2516
- * VML override private method to update elements based on internal
2517
- * properties based on SVG transform
2518
- */
2519
- htmlUpdateTransform: function () {
2520
- // aligning non added elements is expensive
2521
- if (!this.added) {
2522
- this.alignOnAdd = true;
2523
- return;
2524
- }
2525
-
2526
- var wrapper = this,
2527
- renderer = wrapper.renderer,
2528
- elem = wrapper.element,
2529
- translateX = wrapper.translateX || 0,
2530
- translateY = wrapper.translateY || 0,
2531
- x = wrapper.x || 0,
2532
- y = wrapper.y || 0,
2533
- align = wrapper.textAlign || 'left',
2534
- alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
2535
- nonLeft = align && align !== 'left',
2536
- shadows = wrapper.shadows;
2537
-
2538
- // apply translate
2539
- css(elem, {
2540
- marginLeft: translateX,
2541
- marginTop: translateY
2542
- });
2543
- if (shadows) { // used in labels/tooltip
2544
- each(shadows, function (shadow) {
2545
- css(shadow, {
2546
- marginLeft: translateX + 1,
2547
- marginTop: translateY + 1
2548
- });
2549
- });
2550
- }
2551
-
2552
- // apply inversion
2553
- if (wrapper.inverted) { // wrapper is a group
2554
- each(elem.childNodes, function (child) {
2555
- renderer.invertChild(child, elem);
2556
- });
2557
- }
2558
-
2559
- if (elem.tagName === 'SPAN') {
2560
-
2561
- var width, height,
2562
- rotation = wrapper.rotation,
2563
- baseline,
2564
- radians = 0,
2565
- costheta = 1,
2566
- sintheta = 0,
2567
- quad,
2568
- textWidth = pInt(wrapper.textWidth),
2569
- xCorr = wrapper.xCorr || 0,
2570
- yCorr = wrapper.yCorr || 0,
2571
- currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','),
2572
- rotationStyle = {},
2573
- cssTransformKey;
2574
-
2575
- if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed
2576
-
2577
- if (defined(rotation)) {
2578
-
2579
- if (renderer.isSVG) { // #916
2580
- cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
2581
- rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
2582
-
2583
- } else {
2584
- radians = rotation * deg2rad; // deg to rad
2585
- costheta = mathCos(radians);
2586
- sintheta = mathSin(radians);
2587
-
2588
- // Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented
2589
- // but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+
2590
- // has support for CSS3 transform. The getBBox method also needs to be updated
2591
- // to compensate for the rotation, like it currently does for SVG.
2592
- // Test case: http://highcharts.com/tests/?file=text-rotation
2593
- rotationStyle.filter = rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta,
2594
- ', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
2595
- ', sizingMethod=\'auto expand\')'].join('') : NONE;
2596
- }
2597
- css(elem, rotationStyle);
2598
- }
2599
-
2600
- width = pick(wrapper.elemWidth, elem.offsetWidth);
2601
- height = pick(wrapper.elemHeight, elem.offsetHeight);
2602
-
2603
- // update textWidth
2604
- if (width > textWidth && /[ \-]/.test(elem.textContent || elem.innerText)) { // #983, #1254
2605
- css(elem, {
2606
- width: textWidth + PX,
2607
- display: 'block',
2608
- whiteSpace: 'normal'
2609
- });
2610
- width = textWidth;
2611
- }
2612
-
2613
- // correct x and y
2614
- baseline = renderer.fontMetrics(elem.style.fontSize).b;
2615
- xCorr = costheta < 0 && -width;
2616
- yCorr = sintheta < 0 && -height;
2617
-
2618
- // correct for baseline and corners spilling out after rotation
2619
- quad = costheta * sintheta < 0;
2620
- xCorr += sintheta * baseline * (quad ? 1 - alignCorrection : alignCorrection);
2621
- yCorr -= costheta * baseline * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
2622
-
2623
- // correct for the length/height of the text
2624
- if (nonLeft) {
2625
- xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
2626
- if (rotation) {
2627
- yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1);
2628
- }
2629
- css(elem, {
2630
- textAlign: align
2631
- });
2632
- }
2633
-
2634
- // record correction
2635
- wrapper.xCorr = xCorr;
2636
- wrapper.yCorr = yCorr;
2637
- }
2638
-
2639
- // apply position with correction
2640
- css(elem, {
2641
- left: (x + xCorr) + PX,
2642
- top: (y + yCorr) + PX
2643
- });
2644
-
2645
- // force reflow in webkit to apply the left and top on useHTML element (#1249)
2646
- if (isWebKit) {
2647
- height = elem.offsetHeight; // assigned to height for JSLint purpose
2648
- }
2649
-
2650
- // record current text transform
2651
- wrapper.cTT = currentTextTransform;
2652
- }
2653
- },
2654
-
2655
- /**
2656
- * Private method to update the transform attribute based on internal
2657
- * properties
2658
- */
2659
- updateTransform: function () {
2660
- var wrapper = this,
2661
- translateX = wrapper.translateX || 0,
2662
- translateY = wrapper.translateY || 0,
2663
- scaleX = wrapper.scaleX,
2664
- scaleY = wrapper.scaleY,
2665
- inverted = wrapper.inverted,
2666
- rotation = wrapper.rotation,
2667
- transform;
2668
-
2669
- // flipping affects translate as adjustment for flipping around the group's axis
2670
- if (inverted) {
2671
- translateX += wrapper.attr('width');
2672
- translateY += wrapper.attr('height');
2673
- }
2674
-
2675
- // Apply translate. Nearly all transformed elements have translation, so instead
2676
- // of checking for translate = 0, do it always (#1767, #1846).
2677
- transform = ['translate(' + translateX + ',' + translateY + ')'];
2678
-
2679
- // apply rotation
2680
- if (inverted) {
2681
- transform.push('rotate(90) scale(-1,1)');
2682
- } else if (rotation) { // text rotation
2683
- transform.push('rotate(' + rotation + ' ' + (wrapper.x || 0) + ' ' + (wrapper.y || 0) + ')');
2684
- }
2685
-
2686
- // apply scale
2687
- if (defined(scaleX) || defined(scaleY)) {
2688
- transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
2689
- }
2690
-
2691
- if (transform.length) {
2692
- attr(wrapper.element, 'transform', transform.join(' '));
2693
- }
2694
- },
2695
- /**
2696
- * Bring the element to the front
2697
- */
2698
- toFront: function () {
2699
- var element = this.element;
2700
- element.parentNode.appendChild(element);
2701
- return this;
2702
- },
2703
-
2704
-
2705
- /**
2706
- * Break down alignment options like align, verticalAlign, x and y
2707
- * to x and y relative to the chart.
2708
- *
2709
- * @param {Object} alignOptions
2710
- * @param {Boolean} alignByTranslate
2711
- * @param {String[Object} box The box to align to, needs a width and height. When the
2712
- * box is a string, it refers to an object in the Renderer. For example, when
2713
- * box is 'spacingBox', it refers to Renderer.spacingBox which holds width, height
2714
- * x and y properties.
2715
- *
2716
- */
2717
- align: function (alignOptions, alignByTranslate, box) {
2718
- var align,
2719
- vAlign,
2720
- x,
2721
- y,
2722
- attribs = {},
2723
- alignTo,
2724
- renderer = this.renderer,
2725
- alignedObjects = renderer.alignedObjects;
2726
-
2727
- // First call on instanciate
2728
- if (alignOptions) {
2729
- this.alignOptions = alignOptions;
2730
- this.alignByTranslate = alignByTranslate;
2731
- if (!box || isString(box)) { // boxes other than renderer handle this internally
2732
- this.alignTo = alignTo = box || 'renderer';
2733
- erase(alignedObjects, this); // prevent duplicates, like legendGroup after resize
2734
- alignedObjects.push(this);
2735
- box = null; // reassign it below
2736
- }
2737
-
2738
- // When called on resize, no arguments are supplied
2739
- } else {
2740
- alignOptions = this.alignOptions;
2741
- alignByTranslate = this.alignByTranslate;
2742
- alignTo = this.alignTo;
2743
- }
2744
-
2745
- box = pick(box, renderer[alignTo], renderer);
2746
-
2747
- // Assign variables
2748
- align = alignOptions.align;
2749
- vAlign = alignOptions.verticalAlign;
2750
- x = (box.x || 0) + (alignOptions.x || 0); // default: left align
2751
- y = (box.y || 0) + (alignOptions.y || 0); // default: top align
2752
-
2753
- // Align
2754
- if (align === 'right' || align === 'center') {
2755
- x += (box.width - (alignOptions.width || 0)) /
2756
- { right: 1, center: 2 }[align];
2757
- }
2758
- attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x);
2759
-
2760
-
2761
- // Vertical align
2762
- if (vAlign === 'bottom' || vAlign === 'middle') {
2763
- y += (box.height - (alignOptions.height || 0)) /
2764
- ({ bottom: 1, middle: 2 }[vAlign] || 1);
2765
-
2766
- }
2767
- attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y);
2768
-
2769
- // Animate only if already placed
2770
- this[this.placed ? 'animate' : 'attr'](attribs);
2771
- this.placed = true;
2772
- this.alignAttr = attribs;
2773
-
2774
- return this;
2775
- },
2776
-
2777
- /**
2778
- * Get the bounding box (width, height, x and y) for the element
2779
- */
2780
- getBBox: function () {
2781
- var wrapper = this,
2782
- bBox = wrapper.bBox,
2783
- renderer = wrapper.renderer,
2784
- width,
2785
- height,
2786
- rotation = wrapper.rotation,
2787
- element = wrapper.element,
2788
- styles = wrapper.styles,
2789
- rad = rotation * deg2rad;
2790
-
2791
- if (!bBox) {
2792
- // SVG elements
2793
- if (element.namespaceURI === SVG_NS || renderer.forExport) {
2794
- try { // Fails in Firefox if the container has display: none.
2795
-
2796
- bBox = element.getBBox ?
2797
- // SVG: use extend because IE9 is not allowed to change width and height in case
2798
- // of rotation (below)
2799
- extend({}, element.getBBox()) :
2800
- // Canvas renderer and legacy IE in export mode
2801
- {
2802
- width: element.offsetWidth,
2803
- height: element.offsetHeight
2804
- };
2805
- } catch (e) {}
2806
-
2807
- // If the bBox is not set, the try-catch block above failed. The other condition
2808
- // is for Opera that returns a width of -Infinity on hidden elements.
2809
- if (!bBox || bBox.width < 0) {
2810
- bBox = { width: 0, height: 0 };
2811
- }
2812
-
2813
-
2814
- // VML Renderer or useHTML within SVG
2815
- } else {
2816
-
2817
- bBox = wrapper.htmlGetBBox();
2818
-
2819
- }
2820
-
2821
- // True SVG elements as well as HTML elements in modern browsers using the .useHTML option
2822
- // need to compensated for rotation
2823
- if (renderer.isSVG) {
2824
- width = bBox.width;
2825
- height = bBox.height;
2826
-
2827
- // Workaround for wrong bounding box in IE9 and IE10 (#1101, #1505, #1669)
2828
- if (isIE && styles && styles.fontSize === '11px' && height.toPrecision(3) === '22.7') {
2829
- bBox.height = height = 14;
2830
- }
2831
-
2832
- // Adjust for rotated text
2833
- if (rotation) {
2834
- bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad));
2835
- bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad));
2836
- }
2837
- }
2838
-
2839
- wrapper.bBox = bBox;
2840
- }
2841
- return bBox;
2842
- },
2843
-
2844
- /**
2845
- * Show the element
2846
- */
2847
- show: function () {
2848
- return this.attr({ visibility: VISIBLE });
2849
- },
2850
-
2851
- /**
2852
- * Hide the element
2853
- */
2854
- hide: function () {
2855
- return this.attr({ visibility: HIDDEN });
2856
- },
2857
-
2858
- fadeOut: function (duration) {
2859
- var elemWrapper = this;
2860
- elemWrapper.animate({
2861
- opacity: 0
2862
- }, {
2863
- duration: duration || 150,
2864
- complete: function () {
2865
- elemWrapper.hide();
2866
- }
2867
- });
2868
- },
2869
-
2870
- /**
2871
- * Add the element
2872
- * @param {Object|Undefined} parent Can be an element, an element wrapper or undefined
2873
- * to append the element to the renderer.box.
2874
- */
2875
- add: function (parent) {
2876
-
2877
- var renderer = this.renderer,
2878
- parentWrapper = parent || renderer,
2879
- parentNode = parentWrapper.element || renderer.box,
2880
- childNodes = parentNode.childNodes,
2881
- element = this.element,
2882
- zIndex = attr(element, 'zIndex'),
2883
- otherElement,
2884
- otherZIndex,
2885
- i,
2886
- inserted;
2887
-
2888
- if (parent) {
2889
- this.parentGroup = parent;
2890
- }
2891
-
2892
- // mark as inverted
2893
- this.parentInverted = parent && parent.inverted;
2894
-
2895
- // build formatted text
2896
- if (this.textStr !== undefined) {
2897
- renderer.buildText(this);
2898
- }
2899
-
2900
- // mark the container as having z indexed children
2901
- if (zIndex) {
2902
- parentWrapper.handleZ = true;
2903
- zIndex = pInt(zIndex);
2904
- }
2905
-
2906
- // insert according to this and other elements' zIndex
2907
- if (parentWrapper.handleZ) { // this element or any of its siblings has a z index
2908
- for (i = 0; i < childNodes.length; i++) {
2909
- otherElement = childNodes[i];
2910
- otherZIndex = attr(otherElement, 'zIndex');
2911
- if (otherElement !== element && (
2912
- // insert before the first element with a higher zIndex
2913
- pInt(otherZIndex) > zIndex ||
2914
- // if no zIndex given, insert before the first element with a zIndex
2915
- (!defined(zIndex) && defined(otherZIndex))
2916
-
2917
- )) {
2918
- parentNode.insertBefore(element, otherElement);
2919
- inserted = true;
2920
- break;
2921
- }
2922
- }
2923
- }
2924
-
2925
- // default: append at the end
2926
- if (!inserted) {
2927
- parentNode.appendChild(element);
2928
- }
2929
-
2930
- // mark as added
2931
- this.added = true;
2932
-
2933
- // fire an event for internal hooks
2934
- fireEvent(this, 'add');
2935
-
2936
- return this;
2937
- },
2938
-
2939
- /**
2940
- * Removes a child either by removeChild or move to garbageBin.
2941
- * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
2942
- */
2943
- safeRemoveChild: function (element) {
2944
- var parentNode = element.parentNode;
2945
- if (parentNode) {
2946
- parentNode.removeChild(element);
2947
- }
2948
- },
2949
-
2950
- /**
2951
- * Destroy the element and element wrapper
2952
- */
2953
- destroy: function () {
2954
- var wrapper = this,
2955
- element = wrapper.element || {},
2956
- shadows = wrapper.shadows,
2957
- key,
2958
- i;
2959
-
2960
- // remove events
2961
- element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null;
2962
- stop(wrapper); // stop running animations
2963
-
2964
- if (wrapper.clipPath) {
2965
- wrapper.clipPath = wrapper.clipPath.destroy();
2966
- }
2967
-
2968
- // Destroy stops in case this is a gradient object
2969
- if (wrapper.stops) {
2970
- for (i = 0; i < wrapper.stops.length; i++) {
2971
- wrapper.stops[i] = wrapper.stops[i].destroy();
2972
- }
2973
- wrapper.stops = null;
2974
- }
2975
-
2976
- // remove element
2977
- wrapper.safeRemoveChild(element);
2978
-
2979
- // destroy shadows
2980
- if (shadows) {
2981
- each(shadows, function (shadow) {
2982
- wrapper.safeRemoveChild(shadow);
2983
- });
2984
- }
2985
-
2986
- // remove from alignObjects
2987
- if (wrapper.alignTo) {
2988
- erase(wrapper.renderer.alignedObjects, wrapper);
2989
- }
2990
-
2991
- for (key in wrapper) {
2992
- delete wrapper[key];
2993
- }
2994
-
2995
- return null;
2996
- },
2997
-
2998
- /**
2999
- * Add a shadow to the element. Must be done after the element is added to the DOM
3000
- * @param {Boolean|Object} shadowOptions
3001
- */
3002
- shadow: function (shadowOptions, group, cutOff) {
3003
- var shadows = [],
3004
- i,
3005
- shadow,
3006
- element = this.element,
3007
- strokeWidth,
3008
- shadowWidth,
3009
- shadowElementOpacity,
3010
-
3011
- // compensate for inverted plot area
3012
- transform;
3013
-
3014
-
3015
- if (shadowOptions) {
3016
- shadowWidth = pick(shadowOptions.width, 3);
3017
- shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
3018
- transform = this.parentInverted ?
3019
- '(-1,-1)' :
3020
- '(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')';
3021
- for (i = 1; i <= shadowWidth; i++) {
3022
- shadow = element.cloneNode(0);
3023
- strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
3024
- attr(shadow, {
3025
- 'isShadow': 'true',
3026
- 'stroke': shadowOptions.color || 'black',
3027
- 'stroke-opacity': shadowElementOpacity * i,
3028
- 'stroke-width': strokeWidth,
3029
- 'transform': 'translate' + transform,
3030
- 'fill': NONE
3031
- });
3032
- if (cutOff) {
3033
- attr(shadow, 'height', mathMax(attr(shadow, 'height') - strokeWidth, 0));
3034
- shadow.cutHeight = strokeWidth;
3035
- }
3036
-
3037
- if (group) {
3038
- group.element.appendChild(shadow);
3039
- } else {
3040
- element.parentNode.insertBefore(shadow, element);
3041
- }
3042
-
3043
- shadows.push(shadow);
3044
- }
3045
-
3046
- this.shadows = shadows;
3047
- }
3048
- return this;
3049
-
3050
- }
3051
- };
3052
-
3053
-
3054
- /**
3055
- * The default SVG renderer
3056
- */
3057
- var SVGRenderer = function () {
3058
- this.init.apply(this, arguments);
3059
- };
3060
- SVGRenderer.prototype = {
3061
- Element: SVGElement,
3062
-
3063
- /**
3064
- * Initialize the SVGRenderer
3065
- * @param {Object} container
3066
- * @param {Number} width
3067
- * @param {Number} height
3068
- * @param {Boolean} forExport
3069
- */
3070
- init: function (container, width, height, forExport) {
3071
- var renderer = this,
3072
- loc = location,
3073
- boxWrapper,
3074
- desc;
3075
-
3076
- boxWrapper = renderer.createElement('svg')
3077
- .attr({
3078
- xmlns: SVG_NS,
3079
- version: '1.1'
3080
- });
3081
- container.appendChild(boxWrapper.element);
3082
-
3083
- // object properties
3084
- renderer.isSVG = true;
3085
- renderer.box = boxWrapper.element;
3086
- renderer.boxWrapper = boxWrapper;
3087
- renderer.alignedObjects = [];
3088
-
3089
- // Page url used for internal references. #24, #672, #1070
3090
- renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ?
3091
- loc.href
3092
- .replace(/#.*?$/, '') // remove the hash
3093
- .replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes
3094
- .replace(/ /g, '%20') : // replace spaces (needed for Safari only)
3095
- '';
3096
-
3097
- // Add description
3098
- desc = this.createElement('desc').add();
3099
- desc.element.appendChild(doc.createTextNode('Created with ' + PRODUCT + ' ' + VERSION));
3100
-
3101
-
3102
- renderer.defs = this.createElement('defs').add();
3103
- renderer.forExport = forExport;
3104
- renderer.gradients = {}; // Object where gradient SvgElements are stored
3105
-
3106
- renderer.setSize(width, height, false);
3107
-
3108
-
3109
-
3110
- // Issue 110 workaround:
3111
- // In Firefox, if a div is positioned by percentage, its pixel position may land
3112
- // between pixels. The container itself doesn't display this, but an SVG element
3113
- // inside this container will be drawn at subpixel precision. In order to draw
3114
- // sharp lines, this must be compensated for. This doesn't seem to work inside
3115
- // iframes though (like in jsFiddle).
3116
- var subPixelFix, rect;
3117
- if (isFirefox && container.getBoundingClientRect) {
3118
- renderer.subPixelFix = subPixelFix = function () {
3119
- css(container, { left: 0, top: 0 });
3120
- rect = container.getBoundingClientRect();
3121
- css(container, {
3122
- left: (mathCeil(rect.left) - rect.left) + PX,
3123
- top: (mathCeil(rect.top) - rect.top) + PX
3124
- });
3125
- };
3126
-
3127
- // run the fix now
3128
- subPixelFix();
3129
-
3130
- // run it on resize
3131
- addEvent(win, 'resize', subPixelFix);
3132
- }
3133
- },
3134
-
3135
- /**
3136
- * Detect whether the renderer is hidden. This happens when one of the parent elements
3137
- * has display: none. #608.
3138
- */
3139
- isHidden: function () {
3140
- return !this.boxWrapper.getBBox().width;
3141
- },
3142
-
3143
- /**
3144
- * Destroys the renderer and its allocated members.
3145
- */
3146
- destroy: function () {
3147
- var renderer = this,
3148
- rendererDefs = renderer.defs;
3149
- renderer.box = null;
3150
- renderer.boxWrapper = renderer.boxWrapper.destroy();
3151
-
3152
- // Call destroy on all gradient elements
3153
- destroyObjectProperties(renderer.gradients || {});
3154
- renderer.gradients = null;
3155
-
3156
- // Defs are null in VMLRenderer
3157
- // Otherwise, destroy them here.
3158
- if (rendererDefs) {
3159
- renderer.defs = rendererDefs.destroy();
3160
- }
3161
-
3162
- // Remove sub pixel fix handler
3163
- // We need to check that there is a handler, otherwise all functions that are registered for event 'resize' are removed
3164
- // See issue #982
3165
- if (renderer.subPixelFix) {
3166
- removeEvent(win, 'resize', renderer.subPixelFix);
3167
- }
3168
-
3169
- renderer.alignedObjects = null;
3170
-
3171
- return null;
3172
- },
3173
-
3174
- /**
3175
- * Create a wrapper for an SVG element
3176
- * @param {Object} nodeName
3177
- */
3178
- createElement: function (nodeName) {
3179
- var wrapper = new this.Element();
3180
- wrapper.init(this, nodeName);
3181
- return wrapper;
3182
- },
3183
-
3184
- /**
3185
- * Dummy function for use in canvas renderer
3186
- */
3187
- draw: function () {},
3188
-
3189
- /**
3190
- * Parse a simple HTML string into SVG tspans
3191
- *
3192
- * @param {Object} textNode The parent text SVG node
3193
- */
3194
- buildText: function (wrapper) {
3195
- var textNode = wrapper.element,
3196
- renderer = this,
3197
- forExport = renderer.forExport,
3198
- lines = pick(wrapper.textStr, '').toString()
3199
- .replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
3200
- .replace(/<(i|em)>/g, '<span style="font-style:italic">')
3201
- .replace(/<a/g, '<span')
3202
- .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
3203
- .split(/<br.*?>/g),
3204
- childNodes = textNode.childNodes,
3205
- styleRegex = /style="([^"]+)"/,
3206
- hrefRegex = /href="([^"]+)"/,
3207
- parentX = attr(textNode, 'x'),
3208
- textStyles = wrapper.styles,
3209
- width = textStyles && textStyles.width && pInt(textStyles.width),
3210
- textLineHeight = textStyles && textStyles.lineHeight,
3211
- i = childNodes.length;
3212
-
3213
- /// remove old text
3214
- while (i--) {
3215
- textNode.removeChild(childNodes[i]);
3216
- }
3217
-
3218
- if (width && !wrapper.added) {
3219
- this.box.appendChild(textNode); // attach it to the DOM to read offset width
3220
- }
3221
-
3222
- // remove empty line at end
3223
- if (lines[lines.length - 1] === '') {
3224
- lines.pop();
3225
- }
3226
-
3227
- // build the lines
3228
- each(lines, function (line, lineNo) {
3229
- var spans, spanNo = 0;
3230
-
3231
- line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||');
3232
- spans = line.split('|||');
3233
-
3234
- each(spans, function (span) {
3235
- if (span !== '' || spans.length === 1) {
3236
- var attributes = {},
3237
- tspan = doc.createElementNS(SVG_NS, 'tspan'),
3238
- spanStyle; // #390
3239
- if (styleRegex.test(span)) {
3240
- spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2');
3241
- attr(tspan, 'style', spanStyle);
3242
- }
3243
- if (hrefRegex.test(span) && !forExport) { // Not for export - #1529
3244
- attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"');
3245
- css(tspan, { cursor: 'pointer' });
3246
- }
3247
-
3248
- span = (span.replace(/<(.|\n)*?>/g, '') || ' ')
3249
- .replace(/&lt;/g, '<')
3250
- .replace(/&gt;/g, '>');
3251
-
3252
- // add the text node
3253
- tspan.appendChild(doc.createTextNode(span));
3254
-
3255
- if (!spanNo) { // first span in a line, align it to the left
3256
- attributes.x = parentX;
3257
- } else {
3258
- attributes.dx = 0; // #16
3259
- }
3260
-
3261
- // add attributes
3262
- attr(tspan, attributes);
3263
-
3264
- // first span on subsequent line, add the line height
3265
- if (!spanNo && lineNo) {
3266
-
3267
- // allow getting the right offset height in exporting in IE
3268
- if (!hasSVG && forExport) {
3269
- css(tspan, { display: 'block' });
3270
- }
3271
-
3272
- // Set the line height based on the font size of either
3273
- // the text element or the tspan element
3274
- attr(
3275
- tspan,
3276
- 'dy',
3277
- textLineHeight || renderer.fontMetrics(
3278
- /px$/.test(tspan.style.fontSize) ?
3279
- tspan.style.fontSize :
3280
- textStyles.fontSize
3281
- ).h,
3282
- // Safari 6.0.2 - too optimized for its own good (#1539)
3283
- // TODO: revisit this with future versions of Safari
3284
- isWebKit && tspan.offsetHeight
3285
- );
3286
- }
3287
-
3288
- // Append it
3289
- textNode.appendChild(tspan);
3290
-
3291
- spanNo++;
3292
-
3293
- // check width and apply soft breaks
3294
- if (width) {
3295
- var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
3296
- tooLong,
3297
- actualWidth,
3298
- rest = [];
3299
-
3300
- while (words.length || rest.length) {
3301
- delete wrapper.bBox; // delete cache
3302
- actualWidth = wrapper.getBBox().width;
3303
- tooLong = actualWidth > width;
3304
- if (!tooLong || words.length === 1) { // new line needed
3305
- words = rest;
3306
- rest = [];
3307
- if (words.length) {
3308
- tspan = doc.createElementNS(SVG_NS, 'tspan');
3309
- attr(tspan, {
3310
- dy: textLineHeight || 16,
3311
- x: parentX
3312
- });
3313
- if (spanStyle) { // #390
3314
- attr(tspan, 'style', spanStyle);
3315
- }
3316
- textNode.appendChild(tspan);
3317
-
3318
- if (actualWidth > width) { // a single word is pressing it out
3319
- width = actualWidth;
3320
- }
3321
- }
3322
- } else { // append to existing line tspan
3323
- tspan.removeChild(tspan.firstChild);
3324
- rest.unshift(words.pop());
3325
- }
3326
- if (words.length) {
3327
- tspan.appendChild(doc.createTextNode(words.join(' ').replace(/- /g, '-')));
3328
- }
3329
- }
3330
- }
3331
- }
3332
- });
3333
- });
3334
- },
3335
-
3336
- /**
3337
- * Create a button with preset states
3338
- * @param {String} text
3339
- * @param {Number} x
3340
- * @param {Number} y
3341
- * @param {Function} callback
3342
- * @param {Object} normalState
3343
- * @param {Object} hoverState
3344
- * @param {Object} pressedState
3345
- */
3346
- button: function (text, x, y, callback, normalState, hoverState, pressedState) {
3347
- var label = this.label(text, x, y, null, null, null, null, null, 'button'),
3348
- curState = 0,
3349
- stateOptions,
3350
- stateStyle,
3351
- normalStyle,
3352
- hoverStyle,
3353
- pressedStyle,
3354
- STYLE = 'style',
3355
- verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 };
3356
-
3357
- // Normal state - prepare the attributes
3358
- normalState = merge({
3359
- 'stroke-width': 1,
3360
- stroke: '#CCCCCC',
3361
- fill: {
3362
- linearGradient: verticalGradient,
3363
- stops: [
3364
- [0, '#FEFEFE'],
3365
- [1, '#F6F6F6']
3366
- ]
3367
- },
3368
- r: 2,
3369
- padding: 5,
3370
- style: {
3371
- color: 'black'
3372
- }
3373
- }, normalState);
3374
- normalStyle = normalState[STYLE];
3375
- delete normalState[STYLE];
3376
-
3377
- // Hover state
3378
- hoverState = merge(normalState, {
3379
- stroke: '#68A',
3380
- fill: {
3381
- linearGradient: verticalGradient,
3382
- stops: [
3383
- [0, '#FFF'],
3384
- [1, '#ACF']
3385
- ]
3386
- }
3387
- }, hoverState);
3388
- hoverStyle = hoverState[STYLE];
3389
- delete hoverState[STYLE];
3390
-
3391
- // Pressed state
3392
- pressedState = merge(normalState, {
3393
- stroke: '#68A',
3394
- fill: {
3395
- linearGradient: verticalGradient,
3396
- stops: [
3397
- [0, '#9BD'],
3398
- [1, '#CDF']
3399
- ]
3400
- }
3401
- }, pressedState);
3402
- pressedStyle = pressedState[STYLE];
3403
- delete pressedState[STYLE];
3404
-
3405
- // add the events
3406
- addEvent(label.element, 'mouseenter', function () {
3407
- label.attr(hoverState)
3408
- .css(hoverStyle);
3409
- });
3410
- addEvent(label.element, 'mouseleave', function () {
3411
- stateOptions = [normalState, hoverState, pressedState][curState];
3412
- stateStyle = [normalStyle, hoverStyle, pressedStyle][curState];
3413
- label.attr(stateOptions)
3414
- .css(stateStyle);
3415
- });
3416
-
3417
- label.setState = function (state) {
3418
- curState = state;
3419
- if (!state) {
3420
- label.attr(normalState)
3421
- .css(normalStyle);
3422
- } else if (state === 2) {
3423
- label.attr(pressedState)
3424
- .css(pressedStyle);
3425
- }
3426
- };
3427
-
3428
- return label
3429
- .on('click', function () {
3430
- callback.call(label);
3431
- })
3432
- .attr(normalState)
3433
- .css(extend({ cursor: 'default' }, normalStyle));
3434
- },
3435
-
3436
- /**
3437
- * Make a straight line crisper by not spilling out to neighbour pixels
3438
- * @param {Array} points
3439
- * @param {Number} width
3440
- */
3441
- crispLine: function (points, width) {
3442
- // points format: [M, 0, 0, L, 100, 0]
3443
- // normalize to a crisp line
3444
- if (points[1] === points[4]) {
3445
- // Substract due to #1129. Now bottom and left axis gridlines behave the same.
3446
- points[1] = points[4] = mathRound(points[1]) - (width % 2 / 2);
3447
- }
3448
- if (points[2] === points[5]) {
3449
- points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2);
3450
- }
3451
- return points;
3452
- },
3453
-
3454
-
3455
- /**
3456
- * Draw a path
3457
- * @param {Array} path An SVG path in array form
3458
- */
3459
- path: function (path) {
3460
- var attr = {
3461
- fill: NONE
3462
- };
3463
- if (isArray(path)) {
3464
- attr.d = path;
3465
- } else if (isObject(path)) { // attributes
3466
- extend(attr, path);
3467
- }
3468
- return this.createElement('path').attr(attr);
3469
- },
3470
-
3471
- /**
3472
- * Draw and return an SVG circle
3473
- * @param {Number} x The x position
3474
- * @param {Number} y The y position
3475
- * @param {Number} r The radius
3476
- */
3477
- circle: function (x, y, r) {
3478
- var attr = isObject(x) ?
3479
- x :
3480
- {
3481
- x: x,
3482
- y: y,
3483
- r: r
3484
- };
3485
-
3486
- return this.createElement('circle').attr(attr);
3487
- },
3488
-
3489
- /**
3490
- * Draw and return an arc
3491
- * @param {Number} x X position
3492
- * @param {Number} y Y position
3493
- * @param {Number} r Radius
3494
- * @param {Number} innerR Inner radius like used in donut charts
3495
- * @param {Number} start Starting angle
3496
- * @param {Number} end Ending angle
3497
- */
3498
- arc: function (x, y, r, innerR, start, end) {
3499
- // arcs are defined as symbols for the ability to set
3500
- // attributes in attr and animate
3501
-
3502
- if (isObject(x)) {
3503
- y = x.y;
3504
- r = x.r;
3505
- innerR = x.innerR;
3506
- start = x.start;
3507
- end = x.end;
3508
- x = x.x;
3509
- }
3510
- return this.symbol('arc', x || 0, y || 0, r || 0, r || 0, {
3511
- innerR: innerR || 0,
3512
- start: start || 0,
3513
- end: end || 0
3514
- });
3515
- },
3516
-
3517
- /**
3518
- * Draw and return a rectangle
3519
- * @param {Number} x Left position
3520
- * @param {Number} y Top position
3521
- * @param {Number} width
3522
- * @param {Number} height
3523
- * @param {Number} r Border corner radius
3524
- * @param {Number} strokeWidth A stroke width can be supplied to allow crisp drawing
3525
- */
3526
- rect: function (x, y, width, height, r, strokeWidth) {
3527
-
3528
- r = isObject(x) ? x.r : r;
3529
-
3530
- var wrapper = this.createElement('rect').attr({
3531
- rx: r,
3532
- ry: r,
3533
- fill: NONE
3534
- });
3535
- return wrapper.attr(
3536
- isObject(x) ?
3537
- x :
3538
- // do not crispify when an object is passed in (as in column charts)
3539
- wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0))
3540
- );
3541
- },
3542
-
3543
- /**
3544
- * Resize the box and re-align all aligned elements
3545
- * @param {Object} width
3546
- * @param {Object} height
3547
- * @param {Boolean} animate
3548
- *
3549
- */
3550
- setSize: function (width, height, animate) {
3551
- var renderer = this,
3552
- alignedObjects = renderer.alignedObjects,
3553
- i = alignedObjects.length;
3554
-
3555
- renderer.width = width;
3556
- renderer.height = height;
3557
-
3558
- renderer.boxWrapper[pick(animate, true) ? 'animate' : 'attr']({
3559
- width: width,
3560
- height: height
3561
- });
3562
-
3563
- while (i--) {
3564
- alignedObjects[i].align();
3565
- }
3566
- },
3567
-
3568
- /**
3569
- * Create a group
3570
- * @param {String} name The group will be given a class name of 'highcharts-{name}'.
3571
- * This can be used for styling and scripting.
3572
- */
3573
- g: function (name) {
3574
- var elem = this.createElement('g');
3575
- return defined(name) ? elem.attr({ 'class': PREFIX + name }) : elem;
3576
- },
3577
-
3578
- /**
3579
- * Display an image
3580
- * @param {String} src
3581
- * @param {Number} x
3582
- * @param {Number} y
3583
- * @param {Number} width
3584
- * @param {Number} height
3585
- */
3586
- image: function (src, x, y, width, height) {
3587
- var attribs = {
3588
- preserveAspectRatio: NONE
3589
- },
3590
- elemWrapper;
3591
-
3592
- // optional properties
3593
- if (arguments.length > 1) {
3594
- extend(attribs, {
3595
- x: x,
3596
- y: y,
3597
- width: width,
3598
- height: height
3599
- });
3600
- }
3601
-
3602
- elemWrapper = this.createElement('image').attr(attribs);
3603
-
3604
- // set the href in the xlink namespace
3605
- if (elemWrapper.element.setAttributeNS) {
3606
- elemWrapper.element.setAttributeNS('http://www.w3.org/1999/xlink',
3607
- 'href', src);
3608
- } else {
3609
- // could be exporting in IE
3610
- // using href throws "not supported" in ie7 and under, requries regex shim to fix later
3611
- elemWrapper.element.setAttribute('hc-svg-href', src);
3612
- }
3613
-
3614
- return elemWrapper;
3615
- },
3616
-
3617
- /**
3618
- * Draw a symbol out of pre-defined shape paths from the namespace 'symbol' object.
3619
- *
3620
- * @param {Object} symbol
3621
- * @param {Object} x
3622
- * @param {Object} y
3623
- * @param {Object} radius
3624
- * @param {Object} options
3625
- */
3626
- symbol: function (symbol, x, y, width, height, options) {
3627
-
3628
- var obj,
3629
-
3630
- // get the symbol definition function
3631
- symbolFn = this.symbols[symbol],
3632
-
3633
- // check if there's a path defined for this symbol
3634
- path = symbolFn && symbolFn(
3635
- mathRound(x),
3636
- mathRound(y),
3637
- width,
3638
- height,
3639
- options
3640
- ),
3641
-
3642
- imageElement,
3643
- imageRegex = /^url\((.*?)\)$/,
3644
- imageSrc,
3645
- imageSize,
3646
- centerImage;
3647
-
3648
- if (path) {
3649
-
3650
- obj = this.path(path);
3651
- // expando properties for use in animate and attr
3652
- extend(obj, {
3653
- symbolName: symbol,
3654
- x: x,
3655
- y: y,
3656
- width: width,
3657
- height: height
3658
- });
3659
- if (options) {
3660
- extend(obj, options);
3661
- }
3662
-
3663
-
3664
- // image symbols
3665
- } else if (imageRegex.test(symbol)) {
3666
-
3667
- // On image load, set the size and position
3668
- centerImage = function (img, size) {
3669
- if (img.element) { // it may be destroyed in the meantime (#1390)
3670
- img.attr({
3671
- width: size[0],
3672
- height: size[1]
3673
- });
3674
-
3675
- if (!img.alignByTranslate) { // #185
3676
- img.translate(
3677
- mathRound((width - size[0]) / 2), // #1378
3678
- mathRound((height - size[1]) / 2)
3679
- );
3680
- }
3681
- }
3682
- };
3683
-
3684
- imageSrc = symbol.match(imageRegex)[1];
3685
- imageSize = symbolSizes[imageSrc];
3686
-
3687
- // Ireate the image synchronously, add attribs async
3688
- obj = this.image(imageSrc)
3689
- .attr({
3690
- x: x,
3691
- y: y
3692
- });
3693
- obj.isImg = true;
3694
-
3695
- if (imageSize) {
3696
- centerImage(obj, imageSize);
3697
- } else {
3698
- // Initialize image to be 0 size so export will still function if there's no cached sizes.
3699
- //
3700
- obj.attr({ width: 0, height: 0 });
3701
-
3702
- // Create a dummy JavaScript image to get the width and height. Due to a bug in IE < 8,
3703
- // the created element must be assigned to a variable in order to load (#292).
3704
- imageElement = createElement('img', {
3705
- onload: function () {
3706
- centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]);
3707
- },
3708
- src: imageSrc
3709
- });
3710
- }
3711
- }
3712
-
3713
- return obj;
3714
- },
3715
-
3716
- /**
3717
- * An extendable collection of functions for defining symbol paths.
3718
- */
3719
- symbols: {
3720
- 'circle': function (x, y, w, h) {
3721
- var cpw = 0.166 * w;
3722
- return [
3723
- M, x + w / 2, y,
3724
- 'C', x + w + cpw, y, x + w + cpw, y + h, x + w / 2, y + h,
3725
- 'C', x - cpw, y + h, x - cpw, y, x + w / 2, y,
3726
- 'Z'
3727
- ];
3728
- },
3729
-
3730
- 'square': function (x, y, w, h) {
3731
- return [
3732
- M, x, y,
3733
- L, x + w, y,
3734
- x + w, y + h,
3735
- x, y + h,
3736
- 'Z'
3737
- ];
3738
- },
3739
-
3740
- 'triangle': function (x, y, w, h) {
3741
- return [
3742
- M, x + w / 2, y,
3743
- L, x + w, y + h,
3744
- x, y + h,
3745
- 'Z'
3746
- ];
3747
- },
3748
-
3749
- 'triangle-down': function (x, y, w, h) {
3750
- return [
3751
- M, x, y,
3752
- L, x + w, y,
3753
- x + w / 2, y + h,
3754
- 'Z'
3755
- ];
3756
- },
3757
- 'diamond': function (x, y, w, h) {
3758
- return [
3759
- M, x + w / 2, y,
3760
- L, x + w, y + h / 2,
3761
- x + w / 2, y + h,
3762
- x, y + h / 2,
3763
- 'Z'
3764
- ];
3765
- },
3766
- 'arc': function (x, y, w, h, options) {
3767
- var start = options.start,
3768
- radius = options.r || w || h,
3769
- end = options.end - 0.001, // to prevent cos and sin of start and end from becoming equal on 360 arcs (related: #1561)
3770
- innerRadius = options.innerR,
3771
- open = options.open,
3772
- cosStart = mathCos(start),
3773
- sinStart = mathSin(start),
3774
- cosEnd = mathCos(end),
3775
- sinEnd = mathSin(end),
3776
- longArc = options.end - start < mathPI ? 0 : 1;
3777
-
3778
- return [
3779
- M,
3780
- x + radius * cosStart,
3781
- y + radius * sinStart,
3782
- 'A', // arcTo
3783
- radius, // x radius
3784
- radius, // y radius
3785
- 0, // slanting
3786
- longArc, // long or short arc
3787
- 1, // clockwise
3788
- x + radius * cosEnd,
3789
- y + radius * sinEnd,
3790
- open ? M : L,
3791
- x + innerRadius * cosEnd,
3792
- y + innerRadius * sinEnd,
3793
- 'A', // arcTo
3794
- innerRadius, // x radius
3795
- innerRadius, // y radius
3796
- 0, // slanting
3797
- longArc, // long or short arc
3798
- 0, // clockwise
3799
- x + innerRadius * cosStart,
3800
- y + innerRadius * sinStart,
3801
-
3802
- open ? '' : 'Z' // close
3803
- ];
3804
- }
3805
- },
3806
-
3807
- /**
3808
- * Define a clipping rectangle
3809
- * @param {String} id
3810
- * @param {Number} x
3811
- * @param {Number} y
3812
- * @param {Number} width
3813
- * @param {Number} height
3814
- */
3815
- clipRect: function (x, y, width, height) {
3816
- var wrapper,
3817
- id = PREFIX + idCounter++,
3818
-
3819
- clipPath = this.createElement('clipPath').attr({
3820
- id: id
3821
- }).add(this.defs);
3822
-
3823
- wrapper = this.rect(x, y, width, height, 0).add(clipPath);
3824
- wrapper.id = id;
3825
- wrapper.clipPath = clipPath;
3826
-
3827
- return wrapper;
3828
- },
3829
-
3830
-
3831
- /**
3832
- * Take a color and return it if it's a string, make it a gradient if it's a
3833
- * gradient configuration object. Prior to Highstock, an array was used to define
3834
- * a linear gradient with pixel positions relative to the SVG. In newer versions
3835
- * we change the coordinates to apply relative to the shape, using coordinates
3836
- * 0-1 within the shape. To preserve backwards compatibility, linearGradient
3837
- * in this definition is an object of x1, y1, x2 and y2.
3838
- *
3839
- * @param {Object} color The color or config object
3840
- */
3841
- color: function (color, elem, prop) {
3842
- var renderer = this,
3843
- colorObject,
3844
- regexRgba = /^rgba/,
3845
- gradName,
3846
- gradAttr,
3847
- gradients,
3848
- gradientObject,
3849
- stops,
3850
- stopColor,
3851
- stopOpacity,
3852
- radialReference,
3853
- n,
3854
- id,
3855
- key = [];
3856
-
3857
- // Apply linear or radial gradients
3858
- if (color && color.linearGradient) {
3859
- gradName = 'linearGradient';
3860
- } else if (color && color.radialGradient) {
3861
- gradName = 'radialGradient';
3862
- }
3863
-
3864
- if (gradName) {
3865
- gradAttr = color[gradName];
3866
- gradients = renderer.gradients;
3867
- stops = color.stops;
3868
- radialReference = elem.radialReference;
3869
-
3870
- // Keep < 2.2 kompatibility
3871
- if (isArray(gradAttr)) {
3872
- color[gradName] = gradAttr = {
3873
- x1: gradAttr[0],
3874
- y1: gradAttr[1],
3875
- x2: gradAttr[2],
3876
- y2: gradAttr[3],
3877
- gradientUnits: 'userSpaceOnUse'
3878
- };
3879
- }
3880
-
3881
- // Correct the radial gradient for the radial reference system
3882
- if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) {
3883
- gradAttr = merge(gradAttr, {
3884
- cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2],
3885
- cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2],
3886
- r: gradAttr.r * radialReference[2],
3887
- gradientUnits: 'userSpaceOnUse'
3888
- });
3889
- }
3890
-
3891
- // Build the unique key to detect whether we need to create a new element (#1282)
3892
- for (n in gradAttr) {
3893
- if (n !== 'id') {
3894
- key.push(n, gradAttr[n]);
3895
- }
3896
- }
3897
- for (n in stops) {
3898
- key.push(stops[n]);
3899
- }
3900
- key = key.join(',');
3901
-
3902
- // Check if a gradient object with the same config object is created within this renderer
3903
- if (gradients[key]) {
3904
- id = gradients[key].id;
3905
-
3906
- } else {
3907
-
3908
- // Set the id and create the element
3909
- gradAttr.id = id = PREFIX + idCounter++;
3910
- gradients[key] = gradientObject = renderer.createElement(gradName)
3911
- .attr(gradAttr)
3912
- .add(renderer.defs);
3913
-
3914
-
3915
- // The gradient needs to keep a list of stops to be able to destroy them
3916
- gradientObject.stops = [];
3917
- each(stops, function (stop) {
3918
- var stopObject;
3919
- if (regexRgba.test(stop[1])) {
3920
- colorObject = Color(stop[1]);
3921
- stopColor = colorObject.get('rgb');
3922
- stopOpacity = colorObject.get('a');
3923
- } else {
3924
- stopColor = stop[1];
3925
- stopOpacity = 1;
3926
- }
3927
- stopObject = renderer.createElement('stop').attr({
3928
- offset: stop[0],
3929
- 'stop-color': stopColor,
3930
- 'stop-opacity': stopOpacity
3931
- }).add(gradientObject);
3932
-
3933
- // Add the stop element to the gradient
3934
- gradientObject.stops.push(stopObject);
3935
- });
3936
- }
3937
-
3938
- // Return the reference to the gradient object
3939
- return 'url(' + renderer.url + '#' + id + ')';
3940
-
3941
- // Webkit and Batik can't show rgba.
3942
- } else if (regexRgba.test(color)) {
3943
- colorObject = Color(color);
3944
- attr(elem, prop + '-opacity', colorObject.get('a'));
3945
-
3946
- return colorObject.get('rgb');
3947
-
3948
-
3949
- } else {
3950
- // Remove the opacity attribute added above. Does not throw if the attribute is not there.
3951
- elem.removeAttribute(prop + '-opacity');
3952
-
3953
- return color;
3954
- }
3955
-
3956
- },
3957
-
3958
-
3959
- /**
3960
- * Add text to the SVG object
3961
- * @param {String} str
3962
- * @param {Number} x Left position
3963
- * @param {Number} y Top position
3964
- * @param {Boolean} useHTML Use HTML to render the text
3965
- */
3966
- text: function (str, x, y, useHTML) {
3967
-
3968
- // declare variables
3969
- var renderer = this,
3970
- defaultChartStyle = defaultOptions.chart.style,
3971
- fakeSVG = useCanVG || (!hasSVG && renderer.forExport),
3972
- wrapper;
3973
-
3974
- if (useHTML && !renderer.forExport) {
3975
- return renderer.html(str, x, y);
3976
- }
3977
-
3978
- x = mathRound(pick(x, 0));
3979
- y = mathRound(pick(y, 0));
3980
-
3981
- wrapper = renderer.createElement('text')
3982
- .attr({
3983
- x: x,
3984
- y: y,
3985
- text: str
3986
- })
3987
- .css({
3988
- fontFamily: defaultChartStyle.fontFamily,
3989
- fontSize: defaultChartStyle.fontSize
3990
- });
3991
-
3992
- // Prevent wrapping from creating false offsetWidths in export in legacy IE (#1079, #1063)
3993
- if (fakeSVG) {
3994
- wrapper.css({
3995
- position: ABSOLUTE
3996
- });
3997
- }
3998
-
3999
- wrapper.x = x;
4000
- wrapper.y = y;
4001
- return wrapper;
4002
- },
4003
-
4004
-
4005
- /**
4006
- * Create HTML text node. This is used by the VML renderer as well as the SVG
4007
- * renderer through the useHTML option.
4008
- *
4009
- * @param {String} str
4010
- * @param {Number} x
4011
- * @param {Number} y
4012
- */
4013
- html: function (str, x, y) {
4014
- var defaultChartStyle = defaultOptions.chart.style,
4015
- wrapper = this.createElement('span'),
4016
- attrSetters = wrapper.attrSetters,
4017
- element = wrapper.element,
4018
- renderer = wrapper.renderer;
4019
-
4020
- // Text setter
4021
- attrSetters.text = function (value) {
4022
- if (value !== element.innerHTML) {
4023
- delete this.bBox;
4024
- }
4025
- element.innerHTML = value;
4026
- return false;
4027
- };
4028
-
4029
- // Various setters which rely on update transform
4030
- attrSetters.x = attrSetters.y = attrSetters.align = function (value, key) {
4031
- if (key === 'align') {
4032
- key = 'textAlign'; // Do not overwrite the SVGElement.align method. Same as VML.
4033
- }
4034
- wrapper[key] = value;
4035
- wrapper.htmlUpdateTransform();
4036
- return false;
4037
- };
4038
-
4039
- // Set the default attributes
4040
- wrapper.attr({
4041
- text: str,
4042
- x: mathRound(x),
4043
- y: mathRound(y)
4044
- })
4045
- .css({
4046
- position: ABSOLUTE,
4047
- whiteSpace: 'nowrap',
4048
- fontFamily: defaultChartStyle.fontFamily,
4049
- fontSize: defaultChartStyle.fontSize
4050
- });
4051
-
4052
- // Use the HTML specific .css method
4053
- wrapper.css = wrapper.htmlCss;
4054
-
4055
- // This is specific for HTML within SVG
4056
- if (renderer.isSVG) {
4057
- wrapper.add = function (svgGroupWrapper) {
4058
-
4059
- var htmlGroup,
4060
- container = renderer.box.parentNode,
4061
- parentGroup,
4062
- parents = [];
4063
-
4064
- // Create a mock group to hold the HTML elements
4065
- if (svgGroupWrapper) {
4066
- htmlGroup = svgGroupWrapper.div;
4067
- if (!htmlGroup) {
4068
-
4069
- // Read the parent chain into an array and read from top down
4070
- parentGroup = svgGroupWrapper;
4071
- while (parentGroup) {
4072
-
4073
- parents.push(parentGroup);
4074
-
4075
- // Move up to the next parent group
4076
- parentGroup = parentGroup.parentGroup;
4077
- }
4078
-
4079
- // Ensure dynamically updating position when any parent is translated
4080
- each(parents.reverse(), function (parentGroup) {
4081
- var htmlGroupStyle;
4082
-
4083
- // Create a HTML div and append it to the parent div to emulate
4084
- // the SVG group structure
4085
- htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, {
4086
- className: attr(parentGroup.element, 'class')
4087
- }, {
4088
- position: ABSOLUTE,
4089
- left: (parentGroup.translateX || 0) + PX,
4090
- top: (parentGroup.translateY || 0) + PX
4091
- }, htmlGroup || container); // the top group is appended to container
4092
-
4093
- // Shortcut
4094
- htmlGroupStyle = htmlGroup.style;
4095
-
4096
- // Set listeners to update the HTML div's position whenever the SVG group
4097
- // position is changed
4098
- extend(parentGroup.attrSetters, {
4099
- translateX: function (value) {
4100
- htmlGroupStyle.left = value + PX;
4101
- },
4102
- translateY: function (value) {
4103
- htmlGroupStyle.top = value + PX;
4104
- },
4105
- visibility: function (value, key) {
4106
- htmlGroupStyle[key] = value;
4107
- }
4108
- });
4109
- });
4110
-
4111
- }
4112
- } else {
4113
- htmlGroup = container;
4114
- }
4115
-
4116
- htmlGroup.appendChild(element);
4117
-
4118
- // Shared with VML:
4119
- wrapper.added = true;
4120
- if (wrapper.alignOnAdd) {
4121
- wrapper.htmlUpdateTransform();
4122
- }
4123
-
4124
- return wrapper;
4125
- };
4126
- }
4127
- return wrapper;
4128
- },
4129
-
4130
- /**
4131
- * Utility to return the baseline offset and total line height from the font size
4132
- */
4133
- fontMetrics: function (fontSize) {
4134
- fontSize = pInt(fontSize || 11);
4135
-
4136
- // Empirical values found by comparing font size and bounding box height.
4137
- // Applies to the default font family. http://jsfiddle.net/highcharts/7xvn7/
4138
- var lineHeight = fontSize < 24 ? fontSize + 4 : mathRound(fontSize * 1.2),
4139
- baseline = mathRound(lineHeight * 0.8);
4140
-
4141
- return {
4142
- h: lineHeight,
4143
- b: baseline
4144
- };
4145
- },
4146
-
4147
- /**
4148
- * Add a label, a text item that can hold a colored or gradient background
4149
- * as well as a border and shadow.
4150
- * @param {string} str
4151
- * @param {Number} x
4152
- * @param {Number} y
4153
- * @param {String} shape
4154
- * @param {Number} anchorX In case the shape has a pointer, like a flag, this is the
4155
- * coordinates it should be pinned to
4156
- * @param {Number} anchorY
4157
- * @param {Boolean} baseline Whether to position the label relative to the text baseline,
4158
- * like renderer.text, or to the upper border of the rectangle.
4159
- * @param {String} className Class name for the group
4160
- */
4161
- label: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
4162
-
4163
- var renderer = this,
4164
- wrapper = renderer.g(className),
4165
- text = renderer.text('', 0, 0, useHTML)
4166
- .attr({
4167
- zIndex: 1
4168
- }),
4169
- //.add(wrapper),
4170
- box,
4171
- bBox,
4172
- alignFactor = 0,
4173
- padding = 3,
4174
- paddingLeft = 0,
4175
- width,
4176
- height,
4177
- wrapperX,
4178
- wrapperY,
4179
- crispAdjust = 0,
4180
- deferredAttr = {},
4181
- baselineOffset,
4182
- attrSetters = wrapper.attrSetters,
4183
- needsBox;
4184
-
4185
- /**
4186
- * This function runs after the label is added to the DOM (when the bounding box is
4187
- * available), and after the text of the label is updated to detect the new bounding
4188
- * box and reflect it in the border box.
4189
- */
4190
- function updateBoxSize() {
4191
- var boxX,
4192
- boxY,
4193
- style = text.element.style;
4194
-
4195
- bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) &&
4196
- text.getBBox();
4197
- wrapper.width = (width || bBox.width || 0) + 2 * padding + paddingLeft;
4198
- wrapper.height = (height || bBox.height || 0) + 2 * padding;
4199
-
4200
- // update the label-scoped y offset
4201
- baselineOffset = padding + renderer.fontMetrics(style && style.fontSize).b;
4202
-
4203
- if (needsBox) {
4204
-
4205
- // create the border box if it is not already present
4206
- if (!box) {
4207
- boxX = mathRound(-alignFactor * padding);
4208
- boxY = baseline ? -baselineOffset : 0;
4209
-
4210
- wrapper.box = box = shape ?
4211
- renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height) :
4212
- renderer.rect(boxX, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
4213
- box.add(wrapper);
4214
- }
4215
-
4216
- // apply the box attributes
4217
- if (!box.isImg) { // #1630
4218
- box.attr(merge({
4219
- width: wrapper.width,
4220
- height: wrapper.height
4221
- }, deferredAttr));
4222
- }
4223
- deferredAttr = null;
4224
- }
4225
- }
4226
-
4227
- /**
4228
- * This function runs after setting text or padding, but only if padding is changed
4229
- */
4230
- function updateTextPadding() {
4231
- var styles = wrapper.styles,
4232
- textAlign = styles && styles.textAlign,
4233
- x = paddingLeft + padding * (1 - alignFactor),
4234
- y;
4235
-
4236
- // determin y based on the baseline
4237
- y = baseline ? 0 : baselineOffset;
4238
-
4239
- // compensate for alignment
4240
- if (defined(width) && (textAlign === 'center' || textAlign === 'right')) {
4241
- x += { center: 0.5, right: 1 }[textAlign] * (width - bBox.width);
4242
- }
4243
-
4244
- // update if anything changed
4245
- if (x !== text.x || y !== text.y) {
4246
- text.attr({
4247
- x: x,
4248
- y: y
4249
- });
4250
- }
4251
-
4252
- // record current values
4253
- text.x = x;
4254
- text.y = y;
4255
- }
4256
-
4257
- /**
4258
- * Set a box attribute, or defer it if the box is not yet created
4259
- * @param {Object} key
4260
- * @param {Object} value
4261
- */
4262
- function boxAttr(key, value) {
4263
- if (box) {
4264
- box.attr(key, value);
4265
- } else {
4266
- deferredAttr[key] = value;
4267
- }
4268
- }
4269
-
4270
- function getSizeAfterAdd() {
4271
- text.add(wrapper);
4272
- wrapper.attr({
4273
- text: str, // alignment is available now
4274
- x: x,
4275
- y: y
4276
- });
4277
-
4278
- if (box && defined(anchorX)) {
4279
- wrapper.attr({
4280
- anchorX: anchorX,
4281
- anchorY: anchorY
4282
- });
4283
- }
4284
- }
4285
-
4286
- /**
4287
- * After the text element is added, get the desired size of the border box
4288
- * and add it before the text in the DOM.
4289
- */
4290
- addEvent(wrapper, 'add', getSizeAfterAdd);
4291
-
4292
- /*
4293
- * Add specific attribute setters.
4294
- */
4295
-
4296
- // only change local variables
4297
- attrSetters.width = function (value) {
4298
- width = value;
4299
- return false;
4300
- };
4301
- attrSetters.height = function (value) {
4302
- height = value;
4303
- return false;
4304
- };
4305
- attrSetters.padding = function (value) {
4306
- if (defined(value) && value !== padding) {
4307
- padding = value;
4308
- updateTextPadding();
4309
- }
4310
- return false;
4311
- };
4312
- attrSetters.paddingLeft = function (value) {
4313
- if (defined(value) && value !== paddingLeft) {
4314
- paddingLeft = value;
4315
- updateTextPadding();
4316
- }
4317
- return false;
4318
- };
4319
-
4320
-
4321
- // change local variable and set attribue as well
4322
- attrSetters.align = function (value) {
4323
- alignFactor = { left: 0, center: 0.5, right: 1 }[value];
4324
- return false; // prevent setting text-anchor on the group
4325
- };
4326
-
4327
- // apply these to the box and the text alike
4328
- attrSetters.text = function (value, key) {
4329
- text.attr(key, value);
4330
- updateBoxSize();
4331
- updateTextPadding();
4332
- return false;
4333
- };
4334
-
4335
- // apply these to the box but not to the text
4336
- attrSetters[STROKE_WIDTH] = function (value, key) {
4337
- needsBox = true;
4338
- crispAdjust = value % 2 / 2;
4339
- boxAttr(key, value);
4340
- return false;
4341
- };
4342
- attrSetters.stroke = attrSetters.fill = attrSetters.r = function (value, key) {
4343
- if (key === 'fill') {
4344
- needsBox = true;
4345
- }
4346
- boxAttr(key, value);
4347
- return false;
4348
- };
4349
- attrSetters.anchorX = function (value, key) {
4350
- anchorX = value;
4351
- boxAttr(key, value + crispAdjust - wrapperX);
4352
- return false;
4353
- };
4354
- attrSetters.anchorY = function (value, key) {
4355
- anchorY = value;
4356
- boxAttr(key, value - wrapperY);
4357
- return false;
4358
- };
4359
-
4360
- // rename attributes
4361
- attrSetters.x = function (value) {
4362
- wrapper.x = value; // for animation getter
4363
- value -= alignFactor * ((width || bBox.width) + padding);
4364
- wrapperX = mathRound(value);
4365
-
4366
- wrapper.attr('translateX', wrapperX);
4367
- return false;
4368
- };
4369
- attrSetters.y = function (value) {
4370
- wrapperY = wrapper.y = mathRound(value);
4371
- wrapper.attr('translateY', wrapperY);
4372
- return false;
4373
- };
4374
-
4375
- // Redirect certain methods to either the box or the text
4376
- var baseCss = wrapper.css;
4377
- return extend(wrapper, {
4378
- /**
4379
- * Pick up some properties and apply them to the text instead of the wrapper
4380
- */
4381
- css: function (styles) {
4382
- if (styles) {
4383
- var textStyles = {};
4384
- styles = merge(styles); // create a copy to avoid altering the original object (#537)
4385
- each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width', 'textDecoration'], function (prop) {
4386
- if (styles[prop] !== UNDEFINED) {
4387
- textStyles[prop] = styles[prop];
4388
- delete styles[prop];
4389
- }
4390
- });
4391
- text.css(textStyles);
4392
- }
4393
- return baseCss.call(wrapper, styles);
4394
- },
4395
- /**
4396
- * Return the bounding box of the box, not the group
4397
- */
4398
- getBBox: function () {
4399
- return {
4400
- width: bBox.width + 2 * padding,
4401
- height: bBox.height + 2 * padding,
4402
- x: bBox.x - padding,
4403
- y: bBox.y - padding
4404
- };
4405
- },
4406
- /**
4407
- * Apply the shadow to the box
4408
- */
4409
- shadow: function (b) {
4410
- if (box) {
4411
- box.shadow(b);
4412
- }
4413
- return wrapper;
4414
- },
4415
- /**
4416
- * Destroy and release memory.
4417
- */
4418
- destroy: function () {
4419
- removeEvent(wrapper, 'add', getSizeAfterAdd);
4420
-
4421
- // Added by button implementation
4422
- removeEvent(wrapper.element, 'mouseenter');
4423
- removeEvent(wrapper.element, 'mouseleave');
4424
-
4425
- if (text) {
4426
- text = text.destroy();
4427
- }
4428
- if (box) {
4429
- box = box.destroy();
4430
- }
4431
- // Call base implementation to destroy the rest
4432
- SVGElement.prototype.destroy.call(wrapper);
4433
-
4434
- // Release local pointers (#1298)
4435
- wrapper = renderer = updateBoxSize = updateTextPadding = boxAttr = getSizeAfterAdd = null;
4436
- }
4437
- });
4438
- }
4439
- }; // end SVGRenderer
4440
-
4441
-
4442
- // general renderer
4443
- Renderer = SVGRenderer;
4444
-
4445
-
4446
- /* ****************************************************************************
4447
- * *
4448
- * START OF INTERNET EXPLORER <= 8 SPECIFIC CODE *
4449
- * *
4450
- * For applications and websites that don't need IE support, like platform *
4451
- * targeted mobile apps and web apps, this code can be removed. *
4452
- * *
4453
- *****************************************************************************/
4454
-
4455
- /**
4456
- * @constructor
4457
- */
4458
- var VMLRenderer, VMLElement;
4459
- if (!hasSVG && !useCanVG) {
4460
-
4461
- /**
4462
- * The VML element wrapper.
4463
- */
4464
- Highcharts.VMLElement = VMLElement = {
4465
-
4466
- /**
4467
- * Initialize a new VML element wrapper. It builds the markup as a string
4468
- * to minimize DOM traffic.
4469
- * @param {Object} renderer
4470
- * @param {Object} nodeName
4471
- */
4472
- init: function (renderer, nodeName) {
4473
- var wrapper = this,
4474
- markup = ['<', nodeName, ' filled="f" stroked="f"'],
4475
- style = ['position: ', ABSOLUTE, ';'],
4476
- isDiv = nodeName === DIV;
4477
-
4478
- // divs and shapes need size
4479
- if (nodeName === 'shape' || isDiv) {
4480
- style.push('left:0;top:0;width:1px;height:1px;');
4481
- }
4482
- style.push('visibility: ', isDiv ? HIDDEN : VISIBLE);
4483
-
4484
- markup.push(' style="', style.join(''), '"/>');
4485
-
4486
- // create element with default attributes and style
4487
- if (nodeName) {
4488
- markup = isDiv || nodeName === 'span' || nodeName === 'img' ?
4489
- markup.join('')
4490
- : renderer.prepVML(markup);
4491
- wrapper.element = createElement(markup);
4492
- }
4493
-
4494
- wrapper.renderer = renderer;
4495
- wrapper.attrSetters = {};
4496
- },
4497
-
4498
- /**
4499
- * Add the node to the given parent
4500
- * @param {Object} parent
4501
- */
4502
- add: function (parent) {
4503
- var wrapper = this,
4504
- renderer = wrapper.renderer,
4505
- element = wrapper.element,
4506
- box = renderer.box,
4507
- inverted = parent && parent.inverted,
4508
-
4509
- // get the parent node
4510
- parentNode = parent ?
4511
- parent.element || parent :
4512
- box;
4513
-
4514
-
4515
- // if the parent group is inverted, apply inversion on all children
4516
- if (inverted) { // only on groups
4517
- renderer.invertChild(element, parentNode);
4518
- }
4519
-
4520
- // append it
4521
- parentNode.appendChild(element);
4522
-
4523
- // align text after adding to be able to read offset
4524
- wrapper.added = true;
4525
- if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) {
4526
- wrapper.updateTransform();
4527
- }
4528
-
4529
- // fire an event for internal hooks
4530
- fireEvent(wrapper, 'add');
4531
-
4532
- return wrapper;
4533
- },
4534
-
4535
- /**
4536
- * VML always uses htmlUpdateTransform
4537
- */
4538
- updateTransform: SVGElement.prototype.htmlUpdateTransform,
4539
-
4540
- /**
4541
- * Get or set attributes
4542
- */
4543
- attr: function (hash, val) {
4544
- var wrapper = this,
4545
- key,
4546
- value,
4547
- i,
4548
- result,
4549
- element = wrapper.element || {},
4550
- elemStyle = element.style,
4551
- nodeName = element.nodeName,
4552
- renderer = wrapper.renderer,
4553
- symbolName = wrapper.symbolName,
4554
- hasSetSymbolSize,
4555
- shadows = wrapper.shadows,
4556
- skipAttr,
4557
- attrSetters = wrapper.attrSetters,
4558
- ret = wrapper;
4559
-
4560
- // single key-value pair
4561
- if (isString(hash) && defined(val)) {
4562
- key = hash;
4563
- hash = {};
4564
- hash[key] = val;
4565
- }
4566
-
4567
- // used as a getter, val is undefined
4568
- if (isString(hash)) {
4569
- key = hash;
4570
- if (key === 'strokeWidth' || key === 'stroke-width') {
4571
- ret = wrapper.strokeweight;
4572
- } else {
4573
- ret = wrapper[key];
4574
- }
4575
-
4576
- // setter
4577
- } else {
4578
- for (key in hash) {
4579
- value = hash[key];
4580
- skipAttr = false;
4581
-
4582
- // check for a specific attribute setter
4583
- result = attrSetters[key] && attrSetters[key].call(wrapper, value, key);
4584
-
4585
- if (result !== false && value !== null) { // #620
4586
-
4587
- if (result !== UNDEFINED) {
4588
- value = result; // the attribute setter has returned a new value to set
4589
- }
4590
-
4591
-
4592
- // prepare paths
4593
- // symbols
4594
- if (symbolName && /^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(key)) {
4595
- // if one of the symbol size affecting parameters are changed,
4596
- // check all the others only once for each call to an element's
4597
- // .attr() method
4598
- if (!hasSetSymbolSize) {
4599
- wrapper.symbolAttr(hash);
4600
-
4601
- hasSetSymbolSize = true;
4602
- }
4603
- skipAttr = true;
4604
-
4605
- } else if (key === 'd') {
4606
- value = value || [];
4607
- wrapper.d = value.join(' '); // used in getter for animation
4608
-
4609
- // convert paths
4610
- i = value.length;
4611
- var convertedPath = [],
4612
- clockwise;
4613
- while (i--) {
4614
-
4615
- // Multiply by 10 to allow subpixel precision.
4616
- // Substracting half a pixel seems to make the coordinates
4617
- // align with SVG, but this hasn't been tested thoroughly
4618
- if (isNumber(value[i])) {
4619
- convertedPath[i] = mathRound(value[i] * 10) - 5;
4620
- } else if (value[i] === 'Z') { // close the path
4621
- convertedPath[i] = 'x';
4622
- } else {
4623
- convertedPath[i] = value[i];
4624
-
4625
- // When the start X and end X coordinates of an arc are too close,
4626
- // they are rounded to the same value above. In this case, substract 1 from the end X
4627
- // position. #760, #1371.
4628
- if (value.isArc && (value[i] === 'wa' || value[i] === 'at')) {
4629
- clockwise = value[i] === 'wa' ? 1 : -1; // #1642
4630
- if (convertedPath[i + 5] === convertedPath[i + 7]) {
4631
- convertedPath[i + 7] -= clockwise;
4632
- }
4633
- // Start and end Y (#1410)
4634
- if (convertedPath[i + 6] === convertedPath[i + 8]) {
4635
- convertedPath[i + 8] -= clockwise;
4636
- }
4637
- }
4638
- }
4639
-
4640
- }
4641
- value = convertedPath.join(' ') || 'x';
4642
- element.path = value;
4643
-
4644
- // update shadows
4645
- if (shadows) {
4646
- i = shadows.length;
4647
- while (i--) {
4648
- shadows[i].path = shadows[i].cutOff ? this.cutOffPath(value, shadows[i].cutOff) : value;
4649
- }
4650
- }
4651
- skipAttr = true;
4652
-
4653
- // handle visibility
4654
- } else if (key === 'visibility') {
4655
-
4656
- // let the shadow follow the main element
4657
- if (shadows) {
4658
- i = shadows.length;
4659
- while (i--) {
4660
- shadows[i].style[key] = value;
4661
- }
4662
- }
4663
-
4664
- // Instead of toggling the visibility CSS property, move the div out of the viewport.
4665
- // This works around #61 and #586
4666
- if (nodeName === 'DIV') {
4667
- value = value === HIDDEN ? '-999em' : 0;
4668
-
4669
- // In order to redraw, IE7 needs the div to be visible when tucked away
4670
- // outside the viewport. So the visibility is actually opposite of
4671
- // the expected value. This applies to the tooltip only.
4672
- if (!docMode8) {
4673
- elemStyle[key] = value ? VISIBLE : HIDDEN;
4674
- }
4675
- key = 'top';
4676
- }
4677
- elemStyle[key] = value;
4678
- skipAttr = true;
4679
-
4680
- // directly mapped to css
4681
- } else if (key === 'zIndex') {
4682
-
4683
- if (value) {
4684
- elemStyle[key] = value;
4685
- }
4686
- skipAttr = true;
4687
-
4688
- // x, y, width, height
4689
- } else if (inArray(key, ['x', 'y', 'width', 'height']) !== -1) {
4690
-
4691
- wrapper[key] = value; // used in getter
4692
-
4693
- if (key === 'x' || key === 'y') {
4694
- key = { x: 'left', y: 'top' }[key];
4695
- } else {
4696
- value = mathMax(0, value); // don't set width or height below zero (#311)
4697
- }
4698
-
4699
- // clipping rectangle special
4700
- if (wrapper.updateClipping) {
4701
- wrapper[key] = value; // the key is now 'left' or 'top' for 'x' and 'y'
4702
- wrapper.updateClipping();
4703
- } else {
4704
- // normal
4705
- elemStyle[key] = value;
4706
- }
4707
-
4708
- skipAttr = true;
4709
-
4710
- // class name
4711
- } else if (key === 'class' && nodeName === 'DIV') {
4712
- // IE8 Standards mode has problems retrieving the className
4713
- element.className = value;
4714
-
4715
- // stroke
4716
- } else if (key === 'stroke') {
4717
-
4718
- value = renderer.color(value, element, key);
4719
-
4720
- key = 'strokecolor';
4721
-
4722
- // stroke width
4723
- } else if (key === 'stroke-width' || key === 'strokeWidth') {
4724
- element.stroked = value ? true : false;
4725
- key = 'strokeweight';
4726
- wrapper[key] = value; // used in getter, issue #113
4727
- if (isNumber(value)) {
4728
- value += PX;
4729
- }
4730
-
4731
- // dashStyle
4732
- } else if (key === 'dashstyle') {
4733
- var strokeElem = element.getElementsByTagName('stroke')[0] ||
4734
- createElement(renderer.prepVML(['<stroke/>']), null, null, element);
4735
- strokeElem[key] = value || 'solid';
4736
- wrapper.dashstyle = value; /* because changing stroke-width will change the dash length
4737
- and cause an epileptic effect */
4738
- skipAttr = true;
4739
-
4740
- // fill
4741
- } else if (key === 'fill') {
4742
-
4743
- if (nodeName === 'SPAN') { // text color
4744
- elemStyle.color = value;
4745
- } else if (nodeName !== 'IMG') { // #1336
4746
- element.filled = value !== NONE ? true : false;
4747
-
4748
- value = renderer.color(value, element, key, wrapper);
4749
-
4750
- key = 'fillcolor';
4751
- }
4752
-
4753
- // opacity: don't bother - animation is too slow and filters introduce artifacts
4754
- } else if (key === 'opacity') {
4755
- /*css(element, {
4756
- opacity: value
4757
- });*/
4758
- skipAttr = true;
4759
-
4760
- // rotation on VML elements
4761
- } else if (nodeName === 'shape' && key === 'rotation') {
4762
- wrapper[key] = value;
4763
- // Correction for the 1x1 size of the shape container. Used in gauge needles.
4764
- element.style.left = -mathRound(mathSin(value * deg2rad) + 1) + PX;
4765
- element.style.top = mathRound(mathCos(value * deg2rad)) + PX;
4766
-
4767
- // translation for animation
4768
- } else if (key === 'translateX' || key === 'translateY' || key === 'rotation') {
4769
- wrapper[key] = value;
4770
- wrapper.updateTransform();
4771
-
4772
- skipAttr = true;
4773
-
4774
- // text for rotated and non-rotated elements
4775
- } else if (key === 'text') {
4776
- this.bBox = null;
4777
- element.innerHTML = value;
4778
- skipAttr = true;
4779
- }
4780
-
4781
-
4782
- if (!skipAttr) {
4783
- if (docMode8) { // IE8 setAttribute bug
4784
- element[key] = value;
4785
- } else {
4786
- attr(element, key, value);
4787
- }
4788
- }
4789
-
4790
- }
4791
- }
4792
- }
4793
- return ret;
4794
- },
4795
-
4796
- /**
4797
- * Set the element's clipping to a predefined rectangle
4798
- *
4799
- * @param {String} id The id of the clip rectangle
4800
- */
4801
- clip: function (clipRect) {
4802
- var wrapper = this,
4803
- clipMembers,
4804
- cssRet;
4805
-
4806
- if (clipRect) {
4807
- clipMembers = clipRect.members;
4808
- erase(clipMembers, wrapper); // Ensure unique list of elements (#1258)
4809
- clipMembers.push(wrapper);
4810
- wrapper.destroyClip = function () {
4811
- erase(clipMembers, wrapper);
4812
- };
4813
- cssRet = clipRect.getCSS(wrapper);
4814
-
4815
- } else {
4816
- if (wrapper.destroyClip) {
4817
- wrapper.destroyClip();
4818
- }
4819
- cssRet = { clip: docMode8 ? 'inherit' : 'rect(auto)' }; // #1214
4820
- }
4821
-
4822
- return wrapper.css(cssRet);
4823
-
4824
- },
4825
-
4826
- /**
4827
- * Set styles for the element
4828
- * @param {Object} styles
4829
- */
4830
- css: SVGElement.prototype.htmlCss,
4831
-
4832
- /**
4833
- * Removes a child either by removeChild or move to garbageBin.
4834
- * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
4835
- */
4836
- safeRemoveChild: function (element) {
4837
- // discardElement will detach the node from its parent before attaching it
4838
- // to the garbage bin. Therefore it is important that the node is attached and have parent.
4839
- if (element.parentNode) {
4840
- discardElement(element);
4841
- }
4842
- },
4843
-
4844
- /**
4845
- * Extend element.destroy by removing it from the clip members array
4846
- */
4847
- destroy: function () {
4848
- if (this.destroyClip) {
4849
- this.destroyClip();
4850
- }
4851
-
4852
- return SVGElement.prototype.destroy.apply(this);
4853
- },
4854
-
4855
- /**
4856
- * Add an event listener. VML override for normalizing event parameters.
4857
- * @param {String} eventType
4858
- * @param {Function} handler
4859
- */
4860
- on: function (eventType, handler) {
4861
- // simplest possible event model for internal use
4862
- this.element['on' + eventType] = function () {
4863
- var evt = win.event;
4864
- evt.target = evt.srcElement;
4865
- handler(evt);
4866
- };
4867
- return this;
4868
- },
4869
-
4870
- /**
4871
- * In stacked columns, cut off the shadows so that they don't overlap
4872
- */
4873
- cutOffPath: function (path, length) {
4874
-
4875
- var len;
4876
-
4877
- path = path.split(/[ ,]/);
4878
- len = path.length;
4879
-
4880
- if (len === 9 || len === 11) {
4881
- path[len - 4] = path[len - 2] = pInt(path[len - 2]) - 10 * length;
4882
- }
4883
- return path.join(' ');
4884
- },
4885
-
4886
- /**
4887
- * Apply a drop shadow by copying elements and giving them different strokes
4888
- * @param {Boolean|Object} shadowOptions
4889
- */
4890
- shadow: function (shadowOptions, group, cutOff) {
4891
- var shadows = [],
4892
- i,
4893
- element = this.element,
4894
- renderer = this.renderer,
4895
- shadow,
4896
- elemStyle = element.style,
4897
- markup,
4898
- path = element.path,
4899
- strokeWidth,
4900
- modifiedPath,
4901
- shadowWidth,
4902
- shadowElementOpacity;
4903
-
4904
- // some times empty paths are not strings
4905
- if (path && typeof path.value !== 'string') {
4906
- path = 'x';
4907
- }
4908
- modifiedPath = path;
4909
-
4910
- if (shadowOptions) {
4911
- shadowWidth = pick(shadowOptions.width, 3);
4912
- shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
4913
- for (i = 1; i <= 3; i++) {
4914
-
4915
- strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
4916
-
4917
- // Cut off shadows for stacked column items
4918
- if (cutOff) {
4919
- modifiedPath = this.cutOffPath(path.value, strokeWidth + 0.5);
4920
- }
4921
-
4922
- markup = ['<shape isShadow="true" strokeweight="', strokeWidth,
4923
- '" filled="false" path="', modifiedPath,
4924
- '" coordsize="10 10" style="', element.style.cssText, '" />'];
4925
-
4926
- shadow = createElement(renderer.prepVML(markup),
4927
- null, {
4928
- left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1),
4929
- top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1)
4930
- }
4931
- );
4932
- if (cutOff) {
4933
- shadow.cutOff = strokeWidth + 1;
4934
- }
4935
-
4936
- // apply the opacity
4937
- markup = ['<stroke color="', shadowOptions.color || 'black', '" opacity="', shadowElementOpacity * i, '"/>'];
4938
- createElement(renderer.prepVML(markup), null, null, shadow);
4939
-
4940
-
4941
- // insert it
4942
- if (group) {
4943
- group.element.appendChild(shadow);
4944
- } else {
4945
- element.parentNode.insertBefore(shadow, element);
4946
- }
4947
-
4948
- // record it
4949
- shadows.push(shadow);
4950
-
4951
- }
4952
-
4953
- this.shadows = shadows;
4954
- }
4955
- return this;
4956
-
4957
- }
4958
- };
4959
- VMLElement = extendClass(SVGElement, VMLElement);
4960
-
4961
- /**
4962
- * The VML renderer
4963
- */
4964
- var VMLRendererExtension = { // inherit SVGRenderer
4965
-
4966
- Element: VMLElement,
4967
- isIE8: userAgent.indexOf('MSIE 8.0') > -1,
4968
-
4969
-
4970
- /**
4971
- * Initialize the VMLRenderer
4972
- * @param {Object} container
4973
- * @param {Number} width
4974
- * @param {Number} height
4975
- */
4976
- init: function (container, width, height) {
4977
- var renderer = this,
4978
- boxWrapper,
4979
- box;
4980
-
4981
- renderer.alignedObjects = [];
4982
-
4983
- boxWrapper = renderer.createElement(DIV);
4984
- box = boxWrapper.element;
4985
- box.style.position = RELATIVE; // for freeform drawing using renderer directly
4986
- container.appendChild(boxWrapper.element);
4987
-
4988
-
4989
- // generate the containing box
4990
- renderer.isVML = true;
4991
- renderer.box = box;
4992
- renderer.boxWrapper = boxWrapper;
4993
-
4994
-
4995
- renderer.setSize(width, height, false);
4996
-
4997
- // The only way to make IE6 and IE7 print is to use a global namespace. However,
4998
- // with IE8 the only way to make the dynamic shapes visible in screen and print mode
4999
- // seems to be to add the xmlns attribute and the behaviour style inline.
5000
- if (!doc.namespaces.hcv) {
5001
-
5002
- doc.namespaces.add('hcv', 'urn:schemas-microsoft-com:vml');
5003
-
5004
- // setup default css
5005
- doc.createStyleSheet().cssText =
5006
- 'hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke' +
5007
- '{ behavior:url(#default#VML); display: inline-block; } ';
5008
-
5009
- }
5010
- },
5011
-
5012
-
5013
- /**
5014
- * Detect whether the renderer is hidden. This happens when one of the parent elements
5015
- * has display: none
5016
- */
5017
- isHidden: function () {
5018
- return !this.box.offsetWidth;
5019
- },
5020
-
5021
- /**
5022
- * Define a clipping rectangle. In VML it is accomplished by storing the values
5023
- * for setting the CSS style to all associated members.
5024
- *
5025
- * @param {Number} x
5026
- * @param {Number} y
5027
- * @param {Number} width
5028
- * @param {Number} height
5029
- */
5030
- clipRect: function (x, y, width, height) {
5031
-
5032
- // create a dummy element
5033
- var clipRect = this.createElement(),
5034
- isObj = isObject(x);
5035
-
5036
- // mimic a rectangle with its style object for automatic updating in attr
5037
- return extend(clipRect, {
5038
- members: [],
5039
- left: isObj ? x.x : x,
5040
- top: isObj ? x.y : y,
5041
- width: isObj ? x.width : width,
5042
- height: isObj ? x.height : height,
5043
- getCSS: function (wrapper) {
5044
- var element = wrapper.element,
5045
- nodeName = element.nodeName,
5046
- isShape = nodeName === 'shape',
5047
- inverted = wrapper.inverted,
5048
- rect = this,
5049
- top = rect.top - (isShape ? element.offsetTop : 0),
5050
- left = rect.left,
5051
- right = left + rect.width,
5052
- bottom = top + rect.height,
5053
- ret = {
5054
- clip: 'rect(' +
5055
- mathRound(inverted ? left : top) + 'px,' +
5056
- mathRound(inverted ? bottom : right) + 'px,' +
5057
- mathRound(inverted ? right : bottom) + 'px,' +
5058
- mathRound(inverted ? top : left) + 'px)'
5059
- };
5060
-
5061
- // issue 74 workaround
5062
- if (!inverted && docMode8 && nodeName === 'DIV') {
5063
- extend(ret, {
5064
- width: right + PX,
5065
- height: bottom + PX
5066
- });
5067
- }
5068
- return ret;
5069
- },
5070
-
5071
- // used in attr and animation to update the clipping of all members
5072
- updateClipping: function () {
5073
- each(clipRect.members, function (member) {
5074
- member.css(clipRect.getCSS(member));
5075
- });
5076
- }
5077
- });
5078
-
5079
- },
5080
-
5081
-
5082
- /**
5083
- * Take a color and return it if it's a string, make it a gradient if it's a
5084
- * gradient configuration object, and apply opacity.
5085
- *
5086
- * @param {Object} color The color or config object
5087
- */
5088
- color: function (color, elem, prop, wrapper) {
5089
- var renderer = this,
5090
- colorObject,
5091
- regexRgba = /^rgba/,
5092
- markup,
5093
- fillType,
5094
- ret = NONE;
5095
-
5096
- // Check for linear or radial gradient
5097
- if (color && color.linearGradient) {
5098
- fillType = 'gradient';
5099
- } else if (color && color.radialGradient) {
5100
- fillType = 'pattern';
5101
- }
5102
-
5103
-
5104
- if (fillType) {
5105
-
5106
- var stopColor,
5107
- stopOpacity,
5108
- gradient = color.linearGradient || color.radialGradient,
5109
- x1,
5110
- y1,
5111
- x2,
5112
- y2,
5113
- opacity1,
5114
- opacity2,
5115
- color1,
5116
- color2,
5117
- fillAttr = '',
5118
- stops = color.stops,
5119
- firstStop,
5120
- lastStop,
5121
- colors = [],
5122
- addFillNode = function () {
5123
- // Add the fill subnode. When colors attribute is used, the meanings of opacity and o:opacity2
5124
- // are reversed.
5125
- markup = ['<fill colors="' + colors.join(',') + '" opacity="', opacity2, '" o:opacity2="', opacity1,
5126
- '" type="', fillType, '" ', fillAttr, 'focus="100%" method="any" />'];
5127
- createElement(renderer.prepVML(markup), null, null, elem);
5128
- };
5129
-
5130
- // Extend from 0 to 1
5131
- firstStop = stops[0];
5132
- lastStop = stops[stops.length - 1];
5133
- if (firstStop[0] > 0) {
5134
- stops.unshift([
5135
- 0,
5136
- firstStop[1]
5137
- ]);
5138
- }
5139
- if (lastStop[0] < 1) {
5140
- stops.push([
5141
- 1,
5142
- lastStop[1]
5143
- ]);
5144
- }
5145
-
5146
- // Compute the stops
5147
- each(stops, function (stop, i) {
5148
- if (regexRgba.test(stop[1])) {
5149
- colorObject = Color(stop[1]);
5150
- stopColor = colorObject.get('rgb');
5151
- stopOpacity = colorObject.get('a');
5152
- } else {
5153
- stopColor = stop[1];
5154
- stopOpacity = 1;
5155
- }
5156
-
5157
- // Build the color attribute
5158
- colors.push((stop[0] * 100) + '% ' + stopColor);
5159
-
5160
- // Only start and end opacities are allowed, so we use the first and the last
5161
- if (!i) {
5162
- opacity1 = stopOpacity;
5163
- color2 = stopColor;
5164
- } else {
5165
- opacity2 = stopOpacity;
5166
- color1 = stopColor;
5167
- }
5168
- });
5169
-
5170
- // Apply the gradient to fills only.
5171
- if (prop === 'fill') {
5172
-
5173
- // Handle linear gradient angle
5174
- if (fillType === 'gradient') {
5175
- x1 = gradient.x1 || gradient[0] || 0;
5176
- y1 = gradient.y1 || gradient[1] || 0;
5177
- x2 = gradient.x2 || gradient[2] || 0;
5178
- y2 = gradient.y2 || gradient[3] || 0;
5179
- fillAttr = 'angle="' + (90 - math.atan(
5180
- (y2 - y1) / // y vector
5181
- (x2 - x1) // x vector
5182
- ) * 180 / mathPI) + '"';
5183
-
5184
- addFillNode();
5185
-
5186
- // Radial (circular) gradient
5187
- } else {
5188
-
5189
- var r = gradient.r,
5190
- sizex = r * 2,
5191
- sizey = r * 2,
5192
- cx = gradient.cx,
5193
- cy = gradient.cy,
5194
- radialReference = elem.radialReference,
5195
- bBox,
5196
- applyRadialGradient = function () {
5197
- if (radialReference) {
5198
- bBox = wrapper.getBBox();
5199
- cx += (radialReference[0] - bBox.x) / bBox.width - 0.5;
5200
- cy += (radialReference[1] - bBox.y) / bBox.height - 0.5;
5201
- sizex *= radialReference[2] / bBox.width;
5202
- sizey *= radialReference[2] / bBox.height;
5203
- }
5204
- fillAttr = 'src="' + defaultOptions.global.VMLRadialGradientURL + '" ' +
5205
- 'size="' + sizex + ',' + sizey + '" ' +
5206
- 'origin="0.5,0.5" ' +
5207
- 'position="' + cx + ',' + cy + '" ' +
5208
- 'color2="' + color2 + '" ';
5209
-
5210
- addFillNode();
5211
- };
5212
-
5213
- // Apply radial gradient
5214
- if (wrapper.added) {
5215
- applyRadialGradient();
5216
- } else {
5217
- // We need to know the bounding box to get the size and position right
5218
- addEvent(wrapper, 'add', applyRadialGradient);
5219
- }
5220
-
5221
- // The fill element's color attribute is broken in IE8 standards mode, so we
5222
- // need to set the parent shape's fillcolor attribute instead.
5223
- ret = color1;
5224
- }
5225
-
5226
- // Gradients are not supported for VML stroke, return the first color. #722.
5227
- } else {
5228
- ret = stopColor;
5229
- }
5230
-
5231
- // if the color is an rgba color, split it and add a fill node
5232
- // to hold the opacity component
5233
- } else if (regexRgba.test(color) && elem.tagName !== 'IMG') {
5234
-
5235
- colorObject = Color(color);
5236
-
5237
- markup = ['<', prop, ' opacity="', colorObject.get('a'), '"/>'];
5238
- createElement(this.prepVML(markup), null, null, elem);
5239
-
5240
- ret = colorObject.get('rgb');
5241
-
5242
-
5243
- } else {
5244
- var propNodes = elem.getElementsByTagName(prop); // 'stroke' or 'fill' node
5245
- if (propNodes.length) {
5246
- propNodes[0].opacity = 1;
5247
- propNodes[0].type = 'solid';
5248
- }
5249
- ret = color;
5250
- }
5251
-
5252
- return ret;
5253
- },
5254
-
5255
- /**
5256
- * Take a VML string and prepare it for either IE8 or IE6/IE7.
5257
- * @param {Array} markup A string array of the VML markup to prepare
5258
- */
5259
- prepVML: function (markup) {
5260
- var vmlStyle = 'display:inline-block;behavior:url(#default#VML);',
5261
- isIE8 = this.isIE8;
5262
-
5263
- markup = markup.join('');
5264
-
5265
- if (isIE8) { // add xmlns and style inline
5266
- markup = markup.replace('/>', ' xmlns="urn:schemas-microsoft-com:vml" />');
5267
- if (markup.indexOf('style="') === -1) {
5268
- markup = markup.replace('/>', ' style="' + vmlStyle + '" />');
5269
- } else {
5270
- markup = markup.replace('style="', 'style="' + vmlStyle);
5271
- }
5272
-
5273
- } else { // add namespace
5274
- markup = markup.replace('<', '<hcv:');
5275
- }
5276
-
5277
- return markup;
5278
- },
5279
-
5280
- /**
5281
- * Create rotated and aligned text
5282
- * @param {String} str
5283
- * @param {Number} x
5284
- * @param {Number} y
5285
- */
5286
- text: SVGRenderer.prototype.html,
5287
-
5288
- /**
5289
- * Create and return a path element
5290
- * @param {Array} path
5291
- */
5292
- path: function (path) {
5293
- var attr = {
5294
- // subpixel precision down to 0.1 (width and height = 1px)
5295
- coordsize: '10 10'
5296
- };
5297
- if (isArray(path)) {
5298
- attr.d = path;
5299
- } else if (isObject(path)) { // attributes
5300
- extend(attr, path);
5301
- }
5302
- // create the shape
5303
- return this.createElement('shape').attr(attr);
5304
- },
5305
-
5306
- /**
5307
- * Create and return a circle element. In VML circles are implemented as
5308
- * shapes, which is faster than v:oval
5309
- * @param {Number} x
5310
- * @param {Number} y
5311
- * @param {Number} r
5312
- */
5313
- circle: function (x, y, r) {
5314
- var circle = this.symbol('circle');
5315
- if (isObject(x)) {
5316
- r = x.r;
5317
- y = x.y;
5318
- x = x.x;
5319
- }
5320
- circle.isCircle = true; // Causes x and y to mean center (#1682)
5321
- return circle.attr({ x: x, y: y, width: 2 * r, height: 2 * r });
5322
- },
5323
-
5324
- /**
5325
- * Create a group using an outer div and an inner v:group to allow rotating
5326
- * and flipping. A simple v:group would have problems with positioning
5327
- * child HTML elements and CSS clip.
5328
- *
5329
- * @param {String} name The name of the group
5330
- */
5331
- g: function (name) {
5332
- var wrapper,
5333
- attribs;
5334
-
5335
- // set the class name
5336
- if (name) {
5337
- attribs = { 'className': PREFIX + name, 'class': PREFIX + name };
5338
- }
5339
-
5340
- // the div to hold HTML and clipping
5341
- wrapper = this.createElement(DIV).attr(attribs);
5342
-
5343
- return wrapper;
5344
- },
5345
-
5346
- /**
5347
- * VML override to create a regular HTML image
5348
- * @param {String} src
5349
- * @param {Number} x
5350
- * @param {Number} y
5351
- * @param {Number} width
5352
- * @param {Number} height
5353
- */
5354
- image: function (src, x, y, width, height) {
5355
- var obj = this.createElement('img')
5356
- .attr({ src: src });
5357
-
5358
- if (arguments.length > 1) {
5359
- obj.attr({
5360
- x: x,
5361
- y: y,
5362
- width: width,
5363
- height: height
5364
- });
5365
- }
5366
- return obj;
5367
- },
5368
-
5369
- /**
5370
- * VML uses a shape for rect to overcome bugs and rotation problems
5371
- */
5372
- rect: function (x, y, width, height, r, strokeWidth) {
5373
-
5374
- if (isObject(x)) {
5375
- y = x.y;
5376
- width = x.width;
5377
- height = x.height;
5378
- strokeWidth = x.strokeWidth;
5379
- x = x.x;
5380
- }
5381
- var wrapper = this.symbol('rect');
5382
- wrapper.r = r;
5383
-
5384
- return wrapper.attr(wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0)));
5385
- },
5386
-
5387
- /**
5388
- * In the VML renderer, each child of an inverted div (group) is inverted
5389
- * @param {Object} element
5390
- * @param {Object} parentNode
5391
- */
5392
- invertChild: function (element, parentNode) {
5393
- var parentStyle = parentNode.style;
5394
- css(element, {
5395
- flip: 'x',
5396
- left: pInt(parentStyle.width) - 1,
5397
- top: pInt(parentStyle.height) - 1,
5398
- rotation: -90
5399
- });
5400
- },
5401
-
5402
- /**
5403
- * Symbol definitions that override the parent SVG renderer's symbols
5404
- *
5405
- */
5406
- symbols: {
5407
- // VML specific arc function
5408
- arc: function (x, y, w, h, options) {
5409
- var start = options.start,
5410
- end = options.end,
5411
- radius = options.r || w || h,
5412
- innerRadius = options.innerR,
5413
- cosStart = mathCos(start),
5414
- sinStart = mathSin(start),
5415
- cosEnd = mathCos(end),
5416
- sinEnd = mathSin(end),
5417
- ret;
5418
-
5419
- if (end - start === 0) { // no angle, don't show it.
5420
- return ['x'];
5421
- }
5422
-
5423
- ret = [
5424
- 'wa', // clockwise arc to
5425
- x - radius, // left
5426
- y - radius, // top
5427
- x + radius, // right
5428
- y + radius, // bottom
5429
- x + radius * cosStart, // start x
5430
- y + radius * sinStart, // start y
5431
- x + radius * cosEnd, // end x
5432
- y + radius * sinEnd // end y
5433
- ];
5434
-
5435
- if (options.open && !innerRadius) {
5436
- ret.push(
5437
- 'e',
5438
- M,
5439
- x,// - innerRadius,
5440
- y// - innerRadius
5441
- );
5442
- }
5443
-
5444
- ret.push(
5445
- 'at', // anti clockwise arc to
5446
- x - innerRadius, // left
5447
- y - innerRadius, // top
5448
- x + innerRadius, // right
5449
- y + innerRadius, // bottom
5450
- x + innerRadius * cosEnd, // start x
5451
- y + innerRadius * sinEnd, // start y
5452
- x + innerRadius * cosStart, // end x
5453
- y + innerRadius * sinStart, // end y
5454
- 'x', // finish path
5455
- 'e' // close
5456
- );
5457
-
5458
- ret.isArc = true;
5459
- return ret;
5460
-
5461
- },
5462
- // Add circle symbol path. This performs significantly faster than v:oval.
5463
- circle: function (x, y, w, h, wrapper) {
5464
- // Center correction, #1682
5465
- if (wrapper && wrapper.isCircle) {
5466
- x -= w / 2;
5467
- y -= h / 2;
5468
- }
5469
-
5470
- // Return the path
5471
- return [
5472
- 'wa', // clockwisearcto
5473
- x, // left
5474
- y, // top
5475
- x + w, // right
5476
- y + h, // bottom
5477
- x + w, // start x
5478
- y + h / 2, // start y
5479
- x + w, // end x
5480
- y + h / 2, // end y
5481
- //'x', // finish path
5482
- 'e' // close
5483
- ];
5484
- },
5485
- /**
5486
- * Add rectangle symbol path which eases rotation and omits arcsize problems
5487
- * compared to the built-in VML roundrect shape
5488
- *
5489
- * @param {Number} left Left position
5490
- * @param {Number} top Top position
5491
- * @param {Number} r Border radius
5492
- * @param {Object} options Width and height
5493
- */
5494
-
5495
- rect: function (left, top, width, height, options) {
5496
-
5497
- var right = left + width,
5498
- bottom = top + height,
5499
- ret,
5500
- r;
5501
-
5502
- // No radius, return the more lightweight square
5503
- if (!defined(options) || !options.r) {
5504
- ret = SVGRenderer.prototype.symbols.square.apply(0, arguments);
5505
-
5506
- // Has radius add arcs for the corners
5507
- } else {
5508
-
5509
- r = mathMin(options.r, width, height);
5510
- ret = [
5511
- M,
5512
- left + r, top,
5513
-
5514
- L,
5515
- right - r, top,
5516
- 'wa',
5517
- right - 2 * r, top,
5518
- right, top + 2 * r,
5519
- right - r, top,
5520
- right, top + r,
5521
-
5522
- L,
5523
- right, bottom - r,
5524
- 'wa',
5525
- right - 2 * r, bottom - 2 * r,
5526
- right, bottom,
5527
- right, bottom - r,
5528
- right - r, bottom,
5529
-
5530
- L,
5531
- left + r, bottom,
5532
- 'wa',
5533
- left, bottom - 2 * r,
5534
- left + 2 * r, bottom,
5535
- left + r, bottom,
5536
- left, bottom - r,
5537
-
5538
- L,
5539
- left, top + r,
5540
- 'wa',
5541
- left, top,
5542
- left + 2 * r, top + 2 * r,
5543
- left, top + r,
5544
- left + r, top,
5545
-
5546
-
5547
- 'x',
5548
- 'e'
5549
- ];
5550
- }
5551
- return ret;
5552
- }
5553
- }
5554
- };
5555
- Highcharts.VMLRenderer = VMLRenderer = function () {
5556
- this.init.apply(this, arguments);
5557
- };
5558
- VMLRenderer.prototype = merge(SVGRenderer.prototype, VMLRendererExtension);
5559
-
5560
- // general renderer
5561
- Renderer = VMLRenderer;
5562
- }
5563
-
5564
- /* ****************************************************************************
5565
- * *
5566
- * END OF INTERNET EXPLORER <= 8 SPECIFIC CODE *
5567
- * *
5568
- *****************************************************************************/
5569
- /* ****************************************************************************
5570
- * *
5571
- * START OF ANDROID < 3 SPECIFIC CODE. THIS CAN BE REMOVED IF YOU'RE NOT *
5572
- * TARGETING THAT SYSTEM. *
5573
- * *
5574
- *****************************************************************************/
5575
- var CanVGRenderer,
5576
- CanVGController;
5577
-
5578
- if (useCanVG) {
5579
- /**
5580
- * The CanVGRenderer is empty from start to keep the source footprint small.
5581
- * When requested, the CanVGController downloads the rest of the source packaged
5582
- * together with the canvg library.
5583
- */
5584
- Highcharts.CanVGRenderer = CanVGRenderer = function () {
5585
- // Override the global SVG namespace to fake SVG/HTML that accepts CSS
5586
- SVG_NS = 'http://www.w3.org/1999/xhtml';
5587
- };
5588
-
5589
- /**
5590
- * Start with an empty symbols object. This is needed when exporting is used (exporting.src.js will add a few symbols), but
5591
- * the implementation from SvgRenderer will not be merged in until first render.
5592
- */
5593
- CanVGRenderer.prototype.symbols = {};
5594
-
5595
- /**
5596
- * Handles on demand download of canvg rendering support.
5597
- */
5598
- CanVGController = (function () {
5599
- // List of renderering calls
5600
- var deferredRenderCalls = [];
5601
-
5602
- /**
5603
- * When downloaded, we are ready to draw deferred charts.
5604
- */
5605
- function drawDeferred() {
5606
- var callLength = deferredRenderCalls.length,
5607
- callIndex;
5608
-
5609
- // Draw all pending render calls
5610
- for (callIndex = 0; callIndex < callLength; callIndex++) {
5611
- deferredRenderCalls[callIndex]();
5612
- }
5613
- // Clear the list
5614
- deferredRenderCalls = [];
5615
- }
5616
-
5617
- return {
5618
- push: function (func, scriptLocation) {
5619
- // Only get the script once
5620
- if (deferredRenderCalls.length === 0) {
5621
- getScript(scriptLocation, drawDeferred);
5622
- }
5623
- // Register render call
5624
- deferredRenderCalls.push(func);
5625
- }
5626
- };
5627
- }());
5628
-
5629
- Renderer = CanVGRenderer;
5630
- } // end CanVGRenderer
5631
-
5632
- /* ****************************************************************************
5633
- * *
5634
- * END OF ANDROID < 3 SPECIFIC CODE *
5635
- * *
5636
- *****************************************************************************/
5637
-
5638
- /**
5639
- * The Tick class
5640
- */
5641
- function Tick(axis, pos, type, noLabel) {
5642
- this.axis = axis;
5643
- this.pos = pos;
5644
- this.type = type || '';
5645
- this.isNew = true;
5646
-
5647
- if (!type && !noLabel) {
5648
- this.addLabel();
5649
- }
5650
- }
5651
-
5652
- Tick.prototype = {
5653
- /**
5654
- * Write the tick label
5655
- */
5656
- addLabel: function () {
5657
- var tick = this,
5658
- axis = tick.axis,
5659
- options = axis.options,
5660
- chart = axis.chart,
5661
- horiz = axis.horiz,
5662
- categories = axis.categories,
5663
- names = axis.series[0] && axis.series[0].names,
5664
- pos = tick.pos,
5665
- labelOptions = options.labels,
5666
- str,
5667
- tickPositions = axis.tickPositions,
5668
- width = (horiz && categories &&
5669
- !labelOptions.step && !labelOptions.staggerLines &&
5670
- !labelOptions.rotation &&
5671
- chart.plotWidth / tickPositions.length) ||
5672
- (!horiz && (chart.optionsMarginLeft || chart.plotWidth / 2)), // #1580
5673
- isFirst = pos === tickPositions[0],
5674
- isLast = pos === tickPositions[tickPositions.length - 1],
5675
- css,
5676
- attr,
5677
- value = categories ?
5678
- pick(categories[pos], names && names[pos], pos) :
5679
- pos,
5680
- label = tick.label,
5681
- tickPositionInfo = tickPositions.info,
5682
- dateTimeLabelFormat;
5683
-
5684
- // Set the datetime label format. If a higher rank is set for this position, use that. If not,
5685
- // use the general format.
5686
- if (axis.isDatetimeAxis && tickPositionInfo) {
5687
- dateTimeLabelFormat = options.dateTimeLabelFormats[tickPositionInfo.higherRanks[pos] || tickPositionInfo.unitName];
5688
- }
5689
-
5690
- // set properties for access in render method
5691
- tick.isFirst = isFirst;
5692
- tick.isLast = isLast;
5693
-
5694
- // get the string
5695
- str = axis.labelFormatter.call({
5696
- axis: axis,
5697
- chart: chart,
5698
- isFirst: isFirst,
5699
- isLast: isLast,
5700
- dateTimeLabelFormat: dateTimeLabelFormat,
5701
- value: axis.isLog ? correctFloat(lin2log(value)) : value
5702
- });
5703
-
5704
- // prepare CSS
5705
- css = width && { width: mathMax(1, mathRound(width - 2 * (labelOptions.padding || 10))) + PX };
5706
- css = extend(css, labelOptions.style);
5707
-
5708
- // first call
5709
- if (!defined(label)) {
5710
- attr = {
5711
- align: labelOptions.align
5712
- };
5713
- if (isNumber(labelOptions.rotation)) {
5714
- attr.rotation = labelOptions.rotation;
5715
- }
5716
- tick.label =
5717
- defined(str) && labelOptions.enabled ?
5718
- chart.renderer.text(
5719
- str,
5720
- 0,
5721
- 0,
5722
- labelOptions.useHTML
5723
- )
5724
- .attr(attr)
5725
- // without position absolute, IE export sometimes is wrong
5726
- .css(css)
5727
- .add(axis.labelGroup) :
5728
- null;
5729
-
5730
- // update
5731
- } else if (label) {
5732
- label.attr({
5733
- text: str
5734
- })
5735
- .css(css);
5736
- }
5737
- },
5738
-
5739
- /**
5740
- * Get the offset height or width of the label
5741
- */
5742
- getLabelSize: function () {
5743
- var label = this.label,
5744
- axis = this.axis;
5745
- return label ?
5746
- ((this.labelBBox = label.getBBox()))[axis.horiz ? 'height' : 'width'] :
5747
- 0;
5748
- },
5749
-
5750
- /**
5751
- * Find how far the labels extend to the right and left of the tick's x position. Used for anti-collision
5752
- * detection with overflow logic.
5753
- */
5754
- getLabelSides: function () {
5755
- var bBox = this.labelBBox, // assume getLabelSize has run at this point
5756
- axis = this.axis,
5757
- options = axis.options,
5758
- labelOptions = options.labels,
5759
- width = bBox.width,
5760
- leftSide = width * { left: 0, center: 0.5, right: 1 }[labelOptions.align] - labelOptions.x;
5761
-
5762
- return [-leftSide, width - leftSide];
5763
- },
5764
-
5765
- /**
5766
- * Handle the label overflow by adjusting the labels to the left and right edge, or
5767
- * hide them if they collide into the neighbour label.
5768
- */
5769
- handleOverflow: function (index, xy) {
5770
- var show = true,
5771
- axis = this.axis,
5772
- chart = axis.chart,
5773
- isFirst = this.isFirst,
5774
- isLast = this.isLast,
5775
- x = xy.x,
5776
- reversed = axis.reversed,
5777
- tickPositions = axis.tickPositions;
5778
-
5779
- if (isFirst || isLast) {
5780
-
5781
- var sides = this.getLabelSides(),
5782
- leftSide = sides[0],
5783
- rightSide = sides[1],
5784
- plotLeft = chart.plotLeft,
5785
- plotRight = plotLeft + axis.len,
5786
- neighbour = axis.ticks[tickPositions[index + (isFirst ? 1 : -1)]],
5787
- neighbourEdge = neighbour && neighbour.label.xy && neighbour.label.xy.x + neighbour.getLabelSides()[isFirst ? 0 : 1];
5788
-
5789
- if ((isFirst && !reversed) || (isLast && reversed)) {
5790
- // Is the label spilling out to the left of the plot area?
5791
- if (x + leftSide < plotLeft) {
5792
-
5793
- // Align it to plot left
5794
- x = plotLeft - leftSide;
5795
-
5796
- // Hide it if it now overlaps the neighbour label
5797
- if (neighbour && x + rightSide > neighbourEdge) {
5798
- show = false;
5799
- }
5800
- }
5801
-
5802
- } else {
5803
- // Is the label spilling out to the right of the plot area?
5804
- if (x + rightSide > plotRight) {
5805
-
5806
- // Align it to plot right
5807
- x = plotRight - rightSide;
5808
-
5809
- // Hide it if it now overlaps the neighbour label
5810
- if (neighbour && x + leftSide < neighbourEdge) {
5811
- show = false;
5812
- }
5813
-
5814
- }
5815
- }
5816
-
5817
- // Set the modified x position of the label
5818
- xy.x = x;
5819
- }
5820
- return show;
5821
- },
5822
-
5823
- /**
5824
- * Get the x and y position for ticks and labels
5825
- */
5826
- getPosition: function (horiz, pos, tickmarkOffset, old) {
5827
- var axis = this.axis,
5828
- chart = axis.chart,
5829
- cHeight = (old && chart.oldChartHeight) || chart.chartHeight;
5830
-
5831
- return {
5832
- x: horiz ?
5833
- axis.translate(pos + tickmarkOffset, null, null, old) + axis.transB :
5834
- axis.left + axis.offset + (axis.opposite ? ((old && chart.oldChartWidth) || chart.chartWidth) - axis.right - axis.left : 0),
5835
-
5836
- y: horiz ?
5837
- cHeight - axis.bottom + axis.offset - (axis.opposite ? axis.height : 0) :
5838
- cHeight - axis.translate(pos + tickmarkOffset, null, null, old) - axis.transB
5839
- };
5840
-
5841
- },
5842
-
5843
- /**
5844
- * Get the x, y position of the tick label
5845
- */
5846
- getLabelPosition: function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
5847
- var axis = this.axis,
5848
- transA = axis.transA,
5849
- reversed = axis.reversed,
5850
- staggerLines = axis.staggerLines;
5851
-
5852
- x = x + labelOptions.x - (tickmarkOffset && horiz ?
5853
- tickmarkOffset * transA * (reversed ? -1 : 1) : 0);
5854
- y = y + labelOptions.y - (tickmarkOffset && !horiz ?
5855
- tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
5856
-
5857
- // Vertically centered
5858
- if (!defined(labelOptions.y)) {
5859
- y += pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
5860
- }
5861
-
5862
- // Correct for staggered labels
5863
- if (staggerLines) {
5864
- y += (index / (step || 1) % staggerLines) * 16;
5865
- }
5866
-
5867
- return {
5868
- x: x,
5869
- y: y
5870
- };
5871
- },
5872
-
5873
- /**
5874
- * Extendible method to return the path of the marker
5875
- */
5876
- getMarkPath: function (x, y, tickLength, tickWidth, horiz, renderer) {
5877
- return renderer.crispLine([
5878
- M,
5879
- x,
5880
- y,
5881
- L,
5882
- x + (horiz ? 0 : -tickLength),
5883
- y + (horiz ? tickLength : 0)
5884
- ], tickWidth);
5885
- },
5886
-
5887
- /**
5888
- * Put everything in place
5889
- *
5890
- * @param index {Number}
5891
- * @param old {Boolean} Use old coordinates to prepare an animation into new position
5892
- */
5893
- render: function (index, old, opacity) {
5894
- var tick = this,
5895
- axis = tick.axis,
5896
- options = axis.options,
5897
- chart = axis.chart,
5898
- renderer = chart.renderer,
5899
- horiz = axis.horiz,
5900
- type = tick.type,
5901
- label = tick.label,
5902
- pos = tick.pos,
5903
- labelOptions = options.labels,
5904
- gridLine = tick.gridLine,
5905
- gridPrefix = type ? type + 'Grid' : 'grid',
5906
- tickPrefix = type ? type + 'Tick' : 'tick',
5907
- gridLineWidth = options[gridPrefix + 'LineWidth'],
5908
- gridLineColor = options[gridPrefix + 'LineColor'],
5909
- dashStyle = options[gridPrefix + 'LineDashStyle'],
5910
- tickLength = options[tickPrefix + 'Length'],
5911
- tickWidth = options[tickPrefix + 'Width'] || 0,
5912
- tickColor = options[tickPrefix + 'Color'],
5913
- tickPosition = options[tickPrefix + 'Position'],
5914
- gridLinePath,
5915
- mark = tick.mark,
5916
- markPath,
5917
- step = labelOptions.step,
5918
- attribs,
5919
- show = true,
5920
- tickmarkOffset = axis.tickmarkOffset,
5921
- xy = tick.getPosition(horiz, pos, tickmarkOffset, old),
5922
- x = xy.x,
5923
- y = xy.y,
5924
- reverseCrisp = ((horiz && x === axis.pos) || (!horiz && y === axis.pos + axis.len)) ? -1 : 1, // #1480
5925
- staggerLines = axis.staggerLines;
5926
-
5927
- this.isActive = true;
5928
-
5929
- // create the grid line
5930
- if (gridLineWidth) {
5931
- gridLinePath = axis.getPlotLinePath(pos + tickmarkOffset, gridLineWidth * reverseCrisp, old, true);
5932
-
5933
- if (gridLine === UNDEFINED) {
5934
- attribs = {
5935
- stroke: gridLineColor,
5936
- 'stroke-width': gridLineWidth
5937
- };
5938
- if (dashStyle) {
5939
- attribs.dashstyle = dashStyle;
5940
- }
5941
- if (!type) {
5942
- attribs.zIndex = 1;
5943
- }
5944
- if (old) {
5945
- attribs.opacity = 0;
5946
- }
5947
- tick.gridLine = gridLine =
5948
- gridLineWidth ?
5949
- renderer.path(gridLinePath)
5950
- .attr(attribs).add(axis.gridGroup) :
5951
- null;
5952
- }
5953
-
5954
- // If the parameter 'old' is set, the current call will be followed
5955
- // by another call, therefore do not do any animations this time
5956
- if (!old && gridLine && gridLinePath) {
5957
- gridLine[tick.isNew ? 'attr' : 'animate']({
5958
- d: gridLinePath,
5959
- opacity: opacity
5960
- });
5961
- }
5962
- }
5963
-
5964
- // create the tick mark
5965
- if (tickWidth && tickLength) {
5966
-
5967
- // negate the length
5968
- if (tickPosition === 'inside') {
5969
- tickLength = -tickLength;
5970
- }
5971
- if (axis.opposite) {
5972
- tickLength = -tickLength;
5973
- }
5974
-
5975
- markPath = tick.getMarkPath(x, y, tickLength, tickWidth * reverseCrisp, horiz, renderer);
5976
-
5977
- if (mark) { // updating
5978
- mark.animate({
5979
- d: markPath,
5980
- opacity: opacity
5981
- });
5982
- } else { // first time
5983
- tick.mark = renderer.path(
5984
- markPath
5985
- ).attr({
5986
- stroke: tickColor,
5987
- 'stroke-width': tickWidth,
5988
- opacity: opacity
5989
- }).add(axis.axisGroup);
5990
- }
5991
- }
5992
-
5993
- // the label is created on init - now move it into place
5994
- if (label && !isNaN(x)) {
5995
- label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
5996
-
5997
- // apply show first and show last
5998
- if ((tick.isFirst && !pick(options.showFirstLabel, 1)) ||
5999
- (tick.isLast && !pick(options.showLastLabel, 1))) {
6000
- show = false;
6001
-
6002
- // Handle label overflow and show or hide accordingly
6003
- } else if (!staggerLines && horiz && labelOptions.overflow === 'justify' && !tick.handleOverflow(index, xy)) {
6004
- show = false;
6005
- }
6006
-
6007
- // apply step
6008
- if (step && index % step) {
6009
- // show those indices dividable by step
6010
- show = false;
6011
- }
6012
-
6013
- // Set the new position, and show or hide
6014
- if (show && !isNaN(xy.y)) {
6015
- xy.opacity = opacity;
6016
- label[tick.isNew ? 'attr' : 'animate'](xy);
6017
- tick.isNew = false;
6018
- } else {
6019
- label.attr('y', -9999); // #1338
6020
- }
6021
- }
6022
- },
6023
-
6024
- /**
6025
- * Destructor for the tick prototype
6026
- */
6027
- destroy: function () {
6028
- destroyObjectProperties(this, this.axis);
6029
- }
6030
- };
6031
-
6032
- /**
6033
- * The object wrapper for plot lines and plot bands
6034
- * @param {Object} options
6035
- */
6036
- function PlotLineOrBand(axis, options) {
6037
- this.axis = axis;
6038
-
6039
- if (options) {
6040
- this.options = options;
6041
- this.id = options.id;
6042
- }
6043
- }
6044
-
6045
- PlotLineOrBand.prototype = {
6046
-
6047
- /**
6048
- * Render the plot line or plot band. If it is already existing,
6049
- * move it.
6050
- */
6051
- render: function () {
6052
- var plotLine = this,
6053
- axis = plotLine.axis,
6054
- horiz = axis.horiz,
6055
- halfPointRange = (axis.pointRange || 0) / 2,
6056
- options = plotLine.options,
6057
- optionsLabel = options.label,
6058
- label = plotLine.label,
6059
- width = options.width,
6060
- to = options.to,
6061
- from = options.from,
6062
- isBand = defined(from) && defined(to),
6063
- value = options.value,
6064
- dashStyle = options.dashStyle,
6065
- svgElem = plotLine.svgElem,
6066
- path = [],
6067
- addEvent,
6068
- eventType,
6069
- xs,
6070
- ys,
6071
- x,
6072
- y,
6073
- color = options.color,
6074
- zIndex = options.zIndex,
6075
- events = options.events,
6076
- attribs,
6077
- renderer = axis.chart.renderer;
6078
-
6079
- // logarithmic conversion
6080
- if (axis.isLog) {
6081
- from = log2lin(from);
6082
- to = log2lin(to);
6083
- value = log2lin(value);
6084
- }
6085
-
6086
- // plot line
6087
- if (width) {
6088
- path = axis.getPlotLinePath(value, width);
6089
- attribs = {
6090
- stroke: color,
6091
- 'stroke-width': width
6092
- };
6093
- if (dashStyle) {
6094
- attribs.dashstyle = dashStyle;
6095
- }
6096
- } else if (isBand) { // plot band
6097
-
6098
- // keep within plot area
6099
- from = mathMax(from, axis.min - halfPointRange);
6100
- to = mathMin(to, axis.max + halfPointRange);
6101
-
6102
- path = axis.getPlotBandPath(from, to, options);
6103
- attribs = {
6104
- fill: color
6105
- };
6106
- if (options.borderWidth) {
6107
- attribs.stroke = options.borderColor;
6108
- attribs['stroke-width'] = options.borderWidth;
6109
- }
6110
- } else {
6111
- return;
6112
- }
6113
- // zIndex
6114
- if (defined(zIndex)) {
6115
- attribs.zIndex = zIndex;
6116
- }
6117
-
6118
- // common for lines and bands
6119
- if (svgElem) {
6120
- if (path) {
6121
- svgElem.animate({
6122
- d: path
6123
- }, null, svgElem.onGetPath);
6124
- } else {
6125
- svgElem.hide();
6126
- svgElem.onGetPath = function () {
6127
- svgElem.show();
6128
- };
6129
- }
6130
- } else if (path && path.length) {
6131
- plotLine.svgElem = svgElem = renderer.path(path)
6132
- .attr(attribs).add();
6133
-
6134
- // events
6135
- if (events) {
6136
- addEvent = function (eventType) {
6137
- svgElem.on(eventType, function (e) {
6138
- events[eventType].apply(plotLine, [e]);
6139
- });
6140
- };
6141
- for (eventType in events) {
6142
- addEvent(eventType);
6143
- }
6144
- }
6145
- }
6146
-
6147
- // the plot band/line label
6148
- if (optionsLabel && defined(optionsLabel.text) && path && path.length && axis.width > 0 && axis.height > 0) {
6149
- // apply defaults
6150
- optionsLabel = merge({
6151
- align: horiz && isBand && 'center',
6152
- x: horiz ? !isBand && 4 : 10,
6153
- verticalAlign : !horiz && isBand && 'middle',
6154
- y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
6155
- rotation: horiz && !isBand && 90
6156
- }, optionsLabel);
6157
-
6158
- // add the SVG element
6159
- if (!label) {
6160
- plotLine.label = label = renderer.text(
6161
- optionsLabel.text,
6162
- 0,
6163
- 0
6164
- )
6165
- .attr({
6166
- align: optionsLabel.textAlign || optionsLabel.align,
6167
- rotation: optionsLabel.rotation,
6168
- zIndex: zIndex
6169
- })
6170
- .css(optionsLabel.style)
6171
- .add();
6172
- }
6173
-
6174
- // get the bounding box and align the label
6175
- xs = [path[1], path[4], pick(path[6], path[1])];
6176
- ys = [path[2], path[5], pick(path[7], path[2])];
6177
- x = arrayMin(xs);
6178
- y = arrayMin(ys);
6179
-
6180
- label.align(optionsLabel, false, {
6181
- x: x,
6182
- y: y,
6183
- width: arrayMax(xs) - x,
6184
- height: arrayMax(ys) - y
6185
- });
6186
- label.show();
6187
-
6188
- } else if (label) { // move out of sight
6189
- label.hide();
6190
- }
6191
-
6192
- // chainable
6193
- return plotLine;
6194
- },
6195
-
6196
- /**
6197
- * Remove the plot line or band
6198
- */
6199
- destroy: function () {
6200
- var plotLine = this,
6201
- axis = plotLine.axis;
6202
-
6203
- // remove it from the lookup
6204
- erase(axis.plotLinesAndBands, plotLine);
6205
-
6206
- destroyObjectProperties(plotLine, this.axis);
6207
- }
6208
- };
6209
- /**
6210
- * The class for stack items
6211
- */
6212
- function StackItem(axis, options, isNegative, x, stackOption, stacking) {
6213
-
6214
- var inverted = axis.chart.inverted;
6215
-
6216
- this.axis = axis;
6217
-
6218
- // Tells if the stack is negative
6219
- this.isNegative = isNegative;
6220
-
6221
- // Save the options to be able to style the label
6222
- this.options = options;
6223
-
6224
- // Save the x value to be able to position the label later
6225
- this.x = x;
6226
-
6227
- // Save the stack option on the series configuration object, and whether to treat it as percent
6228
- this.stack = stackOption;
6229
- this.percent = stacking === 'percent';
6230
-
6231
- // The align options and text align varies on whether the stack is negative and
6232
- // if the chart is inverted or not.
6233
- // First test the user supplied value, then use the dynamic.
6234
- this.alignOptions = {
6235
- align: options.align || (inverted ? (isNegative ? 'left' : 'right') : 'center'),
6236
- verticalAlign: options.verticalAlign || (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
6237
- y: pick(options.y, inverted ? 4 : (isNegative ? 14 : -6)),
6238
- x: pick(options.x, inverted ? (isNegative ? -6 : 6) : 0)
6239
- };
6240
-
6241
- this.textAlign = options.textAlign || (inverted ? (isNegative ? 'right' : 'left') : 'center');
6242
- }
6243
-
6244
- StackItem.prototype = {
6245
- destroy: function () {
6246
- destroyObjectProperties(this, this.axis);
6247
- },
6248
-
6249
- /**
6250
- * Sets the total of this stack. Should be called when a serie is hidden or shown
6251
- * since that will affect the total of other stacks.
6252
- */
6253
- setTotal: function (total) {
6254
- this.total = total;
6255
- this.cum = total;
6256
- },
6257
-
6258
- /**
6259
- * Renders the stack total label and adds it to the stack label group.
6260
- */
6261
- render: function (group) {
6262
- var options = this.options,
6263
- formatOption = options.format, // docs: added stackLabel.format option
6264
- str = formatOption ?
6265
- format(formatOption, this) :
6266
- options.formatter.call(this); // format the text in the label
6267
-
6268
- // Change the text to reflect the new total and set visibility to hidden in case the serie is hidden
6269
- if (this.label) {
6270
- this.label.attr({text: str, visibility: HIDDEN});
6271
- // Create new label
6272
- } else {
6273
- this.label =
6274
- this.axis.chart.renderer.text(str, 0, 0, options.useHTML) // dummy positions, actual position updated with setOffset method in columnseries
6275
- .css(options.style) // apply style
6276
- .attr({
6277
- align: this.textAlign, // fix the text-anchor
6278
- rotation: options.rotation, // rotation
6279
- visibility: HIDDEN // hidden until setOffset is called
6280
- })
6281
- .add(group); // add to the labels-group
6282
- }
6283
- },
6284
-
6285
- /**
6286
- * Sets the offset that the stack has from the x value and repositions the label.
6287
- */
6288
- setOffset: function (xOffset, xWidth) {
6289
- var stackItem = this,
6290
- axis = stackItem.axis,
6291
- chart = axis.chart,
6292
- inverted = chart.inverted,
6293
- neg = this.isNegative, // special treatment is needed for negative stacks
6294
- y = axis.translate(this.percent ? 100 : this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates
6295
- yZero = axis.translate(0), // stack origin
6296
- h = mathAbs(y - yZero), // stack height
6297
- x = chart.xAxis[0].translate(this.x) + xOffset, // stack x position
6298
- plotHeight = chart.plotHeight,
6299
- stackBox = { // this is the box for the complete stack
6300
- x: inverted ? (neg ? y : y - h) : x,
6301
- y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y),
6302
- width: inverted ? h : xWidth,
6303
- height: inverted ? xWidth : h
6304
- },
6305
- label = this.label,
6306
- alignAttr;
6307
-
6308
- if (label) {
6309
- label.align(this.alignOptions, null, stackBox); // align the label to the box
6310
-
6311
- // Set visibility (#678)
6312
- alignAttr = label.alignAttr;
6313
- label.attr({
6314
- visibility: this.options.crop === false || chart.isInsidePlot(alignAttr.x, alignAttr.y) ?
6315
- (hasSVG ? 'inherit' : VISIBLE) :
6316
- HIDDEN
6317
- });
6318
- }
6319
- }
6320
- };
6321
- /**
6322
- * Create a new axis object
6323
- * @param {Object} chart
6324
- * @param {Object} options
6325
- */
6326
- function Axis() {
6327
- this.init.apply(this, arguments);
6328
- }
6329
-
6330
- Axis.prototype = {
6331
-
6332
- /**
6333
- * Default options for the X axis - the Y axis has extended defaults
6334
- */
6335
- defaultOptions: {
6336
- // allowDecimals: null,
6337
- // alternateGridColor: null,
6338
- // categories: [],
6339
- dateTimeLabelFormats: {
6340
- millisecond: '%H:%M:%S.%L',
6341
- second: '%H:%M:%S',
6342
- minute: '%H:%M',
6343
- hour: '%H:%M',
6344
- day: '%e. %b',
6345
- week: '%e. %b',
6346
- month: '%b \'%y',
6347
- year: '%Y'
6348
- },
6349
- endOnTick: false,
6350
- gridLineColor: '#C0C0C0',
6351
- // gridLineDashStyle: 'solid',
6352
- // gridLineWidth: 0,
6353
- // reversed: false,
6354
-
6355
- labels: defaultLabelOptions,
6356
- // { step: null },
6357
- lineColor: '#C0D0E0',
6358
- lineWidth: 1,
6359
- //linkedTo: null,
6360
- //max: undefined,
6361
- //min: undefined,
6362
- minPadding: 0.01,
6363
- maxPadding: 0.01,
6364
- //minRange: null,
6365
- minorGridLineColor: '#E0E0E0',
6366
- // minorGridLineDashStyle: null,
6367
- minorGridLineWidth: 1,
6368
- minorTickColor: '#A0A0A0',
6369
- //minorTickInterval: null,
6370
- minorTickLength: 2,
6371
- minorTickPosition: 'outside', // inside or outside
6372
- //minorTickWidth: 0,
6373
- //opposite: false,
6374
- //offset: 0,
6375
- //plotBands: [{
6376
- // events: {},
6377
- // zIndex: 1,
6378
- // labels: { align, x, verticalAlign, y, style, rotation, textAlign }
6379
- //}],
6380
- //plotLines: [{
6381
- // events: {}
6382
- // dashStyle: {}
6383
- // zIndex:
6384
- // labels: { align, x, verticalAlign, y, style, rotation, textAlign }
6385
- //}],
6386
- //reversed: false,
6387
- // showFirstLabel: true,
6388
- // showLastLabel: true,
6389
- startOfWeek: 1,
6390
- startOnTick: false,
6391
- tickColor: '#C0D0E0',
6392
- //tickInterval: null,
6393
- tickLength: 5,
6394
- tickmarkPlacement: 'between', // on or between
6395
- tickPixelInterval: 100,
6396
- tickPosition: 'outside',
6397
- tickWidth: 1,
6398
- title: {
6399
- //text: null,
6400
- align: 'middle', // low, middle or high
6401
- //margin: 0 for horizontal, 10 for vertical axes,
6402
- //rotation: 0,
6403
- //side: 'outside',
6404
- style: {
6405
- color: '#4d759e',
6406
- //font: defaultFont.replace('normal', 'bold')
6407
- fontWeight: 'bold'
6408
- }
6409
- //x: 0,
6410
- //y: 0
6411
- },
6412
- type: 'linear' // linear, logarithmic or datetime
6413
- },
6414
-
6415
- /**
6416
- * This options set extends the defaultOptions for Y axes
6417
- */
6418
- defaultYAxisOptions: {
6419
- endOnTick: true,
6420
- gridLineWidth: 1,
6421
- tickPixelInterval: 72,
6422
- showLastLabel: true,
6423
- labels: {
6424
- align: 'right',
6425
- x: -8,
6426
- y: 3
6427
- },
6428
- lineWidth: 0,
6429
- maxPadding: 0.05,
6430
- minPadding: 0.05,
6431
- startOnTick: true,
6432
- tickWidth: 0,
6433
- title: {
6434
- rotation: 270,
6435
- text: 'Values'
6436
- },
6437
- stackLabels: {
6438
- enabled: false,
6439
- //align: dynamic,
6440
- //y: dynamic,
6441
- //x: dynamic,
6442
- //verticalAlign: dynamic,
6443
- //textAlign: dynamic,
6444
- //rotation: 0,
6445
- formatter: function () {
6446
- return numberFormat(this.total, -1);
6447
- },
6448
- style: defaultLabelOptions.style
6449
- }
6450
- },
6451
-
6452
- /**
6453
- * These options extend the defaultOptions for left axes
6454
- */
6455
- defaultLeftAxisOptions: {
6456
- labels: {
6457
- align: 'right',
6458
- x: -8,
6459
- y: null
6460
- },
6461
- title: {
6462
- rotation: 270
6463
- }
6464
- },
6465
-
6466
- /**
6467
- * These options extend the defaultOptions for right axes
6468
- */
6469
- defaultRightAxisOptions: {
6470
- labels: {
6471
- align: 'left',
6472
- x: 8,
6473
- y: null
6474
- },
6475
- title: {
6476
- rotation: 90
6477
- }
6478
- },
6479
-
6480
- /**
6481
- * These options extend the defaultOptions for bottom axes
6482
- */
6483
- defaultBottomAxisOptions: {
6484
- labels: {
6485
- align: 'center',
6486
- x: 0,
6487
- y: 14
6488
- // overflow: undefined,
6489
- // staggerLines: null
6490
- },
6491
- title: {
6492
- rotation: 0
6493
- }
6494
- },
6495
- /**
6496
- * These options extend the defaultOptions for left axes
6497
- */
6498
- defaultTopAxisOptions: {
6499
- labels: {
6500
- align: 'center',
6501
- x: 0,
6502
- y: -5
6503
- // overflow: undefined
6504
- // staggerLines: null
6505
- },
6506
- title: {
6507
- rotation: 0
6508
- }
6509
- },
6510
-
6511
- /**
6512
- * Initialize the axis
6513
- */
6514
- init: function (chart, userOptions) {
6515
-
6516
-
6517
- var isXAxis = userOptions.isX,
6518
- axis = this;
6519
-
6520
- // Flag, is the axis horizontal
6521
- axis.horiz = chart.inverted ? !isXAxis : isXAxis;
6522
-
6523
- // Flag, isXAxis
6524
- axis.isXAxis = isXAxis;
6525
- axis.xOrY = isXAxis ? 'x' : 'y';
6526
-
6527
-
6528
- axis.opposite = userOptions.opposite; // needed in setOptions
6529
- axis.side = axis.horiz ?
6530
- (axis.opposite ? 0 : 2) : // top : bottom
6531
- (axis.opposite ? 1 : 3); // right : left
6532
-
6533
- axis.setOptions(userOptions);
6534
-
6535
-
6536
- var options = this.options,
6537
- type = options.type,
6538
- isDatetimeAxis = type === 'datetime';
6539
-
6540
- axis.labelFormatter = options.labels.formatter || axis.defaultLabelFormatter; // can be overwritten by dynamic format
6541
-
6542
-
6543
- // Flag, stagger lines or not
6544
- axis.staggerLines = axis.horiz && options.labels.staggerLines;
6545
- axis.userOptions = userOptions;
6546
-
6547
- //axis.axisTitleMargin = UNDEFINED,// = options.title.margin,
6548
- axis.minPixelPadding = 0;
6549
- //axis.ignoreMinPadding = UNDEFINED; // can be set to true by a column or bar series
6550
- //axis.ignoreMaxPadding = UNDEFINED;
6551
-
6552
- axis.chart = chart;
6553
- axis.reversed = options.reversed;
6554
- axis.zoomEnabled = options.zoomEnabled !== false;
6555
-
6556
- // Initial categories
6557
- axis.categories = options.categories || type === 'category';
6558
-
6559
- // Elements
6560
- //axis.axisGroup = UNDEFINED;
6561
- //axis.gridGroup = UNDEFINED;
6562
- //axis.axisTitle = UNDEFINED;
6563
- //axis.axisLine = UNDEFINED;
6564
-
6565
- // Shorthand types
6566
- axis.isLog = type === 'logarithmic';
6567
- axis.isDatetimeAxis = isDatetimeAxis;
6568
-
6569
- // Flag, if axis is linked to another axis
6570
- axis.isLinked = defined(options.linkedTo);
6571
- // Linked axis.
6572
- //axis.linkedParent = UNDEFINED;
6573
-
6574
-
6575
- // Flag if percentage mode
6576
- //axis.usePercentage = UNDEFINED;
6577
-
6578
-
6579
- // Tick positions
6580
- //axis.tickPositions = UNDEFINED; // array containing predefined positions
6581
- // Tick intervals
6582
- //axis.tickInterval = UNDEFINED;
6583
- //axis.minorTickInterval = UNDEFINED;
6584
-
6585
- axis.tickmarkOffset = (axis.categories && options.tickmarkPlacement === 'between') ? 0.5 : 0;
6586
-
6587
- // Major ticks
6588
- axis.ticks = {};
6589
- // Minor ticks
6590
- axis.minorTicks = {};
6591
- //axis.tickAmount = UNDEFINED;
6592
-
6593
- // List of plotLines/Bands
6594
- axis.plotLinesAndBands = [];
6595
-
6596
- // Alternate bands
6597
- axis.alternateBands = {};
6598
-
6599
- // Axis metrics
6600
- //axis.left = UNDEFINED;
6601
- //axis.top = UNDEFINED;
6602
- //axis.width = UNDEFINED;
6603
- //axis.height = UNDEFINED;
6604
- //axis.bottom = UNDEFINED;
6605
- //axis.right = UNDEFINED;
6606
- //axis.transA = UNDEFINED;
6607
- //axis.transB = UNDEFINED;
6608
- //axis.oldTransA = UNDEFINED;
6609
- axis.len = 0;
6610
- //axis.oldMin = UNDEFINED;
6611
- //axis.oldMax = UNDEFINED;
6612
- //axis.oldUserMin = UNDEFINED;
6613
- //axis.oldUserMax = UNDEFINED;
6614
- //axis.oldAxisLength = UNDEFINED;
6615
- axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
6616
- axis.range = options.range;
6617
- axis.offset = options.offset || 0;
6618
-
6619
-
6620
- // Dictionary for stacks
6621
- axis.stacks = {};
6622
- axis._stacksTouched = 0;
6623
-
6624
- // Min and max in the data
6625
- //axis.dataMin = UNDEFINED,
6626
- //axis.dataMax = UNDEFINED,
6627
-
6628
- // The axis range
6629
- axis.max = null;
6630
- axis.min = null;
6631
-
6632
- // User set min and max
6633
- //axis.userMin = UNDEFINED,
6634
- //axis.userMax = UNDEFINED,
6635
-
6636
- // Run Axis
6637
-
6638
- var eventType,
6639
- events = axis.options.events;
6640
-
6641
- // Register
6642
- if (inArray(axis, chart.axes) === -1) { // don't add it again on Axis.update()
6643
- chart.axes.push(axis);
6644
- chart[isXAxis ? 'xAxis' : 'yAxis'].push(axis);
6645
- }
6646
-
6647
- axis.series = axis.series || []; // populated by Series
6648
-
6649
- // inverted charts have reversed xAxes as default
6650
- if (chart.inverted && isXAxis && axis.reversed === UNDEFINED) {
6651
- axis.reversed = true;
6652
- }
6653
-
6654
- axis.removePlotBand = axis.removePlotBandOrLine;
6655
- axis.removePlotLine = axis.removePlotBandOrLine;
6656
-
6657
-
6658
- // register event listeners
6659
- for (eventType in events) {
6660
- addEvent(axis, eventType, events[eventType]);
6661
- }
6662
-
6663
- // extend logarithmic axis
6664
- if (axis.isLog) {
6665
- axis.val2lin = log2lin;
6666
- axis.lin2val = lin2log;
6667
- }
6668
- },
6669
-
6670
- /**
6671
- * Merge and set options
6672
- */
6673
- setOptions: function (userOptions) {
6674
- this.options = merge(
6675
- this.defaultOptions,
6676
- this.isXAxis ? {} : this.defaultYAxisOptions,
6677
- [this.defaultTopAxisOptions, this.defaultRightAxisOptions,
6678
- this.defaultBottomAxisOptions, this.defaultLeftAxisOptions][this.side],
6679
- merge(
6680
- defaultOptions[this.isXAxis ? 'xAxis' : 'yAxis'], // if set in setOptions (#1053)
6681
- userOptions
6682
- )
6683
- );
6684
- },
6685
-
6686
- /**
6687
- * Update the axis with a new options structure
6688
- */
6689
- update: function (newOptions, redraw) {
6690
- var chart = this.chart;
6691
-
6692
- newOptions = chart.options[this.xOrY + 'Axis'][this.options.index] = merge(this.userOptions, newOptions);
6693
-
6694
- this.destroy();
6695
- this._addedPlotLB = false; // #1611
6696
-
6697
- this.init(chart, newOptions);
6698
-
6699
- chart.isDirtyBox = true;
6700
- if (pick(redraw, true)) {
6701
- chart.redraw();
6702
- }
6703
- },
6704
-
6705
- /**
6706
- * Remove the axis from the chart
6707
- */
6708
- remove: function (redraw) {
6709
- var chart = this.chart,
6710
- key = this.xOrY + 'Axis'; // xAxis or yAxis
6711
-
6712
- // Remove associated series
6713
- each(this.series, function (series) {
6714
- series.remove(false);
6715
- });
6716
-
6717
- // Remove the axis
6718
- erase(chart.axes, this);
6719
- erase(chart[key], this);
6720
- chart.options[key].splice(this.options.index, 1);
6721
- each(chart[key], function (axis, i) { // Re-index, #1706
6722
- axis.options.index = i;
6723
- });
6724
- this.destroy();
6725
- chart.isDirtyBox = true;
6726
-
6727
- if (pick(redraw, true)) {
6728
- chart.redraw();
6729
- }
6730
- },
6731
-
6732
- /**
6733
- * The default label formatter. The context is a special config object for the label.
6734
- */
6735
- defaultLabelFormatter: function () {
6736
- var axis = this.axis,
6737
- value = this.value,
6738
- categories = axis.categories,
6739
- dateTimeLabelFormat = this.dateTimeLabelFormat,
6740
- numericSymbols = defaultOptions.lang.numericSymbols,
6741
- i = numericSymbols && numericSymbols.length,
6742
- multi,
6743
- ret,
6744
- formatOption = axis.options.labels.format,
6745
-
6746
- // make sure the same symbol is added for all labels on a linear axis
6747
- numericSymbolDetector = axis.isLog ? value : axis.tickInterval;
6748
-
6749
- if (formatOption) {
6750
- ret = format(formatOption, this);
6751
-
6752
- } else if (categories) {
6753
- ret = value;
6754
-
6755
- } else if (dateTimeLabelFormat) { // datetime axis
6756
- ret = dateFormat(dateTimeLabelFormat, value);
6757
-
6758
- } else if (i && numericSymbolDetector >= 1000) {
6759
- // Decide whether we should add a numeric symbol like k (thousands) or M (millions).
6760
- // If we are to enable this in tooltip or other places as well, we can move this
6761
- // logic to the numberFormatter and enable it by a parameter.
6762
- while (i-- && ret === UNDEFINED) {
6763
- multi = Math.pow(1000, i + 1);
6764
- if (numericSymbolDetector >= multi && numericSymbols[i] !== null) {
6765
- ret = numberFormat(value / multi, -1) + numericSymbols[i];
6766
- }
6767
- }
6768
- }
6769
-
6770
- if (ret === UNDEFINED) {
6771
- if (value >= 1000) { // add thousands separators
6772
- ret = numberFormat(value, 0);
6773
-
6774
- } else { // small numbers
6775
- ret = numberFormat(value, -1);
6776
- }
6777
- }
6778
-
6779
- return ret;
6780
- },
6781
-
6782
- /**
6783
- * Get the minimum and maximum for the series of each axis
6784
- */
6785
- getSeriesExtremes: function () {
6786
- var axis = this,
6787
- chart = axis.chart,
6788
- stacks = axis.stacks,
6789
- posStack = [],
6790
- negStack = [],
6791
- stacksTouched = axis._stacksTouched = axis._stacksTouched + 1,
6792
- type,
6793
- i;
6794
-
6795
- axis.hasVisibleSeries = false;
6796
-
6797
- // reset dataMin and dataMax in case we're redrawing
6798
- axis.dataMin = axis.dataMax = null;
6799
-
6800
- // loop through this axis' series
6801
- each(axis.series, function (series) {
6802
-
6803
- if (series.visible || !chart.options.chart.ignoreHiddenSeries) {
6804
-
6805
- var seriesOptions = series.options,
6806
- stacking,
6807
- posPointStack,
6808
- negPointStack,
6809
- stackKey,
6810
- stackOption,
6811
- negKey,
6812
- xData,
6813
- yData,
6814
- x,
6815
- y,
6816
- threshold = seriesOptions.threshold,
6817
- yDataLength,
6818
- activeYData = [],
6819
- seriesDataMin,
6820
- seriesDataMax,
6821
- activeCounter = 0;
6822
-
6823
- axis.hasVisibleSeries = true;
6824
-
6825
- // Validate threshold in logarithmic axes
6826
- if (axis.isLog && threshold <= 0) {
6827
- threshold = seriesOptions.threshold = null;
6828
- }
6829
-
6830
- // Get dataMin and dataMax for X axes
6831
- if (axis.isXAxis) {
6832
- xData = series.xData;
6833
- if (xData.length) {
6834
- axis.dataMin = mathMin(pick(axis.dataMin, xData[0]), arrayMin(xData));
6835
- axis.dataMax = mathMax(pick(axis.dataMax, xData[0]), arrayMax(xData));
6836
- }
6837
-
6838
- // Get dataMin and dataMax for Y axes, as well as handle stacking and processed data
6839
- } else {
6840
- var isNegative,
6841
- pointStack,
6842
- key,
6843
- cropped = series.cropped,
6844
- xExtremes = series.xAxis.getExtremes(),
6845
- //findPointRange,
6846
- //pointRange,
6847
- j,
6848
- hasModifyValue = !!series.modifyValue;
6849
-
6850
- // Handle stacking
6851
- stacking = seriesOptions.stacking;
6852
- axis.usePercentage = stacking === 'percent';
6853
-
6854
- // create a stack for this particular series type
6855
- if (stacking) {
6856
- stackOption = seriesOptions.stack;
6857
- stackKey = series.type + pick(stackOption, '');
6858
- negKey = '-' + stackKey;
6859
- series.stackKey = stackKey; // used in translate
6860
-
6861
- posPointStack = posStack[stackKey] || []; // contains the total values for each x
6862
- posStack[stackKey] = posPointStack;
6863
-
6864
- negPointStack = negStack[negKey] || [];
6865
- negStack[negKey] = negPointStack;
6866
- }
6867
- if (axis.usePercentage) {
6868
- axis.dataMin = 0;
6869
- axis.dataMax = 99;
6870
- }
6871
-
6872
- // processData can alter series.pointRange, so this goes after
6873
- //findPointRange = series.pointRange === null;
6874
-
6875
- xData = series.processedXData;
6876
- yData = series.processedYData;
6877
- yDataLength = yData.length;
6878
-
6879
- // loop over the non-null y values and read them into a local array
6880
- for (i = 0; i < yDataLength; i++) {
6881
- x = xData[i];
6882
- y = yData[i];
6883
-
6884
- // Read stacked values into a stack based on the x value,
6885
- // the sign of y and the stack key. Stacking is also handled for null values (#739)
6886
- if (stacking) {
6887
- isNegative = y < threshold;
6888
- pointStack = isNegative ? negPointStack : posPointStack;
6889
- key = isNegative ? negKey : stackKey;
6890
-
6891
- // Set the stack value and y for extremes
6892
- if (defined(pointStack[x])) { // we're adding to the stack
6893
- pointStack[x] = correctFloat(pointStack[x] + y);
6894
- y = [y, pointStack[x]]; // consider both the actual value and the stack (#1376)
6895
-
6896
- } else { // it's the first point in the stack
6897
- pointStack[x] = y;
6898
- }
6899
-
6900
- // add the series
6901
- if (!stacks[key]) {
6902
- stacks[key] = {};
6903
- }
6904
-
6905
- // If the StackItem is there, just update the values,
6906
- // if not, create one first
6907
- if (!stacks[key][x]) {
6908
- stacks[key][x] = new StackItem(axis, axis.options.stackLabels, isNegative, x, stackOption, stacking);
6909
- }
6910
- stacks[key][x].setTotal(pointStack[x]);
6911
- stacks[key][x].touched = stacksTouched;
6912
- }
6913
-
6914
- // Handle non null values
6915
- if (y !== null && y !== UNDEFINED && (!axis.isLog || (y.length || y > 0))) {
6916
-
6917
- // general hook, used for Highstock compare values feature
6918
- if (hasModifyValue) {
6919
- y = series.modifyValue(y);
6920
- }
6921
-
6922
- // For points within the visible range, including the first point outside the
6923
- // visible range, consider y extremes
6924
- if (series.getExtremesFromAll || cropped || ((xData[i + 1] || x) >= xExtremes.min &&
6925
- (xData[i - 1] || x) <= xExtremes.max)) {
6926
-
6927
- j = y.length;
6928
- if (j) { // array, like ohlc or range data
6929
- while (j--) {
6930
- if (y[j] !== null) {
6931
- activeYData[activeCounter++] = y[j];
6932
- }
6933
- }
6934
- } else {
6935
- activeYData[activeCounter++] = y;
6936
- }
6937
- }
6938
- }
6939
- }
6940
-
6941
- // Get the dataMin and dataMax so far. If percentage is used, the min and max are
6942
- // always 0 and 100. If the length of activeYData is 0, continue with null values.
6943
- if (!axis.usePercentage && activeYData.length) {
6944
- series.dataMin = seriesDataMin = arrayMin(activeYData);
6945
- series.dataMax = seriesDataMax = arrayMax(activeYData);
6946
- axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin);
6947
- axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax);
6948
- }
6949
-
6950
- // Adjust to threshold
6951
- if (defined(threshold)) {
6952
- if (axis.dataMin >= threshold) {
6953
- axis.dataMin = threshold;
6954
- axis.ignoreMinPadding = true;
6955
- } else if (axis.dataMax < threshold) {
6956
- axis.dataMax = threshold;
6957
- axis.ignoreMaxPadding = true;
6958
- }
6959
- }
6960
- }
6961
- }
6962
- });
6963
-
6964
- // Destroy unused stacks (#1044)
6965
- for (type in stacks) {
6966
- for (i in stacks[type]) {
6967
- if (stacks[type][i].touched < stacksTouched) {
6968
- stacks[type][i].destroy();
6969
- delete stacks[type][i];
6970
- }
6971
- }
6972
- }
6973
-
6974
- },
6975
-
6976
- /**
6977
- * Translate from axis value to pixel position on the chart, or back
6978
- *
6979
- */
6980
- translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacementBetween) {
6981
- var axis = this,
6982
- axisLength = axis.len,
6983
- sign = 1,
6984
- cvsOffset = 0,
6985
- localA = old ? axis.oldTransA : axis.transA,
6986
- localMin = old ? axis.oldMin : axis.min,
6987
- returnValue,
6988
- minPixelPadding = axis.minPixelPadding,
6989
- postTranslate = (axis.options.ordinal || (axis.isLog && handleLog)) && axis.lin2val;
6990
-
6991
- if (!localA) {
6992
- localA = axis.transA;
6993
- }
6994
-
6995
- // In vertical axes, the canvas coordinates start from 0 at the top like in
6996
- // SVG.
6997
- if (cvsCoord) {
6998
- sign *= -1; // canvas coordinates inverts the value
6999
- cvsOffset = axisLength;
7000
- }
7001
-
7002
- // Handle reversed axis
7003
- if (axis.reversed) {
7004
- sign *= -1;
7005
- cvsOffset -= sign * axisLength;
7006
- }
7007
-
7008
- // From pixels to value
7009
- if (backwards) { // reverse translation
7010
-
7011
- val = val * sign + cvsOffset;
7012
- val -= minPixelPadding;
7013
- returnValue = val / localA + localMin; // from chart pixel to value
7014
- if (postTranslate) { // log and ordinal axes
7015
- returnValue = axis.lin2val(returnValue);
7016
- }
7017
-
7018
- // From value to pixels
7019
- } else {
7020
- if (postTranslate) { // log and ordinal axes
7021
- val = axis.val2lin(val);
7022
- }
7023
-
7024
- returnValue = sign * (val - localMin) * localA + cvsOffset + (sign * minPixelPadding) +
7025
- (pointPlacementBetween ? localA * axis.pointRange / 2 : 0);
7026
- }
7027
-
7028
- return returnValue;
7029
- },
7030
-
7031
- /**
7032
- * Utility method to translate an axis value to pixel position.
7033
- * @param {Number} value A value in terms of axis units
7034
- * @param {Boolean} paneCoordinates Whether to return the pixel coordinate relative to the chart
7035
- * or just the axis/pane itself.
7036
- */
7037
- toPixels: function (value, paneCoordinates) {
7038
- return this.translate(value, false, !this.horiz, null, true) + (paneCoordinates ? 0 : this.pos);
7039
- },
7040
-
7041
- /*
7042
- * Utility method to translate a pixel position in to an axis value
7043
- * @param {Number} pixel The pixel value coordinate
7044
- * @param {Boolean} paneCoordiantes Whether the input pixel is relative to the chart or just the
7045
- * axis/pane itself.
7046
- */
7047
- toValue: function (pixel, paneCoordinates) {
7048
- return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true);
7049
- },
7050
-
7051
- /**
7052
- * Create the path for a plot line that goes from the given value on
7053
- * this axis, across the plot to the opposite side
7054
- * @param {Number} value
7055
- * @param {Number} lineWidth Used for calculation crisp line
7056
- * @param {Number] old Use old coordinates (for resizing and rescaling)
7057
- */
7058
- getPlotLinePath: function (value, lineWidth, old, force) {
7059
- var axis = this,
7060
- chart = axis.chart,
7061
- axisLeft = axis.left,
7062
- axisTop = axis.top,
7063
- x1,
7064
- y1,
7065
- x2,
7066
- y2,
7067
- translatedValue = axis.translate(value, null, null, old),
7068
- cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
7069
- cWidth = (old && chart.oldChartWidth) || chart.chartWidth,
7070
- skip,
7071
- transB = axis.transB;
7072
-
7073
- x1 = x2 = mathRound(translatedValue + transB);
7074
- y1 = y2 = mathRound(cHeight - translatedValue - transB);
7075
-
7076
- if (isNaN(translatedValue)) { // no min or max
7077
- skip = true;
7078
-
7079
- } else if (axis.horiz) {
7080
- y1 = axisTop;
7081
- y2 = cHeight - axis.bottom;
7082
- if (x1 < axisLeft || x1 > axisLeft + axis.width) {
7083
- skip = true;
7084
- }
7085
- } else {
7086
- x1 = axisLeft;
7087
- x2 = cWidth - axis.right;
7088
-
7089
- if (y1 < axisTop || y1 > axisTop + axis.height) {
7090
- skip = true;
7091
- }
7092
- }
7093
- return skip && !force ?
7094
- null :
7095
- chart.renderer.crispLine([M, x1, y1, L, x2, y2], lineWidth || 0);
7096
- },
7097
-
7098
- /**
7099
- * Create the path for a plot band
7100
- */
7101
- getPlotBandPath: function (from, to) {
7102
-
7103
- var toPath = this.getPlotLinePath(to),
7104
- path = this.getPlotLinePath(from);
7105
-
7106
- if (path && toPath) {
7107
- path.push(
7108
- toPath[4],
7109
- toPath[5],
7110
- toPath[1],
7111
- toPath[2]
7112
- );
7113
- } else { // outside the axis area
7114
- path = null;
7115
- }
7116
-
7117
- return path;
7118
- },
7119
-
7120
- /**
7121
- * Set the tick positions of a linear axis to round values like whole tens or every five.
7122
- */
7123
- getLinearTickPositions: function (tickInterval, min, max) {
7124
- var pos,
7125
- lastPos,
7126
- roundedMin = correctFloat(mathFloor(min / tickInterval) * tickInterval),
7127
- roundedMax = correctFloat(mathCeil(max / tickInterval) * tickInterval),
7128
- tickPositions = [];
7129
-
7130
- // Populate the intermediate values
7131
- pos = roundedMin;
7132
- while (pos <= roundedMax) {
7133
-
7134
- // Place the tick on the rounded value
7135
- tickPositions.push(pos);
7136
-
7137
- // Always add the raw tickInterval, not the corrected one.
7138
- pos = correctFloat(pos + tickInterval);
7139
-
7140
- // If the interval is not big enough in the current min - max range to actually increase
7141
- // the loop variable, we need to break out to prevent endless loop. Issue #619
7142
- if (pos === lastPos) {
7143
- break;
7144
- }
7145
-
7146
- // Record the last value
7147
- lastPos = pos;
7148
- }
7149
- return tickPositions;
7150
- },
7151
-
7152
- /**
7153
- * Set the tick positions of a logarithmic axis
7154
- */
7155
- getLogTickPositions: function (interval, min, max, minor) {
7156
- var axis = this,
7157
- options = axis.options,
7158
- axisLength = axis.len,
7159
- // Since we use this method for both major and minor ticks,
7160
- // use a local variable and return the result
7161
- positions = [];
7162
-
7163
- // Reset
7164
- if (!minor) {
7165
- axis._minorAutoInterval = null;
7166
- }
7167
-
7168
- // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
7169
- if (interval >= 0.5) {
7170
- interval = mathRound(interval);
7171
- positions = axis.getLinearTickPositions(interval, min, max);
7172
-
7173
- // Second case: We need intermediary ticks. For example
7174
- // 1, 2, 4, 6, 8, 10, 20, 40 etc.
7175
- } else if (interval >= 0.08) {
7176
- var roundedMin = mathFloor(min),
7177
- intermediate,
7178
- i,
7179
- j,
7180
- len,
7181
- pos,
7182
- lastPos,
7183
- break2;
7184
-
7185
- if (interval > 0.3) {
7186
- intermediate = [1, 2, 4];
7187
- } else if (interval > 0.15) { // 0.2 equals five minor ticks per 1, 10, 100 etc
7188
- intermediate = [1, 2, 4, 6, 8];
7189
- } else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
7190
- intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
7191
- }
7192
-
7193
- for (i = roundedMin; i < max + 1 && !break2; i++) {
7194
- len = intermediate.length;
7195
- for (j = 0; j < len && !break2; j++) {
7196
- pos = log2lin(lin2log(i) * intermediate[j]);
7197
-
7198
- if (pos > min && (!minor || lastPos <= max)) { // #1670
7199
- positions.push(lastPos);
7200
- }
7201
-
7202
- if (lastPos > max) {
7203
- break2 = true;
7204
- }
7205
- lastPos = pos;
7206
- }
7207
- }
7208
-
7209
- // Third case: We are so deep in between whole logarithmic values that
7210
- // we might as well handle the tick positions like a linear axis. For
7211
- // example 1.01, 1.02, 1.03, 1.04.
7212
- } else {
7213
- var realMin = lin2log(min),
7214
- realMax = lin2log(max),
7215
- tickIntervalOption = options[minor ? 'minorTickInterval' : 'tickInterval'],
7216
- filteredTickIntervalOption = tickIntervalOption === 'auto' ? null : tickIntervalOption,
7217
- tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1),
7218
- totalPixelLength = minor ? axisLength / axis.tickPositions.length : axisLength;
7219
-
7220
- interval = pick(
7221
- filteredTickIntervalOption,
7222
- axis._minorAutoInterval,
7223
- (realMax - realMin) * tickPixelIntervalOption / (totalPixelLength || 1)
7224
- );
7225
-
7226
- interval = normalizeTickInterval(
7227
- interval,
7228
- null,
7229
- math.pow(10, mathFloor(math.log(interval) / math.LN10))
7230
- );
7231
-
7232
- positions = map(axis.getLinearTickPositions(
7233
- interval,
7234
- realMin,
7235
- realMax
7236
- ), log2lin);
7237
-
7238
- if (!minor) {
7239
- axis._minorAutoInterval = interval / 5;
7240
- }
7241
- }
7242
-
7243
- // Set the axis-level tickInterval variable
7244
- if (!minor) {
7245
- axis.tickInterval = interval;
7246
- }
7247
- return positions;
7248
- },
7249
-
7250
- /**
7251
- * Return the minor tick positions. For logarithmic axes, reuse the same logic
7252
- * as for major ticks.
7253
- */
7254
- getMinorTickPositions: function () {
7255
- var axis = this,
7256
- options = axis.options,
7257
- tickPositions = axis.tickPositions,
7258
- minorTickInterval = axis.minorTickInterval,
7259
- minorTickPositions = [],
7260
- pos,
7261
- i,
7262
- len;
7263
-
7264
- if (axis.isLog) {
7265
- len = tickPositions.length;
7266
- for (i = 1; i < len; i++) {
7267
- minorTickPositions = minorTickPositions.concat(
7268
- axis.getLogTickPositions(minorTickInterval, tickPositions[i - 1], tickPositions[i], true)
7269
- );
7270
- }
7271
- } else if (axis.isDatetimeAxis && options.minorTickInterval === 'auto') { // #1314
7272
- minorTickPositions = minorTickPositions.concat(
7273
- getTimeTicks(
7274
- normalizeTimeTickInterval(minorTickInterval),
7275
- axis.min,
7276
- axis.max,
7277
- options.startOfWeek
7278
- )
7279
- );
7280
- if (minorTickPositions[0] < axis.min) {
7281
- minorTickPositions.shift();
7282
- }
7283
- } else {
7284
- for (pos = axis.min + (tickPositions[0] - axis.min) % minorTickInterval; pos <= axis.max; pos += minorTickInterval) {
7285
- minorTickPositions.push(pos);
7286
- }
7287
- }
7288
- return minorTickPositions;
7289
- },
7290
-
7291
- /**
7292
- * Adjust the min and max for the minimum range. Keep in mind that the series data is
7293
- * not yet processed, so we don't have information on data cropping and grouping, or
7294
- * updated axis.pointRange or series.pointRange. The data can't be processed until
7295
- * we have finally established min and max.
7296
- */
7297
- adjustForMinRange: function () {
7298
- var axis = this,
7299
- options = axis.options,
7300
- min = axis.min,
7301
- max = axis.max,
7302
- zoomOffset,
7303
- spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange,
7304
- closestDataRange,
7305
- i,
7306
- distance,
7307
- xData,
7308
- loopLength,
7309
- minArgs,
7310
- maxArgs;
7311
-
7312
- // Set the automatic minimum range based on the closest point distance
7313
- if (axis.isXAxis && axis.minRange === UNDEFINED && !axis.isLog) {
7314
-
7315
- if (defined(options.min) || defined(options.max)) {
7316
- axis.minRange = null; // don't do this again
7317
-
7318
- } else {
7319
-
7320
- // Find the closest distance between raw data points, as opposed to
7321
- // closestPointRange that applies to processed points (cropped and grouped)
7322
- each(axis.series, function (series) {
7323
- xData = series.xData;
7324
- loopLength = series.xIncrement ? 1 : xData.length - 1;
7325
- for (i = loopLength; i > 0; i--) {
7326
- distance = xData[i] - xData[i - 1];
7327
- if (closestDataRange === UNDEFINED || distance < closestDataRange) {
7328
- closestDataRange = distance;
7329
- }
7330
- }
7331
- });
7332
- axis.minRange = mathMin(closestDataRange * 5, axis.dataMax - axis.dataMin);
7333
- }
7334
- }
7335
-
7336
- // if minRange is exceeded, adjust
7337
- if (max - min < axis.minRange) {
7338
- var minRange = axis.minRange;
7339
- zoomOffset = (minRange - max + min) / 2;
7340
-
7341
- // if min and max options have been set, don't go beyond it
7342
- minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)];
7343
- if (spaceAvailable) { // if space is available, stay within the data range
7344
- minArgs[2] = axis.dataMin;
7345
- }
7346
- min = arrayMax(minArgs);
7347
-
7348
- maxArgs = [min + minRange, pick(options.max, min + minRange)];
7349
- if (spaceAvailable) { // if space is availabe, stay within the data range
7350
- maxArgs[2] = axis.dataMax;
7351
- }
7352
-
7353
- max = arrayMin(maxArgs);
7354
-
7355
- // now if the max is adjusted, adjust the min back
7356
- if (max - min < minRange) {
7357
- minArgs[0] = max - minRange;
7358
- minArgs[1] = pick(options.min, max - minRange);
7359
- min = arrayMax(minArgs);
7360
- }
7361
- }
7362
-
7363
- // Record modified extremes
7364
- axis.min = min;
7365
- axis.max = max;
7366
- },
7367
-
7368
- /**
7369
- * Update translation information
7370
- */
7371
- setAxisTranslation: function (saveOld) {
7372
- var axis = this,
7373
- range = axis.max - axis.min,
7374
- pointRange = 0,
7375
- closestPointRange,
7376
- minPointOffset = 0,
7377
- pointRangePadding = 0,
7378
- linkedParent = axis.linkedParent,
7379
- ordinalCorrection,
7380
- transA = axis.transA;
7381
-
7382
- // adjust translation for padding
7383
- if (axis.isXAxis) {
7384
- if (linkedParent) {
7385
- minPointOffset = linkedParent.minPointOffset;
7386
- pointRangePadding = linkedParent.pointRangePadding;
7387
-
7388
- } else {
7389
- each(axis.series, function (series) {
7390
- var seriesPointRange = series.pointRange,
7391
- pointPlacement = series.options.pointPlacement,
7392
- seriesClosestPointRange = series.closestPointRange;
7393
-
7394
- if (seriesPointRange > range) { // #1446
7395
- seriesPointRange = 0;
7396
- }
7397
- pointRange = mathMax(pointRange, seriesPointRange);
7398
-
7399
- // minPointOffset is the value padding to the left of the axis in order to make
7400
- // room for points with a pointRange, typically columns. When the pointPlacement option
7401
- // is 'between' or 'on', this padding does not apply.
7402
- minPointOffset = mathMax(
7403
- minPointOffset,
7404
- pointPlacement ? 0 : seriesPointRange / 2
7405
- );
7406
-
7407
- // Determine the total padding needed to the length of the axis to make room for the
7408
- // pointRange. If the series' pointPlacement is 'on', no padding is added.
7409
- pointRangePadding = mathMax(
7410
- pointRangePadding,
7411
- pointPlacement === 'on' ? 0 : seriesPointRange
7412
- );
7413
-
7414
- // Set the closestPointRange
7415
- if (!series.noSharedTooltip && defined(seriesClosestPointRange)) {
7416
- closestPointRange = defined(closestPointRange) ?
7417
- mathMin(closestPointRange, seriesClosestPointRange) :
7418
- seriesClosestPointRange;
7419
- }
7420
- });
7421
- }
7422
-
7423
- // Record minPointOffset and pointRangePadding
7424
- ordinalCorrection = axis.ordinalSlope && closestPointRange ? axis.ordinalSlope / closestPointRange : 1; // #988, #1853
7425
- axis.minPointOffset = minPointOffset = minPointOffset * ordinalCorrection;
7426
- axis.pointRangePadding = pointRangePadding = pointRangePadding * ordinalCorrection;
7427
-
7428
- // pointRange means the width reserved for each point, like in a column chart
7429
- axis.pointRange = mathMin(pointRange, range);
7430
-
7431
- // closestPointRange means the closest distance between points. In columns
7432
- // it is mostly equal to pointRange, but in lines pointRange is 0 while closestPointRange
7433
- // is some other value
7434
- axis.closestPointRange = closestPointRange;
7435
- }
7436
-
7437
- // Secondary values
7438
- if (saveOld) {
7439
- axis.oldTransA = transA;
7440
- }
7441
- axis.translationSlope = axis.transA = transA = axis.len / ((range + pointRangePadding) || 1);
7442
- axis.transB = axis.horiz ? axis.left : axis.bottom; // translation addend
7443
- axis.minPixelPadding = transA * minPointOffset;
7444
- },
7445
-
7446
- /**
7447
- * Set the tick positions to round values and optionally extend the extremes
7448
- * to the nearest tick
7449
- */
7450
- setTickPositions: function (secondPass) {
7451
- var axis = this,
7452
- chart = axis.chart,
7453
- options = axis.options,
7454
- isLog = axis.isLog,
7455
- isDatetimeAxis = axis.isDatetimeAxis,
7456
- isXAxis = axis.isXAxis,
7457
- isLinked = axis.isLinked,
7458
- tickPositioner = axis.options.tickPositioner,
7459
- magnitude,
7460
- maxPadding = options.maxPadding,
7461
- minPadding = options.minPadding,
7462
- length,
7463
- linkedParentExtremes,
7464
- tickIntervalOption = options.tickInterval,
7465
- minTickIntervalOption = options.minTickInterval,
7466
- tickPixelIntervalOption = options.tickPixelInterval,
7467
- tickPositions,
7468
- categories = axis.categories;
7469
-
7470
- // linked axis gets the extremes from the parent axis
7471
- if (isLinked) {
7472
- axis.linkedParent = chart[isXAxis ? 'xAxis' : 'yAxis'][options.linkedTo];
7473
- linkedParentExtremes = axis.linkedParent.getExtremes();
7474
- axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
7475
- axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
7476
- if (options.type !== axis.linkedParent.options.type) {
7477
- error(11, 1); // Can't link axes of different type
7478
- }
7479
- } else { // initial min and max from the extreme data values
7480
- axis.min = pick(axis.userMin, options.min, axis.dataMin);
7481
- axis.max = pick(axis.userMax, options.max, axis.dataMax);
7482
- }
7483
-
7484
- if (isLog) {
7485
- if (!secondPass && mathMin(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
7486
- error(10, 1); // Can't plot negative values on log axis
7487
- }
7488
- axis.min = correctFloat(log2lin(axis.min)); // correctFloat cures #934
7489
- axis.max = correctFloat(log2lin(axis.max));
7490
- }
7491
-
7492
- // handle zoomed range
7493
- if (axis.range) {
7494
- axis.userMin = axis.min = mathMax(axis.min, axis.max - axis.range); // #618
7495
- axis.userMax = axis.max;
7496
- if (secondPass) {
7497
- axis.range = null; // don't use it when running setExtremes
7498
- }
7499
- }
7500
-
7501
- // Hook for adjusting this.min and this.max. Used by bubble series.
7502
- if (axis.beforePadding) {
7503
- axis.beforePadding();
7504
- }
7505
-
7506
- // adjust min and max for the minimum range
7507
- axis.adjustForMinRange();
7508
-
7509
- // Pad the values to get clear of the chart's edges. To avoid tickInterval taking the padding
7510
- // into account, we do this after computing tick interval (#1337).
7511
- if (!categories && !axis.usePercentage && !isLinked && defined(axis.min) && defined(axis.max)) {
7512
- length = axis.max - axis.min;
7513
- if (length) {
7514
- if (!defined(options.min) && !defined(axis.userMin) && minPadding && (axis.dataMin < 0 || !axis.ignoreMinPadding)) {
7515
- axis.min -= length * minPadding;
7516
- }
7517
- if (!defined(options.max) && !defined(axis.userMax) && maxPadding && (axis.dataMax > 0 || !axis.ignoreMaxPadding)) {
7518
- axis.max += length * maxPadding;
7519
- }
7520
- }
7521
- }
7522
-
7523
- // get tickInterval
7524
- if (axis.min === axis.max || axis.min === undefined || axis.max === undefined) {
7525
- axis.tickInterval = 1;
7526
- } else if (isLinked && !tickIntervalOption &&
7527
- tickPixelIntervalOption === axis.linkedParent.options.tickPixelInterval) {
7528
- axis.tickInterval = axis.linkedParent.tickInterval;
7529
- } else {
7530
- axis.tickInterval = pick(
7531
- tickIntervalOption,
7532
- categories ? // for categoried axis, 1 is default, for linear axis use tickPix
7533
- 1 :
7534
- (axis.max - axis.min) * tickPixelIntervalOption / (axis.len || 1)
7535
- );
7536
- }
7537
-
7538
- // Now we're finished detecting min and max, crop and group series data. This
7539
- // is in turn needed in order to find tick positions in ordinal axes.
7540
- if (isXAxis && !secondPass) {
7541
- each(axis.series, function (series) {
7542
- series.processData(axis.min !== axis.oldMin || axis.max !== axis.oldMax);
7543
- });
7544
- }
7545
-
7546
- // set the translation factor used in translate function
7547
- axis.setAxisTranslation(true);
7548
-
7549
- // hook for ordinal axes and radial axes
7550
- if (axis.beforeSetTickPositions) {
7551
- axis.beforeSetTickPositions();
7552
- }
7553
-
7554
- // hook for extensions, used in Highstock ordinal axes
7555
- if (axis.postProcessTickInterval) {
7556
- axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval);
7557
- }
7558
-
7559
- // Before normalizing the tick interval, handle minimum tick interval. This applies only if tickInterval is not defined.
7560
- if (!tickIntervalOption && axis.tickInterval < minTickIntervalOption) {
7561
- axis.tickInterval = minTickIntervalOption;
7562
- }
7563
-
7564
- // for linear axes, get magnitude and normalize the interval
7565
- if (!isDatetimeAxis && !isLog) { // linear
7566
- magnitude = math.pow(10, mathFloor(math.log(axis.tickInterval) / math.LN10));
7567
- if (!tickIntervalOption) {
7568
- axis.tickInterval = normalizeTickInterval(axis.tickInterval, null, magnitude, options);
7569
- }
7570
- }
7571
-
7572
- // get minorTickInterval
7573
- axis.minorTickInterval = options.minorTickInterval === 'auto' && axis.tickInterval ?
7574
- axis.tickInterval / 5 : options.minorTickInterval;
7575
-
7576
- // find the tick positions
7577
- axis.tickPositions = tickPositions = options.tickPositions ?
7578
- [].concat(options.tickPositions) : // Work on a copy (#1565)
7579
- (tickPositioner && tickPositioner.apply(axis, [axis.min, axis.max]));
7580
- if (!tickPositions) {
7581
- if (isDatetimeAxis) {
7582
- tickPositions = (axis.getNonLinearTimeTicks || getTimeTicks)(
7583
- normalizeTimeTickInterval(axis.tickInterval, options.units),
7584
- axis.min,
7585
- axis.max,
7586
- options.startOfWeek,
7587
- axis.ordinalPositions,
7588
- axis.closestPointRange,
7589
- true
7590
- );
7591
- } else if (isLog) {
7592
- tickPositions = axis.getLogTickPositions(axis.tickInterval, axis.min, axis.max);
7593
- } else {
7594
- tickPositions = axis.getLinearTickPositions(axis.tickInterval, axis.min, axis.max);
7595
- }
7596
- axis.tickPositions = tickPositions;
7597
- }
7598
-
7599
- if (!isLinked) {
7600
-
7601
- // reset min/max or remove extremes based on start/end on tick
7602
- var roundedMin = tickPositions[0],
7603
- roundedMax = tickPositions[tickPositions.length - 1],
7604
- minPointOffset = axis.minPointOffset || 0,
7605
- singlePad;
7606
-
7607
- if (options.startOnTick) {
7608
- axis.min = roundedMin;
7609
- } else if (axis.min - minPointOffset > roundedMin) {
7610
- tickPositions.shift();
7611
- }
7612
-
7613
- if (options.endOnTick) {
7614
- axis.max = roundedMax;
7615
- } else if (axis.max + minPointOffset < roundedMax) {
7616
- tickPositions.pop();
7617
- }
7618
-
7619
- // When there is only one point, or all points have the same value on this axis, then min
7620
- // and max are equal and tickPositions.length is 1. In this case, add some padding
7621
- // in order to center the point, but leave it with one tick. #1337.
7622
- if (tickPositions.length === 1) {
7623
- singlePad = 0.001; // The lowest possible number to avoid extra padding on columns
7624
- axis.min -= singlePad;
7625
- axis.max += singlePad;
7626
- }
7627
- }
7628
- },
7629
-
7630
- /**
7631
- * Set the max ticks of either the x and y axis collection
7632
- */
7633
- setMaxTicks: function () {
7634
-
7635
- var chart = this.chart,
7636
- maxTicks = chart.maxTicks || {},
7637
- tickPositions = this.tickPositions,
7638
- key = this._maxTicksKey = [this.xOrY, this.pos, this.len].join('-');
7639
-
7640
- if (!this.isLinked && !this.isDatetimeAxis && tickPositions && tickPositions.length > (maxTicks[key] || 0) && this.options.alignTicks !== false) {
7641
- maxTicks[key] = tickPositions.length;
7642
- }
7643
- chart.maxTicks = maxTicks;
7644
- },
7645
-
7646
- /**
7647
- * When using multiple axes, adjust the number of ticks to match the highest
7648
- * number of ticks in that group
7649
- */
7650
- adjustTickAmount: function () {
7651
- var axis = this,
7652
- chart = axis.chart,
7653
- key = axis._maxTicksKey,
7654
- tickPositions = axis.tickPositions,
7655
- maxTicks = chart.maxTicks;
7656
-
7657
- if (maxTicks && maxTicks[key] && !axis.isDatetimeAxis && !axis.categories && !axis.isLinked && axis.options.alignTicks !== false) { // only apply to linear scale
7658
- var oldTickAmount = axis.tickAmount,
7659
- calculatedTickAmount = tickPositions.length,
7660
- tickAmount;
7661
-
7662
- // set the axis-level tickAmount to use below
7663
- axis.tickAmount = tickAmount = maxTicks[key];
7664
-
7665
- if (calculatedTickAmount < tickAmount) {
7666
- while (tickPositions.length < tickAmount) {
7667
- tickPositions.push(correctFloat(
7668
- tickPositions[tickPositions.length - 1] + axis.tickInterval
7669
- ));
7670
- }
7671
- axis.transA *= (calculatedTickAmount - 1) / (tickAmount - 1);
7672
- axis.max = tickPositions[tickPositions.length - 1];
7673
-
7674
- }
7675
- if (defined(oldTickAmount) && tickAmount !== oldTickAmount) {
7676
- axis.isDirty = true;
7677
- }
7678
- }
7679
- },
7680
-
7681
- /**
7682
- * Set the scale based on data min and max, user set min and max or options
7683
- *
7684
- */
7685
- setScale: function () {
7686
- var axis = this,
7687
- stacks = axis.stacks,
7688
- type,
7689
- i,
7690
- isDirtyData,
7691
- isDirtyAxisLength;
7692
-
7693
- axis.oldMin = axis.min;
7694
- axis.oldMax = axis.max;
7695
- axis.oldAxisLength = axis.len;
7696
-
7697
- // set the new axisLength
7698
- axis.setAxisSize();
7699
- //axisLength = horiz ? axisWidth : axisHeight;
7700
- isDirtyAxisLength = axis.len !== axis.oldAxisLength;
7701
-
7702
- // is there new data?
7703
- each(axis.series, function (series) {
7704
- if (series.isDirtyData || series.isDirty ||
7705
- series.xAxis.isDirty) { // when x axis is dirty, we need new data extremes for y as well
7706
- isDirtyData = true;
7707
- }
7708
- });
7709
-
7710
- // do we really need to go through all this?
7711
- if (isDirtyAxisLength || isDirtyData || axis.isLinked || axis.forceRedraw ||
7712
- axis.userMin !== axis.oldUserMin || axis.userMax !== axis.oldUserMax) {
7713
-
7714
- axis.forceRedraw = false;
7715
-
7716
- // get data extremes if needed
7717
- axis.getSeriesExtremes();
7718
-
7719
- // get fixed positions based on tickInterval
7720
- axis.setTickPositions();
7721
-
7722
- // record old values to decide whether a rescale is necessary later on (#540)
7723
- axis.oldUserMin = axis.userMin;
7724
- axis.oldUserMax = axis.userMax;
7725
-
7726
- // Mark as dirty if it is not already set to dirty and extremes have changed. #595.
7727
- if (!axis.isDirty) {
7728
- axis.isDirty = isDirtyAxisLength || axis.min !== axis.oldMin || axis.max !== axis.oldMax;
7729
- }
7730
- }
7731
-
7732
-
7733
- // reset stacks
7734
- if (!axis.isXAxis) {
7735
- for (type in stacks) {
7736
- for (i in stacks[type]) {
7737
- stacks[type][i].cum = stacks[type][i].total;
7738
- }
7739
- }
7740
- }
7741
-
7742
- // Set the maximum tick amount
7743
- axis.setMaxTicks();
7744
- },
7745
-
7746
- /**
7747
- * Set the extremes and optionally redraw
7748
- * @param {Number} newMin
7749
- * @param {Number} newMax
7750
- * @param {Boolean} redraw
7751
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
7752
- * configuration
7753
- * @param {Object} eventArguments
7754
- *
7755
- */
7756
- setExtremes: function (newMin, newMax, redraw, animation, eventArguments) {
7757
- var axis = this,
7758
- chart = axis.chart;
7759
-
7760
- redraw = pick(redraw, true); // defaults to true
7761
-
7762
- // Extend the arguments with min and max
7763
- eventArguments = extend(eventArguments, {
7764
- min: newMin,
7765
- max: newMax
7766
- });
7767
-
7768
- // Fire the event
7769
- fireEvent(axis, 'setExtremes', eventArguments, function () { // the default event handler
7770
-
7771
- axis.userMin = newMin;
7772
- axis.userMax = newMax;
7773
-
7774
- // Mark for running afterSetExtremes
7775
- axis.isDirtyExtremes = true;
7776
-
7777
- // redraw
7778
- if (redraw) {
7779
- chart.redraw(animation);
7780
- }
7781
- });
7782
- },
7783
-
7784
- /**
7785
- * Overridable method for zooming chart. Pulled out in a separate method to allow overriding
7786
- * in stock charts.
7787
- */
7788
- zoom: function (newMin, newMax) {
7789
-
7790
- // Prevent pinch zooming out of range
7791
- if (!this.allowZoomOutside) {
7792
- if (newMin <= this.dataMin) {
7793
- newMin = UNDEFINED;
7794
- }
7795
- if (newMax >= this.dataMax) {
7796
- newMax = UNDEFINED;
7797
- }
7798
- }
7799
-
7800
- // In full view, displaying the reset zoom button is not required
7801
- this.displayBtn = newMin !== UNDEFINED || newMax !== UNDEFINED;
7802
-
7803
- // Do it
7804
- this.setExtremes(
7805
- newMin,
7806
- newMax,
7807
- false,
7808
- UNDEFINED,
7809
- { trigger: 'zoom' }
7810
- );
7811
- return true;
7812
- },
7813
-
7814
- /**
7815
- * Update the axis metrics
7816
- */
7817
- setAxisSize: function () {
7818
- var chart = this.chart,
7819
- options = this.options,
7820
- offsetLeft = options.offsetLeft || 0,
7821
- offsetRight = options.offsetRight || 0,
7822
- horiz = this.horiz,
7823
- width,
7824
- height,
7825
- top,
7826
- left;
7827
-
7828
- // Expose basic values to use in Series object and navigator
7829
- this.left = left = pick(options.left, chart.plotLeft + offsetLeft);
7830
- this.top = top = pick(options.top, chart.plotTop);
7831
- this.width = width = pick(options.width, chart.plotWidth - offsetLeft + offsetRight);
7832
- this.height = height = pick(options.height, chart.plotHeight);
7833
- this.bottom = chart.chartHeight - height - top;
7834
- this.right = chart.chartWidth - width - left;
7835
-
7836
- // Direction agnostic properties
7837
- this.len = mathMax(horiz ? width : height, 0); // mathMax fixes #905
7838
- this.pos = horiz ? left : top; // distance from SVG origin
7839
- },
7840
-
7841
- /**
7842
- * Get the actual axis extremes
7843
- */
7844
- getExtremes: function () {
7845
- var axis = this,
7846
- isLog = axis.isLog;
7847
-
7848
- return {
7849
- min: isLog ? correctFloat(lin2log(axis.min)) : axis.min,
7850
- max: isLog ? correctFloat(lin2log(axis.max)) : axis.max,
7851
- dataMin: axis.dataMin,
7852
- dataMax: axis.dataMax,
7853
- userMin: axis.userMin,
7854
- userMax: axis.userMax
7855
- };
7856
- },
7857
-
7858
- /**
7859
- * Get the zero plane either based on zero or on the min or max value.
7860
- * Used in bar and area plots
7861
- */
7862
- getThreshold: function (threshold) {
7863
- var axis = this,
7864
- isLog = axis.isLog;
7865
-
7866
- var realMin = isLog ? lin2log(axis.min) : axis.min,
7867
- realMax = isLog ? lin2log(axis.max) : axis.max;
7868
-
7869
- if (realMin > threshold || threshold === null) {
7870
- threshold = realMin;
7871
- } else if (realMax < threshold) {
7872
- threshold = realMax;
7873
- }
7874
-
7875
- return axis.translate(threshold, 0, 1, 0, 1);
7876
- },
7877
-
7878
- addPlotBand: function (options) {
7879
- this.addPlotBandOrLine(options, 'plotBands');
7880
- },
7881
-
7882
- addPlotLine: function (options) {
7883
- this.addPlotBandOrLine(options, 'plotLines');
7884
- },
7885
-
7886
- /**
7887
- * Add a plot band or plot line after render time
7888
- *
7889
- * @param options {Object} The plotBand or plotLine configuration object
7890
- */
7891
- addPlotBandOrLine: function (options, coll) {
7892
- var obj = new PlotLineOrBand(this, options).render(),
7893
- userOptions = this.userOptions;
7894
-
7895
- // Add it to the user options for exporting and Axis.update
7896
- if (coll) {
7897
- userOptions[coll] = userOptions[coll] || [];
7898
- userOptions[coll].push(options);
7899
- }
7900
-
7901
- this.plotLinesAndBands.push(obj);
7902
-
7903
- return obj;
7904
- },
7905
-
7906
- /**
7907
- * Render the tick labels to a preliminary position to get their sizes
7908
- */
7909
- getOffset: function () {
7910
- var axis = this,
7911
- chart = axis.chart,
7912
- renderer = chart.renderer,
7913
- options = axis.options,
7914
- tickPositions = axis.tickPositions,
7915
- ticks = axis.ticks,
7916
- horiz = axis.horiz,
7917
- side = axis.side,
7918
- invertedSide = chart.inverted ? [1, 0, 3, 2][side] : side,
7919
- hasData,
7920
- showAxis,
7921
- titleOffset = 0,
7922
- titleOffsetOption,
7923
- titleMargin = 0,
7924
- axisTitleOptions = options.title,
7925
- labelOptions = options.labels,
7926
- labelOffset = 0, // reset
7927
- axisOffset = chart.axisOffset,
7928
- clipOffset = chart.clipOffset,
7929
- directionFactor = [-1, 1, 1, -1][side],
7930
- n;
7931
-
7932
- // For reuse in Axis.render
7933
- axis.hasData = hasData = (axis.hasVisibleSeries || (defined(axis.min) && defined(axis.max) && !!tickPositions));
7934
- axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
7935
-
7936
- // Create the axisGroup and gridGroup elements on first iteration
7937
- if (!axis.axisGroup) {
7938
- axis.gridGroup = renderer.g('grid')
7939
- .attr({ zIndex: options.gridZIndex || 1 })
7940
- .add();
7941
- axis.axisGroup = renderer.g('axis')
7942
- .attr({ zIndex: options.zIndex || 2 })
7943
- .add();
7944
- axis.labelGroup = renderer.g('axis-labels')
7945
- .attr({ zIndex: labelOptions.zIndex || 7 })
7946
- .add();
7947
- }
7948
-
7949
- if (hasData || axis.isLinked) {
7950
- each(tickPositions, function (pos) {
7951
- if (!ticks[pos]) {
7952
- ticks[pos] = new Tick(axis, pos);
7953
- } else {
7954
- ticks[pos].addLabel(); // update labels depending on tick interval
7955
- }
7956
-
7957
- });
7958
-
7959
- each(tickPositions, function (pos) {
7960
- // left side must be align: right and right side must have align: left for labels
7961
- if (side === 0 || side === 2 || { 1: 'left', 3: 'right' }[side] === labelOptions.align) {
7962
-
7963
- // get the highest offset
7964
- labelOffset = mathMax(
7965
- ticks[pos].getLabelSize(),
7966
- labelOffset
7967
- );
7968
- }
7969
-
7970
- });
7971
-
7972
- if (axis.staggerLines) {
7973
- labelOffset += (axis.staggerLines - 1) * 16;
7974
- }
7975
-
7976
- } else { // doesn't have data
7977
- for (n in ticks) {
7978
- ticks[n].destroy();
7979
- delete ticks[n];
7980
- }
7981
- }
7982
-
7983
- if (axisTitleOptions && axisTitleOptions.text && axisTitleOptions.enabled !== false) {
7984
- if (!axis.axisTitle) {
7985
- axis.axisTitle = renderer.text(
7986
- axisTitleOptions.text,
7987
- 0,
7988
- 0,
7989
- axisTitleOptions.useHTML
7990
- )
7991
- .attr({
7992
- zIndex: 7,
7993
- rotation: axisTitleOptions.rotation || 0,
7994
- align:
7995
- axisTitleOptions.textAlign ||
7996
- { low: 'left', middle: 'center', high: 'right' }[axisTitleOptions.align]
7997
- })
7998
- .css(axisTitleOptions.style)
7999
- .add(axis.axisGroup);
8000
- axis.axisTitle.isNew = true;
8001
- }
8002
-
8003
- if (showAxis) {
8004
- titleOffset = axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
8005
- titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10);
8006
- titleOffsetOption = axisTitleOptions.offset;
8007
- }
8008
-
8009
- // hide or show the title depending on whether showEmpty is set
8010
- axis.axisTitle[showAxis ? 'show' : 'hide']();
8011
- }
8012
-
8013
- // handle automatic or user set offset
8014
- axis.offset = directionFactor * pick(options.offset, axisOffset[side]);
8015
-
8016
- axis.axisTitleMargin =
8017
- pick(titleOffsetOption,
8018
- labelOffset + titleMargin +
8019
- (side !== 2 && labelOffset && directionFactor * options.labels[horiz ? 'y' : 'x'])
8020
- );
8021
-
8022
- axisOffset[side] = mathMax(
8023
- axisOffset[side],
8024
- axis.axisTitleMargin + titleOffset + directionFactor * axis.offset
8025
- );
8026
- clipOffset[invertedSide] = mathMax(clipOffset[invertedSide], options.lineWidth);
8027
-
8028
- },
8029
-
8030
- /**
8031
- * Get the path for the axis line
8032
- */
8033
- getLinePath: function (lineWidth) {
8034
- var chart = this.chart,
8035
- opposite = this.opposite,
8036
- offset = this.offset,
8037
- horiz = this.horiz,
8038
- lineLeft = this.left + (opposite ? this.width : 0) + offset,
8039
- lineTop = chart.chartHeight - this.bottom - (opposite ? this.height : 0) + offset;
8040
-
8041
- this.lineTop = lineTop; // used by flag series
8042
- if (!opposite) {
8043
- lineWidth *= -1; // crispify the other way - #1480
8044
- }
8045
-
8046
- return chart.renderer.crispLine([
8047
- M,
8048
- horiz ?
8049
- this.left :
8050
- lineLeft,
8051
- horiz ?
8052
- lineTop :
8053
- this.top,
8054
- L,
8055
- horiz ?
8056
- chart.chartWidth - this.right :
8057
- lineLeft,
8058
- horiz ?
8059
- lineTop :
8060
- chart.chartHeight - this.bottom
8061
- ], lineWidth);
8062
- },
8063
-
8064
- /**
8065
- * Position the title
8066
- */
8067
- getTitlePosition: function () {
8068
- // compute anchor points for each of the title align options
8069
- var horiz = this.horiz,
8070
- axisLeft = this.left,
8071
- axisTop = this.top,
8072
- axisLength = this.len,
8073
- axisTitleOptions = this.options.title,
8074
- margin = horiz ? axisLeft : axisTop,
8075
- opposite = this.opposite,
8076
- offset = this.offset,
8077
- fontSize = pInt(axisTitleOptions.style.fontSize || 12),
8078
-
8079
- // the position in the length direction of the axis
8080
- alongAxis = {
8081
- low: margin + (horiz ? 0 : axisLength),
8082
- middle: margin + axisLength / 2,
8083
- high: margin + (horiz ? axisLength : 0)
8084
- }[axisTitleOptions.align],
8085
-
8086
- // the position in the perpendicular direction of the axis
8087
- offAxis = (horiz ? axisTop + this.height : axisLeft) +
8088
- (horiz ? 1 : -1) * // horizontal axis reverses the margin
8089
- (opposite ? -1 : 1) * // so does opposite axes
8090
- this.axisTitleMargin +
8091
- (this.side === 2 ? fontSize : 0);
8092
-
8093
- return {
8094
- x: horiz ?
8095
- alongAxis :
8096
- offAxis + (opposite ? this.width : 0) + offset +
8097
- (axisTitleOptions.x || 0), // x
8098
- y: horiz ?
8099
- offAxis - (opposite ? this.height : 0) + offset :
8100
- alongAxis + (axisTitleOptions.y || 0) // y
8101
- };
8102
- },
8103
-
8104
- /**
8105
- * Render the axis
8106
- */
8107
- render: function () {
8108
- var axis = this,
8109
- chart = axis.chart,
8110
- renderer = chart.renderer,
8111
- options = axis.options,
8112
- isLog = axis.isLog,
8113
- isLinked = axis.isLinked,
8114
- tickPositions = axis.tickPositions,
8115
- axisTitle = axis.axisTitle,
8116
- stacks = axis.stacks,
8117
- ticks = axis.ticks,
8118
- minorTicks = axis.minorTicks,
8119
- alternateBands = axis.alternateBands,
8120
- stackLabelOptions = options.stackLabels,
8121
- alternateGridColor = options.alternateGridColor,
8122
- tickmarkOffset = axis.tickmarkOffset,
8123
- lineWidth = options.lineWidth,
8124
- linePath,
8125
- hasRendered = chart.hasRendered,
8126
- slideInTicks = hasRendered && defined(axis.oldMin) && !isNaN(axis.oldMin),
8127
- hasData = axis.hasData,
8128
- showAxis = axis.showAxis,
8129
- from,
8130
- to;
8131
-
8132
- // Mark all elements inActive before we go over and mark the active ones
8133
- each([ticks, minorTicks, alternateBands], function (coll) {
8134
- var pos;
8135
- for (pos in coll) {
8136
- coll[pos].isActive = false;
8137
- }
8138
- });
8139
-
8140
- // If the series has data draw the ticks. Else only the line and title
8141
- if (hasData || isLinked) {
8142
-
8143
- // minor ticks
8144
- if (axis.minorTickInterval && !axis.categories) {
8145
- each(axis.getMinorTickPositions(), function (pos) {
8146
- if (!minorTicks[pos]) {
8147
- minorTicks[pos] = new Tick(axis, pos, 'minor');
8148
- }
8149
-
8150
- // render new ticks in old position
8151
- if (slideInTicks && minorTicks[pos].isNew) {
8152
- minorTicks[pos].render(null, true);
8153
- }
8154
-
8155
- minorTicks[pos].render(null, false, 1);
8156
- });
8157
- }
8158
-
8159
- // Major ticks. Pull out the first item and render it last so that
8160
- // we can get the position of the neighbour label. #808.
8161
- if (tickPositions.length) { // #1300
8162
- each(tickPositions.slice(1).concat([tickPositions[0]]), function (pos, i) {
8163
-
8164
- // Reorganize the indices
8165
- i = (i === tickPositions.length - 1) ? 0 : i + 1;
8166
-
8167
- // linked axes need an extra check to find out if
8168
- if (!isLinked || (pos >= axis.min && pos <= axis.max)) {
8169
-
8170
- if (!ticks[pos]) {
8171
- ticks[pos] = new Tick(axis, pos);
8172
- }
8173
-
8174
- // render new ticks in old position
8175
- if (slideInTicks && ticks[pos].isNew) {
8176
- ticks[pos].render(i, true);
8177
- }
8178
-
8179
- ticks[pos].render(i, false, 1);
8180
- }
8181
-
8182
- });
8183
- // In a categorized axis, the tick marks are displayed between labels. So
8184
- // we need to add a tick mark and grid line at the left edge of the X axis.
8185
- if (tickmarkOffset && axis.min === 0) {
8186
- if (!ticks[-1]) {
8187
- ticks[-1] = new Tick(axis, -1, null, true);
8188
- }
8189
- ticks[-1].render(-1);
8190
- }
8191
-
8192
- }
8193
-
8194
- // alternate grid color
8195
- if (alternateGridColor) {
8196
- each(tickPositions, function (pos, i) {
8197
- if (i % 2 === 0 && pos < axis.max) {
8198
- if (!alternateBands[pos]) {
8199
- alternateBands[pos] = new PlotLineOrBand(axis);
8200
- }
8201
- from = pos + tickmarkOffset; // #949
8202
- to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] + tickmarkOffset : axis.max;
8203
- alternateBands[pos].options = {
8204
- from: isLog ? lin2log(from) : from,
8205
- to: isLog ? lin2log(to) : to,
8206
- color: alternateGridColor
8207
- };
8208
- alternateBands[pos].render();
8209
- alternateBands[pos].isActive = true;
8210
- }
8211
- });
8212
- }
8213
-
8214
- // custom plot lines and bands
8215
- if (!axis._addedPlotLB) { // only first time
8216
- each((options.plotLines || []).concat(options.plotBands || []), function (plotLineOptions) {
8217
- axis.addPlotBandOrLine(plotLineOptions);
8218
- });
8219
- axis._addedPlotLB = true;
8220
- }
8221
-
8222
- } // end if hasData
8223
-
8224
- // Remove inactive ticks
8225
- each([ticks, minorTicks, alternateBands], function (coll) {
8226
- var pos,
8227
- i,
8228
- forDestruction = [],
8229
- delay = globalAnimation ? globalAnimation.duration || 500 : 0,
8230
- destroyInactiveItems = function () {
8231
- i = forDestruction.length;
8232
- while (i--) {
8233
- // When resizing rapidly, the same items may be destroyed in different timeouts,
8234
- // or the may be reactivated
8235
- if (coll[forDestruction[i]] && !coll[forDestruction[i]].isActive) {
8236
- coll[forDestruction[i]].destroy();
8237
- delete coll[forDestruction[i]];
8238
- }
8239
- }
8240
-
8241
- };
8242
-
8243
- for (pos in coll) {
8244
-
8245
- if (!coll[pos].isActive) {
8246
- // Render to zero opacity
8247
- coll[pos].render(pos, false, 0);
8248
- coll[pos].isActive = false;
8249
- forDestruction.push(pos);
8250
- }
8251
- }
8252
-
8253
- // When the objects are finished fading out, destroy them
8254
- if (coll === alternateBands || !chart.hasRendered || !delay) {
8255
- destroyInactiveItems();
8256
- } else if (delay) {
8257
- setTimeout(destroyInactiveItems, delay);
8258
- }
8259
- });
8260
-
8261
- // Static items. As the axis group is cleared on subsequent calls
8262
- // to render, these items are added outside the group.
8263
- // axis line
8264
- if (lineWidth) {
8265
- linePath = axis.getLinePath(lineWidth);
8266
- if (!axis.axisLine) {
8267
- axis.axisLine = renderer.path(linePath)
8268
- .attr({
8269
- stroke: options.lineColor,
8270
- 'stroke-width': lineWidth,
8271
- zIndex: 7
8272
- })
8273
- .add(axis.axisGroup);
8274
- } else {
8275
- axis.axisLine.animate({ d: linePath });
8276
- }
8277
-
8278
- // show or hide the line depending on options.showEmpty
8279
- axis.axisLine[showAxis ? 'show' : 'hide']();
8280
- }
8281
-
8282
- if (axisTitle && showAxis) {
8283
-
8284
- axisTitle[axisTitle.isNew ? 'attr' : 'animate'](
8285
- axis.getTitlePosition()
8286
- );
8287
- axisTitle.isNew = false;
8288
- }
8289
-
8290
- // Stacked totals:
8291
- if (stackLabelOptions && stackLabelOptions.enabled) {
8292
- var stackKey, oneStack, stackCategory,
8293
- stackTotalGroup = axis.stackTotalGroup;
8294
-
8295
- // Create a separate group for the stack total labels
8296
- if (!stackTotalGroup) {
8297
- axis.stackTotalGroup = stackTotalGroup =
8298
- renderer.g('stack-labels')
8299
- .attr({
8300
- visibility: VISIBLE,
8301
- zIndex: 6
8302
- })
8303
- .add();
8304
- }
8305
-
8306
- // plotLeft/Top will change when y axis gets wider so we need to translate the
8307
- // stackTotalGroup at every render call. See bug #506 and #516
8308
- stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
8309
-
8310
- // Render each stack total
8311
- for (stackKey in stacks) {
8312
- oneStack = stacks[stackKey];
8313
- for (stackCategory in oneStack) {
8314
- oneStack[stackCategory].render(stackTotalGroup);
8315
- }
8316
- }
8317
- }
8318
- // End stacked totals
8319
-
8320
- axis.isDirty = false;
8321
- },
8322
-
8323
- /**
8324
- * Remove a plot band or plot line from the chart by id
8325
- * @param {Object} id
8326
- */
8327
- removePlotBandOrLine: function (id) {
8328
- var plotLinesAndBands = this.plotLinesAndBands,
8329
- i = plotLinesAndBands.length;
8330
- while (i--) {
8331
- if (plotLinesAndBands[i].id === id) {
8332
- plotLinesAndBands[i].destroy();
8333
- }
8334
- }
8335
- },
8336
-
8337
- /**
8338
- * Update the axis title by options
8339
- */
8340
- setTitle: function (newTitleOptions, redraw) {
8341
- this.update({ title: newTitleOptions }, redraw);
8342
- },
8343
-
8344
- /**
8345
- * Redraw the axis to reflect changes in the data or axis extremes
8346
- */
8347
- redraw: function () {
8348
- var axis = this,
8349
- chart = axis.chart,
8350
- pointer = chart.pointer;
8351
-
8352
- // hide tooltip and hover states
8353
- if (pointer.reset) {
8354
- pointer.reset(true);
8355
- }
8356
-
8357
- // render the axis
8358
- axis.render();
8359
-
8360
- // move plot lines and bands
8361
- each(axis.plotLinesAndBands, function (plotLine) {
8362
- plotLine.render();
8363
- });
8364
-
8365
- // mark associated series as dirty and ready for redraw
8366
- each(axis.series, function (series) {
8367
- series.isDirty = true;
8368
- });
8369
-
8370
- },
8371
-
8372
- /**
8373
- * Set new axis categories and optionally redraw
8374
- * @param {Array} categories
8375
- * @param {Boolean} redraw
8376
- */
8377
- setCategories: function (categories, redraw) {
8378
- this.update({ categories: categories }, redraw);
8379
- },
8380
-
8381
- /**
8382
- * Destroys an Axis instance.
8383
- */
8384
- destroy: function () {
8385
- var axis = this,
8386
- stacks = axis.stacks,
8387
- stackKey;
8388
-
8389
- // Remove the events
8390
- removeEvent(axis);
8391
-
8392
- // Destroy each stack total
8393
- for (stackKey in stacks) {
8394
- destroyObjectProperties(stacks[stackKey]);
8395
-
8396
- stacks[stackKey] = null;
8397
- }
8398
-
8399
- // Destroy collections
8400
- each([axis.ticks, axis.minorTicks, axis.alternateBands, axis.plotLinesAndBands], function (coll) {
8401
- destroyObjectProperties(coll);
8402
- });
8403
-
8404
- // Destroy local variables
8405
- each(['stackTotalGroup', 'axisLine', 'axisGroup', 'gridGroup', 'labelGroup', 'axisTitle'], function (prop) {
8406
- if (axis[prop]) {
8407
- axis[prop] = axis[prop].destroy();
8408
- }
8409
- });
8410
- }
8411
-
8412
-
8413
- }; // end Axis
8414
-
8415
- /**
8416
- * The tooltip object
8417
- * @param {Object} chart The chart instance
8418
- * @param {Object} options Tooltip options
8419
- */
8420
- function Tooltip() {
8421
- this.init.apply(this, arguments);
8422
- }
8423
-
8424
- Tooltip.prototype = {
8425
-
8426
- init: function (chart, options) {
8427
-
8428
- var borderWidth = options.borderWidth,
8429
- style = options.style,
8430
- padding = pInt(style.padding);
8431
-
8432
- // Save the chart and options
8433
- this.chart = chart;
8434
- this.options = options;
8435
-
8436
- // Keep track of the current series
8437
- //this.currentSeries = UNDEFINED;
8438
-
8439
- // List of crosshairs
8440
- this.crosshairs = [];
8441
-
8442
- // Current values of x and y when animating
8443
- this.now = { x: 0, y: 0 };
8444
-
8445
- // The tooltip is initially hidden
8446
- this.isHidden = true;
8447
-
8448
-
8449
- // create the label
8450
- this.label = chart.renderer.label('', 0, 0, options.shape, null, null, options.useHTML, null, 'tooltip')
8451
- .attr({
8452
- padding: padding,
8453
- fill: options.backgroundColor,
8454
- 'stroke-width': borderWidth,
8455
- r: options.borderRadius,
8456
- zIndex: 8
8457
- })
8458
- .css(style)
8459
- .css({ padding: 0 }) // Remove it from VML, the padding is applied as an attribute instead (#1117)
8460
- .hide()
8461
- .add();
8462
-
8463
- // When using canVG the shadow shows up as a gray circle
8464
- // even if the tooltip is hidden.
8465
- if (!useCanVG) {
8466
- this.label.shadow(options.shadow);
8467
- }
8468
-
8469
- // Public property for getting the shared state.
8470
- this.shared = options.shared;
8471
- },
8472
-
8473
- /**
8474
- * Destroy the tooltip and its elements.
8475
- */
8476
- destroy: function () {
8477
- each(this.crosshairs, function (crosshair) {
8478
- if (crosshair) {
8479
- crosshair.destroy();
8480
- }
8481
- });
8482
-
8483
- // Destroy and clear local variables
8484
- if (this.label) {
8485
- this.label = this.label.destroy();
8486
- }
8487
- clearTimeout(this.hideTimer);
8488
- clearTimeout(this.tooltipTimeout);
8489
- },
8490
-
8491
- /**
8492
- * Provide a soft movement for the tooltip
8493
- *
8494
- * @param {Number} x
8495
- * @param {Number} y
8496
- * @private
8497
- */
8498
- move: function (x, y, anchorX, anchorY) {
8499
- var tooltip = this,
8500
- now = tooltip.now,
8501
- animate = tooltip.options.animation !== false && !tooltip.isHidden;
8502
-
8503
- // get intermediate values for animation
8504
- extend(now, {
8505
- x: animate ? (2 * now.x + x) / 3 : x,
8506
- y: animate ? (now.y + y) / 2 : y,
8507
- anchorX: animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
8508
- anchorY: animate ? (now.anchorY + anchorY) / 2 : anchorY
8509
- });
8510
-
8511
- // move to the intermediate value
8512
- tooltip.label.attr(now);
8513
-
8514
-
8515
- // run on next tick of the mouse tracker
8516
- if (animate && (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1)) {
8517
-
8518
- // never allow two timeouts
8519
- clearTimeout(this.tooltipTimeout);
8520
-
8521
- // set the fixed interval ticking for the smooth tooltip
8522
- this.tooltipTimeout = setTimeout(function () {
8523
- // The interval function may still be running during destroy, so check that the chart is really there before calling.
8524
- if (tooltip) {
8525
- tooltip.move(x, y, anchorX, anchorY);
8526
- }
8527
- }, 32);
8528
-
8529
- }
8530
- },
8531
-
8532
- /**
8533
- * Hide the tooltip
8534
- */
8535
- hide: function () {
8536
- var tooltip = this,
8537
- hoverPoints;
8538
-
8539
- clearTimeout(this.hideTimer); // disallow duplicate timers (#1728, #1766)
8540
- if (!this.isHidden) {
8541
- hoverPoints = this.chart.hoverPoints;
8542
-
8543
- this.hideTimer = setTimeout(function () {
8544
- tooltip.label.fadeOut();
8545
- tooltip.isHidden = true;
8546
- }, pick(this.options.hideDelay, 500));
8547
-
8548
- // hide previous hoverPoints and set new
8549
- if (hoverPoints) {
8550
- each(hoverPoints, function (point) {
8551
- point.setState();
8552
- });
8553
- }
8554
-
8555
- this.chart.hoverPoints = null;
8556
- }
8557
- },
8558
-
8559
- /**
8560
- * Hide the crosshairs
8561
- */
8562
- hideCrosshairs: function () {
8563
- each(this.crosshairs, function (crosshair) {
8564
- if (crosshair) {
8565
- crosshair.hide();
8566
- }
8567
- });
8568
- },
8569
-
8570
- /**
8571
- * Extendable method to get the anchor position of the tooltip
8572
- * from a point or set of points
8573
- */
8574
- getAnchor: function (points, mouseEvent) {
8575
- var ret,
8576
- chart = this.chart,
8577
- inverted = chart.inverted,
8578
- plotTop = chart.plotTop,
8579
- plotX = 0,
8580
- plotY = 0,
8581
- yAxis;
8582
-
8583
- points = splat(points);
8584
-
8585
- // Pie uses a special tooltipPos
8586
- ret = points[0].tooltipPos;
8587
-
8588
- // When tooltip follows mouse, relate the position to the mouse
8589
- if (this.followPointer && mouseEvent) {
8590
- if (mouseEvent.chartX === UNDEFINED) {
8591
- mouseEvent = chart.pointer.normalize(mouseEvent);
8592
- }
8593
- ret = [
8594
- mouseEvent.chartX - chart.plotLeft,
8595
- mouseEvent.chartY - plotTop
8596
- ];
8597
- }
8598
- // When shared, use the average position
8599
- if (!ret) {
8600
- each(points, function (point) {
8601
- yAxis = point.series.yAxis;
8602
- plotX += point.plotX;
8603
- plotY += (point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY) +
8604
- (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
8605
- });
8606
-
8607
- plotX /= points.length;
8608
- plotY /= points.length;
8609
-
8610
- ret = [
8611
- inverted ? chart.plotWidth - plotY : plotX,
8612
- this.shared && !inverted && points.length > 1 && mouseEvent ?
8613
- mouseEvent.chartY - plotTop : // place shared tooltip next to the mouse (#424)
8614
- inverted ? chart.plotHeight - plotX : plotY
8615
- ];
8616
- }
8617
-
8618
- return map(ret, mathRound);
8619
- },
8620
-
8621
- /**
8622
- * Place the tooltip in a chart without spilling over
8623
- * and not covering the point it self.
8624
- */
8625
- getPosition: function (boxWidth, boxHeight, point) {
8626
-
8627
- // Set up the variables
8628
- var chart = this.chart,
8629
- plotLeft = chart.plotLeft,
8630
- plotTop = chart.plotTop,
8631
- plotWidth = chart.plotWidth,
8632
- plotHeight = chart.plotHeight,
8633
- distance = pick(this.options.distance, 12),
8634
- pointX = point.plotX,
8635
- pointY = point.plotY,
8636
- x = pointX + plotLeft + (chart.inverted ? distance : -boxWidth - distance),
8637
- y = pointY - boxHeight + plotTop + 15, // 15 means the point is 15 pixels up from the bottom of the tooltip
8638
- alignedRight;
8639
-
8640
- // It is too far to the left, adjust it
8641
- if (x < 7) {
8642
- x = plotLeft + mathMax(pointX, 0) + distance;
8643
- }
8644
-
8645
- // Test to see if the tooltip is too far to the right,
8646
- // if it is, move it back to be inside and then up to not cover the point.
8647
- if ((x + boxWidth) > (plotLeft + plotWidth)) {
8648
- x -= (x + boxWidth) - (plotLeft + plotWidth);
8649
- y = pointY - boxHeight + plotTop - distance;
8650
- alignedRight = true;
8651
- }
8652
-
8653
- // If it is now above the plot area, align it to the top of the plot area
8654
- if (y < plotTop + 5) {
8655
- y = plotTop + 5;
8656
-
8657
- // If the tooltip is still covering the point, move it below instead
8658
- if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) {
8659
- y = pointY + plotTop + distance; // below
8660
- }
8661
- }
8662
-
8663
- // Now if the tooltip is below the chart, move it up. It's better to cover the
8664
- // point than to disappear outside the chart. #834.
8665
- if (y + boxHeight > plotTop + plotHeight) {
8666
- y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below
8667
- }
8668
-
8669
- return {x: x, y: y};
8670
- },
8671
-
8672
- /**
8673
- * In case no user defined formatter is given, this will be used. Note that the context
8674
- * here is an object holding point, series, x, y etc.
8675
- */
8676
- defaultFormatter: function (tooltip) {
8677
- var items = this.points || splat(this),
8678
- series = items[0].series,
8679
- s;
8680
-
8681
- // build the header
8682
- s = [series.tooltipHeaderFormatter(items[0])];
8683
-
8684
- // build the values
8685
- each(items, function (item) {
8686
- series = item.series;
8687
- s.push((series.tooltipFormatter && series.tooltipFormatter(item)) ||
8688
- item.point.tooltipFormatter(series.tooltipOptions.pointFormat));
8689
- });
8690
-
8691
- // footer
8692
- s.push(tooltip.options.footerFormat || '');
8693
-
8694
- return s.join('');
8695
- },
8696
-
8697
- /**
8698
- * Refresh the tooltip's text and position.
8699
- * @param {Object} point
8700
- */
8701
- refresh: function (point, mouseEvent) {
8702
- var tooltip = this,
8703
- chart = tooltip.chart,
8704
- label = tooltip.label,
8705
- options = tooltip.options,
8706
- x,
8707
- y,
8708
- show,
8709
- anchor,
8710
- textConfig = {},
8711
- text,
8712
- pointConfig = [],
8713
- formatter = options.formatter || tooltip.defaultFormatter,
8714
- hoverPoints = chart.hoverPoints,
8715
- borderColor,
8716
- crosshairsOptions = options.crosshairs,
8717
- shared = tooltip.shared,
8718
- currentSeries;
8719
-
8720
- clearTimeout(this.hideTimer);
8721
-
8722
- // get the reference point coordinates (pie charts use tooltipPos)
8723
- tooltip.followPointer = splat(point)[0].series.tooltipOptions.followPointer;
8724
- anchor = tooltip.getAnchor(point, mouseEvent);
8725
- x = anchor[0];
8726
- y = anchor[1];
8727
-
8728
- // shared tooltip, array is sent over
8729
- if (shared && !(point.series && point.series.noSharedTooltip)) {
8730
-
8731
- // hide previous hoverPoints and set new
8732
-
8733
- chart.hoverPoints = point;
8734
- if (hoverPoints) {
8735
- each(hoverPoints, function (point) {
8736
- point.setState();
8737
- });
8738
- }
8739
-
8740
- each(point, function (item) {
8741
- item.setState(HOVER_STATE);
8742
-
8743
- pointConfig.push(item.getLabelConfig());
8744
- });
8745
-
8746
- textConfig = {
8747
- x: point[0].category,
8748
- y: point[0].y
8749
- };
8750
- textConfig.points = pointConfig;
8751
- point = point[0];
8752
-
8753
- // single point tooltip
8754
- } else {
8755
- textConfig = point.getLabelConfig();
8756
- }
8757
- text = formatter.call(textConfig, tooltip);
8758
-
8759
- // register the current series
8760
- currentSeries = point.series;
8761
-
8762
-
8763
- // For line type series, hide tooltip if the point falls outside the plot
8764
- show = shared || !currentSeries.isCartesian || currentSeries.tooltipOutsidePlot || chart.isInsidePlot(x, y);
8765
-
8766
- // update the inner HTML
8767
- if (text === false || !show) {
8768
- this.hide();
8769
- } else {
8770
-
8771
- // show it
8772
- if (tooltip.isHidden) {
8773
- stop(label);
8774
- label.attr('opacity', 1).show();
8775
- }
8776
-
8777
- // update text
8778
- label.attr({
8779
- text: text
8780
- });
8781
-
8782
- // set the stroke color of the box
8783
- borderColor = options.borderColor || point.color || currentSeries.color || '#606060';
8784
- label.attr({
8785
- stroke: borderColor
8786
- });
8787
-
8788
- tooltip.updatePosition({ plotX: x, plotY: y });
8789
-
8790
- this.isHidden = false;
8791
- }
8792
-
8793
- // crosshairs
8794
- if (crosshairsOptions) {
8795
- crosshairsOptions = splat(crosshairsOptions); // [x, y]
8796
-
8797
- var path,
8798
- i = crosshairsOptions.length,
8799
- attribs,
8800
- axis,
8801
- val;
8802
-
8803
- while (i--) {
8804
- axis = point.series[i ? 'yAxis' : 'xAxis'];
8805
- if (crosshairsOptions[i] && axis) {
8806
- val = i ? pick(point.stackY, point.y) : point.x; // #814
8807
- if (axis.isLog) { // #1671
8808
- val = log2lin(val);
8809
- }
8810
-
8811
- path = axis.getPlotLinePath(
8812
- val,
8813
- 1
8814
- );
8815
-
8816
- if (tooltip.crosshairs[i]) {
8817
- tooltip.crosshairs[i].attr({ d: path, visibility: VISIBLE });
8818
- } else {
8819
- attribs = {
8820
- 'stroke-width': crosshairsOptions[i].width || 1,
8821
- stroke: crosshairsOptions[i].color || '#C0C0C0',
8822
- zIndex: crosshairsOptions[i].zIndex || 2
8823
- };
8824
- if (crosshairsOptions[i].dashStyle) {
8825
- attribs.dashstyle = crosshairsOptions[i].dashStyle;
8826
- }
8827
- tooltip.crosshairs[i] = chart.renderer.path(path)
8828
- .attr(attribs)
8829
- .add();
8830
- }
8831
- }
8832
- }
8833
- }
8834
- fireEvent(chart, 'tooltipRefresh', {
8835
- text: text,
8836
- x: x + chart.plotLeft,
8837
- y: y + chart.plotTop,
8838
- borderColor: borderColor
8839
- });
8840
- },
8841
-
8842
- /**
8843
- * Find the new position and perform the move
8844
- */
8845
- updatePosition: function (point) {
8846
- var chart = this.chart,
8847
- label = this.label,
8848
- pos = (this.options.positioner || this.getPosition).call(
8849
- this,
8850
- label.width,
8851
- label.height,
8852
- point
8853
- );
8854
-
8855
- // do the move
8856
- this.move(
8857
- mathRound(pos.x),
8858
- mathRound(pos.y),
8859
- point.plotX + chart.plotLeft,
8860
- point.plotY + chart.plotTop
8861
- );
8862
- }
8863
- };
8864
- /**
8865
- * The mouse tracker object. All methods starting with "on" are primary DOM event handlers.
8866
- * Subsequent methods should be named differently from what they are doing.
8867
- * @param {Object} chart The Chart instance
8868
- * @param {Object} options The root options object
8869
- */
8870
- function Pointer(chart, options) {
8871
- this.init(chart, options);
8872
- }
8873
-
8874
- Pointer.prototype = {
8875
- /**
8876
- * Initialize Pointer
8877
- */
8878
- init: function (chart, options) {
8879
-
8880
- var zoomType = useCanVG ? '' : options.chart.zoomType,
8881
- inverted = chart.inverted,
8882
- zoomX,
8883
- zoomY;
8884
-
8885
- // Store references
8886
- this.options = options;
8887
- this.chart = chart;
8888
-
8889
- // Zoom status
8890
- this.zoomX = zoomX = /x/.test(zoomType);
8891
- this.zoomY = zoomY = /y/.test(zoomType);
8892
- this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
8893
- this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
8894
-
8895
- this.pinchDown = [];
8896
- this.lastValidTouch = {};
8897
-
8898
- if (options.tooltip.enabled) {
8899
- chart.tooltip = new Tooltip(chart, options.tooltip);
8900
- }
8901
-
8902
- this.setDOMEvents();
8903
- },
8904
-
8905
- /**
8906
- * Add crossbrowser support for chartX and chartY
8907
- * @param {Object} e The event object in standard browsers
8908
- */
8909
- normalize: function (e) {
8910
- var chartPosition,
8911
- chartX,
8912
- chartY,
8913
- ePos;
8914
-
8915
- // common IE normalizing
8916
- e = e || win.event;
8917
- if (!e.target) {
8918
- e.target = e.srcElement;
8919
- }
8920
-
8921
- // Framework specific normalizing (#1165)
8922
- e = washMouseEvent(e);
8923
-
8924
- // iOS
8925
- ePos = e.touches ? e.touches.item(0) : e;
8926
-
8927
- // get mouse position
8928
- this.chartPosition = chartPosition = offset(this.chart.container);
8929
-
8930
- // chartX and chartY
8931
- if (ePos.pageX === UNDEFINED) { // IE < 9. #886.
8932
- chartX = e.x;
8933
- chartY = e.y;
8934
- } else {
8935
- chartX = ePos.pageX - chartPosition.left;
8936
- chartY = ePos.pageY - chartPosition.top;
8937
- }
8938
-
8939
- return extend(e, {
8940
- chartX: mathRound(chartX),
8941
- chartY: mathRound(chartY)
8942
- });
8943
- },
8944
-
8945
- /**
8946
- * Get the click position in terms of axis values.
8947
- *
8948
- * @param {Object} e A pointer event
8949
- */
8950
- getCoordinates: function (e) {
8951
- var coordinates = {
8952
- xAxis: [],
8953
- yAxis: []
8954
- };
8955
-
8956
- each(this.chart.axes, function (axis) {
8957
- coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
8958
- axis: axis,
8959
- value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
8960
- });
8961
- });
8962
- return coordinates;
8963
- },
8964
-
8965
- /**
8966
- * Return the index in the tooltipPoints array, corresponding to pixel position in
8967
- * the plot area.
8968
- */
8969
- getIndex: function (e) {
8970
- var chart = this.chart;
8971
- return chart.inverted ?
8972
- chart.plotHeight + chart.plotTop - e.chartY :
8973
- e.chartX - chart.plotLeft;
8974
- },
8975
-
8976
- /**
8977
- * With line type charts with a single tracker, get the point closest to the mouse.
8978
- * Run Point.onMouseOver and display tooltip for the point or points.
8979
- */
8980
- runPointActions: function (e) {
8981
- var pointer = this,
8982
- chart = pointer.chart,
8983
- series = chart.series,
8984
- tooltip = chart.tooltip,
8985
- point,
8986
- points,
8987
- hoverPoint = chart.hoverPoint,
8988
- hoverSeries = chart.hoverSeries,
8989
- i,
8990
- j,
8991
- distance = chart.chartWidth,
8992
- index = pointer.getIndex(e),
8993
- anchor;
8994
-
8995
- // shared tooltip
8996
- if (tooltip && pointer.options.tooltip.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) {
8997
- points = [];
8998
-
8999
- // loop over all series and find the ones with points closest to the mouse
9000
- i = series.length;
9001
- for (j = 0; j < i; j++) {
9002
- if (series[j].visible &&
9003
- series[j].options.enableMouseTracking !== false &&
9004
- !series[j].noSharedTooltip && series[j].tooltipPoints.length) {
9005
- point = series[j].tooltipPoints[index];
9006
- if (point.series) { // not a dummy point, #1544
9007
- point._dist = mathAbs(index - point.clientX);
9008
- distance = mathMin(distance, point._dist);
9009
- points.push(point);
9010
- }
9011
- }
9012
- }
9013
- // remove furthest points
9014
- i = points.length;
9015
- while (i--) {
9016
- if (points[i]._dist > distance) {
9017
- points.splice(i, 1);
9018
- }
9019
- }
9020
- // refresh the tooltip if necessary
9021
- if (points.length && (points[0].clientX !== pointer.hoverX)) {
9022
- tooltip.refresh(points, e);
9023
- pointer.hoverX = points[0].clientX;
9024
- }
9025
- }
9026
-
9027
- // separate tooltip and general mouse events
9028
- if (hoverSeries && hoverSeries.tracker) { // only use for line-type series with common tracker
9029
-
9030
- // get the point
9031
- point = hoverSeries.tooltipPoints[index];
9032
-
9033
- // a new point is hovered, refresh the tooltip
9034
- if (point && point !== hoverPoint) {
9035
-
9036
- // trigger the events
9037
- point.onMouseOver(e);
9038
-
9039
- }
9040
-
9041
- } else if (tooltip && tooltip.followPointer && !tooltip.isHidden) {
9042
- anchor = tooltip.getAnchor([{}], e);
9043
- tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
9044
- }
9045
- },
9046
-
9047
-
9048
-
9049
- /**
9050
- * Reset the tracking by hiding the tooltip, the hover series state and the hover point
9051
- *
9052
- * @param allowMove {Boolean} Instead of destroying the tooltip altogether, allow moving it if possible
9053
- */
9054
- reset: function (allowMove) {
9055
- var pointer = this,
9056
- chart = pointer.chart,
9057
- hoverSeries = chart.hoverSeries,
9058
- hoverPoint = chart.hoverPoint,
9059
- tooltip = chart.tooltip,
9060
- tooltipPoints = tooltip && tooltip.shared ? chart.hoverPoints : hoverPoint;
9061
-
9062
- // Narrow in allowMove
9063
- allowMove = allowMove && tooltip && tooltipPoints;
9064
-
9065
- // Check if the points have moved outside the plot area, #1003
9066
- if (allowMove && splat(tooltipPoints)[0].plotX === UNDEFINED) {
9067
- allowMove = false;
9068
- }
9069
-
9070
- // Just move the tooltip, #349
9071
- if (allowMove) {
9072
- tooltip.refresh(tooltipPoints);
9073
-
9074
- // Full reset
9075
- } else {
9076
-
9077
- if (hoverPoint) {
9078
- hoverPoint.onMouseOut();
9079
- }
9080
-
9081
- if (hoverSeries) {
9082
- hoverSeries.onMouseOut();
9083
- }
9084
-
9085
- if (tooltip) {
9086
- tooltip.hide();
9087
- tooltip.hideCrosshairs();
9088
- }
9089
-
9090
- pointer.hoverX = null;
9091
-
9092
- }
9093
- },
9094
-
9095
- /**
9096
- * Scale series groups to a certain scale and translation
9097
- */
9098
- scaleGroups: function (attribs, clip) {
9099
-
9100
- var chart = this.chart;
9101
-
9102
- // Scale each series
9103
- each(chart.series, function (series) {
9104
- if (series.xAxis && series.xAxis.zoomEnabled) {
9105
- series.group.attr(attribs);
9106
- if (series.markerGroup) {
9107
- series.markerGroup.attr(attribs);
9108
- series.markerGroup.clip(clip ? chart.clipRect : null);
9109
- }
9110
- if (series.dataLabelsGroup) {
9111
- series.dataLabelsGroup.attr(attribs);
9112
- }
9113
- }
9114
- });
9115
-
9116
- // Clip
9117
- chart.clipRect.attr(clip || chart.clipBox);
9118
- },
9119
-
9120
- /**
9121
- * Run translation operations for each direction (horizontal and vertical) independently
9122
- */
9123
- pinchTranslateDirection: function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
9124
- var chart = this.chart,
9125
- xy = horiz ? 'x' : 'y',
9126
- XY = horiz ? 'X' : 'Y',
9127
- sChartXY = 'chart' + XY,
9128
- wh = horiz ? 'width' : 'height',
9129
- plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')],
9130
- selectionWH,
9131
- selectionXY,
9132
- clipXY,
9133
- scale = 1,
9134
- inverted = chart.inverted,
9135
- bounds = chart.bounds[horiz ? 'h' : 'v'],
9136
- singleTouch = pinchDown.length === 1,
9137
- touch0Start = pinchDown[0][sChartXY],
9138
- touch0Now = touches[0][sChartXY],
9139
- touch1Start = !singleTouch && pinchDown[1][sChartXY],
9140
- touch1Now = !singleTouch && touches[1][sChartXY],
9141
- outOfBounds,
9142
- transformScale,
9143
- scaleKey,
9144
- setScale = function () {
9145
- if (!singleTouch && mathAbs(touch0Start - touch1Start) > 20) { // Don't zoom if fingers are too close on this axis
9146
- scale = mathAbs(touch0Now - touch1Now) / mathAbs(touch0Start - touch1Start);
9147
- }
9148
-
9149
- clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
9150
- selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
9151
- };
9152
-
9153
- // Set the scale, first pass
9154
- setScale();
9155
-
9156
- selectionXY = clipXY; // the clip position (x or y) is altered if out of bounds, the selection position is not
9157
-
9158
- // Out of bounds
9159
- if (selectionXY < bounds.min) {
9160
- selectionXY = bounds.min;
9161
- outOfBounds = true;
9162
- } else if (selectionXY + selectionWH > bounds.max) {
9163
- selectionXY = bounds.max - selectionWH;
9164
- outOfBounds = true;
9165
- }
9166
-
9167
- // Is the chart dragged off its bounds, determined by dataMin and dataMax?
9168
- if (outOfBounds) {
9169
-
9170
- // Modify the touchNow position in order to create an elastic drag movement. This indicates
9171
- // to the user that the chart is responsive but can't be dragged further.
9172
- touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
9173
- if (!singleTouch) {
9174
- touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
9175
- }
9176
-
9177
- // Set the scale, second pass to adapt to the modified touchNow positions
9178
- setScale();
9179
-
9180
- } else {
9181
- lastValidTouch[xy] = [touch0Now, touch1Now];
9182
- }
9183
-
9184
-
9185
- // Set geometry for clipping, selection and transformation
9186
- if (!inverted) { // TODO: implement clipping for inverted charts
9187
- clip[xy] = clipXY - plotLeftTop;
9188
- clip[wh] = selectionWH;
9189
- }
9190
- scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
9191
- transformScale = inverted ? 1 / scale : scale;
9192
-
9193
- selectionMarker[wh] = selectionWH;
9194
- selectionMarker[xy] = selectionXY;
9195
- transform[scaleKey] = scale;
9196
- transform['translate' + XY] = (transformScale * plotLeftTop) + (touch0Now - (transformScale * touch0Start));
9197
- },
9198
-
9199
- /**
9200
- * Handle touch events with two touches
9201
- */
9202
- pinch: function (e) {
9203
-
9204
- var self = this,
9205
- chart = self.chart,
9206
- pinchDown = self.pinchDown,
9207
- followTouchMove = chart.tooltip && chart.tooltip.options.followTouchMove,
9208
- touches = e.touches,
9209
- touchesLength = touches.length,
9210
- lastValidTouch = self.lastValidTouch,
9211
- zoomHor = self.zoomHor || self.pinchHor,
9212
- zoomVert = self.zoomVert || self.pinchVert,
9213
- hasZoom = zoomHor || zoomVert,
9214
- selectionMarker = self.selectionMarker,
9215
- transform = {},
9216
- clip = {};
9217
-
9218
- // On touch devices, only proceed to trigger click if a handler is defined
9219
- if (e.type === 'touchstart') {
9220
- if (followTouchMove || hasZoom) {
9221
- e.preventDefault();
9222
- }
9223
- }
9224
-
9225
- // Normalize each touch
9226
- map(touches, function (e) {
9227
- return self.normalize(e);
9228
- });
9229
-
9230
- // Register the touch start position
9231
- if (e.type === 'touchstart') {
9232
- each(touches, function (e, i) {
9233
- pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
9234
- });
9235
- lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] && pinchDown[1].chartX];
9236
- lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] && pinchDown[1].chartY];
9237
-
9238
- // Identify the data bounds in pixels
9239
- each(chart.axes, function (axis) {
9240
- if (axis.zoomEnabled) {
9241
- var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
9242
- minPixelPadding = axis.minPixelPadding,
9243
- min = axis.toPixels(axis.dataMin),
9244
- max = axis.toPixels(axis.dataMax),
9245
- absMin = mathMin(min, max),
9246
- absMax = mathMax(min, max);
9247
-
9248
- // Store the bounds for use in the touchmove handler
9249
- bounds.min = mathMin(axis.pos, absMin - minPixelPadding);
9250
- bounds.max = mathMax(axis.pos + axis.len, absMax + minPixelPadding);
9251
- }
9252
- });
9253
-
9254
- // Event type is touchmove, handle panning and pinching
9255
- } else if (pinchDown.length) { // can be 0 when releasing, if touchend fires first
9256
-
9257
-
9258
- // Set the marker
9259
- if (!selectionMarker) {
9260
- self.selectionMarker = selectionMarker = extend({
9261
- destroy: noop
9262
- }, chart.plotBox);
9263
- }
9264
-
9265
-
9266
-
9267
- if (zoomHor) {
9268
- self.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
9269
- }
9270
- if (zoomVert) {
9271
- self.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
9272
- }
9273
-
9274
- self.hasPinched = hasZoom;
9275
-
9276
- // Scale and translate the groups to provide visual feedback during pinching
9277
- self.scaleGroups(transform, clip);
9278
-
9279
- // Optionally move the tooltip on touchmove
9280
- if (!hasZoom && followTouchMove && touchesLength === 1) {
9281
- this.runPointActions(self.normalize(e));
9282
- }
9283
- }
9284
- },
9285
-
9286
- /**
9287
- * Start a drag operation
9288
- */
9289
- dragStart: function (e) {
9290
- var chart = this.chart;
9291
-
9292
- // Record the start position
9293
- chart.mouseIsDown = e.type;
9294
- chart.cancelClick = false;
9295
- chart.mouseDownX = this.mouseDownX = e.chartX;
9296
- this.mouseDownY = e.chartY;
9297
- },
9298
-
9299
- /**
9300
- * Perform a drag operation in response to a mousemove event while the mouse is down
9301
- */
9302
- drag: function (e) {
9303
-
9304
- var chart = this.chart,
9305
- chartOptions = chart.options.chart,
9306
- chartX = e.chartX,
9307
- chartY = e.chartY,
9308
- zoomHor = this.zoomHor,
9309
- zoomVert = this.zoomVert,
9310
- plotLeft = chart.plotLeft,
9311
- plotTop = chart.plotTop,
9312
- plotWidth = chart.plotWidth,
9313
- plotHeight = chart.plotHeight,
9314
- clickedInside,
9315
- size,
9316
- mouseDownX = this.mouseDownX,
9317
- mouseDownY = this.mouseDownY;
9318
-
9319
- // If the mouse is outside the plot area, adjust to cooordinates
9320
- // inside to prevent the selection marker from going outside
9321
- if (chartX < plotLeft) {
9322
- chartX = plotLeft;
9323
- } else if (chartX > plotLeft + plotWidth) {
9324
- chartX = plotLeft + plotWidth;
9325
- }
9326
-
9327
- if (chartY < plotTop) {
9328
- chartY = plotTop;
9329
- } else if (chartY > plotTop + plotHeight) {
9330
- chartY = plotTop + plotHeight;
9331
- }
9332
-
9333
- // determine if the mouse has moved more than 10px
9334
- this.hasDragged = Math.sqrt(
9335
- Math.pow(mouseDownX - chartX, 2) +
9336
- Math.pow(mouseDownY - chartY, 2)
9337
- );
9338
- if (this.hasDragged > 10) {
9339
- clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop);
9340
-
9341
- // make a selection
9342
- if (chart.hasCartesianSeries && (this.zoomX || this.zoomY) && clickedInside) {
9343
- if (!this.selectionMarker) {
9344
- this.selectionMarker = chart.renderer.rect(
9345
- plotLeft,
9346
- plotTop,
9347
- zoomHor ? 1 : plotWidth,
9348
- zoomVert ? 1 : plotHeight,
9349
- 0
9350
- )
9351
- .attr({
9352
- fill: chartOptions.selectionMarkerFill || 'rgba(69,114,167,0.25)',
9353
- zIndex: 7
9354
- })
9355
- .add();
9356
- }
9357
- }
9358
-
9359
- // adjust the width of the selection marker
9360
- if (this.selectionMarker && zoomHor) {
9361
- size = chartX - mouseDownX;
9362
- this.selectionMarker.attr({
9363
- width: mathAbs(size),
9364
- x: (size > 0 ? 0 : size) + mouseDownX
9365
- });
9366
- }
9367
- // adjust the height of the selection marker
9368
- if (this.selectionMarker && zoomVert) {
9369
- size = chartY - mouseDownY;
9370
- this.selectionMarker.attr({
9371
- height: mathAbs(size),
9372
- y: (size > 0 ? 0 : size) + mouseDownY
9373
- });
9374
- }
9375
-
9376
- // panning
9377
- if (clickedInside && !this.selectionMarker && chartOptions.panning) {
9378
- chart.pan(chartX);
9379
- }
9380
- }
9381
- },
9382
-
9383
- /**
9384
- * On mouse up or touch end across the entire document, drop the selection.
9385
- */
9386
- drop: function (e) {
9387
- var chart = this.chart,
9388
- hasPinched = this.hasPinched;
9389
-
9390
- if (this.selectionMarker) {
9391
- var selectionData = {
9392
- xAxis: [],
9393
- yAxis: [],
9394
- originalEvent: e.originalEvent || e
9395
- },
9396
- selectionBox = this.selectionMarker,
9397
- selectionLeft = selectionBox.x,
9398
- selectionTop = selectionBox.y,
9399
- runZoom;
9400
- // a selection has been made
9401
- if (this.hasDragged || hasPinched) {
9402
-
9403
- // record each axis' min and max
9404
- each(chart.axes, function (axis) {
9405
- if (axis.zoomEnabled) {
9406
- var horiz = axis.horiz,
9407
- selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop)),
9408
- selectionMax = axis.toValue((horiz ? selectionLeft + selectionBox.width : selectionTop + selectionBox.height));
9409
-
9410
- if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859
9411
- selectionData[axis.xOrY + 'Axis'].push({
9412
- axis: axis,
9413
- min: mathMin(selectionMin, selectionMax), // for reversed axes,
9414
- max: mathMax(selectionMin, selectionMax)
9415
- });
9416
- runZoom = true;
9417
- }
9418
- }
9419
- });
9420
- if (runZoom) {
9421
- fireEvent(chart, 'selection', selectionData, function (args) {
9422
- chart.zoom(extend(args, hasPinched ? { animation: false } : null));
9423
- });
9424
- }
9425
-
9426
- }
9427
- this.selectionMarker = this.selectionMarker.destroy();
9428
-
9429
- // Reset scaling preview
9430
- if (hasPinched) {
9431
- this.scaleGroups({
9432
- translateX: chart.plotLeft,
9433
- translateY: chart.plotTop,
9434
- scaleX: 1,
9435
- scaleY: 1
9436
- });
9437
- }
9438
- }
9439
-
9440
- // Reset all
9441
- if (chart) { // it may be destroyed on mouse up - #877
9442
- css(chart.container, { cursor: chart._cursor });
9443
- chart.cancelClick = this.hasDragged > 10; // #370
9444
- chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
9445
- this.pinchDown = [];
9446
- }
9447
- },
9448
-
9449
- onContainerMouseDown: function (e) {
9450
-
9451
- e = this.normalize(e);
9452
-
9453
- // issue #295, dragging not always working in Firefox
9454
- if (e.preventDefault) {
9455
- e.preventDefault();
9456
- }
9457
-
9458
- this.dragStart(e);
9459
- },
9460
-
9461
-
9462
-
9463
- onDocumentMouseUp: function (e) {
9464
- this.drop(e);
9465
- },
9466
-
9467
- /**
9468
- * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea.
9469
- * Issue #149 workaround. The mouseleave event does not always fire.
9470
- */
9471
- onDocumentMouseMove: function (e) {
9472
- var chart = this.chart,
9473
- chartPosition = this.chartPosition,
9474
- hoverSeries = chart.hoverSeries;
9475
-
9476
- // Get e.pageX and e.pageY back in MooTools
9477
- e = washMouseEvent(e);
9478
-
9479
- // If we're outside, hide the tooltip
9480
- if (chartPosition && hoverSeries && hoverSeries.isCartesian &&
9481
- !chart.isInsidePlot(e.pageX - chartPosition.left - chart.plotLeft,
9482
- e.pageY - chartPosition.top - chart.plotTop)) {
9483
- this.reset();
9484
- }
9485
- },
9486
-
9487
- /**
9488
- * When mouse leaves the container, hide the tooltip.
9489
- */
9490
- onContainerMouseLeave: function () {
9491
- this.reset();
9492
- this.chartPosition = null; // also reset the chart position, used in #149 fix
9493
- },
9494
-
9495
- // The mousemove, touchmove and touchstart event handler
9496
- onContainerMouseMove: function (e) {
9497
-
9498
- var chart = this.chart;
9499
-
9500
- // normalize
9501
- e = this.normalize(e);
9502
-
9503
- // #295
9504
- e.returnValue = false;
9505
-
9506
-
9507
- if (chart.mouseIsDown === 'mousedown') {
9508
- this.drag(e);
9509
- }
9510
-
9511
- // Show the tooltip and run mouse over events (#977)
9512
- if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop) && !chart.openMenu) {
9513
- this.runPointActions(e);
9514
- }
9515
- },
9516
-
9517
- /**
9518
- * Utility to detect whether an element has, or has a parent with, a specific
9519
- * class name. Used on detection of tracker objects and on deciding whether
9520
- * hovering the tooltip should cause the active series to mouse out.
9521
- */
9522
- inClass: function (element, className) {
9523
- var elemClassName;
9524
- while (element) {
9525
- elemClassName = attr(element, 'class');
9526
- if (elemClassName) {
9527
- if (elemClassName.indexOf(className) !== -1) {
9528
- return true;
9529
- } else if (elemClassName.indexOf(PREFIX + 'container') !== -1) {
9530
- return false;
9531
- }
9532
- }
9533
- element = element.parentNode;
9534
- }
9535
- },
9536
-
9537
- onTrackerMouseOut: function (e) {
9538
- var series = this.chart.hoverSeries;
9539
- if (series && !series.options.stickyTracking && !this.inClass(e.toElement || e.relatedTarget, PREFIX + 'tooltip')) {
9540
- series.onMouseOut();
9541
- }
9542
- },
9543
-
9544
- onContainerClick: function (e) {
9545
- var chart = this.chart,
9546
- hoverPoint = chart.hoverPoint,
9547
- plotLeft = chart.plotLeft,
9548
- plotTop = chart.plotTop,
9549
- inverted = chart.inverted,
9550
- chartPosition,
9551
- plotX,
9552
- plotY;
9553
-
9554
- e = this.normalize(e);
9555
- e.cancelBubble = true; // IE specific
9556
-
9557
- if (!chart.cancelClick) {
9558
-
9559
- // On tracker click, fire the series and point events. #783, #1583
9560
- if (hoverPoint && this.inClass(e.target, PREFIX + 'tracker')) {
9561
- chartPosition = this.chartPosition;
9562
- plotX = hoverPoint.plotX;
9563
- plotY = hoverPoint.plotY;
9564
-
9565
- // add page position info
9566
- extend(hoverPoint, {
9567
- pageX: chartPosition.left + plotLeft +
9568
- (inverted ? chart.plotWidth - plotY : plotX),
9569
- pageY: chartPosition.top + plotTop +
9570
- (inverted ? chart.plotHeight - plotX : plotY)
9571
- });
9572
-
9573
- // the series click event
9574
- fireEvent(hoverPoint.series, 'click', extend(e, {
9575
- point: hoverPoint
9576
- }));
9577
-
9578
- // the point click event
9579
- if (chart.hoverPoint) { // it may be destroyed (#1844)
9580
- hoverPoint.firePointEvent('click', e);
9581
- }
9582
-
9583
- // When clicking outside a tracker, fire a chart event
9584
- } else {
9585
- extend(e, this.getCoordinates(e));
9586
-
9587
- // fire a click event in the chart
9588
- if (chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)) {
9589
- fireEvent(chart, 'click', e);
9590
- }
9591
- }
9592
-
9593
-
9594
- }
9595
- },
9596
-
9597
- onContainerTouchStart: function (e) {
9598
- var chart = this.chart;
9599
-
9600
- if (e.touches.length === 1) {
9601
-
9602
- e = this.normalize(e);
9603
-
9604
- if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
9605
-
9606
- // Prevent the click pseudo event from firing unless it is set in the options
9607
- /*if (!chart.runChartClick) {
9608
- e.preventDefault();
9609
- }*/
9610
-
9611
- // Run mouse events and display tooltip etc
9612
- this.runPointActions(e);
9613
-
9614
- this.pinch(e);
9615
- }
9616
-
9617
- } else if (e.touches.length === 2) {
9618
- this.pinch(e);
9619
- }
9620
- },
9621
-
9622
- onContainerTouchMove: function (e) {
9623
- if (e.touches.length === 1 || e.touches.length === 2) {
9624
- this.pinch(e);
9625
- }
9626
- },
9627
-
9628
- onDocumentTouchEnd: function (e) {
9629
- this.drop(e);
9630
- },
9631
-
9632
- /**
9633
- * Set the JS DOM events on the container and document. This method should contain
9634
- * a one-to-one assignment between methods and their handlers. Any advanced logic should
9635
- * be moved to the handler reflecting the event's name.
9636
- */
9637
- setDOMEvents: function () {
9638
-
9639
- var pointer = this,
9640
- container = pointer.chart.container,
9641
- events;
9642
-
9643
- this._events = events = [
9644
- [container, 'onmousedown', 'onContainerMouseDown'],
9645
- [container, 'onmousemove', 'onContainerMouseMove'],
9646
- [container, 'onclick', 'onContainerClick'],
9647
- [container, 'mouseleave', 'onContainerMouseLeave'],
9648
- [doc, 'mousemove', 'onDocumentMouseMove'],
9649
- [doc, 'mouseup', 'onDocumentMouseUp']
9650
- ];
9651
-
9652
- if (hasTouch) {
9653
- events.push(
9654
- [container, 'ontouchstart', 'onContainerTouchStart'],
9655
- [container, 'ontouchmove', 'onContainerTouchMove'],
9656
- [doc, 'touchend', 'onDocumentTouchEnd']
9657
- );
9658
- }
9659
-
9660
- each(events, function (eventConfig) {
9661
-
9662
- // First, create the callback function that in turn calls the method on Pointer
9663
- pointer['_' + eventConfig[2]] = function (e) {
9664
- pointer[eventConfig[2]](e);
9665
- };
9666
-
9667
- // Now attach the function, either as a direct property or through addEvent
9668
- if (eventConfig[1].indexOf('on') === 0) {
9669
- eventConfig[0][eventConfig[1]] = pointer['_' + eventConfig[2]];
9670
- } else {
9671
- addEvent(eventConfig[0], eventConfig[1], pointer['_' + eventConfig[2]]);
9672
- }
9673
- });
9674
-
9675
-
9676
- },
9677
-
9678
- /**
9679
- * Destroys the Pointer object and disconnects DOM events.
9680
- */
9681
- destroy: function () {
9682
- var pointer = this;
9683
-
9684
- // Release all DOM events
9685
- each(pointer._events, function (eventConfig) {
9686
- if (eventConfig[1].indexOf('on') === 0) {
9687
- eventConfig[0][eventConfig[1]] = null; // delete breaks oldIE
9688
- } else {
9689
- removeEvent(eventConfig[0], eventConfig[1], pointer['_' + eventConfig[2]]);
9690
- }
9691
- });
9692
- delete pointer._events;
9693
-
9694
- // memory and CPU leak
9695
- clearInterval(pointer.tooltipTimeout);
9696
- }
9697
- };
9698
- /**
9699
- * The overview of the chart's series
9700
- */
9701
- function Legend(chart, options) {
9702
- this.init(chart, options);
9703
- }
9704
-
9705
- Legend.prototype = {
9706
-
9707
- /**
9708
- * Initialize the legend
9709
- */
9710
- init: function (chart, options) {
9711
-
9712
- var legend = this,
9713
- itemStyle = options.itemStyle,
9714
- padding = pick(options.padding, 8),
9715
- itemMarginTop = options.itemMarginTop || 0;
9716
-
9717
- this.options = options;
9718
-
9719
- if (!options.enabled) {
9720
- return;
9721
- }
9722
-
9723
- legend.baseline = pInt(itemStyle.fontSize) + 3 + itemMarginTop; // used in Series prototype
9724
- legend.itemStyle = itemStyle;
9725
- legend.itemHiddenStyle = merge(itemStyle, options.itemHiddenStyle);
9726
- legend.itemMarginTop = itemMarginTop;
9727
- legend.padding = padding;
9728
- legend.initialItemX = padding;
9729
- legend.initialItemY = padding - 5; // 5 is the number of pixels above the text
9730
- legend.maxItemWidth = 0;
9731
- legend.chart = chart;
9732
- legend.itemHeight = 0;
9733
- legend.lastLineHeight = 0;
9734
-
9735
- // Render it
9736
- legend.render();
9737
-
9738
- // move checkboxes
9739
- addEvent(legend.chart, 'endResize', function () {
9740
- legend.positionCheckboxes();
9741
- });
9742
-
9743
- },
9744
-
9745
- /**
9746
- * Set the colors for the legend item
9747
- * @param {Object} item A Series or Point instance
9748
- * @param {Object} visible Dimmed or colored
9749
- */
9750
- colorizeItem: function (item, visible) {
9751
- var legend = this,
9752
- options = legend.options,
9753
- legendItem = item.legendItem,
9754
- legendLine = item.legendLine,
9755
- legendSymbol = item.legendSymbol,
9756
- hiddenColor = legend.itemHiddenStyle.color,
9757
- textColor = visible ? options.itemStyle.color : hiddenColor,
9758
- symbolColor = visible ? item.color : hiddenColor,
9759
- markerOptions = item.options && item.options.marker,
9760
- symbolAttr = {
9761
- stroke: symbolColor,
9762
- fill: symbolColor
9763
- },
9764
- key,
9765
- val;
9766
-
9767
-
9768
- if (legendItem) {
9769
- legendItem.css({ fill: textColor, color: textColor }); // color for #1553, oldIE
9770
- }
9771
- if (legendLine) {
9772
- legendLine.attr({ stroke: symbolColor });
9773
- }
9774
-
9775
- if (legendSymbol) {
9776
-
9777
- // Apply marker options
9778
- if (markerOptions) {
9779
- markerOptions = item.convertAttribs(markerOptions);
9780
- for (key in markerOptions) {
9781
- val = markerOptions[key];
9782
- if (val !== UNDEFINED) {
9783
- symbolAttr[key] = val;
9784
- }
9785
- }
9786
- }
9787
-
9788
- legendSymbol.attr(symbolAttr);
9789
- }
9790
- },
9791
-
9792
- /**
9793
- * Position the legend item
9794
- * @param {Object} item A Series or Point instance
9795
- */
9796
- positionItem: function (item) {
9797
- var legend = this,
9798
- options = legend.options,
9799
- symbolPadding = options.symbolPadding,
9800
- ltr = !options.rtl,
9801
- legendItemPos = item._legendItemPos,
9802
- itemX = legendItemPos[0],
9803
- itemY = legendItemPos[1],
9804
- checkbox = item.checkbox;
9805
-
9806
- if (item.legendGroup) {
9807
- item.legendGroup.translate(
9808
- ltr ? itemX : legend.legendWidth - itemX - 2 * symbolPadding - 4,
9809
- itemY
9810
- );
9811
- }
9812
-
9813
- if (checkbox) {
9814
- checkbox.x = itemX;
9815
- checkbox.y = itemY;
9816
- }
9817
- },
9818
-
9819
- /**
9820
- * Destroy a single legend item
9821
- * @param {Object} item The series or point
9822
- */
9823
- destroyItem: function (item) {
9824
- var checkbox = item.checkbox;
9825
-
9826
- // destroy SVG elements
9827
- each(['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'], function (key) {
9828
- if (item[key]) {
9829
- item[key].destroy();
9830
- }
9831
- });
9832
-
9833
- if (checkbox) {
9834
- discardElement(item.checkbox);
9835
- }
9836
- },
9837
-
9838
- /**
9839
- * Destroys the legend.
9840
- */
9841
- destroy: function () {
9842
- var legend = this,
9843
- legendGroup = legend.group,
9844
- box = legend.box;
9845
-
9846
- if (box) {
9847
- legend.box = box.destroy();
9848
- }
9849
-
9850
- if (legendGroup) {
9851
- legend.group = legendGroup.destroy();
9852
- }
9853
- },
9854
-
9855
- /**
9856
- * Position the checkboxes after the width is determined
9857
- */
9858
- positionCheckboxes: function (scrollOffset) {
9859
- var alignAttr = this.group.alignAttr,
9860
- translateY,
9861
- clipHeight = this.clipHeight || this.legendHeight;
9862
-
9863
- if (alignAttr) {
9864
- translateY = alignAttr.translateY;
9865
- each(this.allItems, function (item) {
9866
- var checkbox = item.checkbox,
9867
- top;
9868
-
9869
- if (checkbox) {
9870
- top = (translateY + checkbox.y + (scrollOffset || 0) + 3);
9871
- css(checkbox, {
9872
- left: (alignAttr.translateX + item.legendItemWidth + checkbox.x - 20) + PX,
9873
- top: top + PX,
9874
- display: top > translateY - 6 && top < translateY + clipHeight - 6 ? '' : NONE
9875
- });
9876
- }
9877
- });
9878
- }
9879
- },
9880
-
9881
- /**
9882
- * Render the legend title on top of the legend
9883
- */
9884
- renderTitle: function () {
9885
- var options = this.options,
9886
- padding = this.padding,
9887
- titleOptions = options.title,
9888
- titleHeight = 0;
9889
-
9890
- if (titleOptions.text) {
9891
- if (!this.title) {
9892
- this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, null, null, 'legend-title')
9893
- .attr({ zIndex: 1 })
9894
- .css(titleOptions.style)
9895
- .add(this.group);
9896
- }
9897
- titleHeight = this.title.getBBox().height;
9898
- this.contentGroup.attr({ translateY: titleHeight });
9899
- }
9900
- this.titleHeight = titleHeight;
9901
- },
9902
-
9903
- /**
9904
- * Render a single specific legend item
9905
- * @param {Object} item A series or point
9906
- */
9907
- renderItem: function (item) {
9908
- var legend = this,
9909
- chart = legend.chart,
9910
- renderer = chart.renderer,
9911
- options = legend.options,
9912
- horizontal = options.layout === 'horizontal',
9913
- symbolWidth = options.symbolWidth,
9914
- symbolPadding = options.symbolPadding,
9915
- itemStyle = legend.itemStyle,
9916
- itemHiddenStyle = legend.itemHiddenStyle,
9917
- padding = legend.padding,
9918
- ltr = !options.rtl,
9919
- itemHeight,
9920
- widthOption = options.width,
9921
- itemMarginBottom = options.itemMarginBottom || 0,
9922
- itemMarginTop = legend.itemMarginTop,
9923
- initialItemX = legend.initialItemX,
9924
- bBox,
9925
- itemWidth,
9926
- li = item.legendItem,
9927
- series = item.series || item,
9928
- itemOptions = series.options,
9929
- showCheckbox = itemOptions.showCheckbox,
9930
- useHTML = options.useHTML;
9931
-
9932
- if (!li) { // generate it once, later move it
9933
-
9934
- // Generate the group box
9935
- // A group to hold the symbol and text. Text is to be appended in Legend class.
9936
- item.legendGroup = renderer.g('legend-item')
9937
- .attr({ zIndex: 1 })
9938
- .add(legend.scrollGroup);
9939
-
9940
- // Draw the legend symbol inside the group box
9941
- series.drawLegendSymbol(legend, item);
9942
-
9943
- // Generate the list item text and add it to the group
9944
- item.legendItem = li = renderer.text(
9945
- options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item),
9946
- ltr ? symbolWidth + symbolPadding : -symbolPadding,
9947
- legend.baseline,
9948
- useHTML
9949
- )
9950
- .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021)
9951
- .attr({
9952
- align: ltr ? 'left' : 'right',
9953
- zIndex: 2
9954
- })
9955
- .add(item.legendGroup);
9956
-
9957
- // Set the events on the item group, or in case of useHTML, the item itself (#1249)
9958
- (useHTML ? li : item.legendGroup).on('mouseover', function () {
9959
- item.setState(HOVER_STATE);
9960
- li.css(legend.options.itemHoverStyle);
9961
- })
9962
- .on('mouseout', function () {
9963
- li.css(item.visible ? itemStyle : itemHiddenStyle);
9964
- item.setState();
9965
- })
9966
- .on('click', function (event) {
9967
- var strLegendItemClick = 'legendItemClick',
9968
- fnLegendItemClick = function () {
9969
- item.setVisible();
9970
- };
9971
-
9972
- // Pass over the click/touch event. #4.
9973
- event = {
9974
- browserEvent: event
9975
- };
9976
-
9977
- // click the name or symbol
9978
- if (item.firePointEvent) { // point
9979
- item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
9980
- } else {
9981
- fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
9982
- }
9983
- });
9984
-
9985
- // Colorize the items
9986
- legend.colorizeItem(item, item.visible);
9987
-
9988
- // add the HTML checkbox on top
9989
- if (itemOptions && showCheckbox) {
9990
- item.checkbox = createElement('input', {
9991
- type: 'checkbox',
9992
- checked: item.selected,
9993
- defaultChecked: item.selected // required by IE7
9994
- }, options.itemCheckboxStyle, chart.container);
9995
-
9996
- addEvent(item.checkbox, 'click', function (event) {
9997
- var target = event.target;
9998
- fireEvent(item, 'checkboxClick', {
9999
- checked: target.checked
10000
- },
10001
- function () {
10002
- item.select();
10003
- }
10004
- );
10005
- });
10006
- }
10007
- }
10008
-
10009
- // calculate the positions for the next line
10010
- bBox = li.getBBox();
10011
-
10012
- itemWidth = item.legendItemWidth =
10013
- options.itemWidth || symbolWidth + symbolPadding + bBox.width + padding +
10014
- (showCheckbox ? 20 : 0);
10015
- legend.itemHeight = itemHeight = bBox.height;
10016
-
10017
- // if the item exceeds the width, start a new line
10018
- if (horizontal && legend.itemX - initialItemX + itemWidth >
10019
- (widthOption || (chart.chartWidth - 2 * padding - initialItemX))) {
10020
- legend.itemX = initialItemX;
10021
- legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
10022
- legend.lastLineHeight = 0; // reset for next line
10023
- }
10024
-
10025
- // If the item exceeds the height, start a new column
10026
- /*if (!horizontal && legend.itemY + options.y + itemHeight > chart.chartHeight - spacingTop - spacingBottom) {
10027
- legend.itemY = legend.initialItemY;
10028
- legend.itemX += legend.maxItemWidth;
10029
- legend.maxItemWidth = 0;
10030
- }*/
10031
-
10032
- // Set the edge positions
10033
- legend.maxItemWidth = mathMax(legend.maxItemWidth, itemWidth);
10034
- legend.lastItemY = itemMarginTop + legend.itemY + itemMarginBottom;
10035
- legend.lastLineHeight = mathMax(itemHeight, legend.lastLineHeight); // #915
10036
-
10037
- // cache the position of the newly generated or reordered items
10038
- item._legendItemPos = [legend.itemX, legend.itemY];
10039
-
10040
- // advance
10041
- if (horizontal) {
10042
- legend.itemX += itemWidth;
10043
-
10044
- } else {
10045
- legend.itemY += itemMarginTop + itemHeight + itemMarginBottom;
10046
- legend.lastLineHeight = itemHeight;
10047
- }
10048
-
10049
- // the width of the widest item
10050
- legend.offsetWidth = widthOption || mathMax(
10051
- horizontal ? legend.itemX - initialItemX : itemWidth,
10052
- legend.offsetWidth
10053
- );
10054
- },
10055
-
10056
- /**
10057
- * Render the legend. This method can be called both before and after
10058
- * chart.render. If called after, it will only rearrange items instead
10059
- * of creating new ones.
10060
- */
10061
- render: function () {
10062
- var legend = this,
10063
- chart = legend.chart,
10064
- renderer = chart.renderer,
10065
- legendGroup = legend.group,
10066
- allItems,
10067
- display,
10068
- legendWidth,
10069
- legendHeight,
10070
- box = legend.box,
10071
- options = legend.options,
10072
- padding = legend.padding,
10073
- legendBorderWidth = options.borderWidth,
10074
- legendBackgroundColor = options.backgroundColor;
10075
-
10076
- legend.itemX = legend.initialItemX;
10077
- legend.itemY = legend.initialItemY;
10078
- legend.offsetWidth = 0;
10079
- legend.lastItemY = 0;
10080
-
10081
- if (!legendGroup) {
10082
- legend.group = legendGroup = renderer.g('legend')
10083
- .attr({ zIndex: 7 })
10084
- .add();
10085
- legend.contentGroup = renderer.g()
10086
- .attr({ zIndex: 1 }) // above background
10087
- .add(legendGroup);
10088
- legend.scrollGroup = renderer.g()
10089
- .add(legend.contentGroup);
10090
- }
10091
-
10092
- legend.renderTitle();
10093
-
10094
- // add each series or point
10095
- allItems = [];
10096
- each(chart.series, function (serie) {
10097
- var seriesOptions = serie.options;
10098
-
10099
- if (!seriesOptions.showInLegend || defined(seriesOptions.linkedTo)) {
10100
- return;
10101
- }
10102
-
10103
- // use points or series for the legend item depending on legendType
10104
- allItems = allItems.concat(
10105
- serie.legendItems ||
10106
- (seriesOptions.legendType === 'point' ?
10107
- serie.data :
10108
- serie)
10109
- );
10110
- });
10111
-
10112
- // sort by legendIndex
10113
- stableSort(allItems, function (a, b) {
10114
- return ((a.options && a.options.legendIndex) || 0) - ((b.options && b.options.legendIndex) || 0);
10115
- });
10116
-
10117
- // reversed legend
10118
- if (options.reversed) {
10119
- allItems.reverse();
10120
- }
10121
-
10122
- legend.allItems = allItems;
10123
- legend.display = display = !!allItems.length;
10124
-
10125
- // render the items
10126
- each(allItems, function (item) {
10127
- legend.renderItem(item);
10128
- });
10129
-
10130
- // Draw the border
10131
- legendWidth = options.width || legend.offsetWidth;
10132
- legendHeight = legend.lastItemY + legend.lastLineHeight + legend.titleHeight;
10133
-
10134
-
10135
- legendHeight = legend.handleOverflow(legendHeight);
10136
-
10137
- if (legendBorderWidth || legendBackgroundColor) {
10138
- legendWidth += padding;
10139
- legendHeight += padding;
10140
-
10141
- if (!box) {
10142
- legend.box = box = renderer.rect(
10143
- 0,
10144
- 0,
10145
- legendWidth,
10146
- legendHeight,
10147
- options.borderRadius,
10148
- legendBorderWidth || 0
10149
- ).attr({
10150
- stroke: options.borderColor,
10151
- 'stroke-width': legendBorderWidth || 0,
10152
- fill: legendBackgroundColor || NONE
10153
- })
10154
- .add(legendGroup)
10155
- .shadow(options.shadow);
10156
- box.isNew = true;
10157
-
10158
- } else if (legendWidth > 0 && legendHeight > 0) {
10159
- box[box.isNew ? 'attr' : 'animate'](
10160
- box.crisp(null, null, null, legendWidth, legendHeight)
10161
- );
10162
- box.isNew = false;
10163
- }
10164
-
10165
- // hide the border if no items
10166
- box[display ? 'show' : 'hide']();
10167
- }
10168
-
10169
- legend.legendWidth = legendWidth;
10170
- legend.legendHeight = legendHeight;
10171
-
10172
- // Now that the legend width and height are established, put the items in the
10173
- // final position
10174
- each(allItems, function (item) {
10175
- legend.positionItem(item);
10176
- });
10177
-
10178
- // 1.x compatibility: positioning based on style
10179
- /*var props = ['left', 'right', 'top', 'bottom'],
10180
- prop,
10181
- i = 4;
10182
- while (i--) {
10183
- prop = props[i];
10184
- if (options.style[prop] && options.style[prop] !== 'auto') {
10185
- options[i < 2 ? 'align' : 'verticalAlign'] = prop;
10186
- options[i < 2 ? 'x' : 'y'] = pInt(options.style[prop]) * (i % 2 ? -1 : 1);
10187
- }
10188
- }*/
10189
-
10190
- if (display) {
10191
- legendGroup.align(extend({
10192
- width: legendWidth,
10193
- height: legendHeight
10194
- }, options), true, 'spacingBox');
10195
- }
10196
-
10197
- if (!chart.isResizing) {
10198
- this.positionCheckboxes();
10199
- }
10200
- },
10201
-
10202
- /**
10203
- * Set up the overflow handling by adding navigation with up and down arrows below the
10204
- * legend.
10205
- */
10206
- handleOverflow: function (legendHeight) {
10207
- var legend = this,
10208
- chart = this.chart,
10209
- renderer = chart.renderer,
10210
- pageCount,
10211
- options = this.options,
10212
- optionsY = options.y,
10213
- alignTop = options.verticalAlign === 'top',
10214
- spaceHeight = chart.spacingBox.height + (alignTop ? -optionsY : optionsY) - this.padding,
10215
- maxHeight = options.maxHeight,
10216
- clipHeight,
10217
- clipRect = this.clipRect,
10218
- navOptions = options.navigation,
10219
- animation = pick(navOptions.animation, true),
10220
- arrowSize = navOptions.arrowSize || 12,
10221
- nav = this.nav;
10222
-
10223
- // Adjust the height
10224
- if (options.layout === 'horizontal') {
10225
- spaceHeight /= 2;
10226
- }
10227
- if (maxHeight) {
10228
- spaceHeight = mathMin(spaceHeight, maxHeight);
10229
- }
10230
-
10231
- // Reset the legend height and adjust the clipping rectangle
10232
- if (legendHeight > spaceHeight && !options.useHTML) {
10233
-
10234
- this.clipHeight = clipHeight = spaceHeight - 20 - this.titleHeight;
10235
- this.pageCount = pageCount = mathCeil(legendHeight / clipHeight);
10236
- this.currentPage = pick(this.currentPage, 1);
10237
- this.fullHeight = legendHeight;
10238
-
10239
- // Only apply clipping if needed. Clipping causes blurred legend in PDF export (#1787)
10240
- if (!clipRect) {
10241
- clipRect = legend.clipRect = renderer.clipRect(0, 0, 9999, 0);
10242
- legend.contentGroup.clip(clipRect);
10243
- }
10244
- clipRect.attr({
10245
- height: clipHeight
10246
- });
10247
-
10248
- // Add navigation elements
10249
- if (!nav) {
10250
- this.nav = nav = renderer.g().attr({ zIndex: 1 }).add(this.group);
10251
- this.up = renderer.symbol('triangle', 0, 0, arrowSize, arrowSize)
10252
- .on('click', function () {
10253
- legend.scroll(-1, animation);
10254
- })
10255
- .add(nav);
10256
- this.pager = renderer.text('', 15, 10)
10257
- .css(navOptions.style)
10258
- .add(nav);
10259
- this.down = renderer.symbol('triangle-down', 0, 0, arrowSize, arrowSize)
10260
- .on('click', function () {
10261
- legend.scroll(1, animation);
10262
- })
10263
- .add(nav);
10264
- }
10265
-
10266
- // Set initial position
10267
- legend.scroll(0);
10268
-
10269
- legendHeight = spaceHeight;
10270
-
10271
- } else if (nav) {
10272
- clipRect.attr({
10273
- height: chart.chartHeight
10274
- });
10275
- nav.hide();
10276
- this.scrollGroup.attr({
10277
- translateY: 1
10278
- });
10279
- this.clipHeight = 0; // #1379
10280
- }
10281
-
10282
- return legendHeight;
10283
- },
10284
-
10285
- /**
10286
- * Scroll the legend by a number of pages
10287
- * @param {Object} scrollBy
10288
- * @param {Object} animation
10289
- */
10290
- scroll: function (scrollBy, animation) {
10291
- var pageCount = this.pageCount,
10292
- currentPage = this.currentPage + scrollBy,
10293
- clipHeight = this.clipHeight,
10294
- navOptions = this.options.navigation,
10295
- activeColor = navOptions.activeColor,
10296
- inactiveColor = navOptions.inactiveColor,
10297
- pager = this.pager,
10298
- padding = this.padding,
10299
- scrollOffset;
10300
-
10301
- // When resizing while looking at the last page
10302
- if (currentPage > pageCount) {
10303
- currentPage = pageCount;
10304
- }
10305
-
10306
- if (currentPage > 0) {
10307
-
10308
- if (animation !== UNDEFINED) {
10309
- setAnimation(animation, this.chart);
10310
- }
10311
-
10312
- this.nav.attr({
10313
- translateX: padding,
10314
- translateY: clipHeight + 7 + this.titleHeight,
10315
- visibility: VISIBLE
10316
- });
10317
- this.up.attr({
10318
- fill: currentPage === 1 ? inactiveColor : activeColor
10319
- })
10320
- .css({
10321
- cursor: currentPage === 1 ? 'default' : 'pointer'
10322
- });
10323
- pager.attr({
10324
- text: currentPage + '/' + this.pageCount
10325
- });
10326
- this.down.attr({
10327
- x: 18 + this.pager.getBBox().width, // adjust to text width
10328
- fill: currentPage === pageCount ? inactiveColor : activeColor
10329
- })
10330
- .css({
10331
- cursor: currentPage === pageCount ? 'default' : 'pointer'
10332
- });
10333
-
10334
- scrollOffset = -mathMin(clipHeight * (currentPage - 1), this.fullHeight - clipHeight + padding) + 1;
10335
- this.scrollGroup.animate({
10336
- translateY: scrollOffset
10337
- });
10338
- pager.attr({
10339
- text: currentPage + '/' + pageCount
10340
- });
10341
-
10342
-
10343
- this.currentPage = currentPage;
10344
- this.positionCheckboxes(scrollOffset);
10345
- }
10346
-
10347
- }
10348
-
10349
- };
10350
-
10351
- /**
10352
- * The chart class
10353
- * @param {Object} options
10354
- * @param {Function} callback Function to run when the chart has loaded
10355
- */
10356
- function Chart() {
10357
- this.init.apply(this, arguments);
10358
- }
10359
-
10360
- Chart.prototype = {
10361
-
10362
- /**
10363
- * Initialize the chart
10364
- */
10365
- init: function (userOptions, callback) {
10366
-
10367
- // Handle regular options
10368
- var options,
10369
- seriesOptions = userOptions.series; // skip merging data points to increase performance
10370
-
10371
- userOptions.series = null;
10372
- options = merge(defaultOptions, userOptions); // do the merge
10373
- options.series = userOptions.series = seriesOptions; // set back the series data
10374
-
10375
- var optionsChart = options.chart,
10376
- optionsMargin = optionsChart.margin,
10377
- margin = isObject(optionsMargin) ?
10378
- optionsMargin :
10379
- [optionsMargin, optionsMargin, optionsMargin, optionsMargin];
10380
-
10381
- this.optionsMarginTop = pick(optionsChart.marginTop, margin[0]);
10382
- this.optionsMarginRight = pick(optionsChart.marginRight, margin[1]);
10383
- this.optionsMarginBottom = pick(optionsChart.marginBottom, margin[2]);
10384
- this.optionsMarginLeft = pick(optionsChart.marginLeft, margin[3]);
10385
-
10386
- var chartEvents = optionsChart.events;
10387
-
10388
- this.runChartClick = chartEvents && !!chartEvents.click;
10389
- this.bounds = { h: {}, v: {} }; // Pixel data bounds for touch zoom
10390
-
10391
- this.callback = callback;
10392
- this.isResizing = 0;
10393
- this.options = options;
10394
- //chartTitleOptions = UNDEFINED;
10395
- //chartSubtitleOptions = UNDEFINED;
10396
-
10397
- this.axes = [];
10398
- this.series = [];
10399
- this.hasCartesianSeries = optionsChart.showAxes;
10400
- //this.axisOffset = UNDEFINED;
10401
- //this.maxTicks = UNDEFINED; // handle the greatest amount of ticks on grouped axes
10402
- //this.inverted = UNDEFINED;
10403
- //this.loadingShown = UNDEFINED;
10404
- //this.container = UNDEFINED;
10405
- //this.chartWidth = UNDEFINED;
10406
- //this.chartHeight = UNDEFINED;
10407
- //this.marginRight = UNDEFINED;
10408
- //this.marginBottom = UNDEFINED;
10409
- //this.containerWidth = UNDEFINED;
10410
- //this.containerHeight = UNDEFINED;
10411
- //this.oldChartWidth = UNDEFINED;
10412
- //this.oldChartHeight = UNDEFINED;
10413
-
10414
- //this.renderTo = UNDEFINED;
10415
- //this.renderToClone = UNDEFINED;
10416
-
10417
- //this.spacingBox = UNDEFINED
10418
-
10419
- //this.legend = UNDEFINED;
10420
-
10421
- // Elements
10422
- //this.chartBackground = UNDEFINED;
10423
- //this.plotBackground = UNDEFINED;
10424
- //this.plotBGImage = UNDEFINED;
10425
- //this.plotBorder = UNDEFINED;
10426
- //this.loadingDiv = UNDEFINED;
10427
- //this.loadingSpan = UNDEFINED;
10428
-
10429
- var chart = this,
10430
- eventType;
10431
-
10432
- // Add the chart to the global lookup
10433
- chart.index = charts.length;
10434
- charts.push(chart);
10435
-
10436
- // Set up auto resize
10437
- if (optionsChart.reflow !== false) {
10438
- addEvent(chart, 'load', function () {
10439
- chart.initReflow();
10440
- });
10441
- }
10442
-
10443
- // Chart event handlers
10444
- if (chartEvents) {
10445
- for (eventType in chartEvents) {
10446
- addEvent(chart, eventType, chartEvents[eventType]);
10447
- }
10448
- }
10449
-
10450
- chart.xAxis = [];
10451
- chart.yAxis = [];
10452
-
10453
- // Expose methods and variables
10454
- chart.animation = useCanVG ? false : pick(optionsChart.animation, true);
10455
- chart.pointCount = 0;
10456
- chart.counters = new ChartCounters();
10457
-
10458
- chart.firstRender();
10459
- },
10460
-
10461
- /**
10462
- * Initialize an individual series, called internally before render time
10463
- */
10464
- initSeries: function (options) {
10465
- var chart = this,
10466
- optionsChart = chart.options.chart,
10467
- type = options.type || optionsChart.type || optionsChart.defaultSeriesType,
10468
- series,
10469
- constr = seriesTypes[type];
10470
-
10471
- // No such series type
10472
- if (!constr) {
10473
- error(17, true);
10474
- }
10475
-
10476
- series = new constr();
10477
- series.init(this, options);
10478
- return series;
10479
- },
10480
-
10481
- /**
10482
- * Add a series dynamically after time
10483
- *
10484
- * @param {Object} options The config options
10485
- * @param {Boolean} redraw Whether to redraw the chart after adding. Defaults to true.
10486
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
10487
- * configuration
10488
- *
10489
- * @return {Object} series The newly created series object
10490
- */
10491
- addSeries: function (options, redraw, animation) {
10492
- var series,
10493
- chart = this;
10494
-
10495
- if (options) {
10496
- redraw = pick(redraw, true); // defaults to true
10497
-
10498
- fireEvent(chart, 'addSeries', { options: options }, function () {
10499
- series = chart.initSeries(options);
10500
-
10501
- chart.isDirtyLegend = true; // the series array is out of sync with the display
10502
- if (redraw) {
10503
- chart.redraw(animation);
10504
- }
10505
- });
10506
- }
10507
-
10508
- return series;
10509
- },
10510
-
10511
- /**
10512
- * Add an axis to the chart
10513
- * @param {Object} options The axis option
10514
- * @param {Boolean} isX Whether it is an X axis or a value axis
10515
- */
10516
- addAxis: function (options, isX, redraw, animation) {
10517
- var key = isX ? 'xAxis' : 'yAxis',
10518
- chartOptions = this.options,
10519
- axis;
10520
-
10521
- /*jslint unused: false*/
10522
- axis = new Axis(this, merge(options, {
10523
- index: this[key].length
10524
- }));
10525
- /*jslint unused: true*/
10526
-
10527
- // Push the new axis options to the chart options
10528
- chartOptions[key] = splat(chartOptions[key] || {});
10529
- chartOptions[key].push(options);
10530
-
10531
- if (pick(redraw, true)) {
10532
- this.redraw(animation);
10533
- }
10534
- },
10535
-
10536
- /**
10537
- * Check whether a given point is within the plot area
10538
- *
10539
- * @param {Number} plotX Pixel x relative to the plot area
10540
- * @param {Number} plotY Pixel y relative to the plot area
10541
- * @param {Boolean} inverted Whether the chart is inverted
10542
- */
10543
- isInsidePlot: function (plotX, plotY, inverted) {
10544
- var x = inverted ? plotY : plotX,
10545
- y = inverted ? plotX : plotY;
10546
-
10547
- return x >= 0 &&
10548
- x <= this.plotWidth &&
10549
- y >= 0 &&
10550
- y <= this.plotHeight;
10551
- },
10552
-
10553
- /**
10554
- * Adjust all axes tick amounts
10555
- */
10556
- adjustTickAmounts: function () {
10557
- if (this.options.chart.alignTicks !== false) {
10558
- each(this.axes, function (axis) {
10559
- axis.adjustTickAmount();
10560
- });
10561
- }
10562
- this.maxTicks = null;
10563
- },
10564
-
10565
- /**
10566
- * Redraw legend, axes or series based on updated data
10567
- *
10568
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
10569
- * configuration
10570
- */
10571
- redraw: function (animation) {
10572
- var chart = this,
10573
- axes = chart.axes,
10574
- series = chart.series,
10575
- pointer = chart.pointer,
10576
- legend = chart.legend,
10577
- redrawLegend = chart.isDirtyLegend,
10578
- hasStackedSeries,
10579
- isDirtyBox = chart.isDirtyBox, // todo: check if it has actually changed?
10580
- seriesLength = series.length,
10581
- i = seriesLength,
10582
- serie,
10583
- renderer = chart.renderer,
10584
- isHiddenChart = renderer.isHidden(),
10585
- afterRedraw = [];
10586
-
10587
- setAnimation(animation, chart);
10588
-
10589
- if (isHiddenChart) {
10590
- chart.cloneRenderTo();
10591
- }
10592
-
10593
- // link stacked series
10594
- while (i--) {
10595
- serie = series[i];
10596
- if (serie.isDirty && serie.options.stacking) {
10597
- hasStackedSeries = true;
10598
- break;
10599
- }
10600
- }
10601
- if (hasStackedSeries) { // mark others as dirty
10602
- i = seriesLength;
10603
- while (i--) {
10604
- serie = series[i];
10605
- if (serie.options.stacking) {
10606
- serie.isDirty = true;
10607
- }
10608
- }
10609
- }
10610
-
10611
- // handle updated data in the series
10612
- each(series, function (serie) {
10613
- if (serie.isDirty) { // prepare the data so axis can read it
10614
- if (serie.options.legendType === 'point') {
10615
- redrawLegend = true;
10616
- }
10617
- }
10618
- });
10619
-
10620
- // handle added or removed series
10621
- if (redrawLegend && legend.options.enabled) { // series or pie points are added or removed
10622
- // draw legend graphics
10623
- legend.render();
10624
-
10625
- chart.isDirtyLegend = false;
10626
- }
10627
-
10628
-
10629
- if (chart.hasCartesianSeries) {
10630
- if (!chart.isResizing) {
10631
-
10632
- // reset maxTicks
10633
- chart.maxTicks = null;
10634
-
10635
- // set axes scales
10636
- each(axes, function (axis) {
10637
- axis.setScale();
10638
- });
10639
- }
10640
- chart.adjustTickAmounts();
10641
- chart.getMargins();
10642
-
10643
- // redraw axes
10644
- each(axes, function (axis) {
10645
-
10646
- // Fire 'afterSetExtremes' only if extremes are set
10647
- if (axis.isDirtyExtremes) { // #821
10648
- axis.isDirtyExtremes = false;
10649
- afterRedraw.push(function () { // prevent a recursive call to chart.redraw() (#1119)
10650
- fireEvent(axis, 'afterSetExtremes', axis.getExtremes()); // #747, #751
10651
- });
10652
- }
10653
-
10654
- if (axis.isDirty || isDirtyBox || hasStackedSeries) {
10655
- axis.redraw();
10656
- isDirtyBox = true; // #792
10657
- }
10658
- });
10659
-
10660
-
10661
- }
10662
- // the plot areas size has changed
10663
- if (isDirtyBox) {
10664
- chart.drawChartBox();
10665
- }
10666
-
10667
-
10668
-
10669
- // redraw affected series
10670
- each(series, function (serie) {
10671
- if (serie.isDirty && serie.visible &&
10672
- (!serie.isCartesian || serie.xAxis)) { // issue #153
10673
- serie.redraw();
10674
- }
10675
- });
10676
-
10677
- // move tooltip or reset
10678
- if (pointer && pointer.reset) {
10679
- pointer.reset(true);
10680
- }
10681
-
10682
- // redraw if canvas
10683
- renderer.draw();
10684
-
10685
- // fire the event
10686
- fireEvent(chart, 'redraw'); // jQuery breaks this when calling it from addEvent. Overwrites chart.redraw
10687
-
10688
- if (isHiddenChart) {
10689
- chart.cloneRenderTo(true);
10690
- }
10691
-
10692
- // Fire callbacks that are put on hold until after the redraw
10693
- each(afterRedraw, function (callback) {
10694
- callback.call();
10695
- });
10696
- },
10697
-
10698
-
10699
-
10700
- /**
10701
- * Dim the chart and show a loading text or symbol
10702
- * @param {String} str An optional text to show in the loading label instead of the default one
10703
- */
10704
- showLoading: function (str) {
10705
- var chart = this,
10706
- options = chart.options,
10707
- loadingDiv = chart.loadingDiv;
10708
-
10709
- var loadingOptions = options.loading;
10710
-
10711
- // create the layer at the first call
10712
- if (!loadingDiv) {
10713
- chart.loadingDiv = loadingDiv = createElement(DIV, {
10714
- className: PREFIX + 'loading'
10715
- }, extend(loadingOptions.style, {
10716
- zIndex: 10,
10717
- display: NONE
10718
- }), chart.container);
10719
-
10720
- chart.loadingSpan = createElement(
10721
- 'span',
10722
- null,
10723
- loadingOptions.labelStyle,
10724
- loadingDiv
10725
- );
10726
-
10727
- }
10728
-
10729
- // update text
10730
- chart.loadingSpan.innerHTML = str || options.lang.loading;
10731
-
10732
- // show it
10733
- if (!chart.loadingShown) {
10734
- css(loadingDiv, {
10735
- opacity: 0,
10736
- display: '',
10737
- left: chart.plotLeft + PX,
10738
- top: chart.plotTop + PX,
10739
- width: chart.plotWidth + PX,
10740
- height: chart.plotHeight + PX
10741
- });
10742
- animate(loadingDiv, {
10743
- opacity: loadingOptions.style.opacity
10744
- }, {
10745
- duration: loadingOptions.showDuration || 0
10746
- });
10747
- chart.loadingShown = true;
10748
- }
10749
- },
10750
-
10751
- /**
10752
- * Hide the loading layer
10753
- */
10754
- hideLoading: function () {
10755
- var options = this.options,
10756
- loadingDiv = this.loadingDiv;
10757
-
10758
- if (loadingDiv) {
10759
- animate(loadingDiv, {
10760
- opacity: 0
10761
- }, {
10762
- duration: options.loading.hideDuration || 100,
10763
- complete: function () {
10764
- css(loadingDiv, { display: NONE });
10765
- }
10766
- });
10767
- }
10768
- this.loadingShown = false;
10769
- },
10770
-
10771
- /**
10772
- * Get an axis, series or point object by id.
10773
- * @param id {String} The id as given in the configuration options
10774
- */
10775
- get: function (id) {
10776
- var chart = this,
10777
- axes = chart.axes,
10778
- series = chart.series;
10779
-
10780
- var i,
10781
- j,
10782
- points;
10783
-
10784
- // search axes
10785
- for (i = 0; i < axes.length; i++) {
10786
- if (axes[i].options.id === id) {
10787
- return axes[i];
10788
- }
10789
- }
10790
-
10791
- // search series
10792
- for (i = 0; i < series.length; i++) {
10793
- if (series[i].options.id === id) {
10794
- return series[i];
10795
- }
10796
- }
10797
-
10798
- // search points
10799
- for (i = 0; i < series.length; i++) {
10800
- points = series[i].points || [];
10801
- for (j = 0; j < points.length; j++) {
10802
- if (points[j].id === id) {
10803
- return points[j];
10804
- }
10805
- }
10806
- }
10807
- return null;
10808
- },
10809
-
10810
- /**
10811
- * Create the Axis instances based on the config options
10812
- */
10813
- getAxes: function () {
10814
- var chart = this,
10815
- options = this.options,
10816
- xAxisOptions = options.xAxis = splat(options.xAxis || {}),
10817
- yAxisOptions = options.yAxis = splat(options.yAxis || {}),
10818
- optionsArray,
10819
- axis;
10820
-
10821
- // make sure the options are arrays and add some members
10822
- each(xAxisOptions, function (axis, i) {
10823
- axis.index = i;
10824
- axis.isX = true;
10825
- });
10826
-
10827
- each(yAxisOptions, function (axis, i) {
10828
- axis.index = i;
10829
- });
10830
-
10831
- // concatenate all axis options into one array
10832
- optionsArray = xAxisOptions.concat(yAxisOptions);
10833
-
10834
- each(optionsArray, function (axisOptions) {
10835
- axis = new Axis(chart, axisOptions);
10836
- });
10837
-
10838
- chart.adjustTickAmounts();
10839
- },
10840
-
10841
-
10842
- /**
10843
- * Get the currently selected points from all series
10844
- */
10845
- getSelectedPoints: function () {
10846
- var points = [];
10847
- each(this.series, function (serie) {
10848
- points = points.concat(grep(serie.points || [], function (point) {
10849
- return point.selected;
10850
- }));
10851
- });
10852
- return points;
10853
- },
10854
-
10855
- /**
10856
- * Get the currently selected series
10857
- */
10858
- getSelectedSeries: function () {
10859
- return grep(this.series, function (serie) {
10860
- return serie.selected;
10861
- });
10862
- },
10863
-
10864
- /**
10865
- * Display the zoom button
10866
- */
10867
- showResetZoom: function () {
10868
- var chart = this,
10869
- lang = defaultOptions.lang,
10870
- btnOptions = chart.options.chart.resetZoomButton,
10871
- theme = btnOptions.theme,
10872
- states = theme.states,
10873
- alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox';
10874
-
10875
- this.resetZoomButton = chart.renderer.button(lang.resetZoom, null, null, function () { chart.zoomOut(); }, theme, states && states.hover)
10876
- .attr({
10877
- align: btnOptions.position.align,
10878
- title: lang.resetZoomTitle
10879
- })
10880
- .add()
10881
- .align(btnOptions.position, false, alignTo);
10882
-
10883
- },
10884
-
10885
- /**
10886
- * Zoom out to 1:1
10887
- */
10888
- zoomOut: function () {
10889
- var chart = this;
10890
- fireEvent(chart, 'selection', { resetSelection: true }, function () {
10891
- chart.zoom();
10892
- });
10893
- },
10894
-
10895
- /**
10896
- * Zoom into a given portion of the chart given by axis coordinates
10897
- * @param {Object} event
10898
- */
10899
- zoom: function (event) {
10900
- var chart = this,
10901
- hasZoomed,
10902
- pointer = chart.pointer,
10903
- displayButton = false,
10904
- resetZoomButton;
10905
-
10906
- // If zoom is called with no arguments, reset the axes
10907
- if (!event || event.resetSelection) {
10908
- each(chart.axes, function (axis) {
10909
- hasZoomed = axis.zoom();
10910
- });
10911
- } else { // else, zoom in on all axes
10912
- each(event.xAxis.concat(event.yAxis), function (axisData) {
10913
- var axis = axisData.axis,
10914
- isXAxis = axis.isXAxis;
10915
-
10916
- // don't zoom more than minRange
10917
- if (pointer[isXAxis ? 'zoomX' : 'zoomY'] || pointer[isXAxis ? 'pinchX' : 'pinchY']) {
10918
- hasZoomed = axis.zoom(axisData.min, axisData.max);
10919
- if (axis.displayBtn) {
10920
- displayButton = true;
10921
- }
10922
- }
10923
- });
10924
- }
10925
-
10926
- // Show or hide the Reset zoom button
10927
- resetZoomButton = chart.resetZoomButton;
10928
- if (displayButton && !resetZoomButton) {
10929
- chart.showResetZoom();
10930
- } else if (!displayButton && isObject(resetZoomButton)) {
10931
- chart.resetZoomButton = resetZoomButton.destroy();
10932
- }
10933
-
10934
-
10935
- // Redraw
10936
- if (hasZoomed) {
10937
- chart.redraw(
10938
- pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100) // animation
10939
- );
10940
- }
10941
- },
10942
-
10943
- /**
10944
- * Pan the chart by dragging the mouse across the pane. This function is called
10945
- * on mouse move, and the distance to pan is computed from chartX compared to
10946
- * the first chartX position in the dragging operation.
10947
- */
10948
- pan: function (chartX) {
10949
- var chart = this,
10950
- xAxis = chart.xAxis[0],
10951
- mouseDownX = chart.mouseDownX,
10952
- halfPointRange = xAxis.pointRange / 2,
10953
- extremes = xAxis.getExtremes(),
10954
- newMin = xAxis.translate(mouseDownX - chartX, true) + halfPointRange,
10955
- newMax = xAxis.translate(mouseDownX + chart.plotWidth - chartX, true) - halfPointRange,
10956
- hoverPoints = chart.hoverPoints;
10957
-
10958
- // remove active points for shared tooltip
10959
- if (hoverPoints) {
10960
- each(hoverPoints, function (point) {
10961
- point.setState();
10962
- });
10963
- }
10964
-
10965
- if (xAxis.series.length && newMin > mathMin(extremes.dataMin, extremes.min) && newMax < mathMax(extremes.dataMax, extremes.max)) {
10966
- xAxis.setExtremes(newMin, newMax, true, false, { trigger: 'pan' });
10967
- }
10968
-
10969
- chart.mouseDownX = chartX; // set new reference for next run
10970
- css(chart.container, { cursor: 'move' });
10971
- },
10972
-
10973
- /**
10974
- * Show the title and subtitle of the chart
10975
- *
10976
- * @param titleOptions {Object} New title options
10977
- * @param subtitleOptions {Object} New subtitle options
10978
- *
10979
- */
10980
- setTitle: function (titleOptions, subtitleOptions) {
10981
- var chart = this,
10982
- options = chart.options,
10983
- chartTitleOptions,
10984
- chartSubtitleOptions;
10985
-
10986
- chartTitleOptions = options.title = merge(options.title, titleOptions);
10987
- chartSubtitleOptions = options.subtitle = merge(options.subtitle, subtitleOptions);
10988
-
10989
- // add title and subtitle
10990
- each([
10991
- ['title', titleOptions, chartTitleOptions],
10992
- ['subtitle', subtitleOptions, chartSubtitleOptions]
10993
- ], function (arr) {
10994
- var name = arr[0],
10995
- title = chart[name],
10996
- titleOptions = arr[1],
10997
- chartTitleOptions = arr[2];
10998
-
10999
- if (title && titleOptions) {
11000
- chart[name] = title = title.destroy(); // remove old
11001
- }
11002
-
11003
- if (chartTitleOptions && chartTitleOptions.text && !title) {
11004
- chart[name] = chart.renderer.text(
11005
- chartTitleOptions.text,
11006
- 0,
11007
- 0,
11008
- chartTitleOptions.useHTML
11009
- )
11010
- .attr({
11011
- align: chartTitleOptions.align,
11012
- 'class': PREFIX + name,
11013
- zIndex: chartTitleOptions.zIndex || 4
11014
- })
11015
- .css(chartTitleOptions.style)
11016
- .add()
11017
- .align(chartTitleOptions, false, 'spacingBox');
11018
- }
11019
- });
11020
-
11021
- },
11022
-
11023
- /**
11024
- * Get chart width and height according to options and container size
11025
- */
11026
- getChartSize: function () {
11027
- var chart = this,
11028
- optionsChart = chart.options.chart,
11029
- renderTo = chart.renderToClone || chart.renderTo;
11030
-
11031
- // get inner width and height from jQuery (#824)
11032
- chart.containerWidth = adapterRun(renderTo, 'width');
11033
- chart.containerHeight = adapterRun(renderTo, 'height');
11034
-
11035
- chart.chartWidth = mathMax(0, optionsChart.width || chart.containerWidth || 600); // #1393, 1460
11036
- chart.chartHeight = mathMax(0, pick(optionsChart.height,
11037
- // the offsetHeight of an empty container is 0 in standard browsers, but 19 in IE7:
11038
- chart.containerHeight > 19 ? chart.containerHeight : 400));
11039
- },
11040
-
11041
- /**
11042
- * Create a clone of the chart's renderTo div and place it outside the viewport to allow
11043
- * size computation on chart.render and chart.redraw
11044
- */
11045
- cloneRenderTo: function (revert) {
11046
- var clone = this.renderToClone,
11047
- container = this.container;
11048
-
11049
- // Destroy the clone and bring the container back to the real renderTo div
11050
- if (revert) {
11051
- if (clone) {
11052
- this.renderTo.appendChild(container);
11053
- discardElement(clone);
11054
- delete this.renderToClone;
11055
- }
11056
-
11057
- // Set up the clone
11058
- } else {
11059
- if (container) {
11060
- this.renderTo.removeChild(container); // do not clone this
11061
- }
11062
- this.renderToClone = clone = this.renderTo.cloneNode(0);
11063
- css(clone, {
11064
- position: ABSOLUTE,
11065
- top: '-9999px',
11066
- display: 'block' // #833
11067
- });
11068
- doc.body.appendChild(clone);
11069
- if (container) {
11070
- clone.appendChild(container);
11071
- }
11072
- }
11073
- },
11074
-
11075
- /**
11076
- * Get the containing element, determine the size and create the inner container
11077
- * div to hold the chart
11078
- */
11079
- getContainer: function () {
11080
- var chart = this,
11081
- container,
11082
- optionsChart = chart.options.chart,
11083
- chartWidth,
11084
- chartHeight,
11085
- renderTo,
11086
- indexAttrName = 'data-highcharts-chart',
11087
- oldChartIndex,
11088
- containerId;
11089
-
11090
- chart.renderTo = renderTo = optionsChart.renderTo;
11091
- containerId = PREFIX + idCounter++;
11092
-
11093
- if (isString(renderTo)) {
11094
- chart.renderTo = renderTo = doc.getElementById(renderTo);
11095
- }
11096
-
11097
- // Display an error if the renderTo is wrong
11098
- if (!renderTo) {
11099
- error(13, true);
11100
- }
11101
-
11102
- // If the container already holds a chart, destroy it
11103
- oldChartIndex = pInt(attr(renderTo, indexAttrName));
11104
- if (!isNaN(oldChartIndex) && charts[oldChartIndex]) {
11105
- charts[oldChartIndex].destroy();
11106
- }
11107
-
11108
- // Make a reference to the chart from the div
11109
- attr(renderTo, indexAttrName, chart.index);
11110
-
11111
- // remove previous chart
11112
- renderTo.innerHTML = '';
11113
-
11114
- // If the container doesn't have an offsetWidth, it has or is a child of a node
11115
- // that has display:none. We need to temporarily move it out to a visible
11116
- // state to determine the size, else the legend and tooltips won't render
11117
- // properly
11118
- if (!renderTo.offsetWidth) {
11119
- chart.cloneRenderTo();
11120
- }
11121
-
11122
- // get the width and height
11123
- chart.getChartSize();
11124
- chartWidth = chart.chartWidth;
11125
- chartHeight = chart.chartHeight;
11126
-
11127
- // create the inner container
11128
- chart.container = container = createElement(DIV, {
11129
- className: PREFIX + 'container' +
11130
- (optionsChart.className ? ' ' + optionsChart.className : ''),
11131
- id: containerId
11132
- }, extend({
11133
- position: RELATIVE,
11134
- overflow: HIDDEN, // needed for context menu (avoid scrollbars) and
11135
- // content overflow in IE
11136
- width: chartWidth + PX,
11137
- height: chartHeight + PX,
11138
- textAlign: 'left',
11139
- lineHeight: 'normal', // #427
11140
- zIndex: 0, // #1072
11141
- '-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
11142
- }, optionsChart.style),
11143
- chart.renderToClone || renderTo
11144
- );
11145
-
11146
- // cache the cursor (#1650)
11147
- chart._cursor = container.style.cursor;
11148
-
11149
- chart.renderer =
11150
- optionsChart.forExport ? // force SVG, used for SVG export
11151
- new SVGRenderer(container, chartWidth, chartHeight, true) :
11152
- new Renderer(container, chartWidth, chartHeight);
11153
-
11154
- if (useCanVG) {
11155
- // If we need canvg library, extend and configure the renderer
11156
- // to get the tracker for translating mouse events
11157
- chart.renderer.create(chart, container, chartWidth, chartHeight);
11158
- }
11159
- },
11160
-
11161
- /**
11162
- * Calculate margins by rendering axis labels in a preliminary position. Title,
11163
- * subtitle and legend have already been rendered at this stage, but will be
11164
- * moved into their final positions
11165
- */
11166
- getMargins: function () {
11167
- var chart = this,
11168
- optionsChart = chart.options.chart,
11169
- spacingTop = optionsChart.spacingTop,
11170
- spacingRight = optionsChart.spacingRight,
11171
- spacingBottom = optionsChart.spacingBottom,
11172
- spacingLeft = optionsChart.spacingLeft,
11173
- axisOffset,
11174
- legend = chart.legend,
11175
- optionsMarginTop = chart.optionsMarginTop,
11176
- optionsMarginLeft = chart.optionsMarginLeft,
11177
- optionsMarginRight = chart.optionsMarginRight,
11178
- optionsMarginBottom = chart.optionsMarginBottom,
11179
- chartTitleOptions = chart.options.title,
11180
- chartSubtitleOptions = chart.options.subtitle,
11181
- legendOptions = chart.options.legend,
11182
- legendMargin = pick(legendOptions.margin, 10),
11183
- legendX = legendOptions.x,
11184
- legendY = legendOptions.y,
11185
- align = legendOptions.align,
11186
- verticalAlign = legendOptions.verticalAlign,
11187
- titleOffset;
11188
-
11189
- chart.resetMargins();
11190
- axisOffset = chart.axisOffset;
11191
-
11192
- // adjust for title and subtitle
11193
- if ((chart.title || chart.subtitle) && !defined(chart.optionsMarginTop)) {
11194
- titleOffset = mathMax(
11195
- (chart.title && !chartTitleOptions.floating && !chartTitleOptions.verticalAlign && chartTitleOptions.y) || 0,
11196
- (chart.subtitle && !chartSubtitleOptions.floating && !chartSubtitleOptions.verticalAlign && chartSubtitleOptions.y) || 0
11197
- );
11198
- if (titleOffset) {
11199
- chart.plotTop = mathMax(chart.plotTop, titleOffset + pick(chartTitleOptions.margin, 15) + spacingTop);
11200
- }
11201
- }
11202
- // adjust for legend
11203
- if (legend.display && !legendOptions.floating) {
11204
- if (align === 'right') { // horizontal alignment handled first
11205
- if (!defined(optionsMarginRight)) {
11206
- chart.marginRight = mathMax(
11207
- chart.marginRight,
11208
- legend.legendWidth - legendX + legendMargin + spacingRight
11209
- );
11210
- }
11211
- } else if (align === 'left') {
11212
- if (!defined(optionsMarginLeft)) {
11213
- chart.plotLeft = mathMax(
11214
- chart.plotLeft,
11215
- legend.legendWidth + legendX + legendMargin + spacingLeft
11216
- );
11217
- }
11218
-
11219
- } else if (verticalAlign === 'top') {
11220
- if (!defined(optionsMarginTop)) {
11221
- chart.plotTop = mathMax(
11222
- chart.plotTop,
11223
- legend.legendHeight + legendY + legendMargin + spacingTop
11224
- );
11225
- }
11226
-
11227
- } else if (verticalAlign === 'bottom') {
11228
- if (!defined(optionsMarginBottom)) {
11229
- chart.marginBottom = mathMax(
11230
- chart.marginBottom,
11231
- legend.legendHeight - legendY + legendMargin + spacingBottom
11232
- );
11233
- }
11234
- }
11235
- }
11236
-
11237
- // adjust for scroller
11238
- if (chart.extraBottomMargin) {
11239
- chart.marginBottom += chart.extraBottomMargin;
11240
- }
11241
- if (chart.extraTopMargin) {
11242
- chart.plotTop += chart.extraTopMargin;
11243
- }
11244
-
11245
- // pre-render axes to get labels offset width
11246
- if (chart.hasCartesianSeries) {
11247
- each(chart.axes, function (axis) {
11248
- axis.getOffset();
11249
- });
11250
- }
11251
-
11252
- if (!defined(optionsMarginLeft)) {
11253
- chart.plotLeft += axisOffset[3];
11254
- }
11255
- if (!defined(optionsMarginTop)) {
11256
- chart.plotTop += axisOffset[0];
11257
- }
11258
- if (!defined(optionsMarginBottom)) {
11259
- chart.marginBottom += axisOffset[2];
11260
- }
11261
- if (!defined(optionsMarginRight)) {
11262
- chart.marginRight += axisOffset[1];
11263
- }
11264
-
11265
- chart.setChartSize();
11266
-
11267
- },
11268
-
11269
- /**
11270
- * Add the event handlers necessary for auto resizing
11271
- *
11272
- */
11273
- initReflow: function () {
11274
- var chart = this,
11275
- optionsChart = chart.options.chart,
11276
- renderTo = chart.renderTo,
11277
- reflowTimeout;
11278
-
11279
- function reflow(e) {
11280
- var width = optionsChart.width || adapterRun(renderTo, 'width'),
11281
- height = optionsChart.height || adapterRun(renderTo, 'height'),
11282
- target = e ? e.target : win; // #805 - MooTools doesn't supply e
11283
-
11284
- // Width and height checks for display:none. Target is doc in IE8 and Opera,
11285
- // win in Firefox, Chrome and IE9.
11286
- if (!chart.hasUserSize && width && height && (target === win || target === doc)) {
11287
-
11288
- if (width !== chart.containerWidth || height !== chart.containerHeight) {
11289
- clearTimeout(reflowTimeout);
11290
- chart.reflowTimeout = reflowTimeout = setTimeout(function () {
11291
- if (chart.container) { // It may have been destroyed in the meantime (#1257)
11292
- chart.setSize(width, height, false);
11293
- chart.hasUserSize = null;
11294
- }
11295
- }, 100);
11296
- }
11297
- chart.containerWidth = width;
11298
- chart.containerHeight = height;
11299
- }
11300
- }
11301
- addEvent(win, 'resize', reflow);
11302
- addEvent(chart, 'destroy', function () {
11303
- removeEvent(win, 'resize', reflow);
11304
- });
11305
- },
11306
-
11307
- /**
11308
- * Resize the chart to a given width and height
11309
- * @param {Number} width
11310
- * @param {Number} height
11311
- * @param {Object|Boolean} animation
11312
- */
11313
- setSize: function (width, height, animation) {
11314
- var chart = this,
11315
- chartWidth,
11316
- chartHeight,
11317
- fireEndResize;
11318
-
11319
- // Handle the isResizing counter
11320
- chart.isResizing += 1;
11321
- fireEndResize = function () {
11322
- if (chart) {
11323
- fireEvent(chart, 'endResize', null, function () {
11324
- chart.isResizing -= 1;
11325
- });
11326
- }
11327
- };
11328
-
11329
- // set the animation for the current process
11330
- setAnimation(animation, chart);
11331
-
11332
- chart.oldChartHeight = chart.chartHeight;
11333
- chart.oldChartWidth = chart.chartWidth;
11334
- if (defined(width)) {
11335
- chart.chartWidth = chartWidth = mathMax(0, mathRound(width));
11336
- chart.hasUserSize = !!chartWidth;
11337
- }
11338
- if (defined(height)) {
11339
- chart.chartHeight = chartHeight = mathMax(0, mathRound(height));
11340
- }
11341
-
11342
- css(chart.container, {
11343
- width: chartWidth + PX,
11344
- height: chartHeight + PX
11345
- });
11346
- chart.setChartSize(true);
11347
- chart.renderer.setSize(chartWidth, chartHeight, animation);
11348
-
11349
- // handle axes
11350
- chart.maxTicks = null;
11351
- each(chart.axes, function (axis) {
11352
- axis.isDirty = true;
11353
- axis.setScale();
11354
- });
11355
-
11356
- // make sure non-cartesian series are also handled
11357
- each(chart.series, function (serie) {
11358
- serie.isDirty = true;
11359
- });
11360
-
11361
- chart.isDirtyLegend = true; // force legend redraw
11362
- chart.isDirtyBox = true; // force redraw of plot and chart border
11363
-
11364
- chart.getMargins();
11365
-
11366
- chart.redraw(animation);
11367
-
11368
-
11369
- chart.oldChartHeight = null;
11370
- fireEvent(chart, 'resize');
11371
-
11372
- // fire endResize and set isResizing back
11373
- // If animation is disabled, fire without delay
11374
- if (globalAnimation === false) {
11375
- fireEndResize();
11376
- } else { // else set a timeout with the animation duration
11377
- setTimeout(fireEndResize, (globalAnimation && globalAnimation.duration) || 500);
11378
- }
11379
- },
11380
-
11381
- /**
11382
- * Set the public chart properties. This is done before and after the pre-render
11383
- * to determine margin sizes
11384
- */
11385
- setChartSize: function (skipAxes) {
11386
- var chart = this,
11387
- inverted = chart.inverted,
11388
- renderer = chart.renderer,
11389
- chartWidth = chart.chartWidth,
11390
- chartHeight = chart.chartHeight,
11391
- optionsChart = chart.options.chart,
11392
- spacingTop = optionsChart.spacingTop,
11393
- spacingRight = optionsChart.spacingRight,
11394
- spacingBottom = optionsChart.spacingBottom,
11395
- spacingLeft = optionsChart.spacingLeft,
11396
- clipOffset = chart.clipOffset,
11397
- clipX,
11398
- clipY,
11399
- plotLeft,
11400
- plotTop,
11401
- plotWidth,
11402
- plotHeight,
11403
- plotBorderWidth;
11404
-
11405
- chart.plotLeft = plotLeft = mathRound(chart.plotLeft);
11406
- chart.plotTop = plotTop = mathRound(chart.plotTop);
11407
- chart.plotWidth = plotWidth = mathMax(0, mathRound(chartWidth - plotLeft - chart.marginRight));
11408
- chart.plotHeight = plotHeight = mathMax(0, mathRound(chartHeight - plotTop - chart.marginBottom));
11409
-
11410
- chart.plotSizeX = inverted ? plotHeight : plotWidth;
11411
- chart.plotSizeY = inverted ? plotWidth : plotHeight;
11412
-
11413
- chart.plotBorderWidth = plotBorderWidth = optionsChart.plotBorderWidth || 0;
11414
-
11415
- // Set boxes used for alignment
11416
- chart.spacingBox = renderer.spacingBox = {
11417
- x: spacingLeft,
11418
- y: spacingTop,
11419
- width: chartWidth - spacingLeft - spacingRight,
11420
- height: chartHeight - spacingTop - spacingBottom
11421
- };
11422
- chart.plotBox = renderer.plotBox = {
11423
- x: plotLeft,
11424
- y: plotTop,
11425
- width: plotWidth,
11426
- height: plotHeight
11427
- };
11428
- clipX = mathCeil(mathMax(plotBorderWidth, clipOffset[3]) / 2);
11429
- clipY = mathCeil(mathMax(plotBorderWidth, clipOffset[0]) / 2);
11430
- chart.clipBox = {
11431
- x: clipX,
11432
- y: clipY,
11433
- width: mathFloor(chart.plotSizeX - mathMax(plotBorderWidth, clipOffset[1]) / 2 - clipX),
11434
- height: mathFloor(chart.plotSizeY - mathMax(plotBorderWidth, clipOffset[2]) / 2 - clipY)
11435
- };
11436
-
11437
- if (!skipAxes) {
11438
- each(chart.axes, function (axis) {
11439
- axis.setAxisSize();
11440
- axis.setAxisTranslation();
11441
- });
11442
- }
11443
- },
11444
-
11445
- /**
11446
- * Initial margins before auto size margins are applied
11447
- */
11448
- resetMargins: function () {
11449
- var chart = this,
11450
- optionsChart = chart.options.chart,
11451
- spacingTop = optionsChart.spacingTop,
11452
- spacingRight = optionsChart.spacingRight,
11453
- spacingBottom = optionsChart.spacingBottom,
11454
- spacingLeft = optionsChart.spacingLeft;
11455
-
11456
- chart.plotTop = pick(chart.optionsMarginTop, spacingTop);
11457
- chart.marginRight = pick(chart.optionsMarginRight, spacingRight);
11458
- chart.marginBottom = pick(chart.optionsMarginBottom, spacingBottom);
11459
- chart.plotLeft = pick(chart.optionsMarginLeft, spacingLeft);
11460
- chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
11461
- chart.clipOffset = [0, 0, 0, 0];
11462
- },
11463
-
11464
- /**
11465
- * Draw the borders and backgrounds for chart and plot area
11466
- */
11467
- drawChartBox: function () {
11468
- var chart = this,
11469
- optionsChart = chart.options.chart,
11470
- renderer = chart.renderer,
11471
- chartWidth = chart.chartWidth,
11472
- chartHeight = chart.chartHeight,
11473
- chartBackground = chart.chartBackground,
11474
- plotBackground = chart.plotBackground,
11475
- plotBorder = chart.plotBorder,
11476
- plotBGImage = chart.plotBGImage,
11477
- chartBorderWidth = optionsChart.borderWidth || 0,
11478
- chartBackgroundColor = optionsChart.backgroundColor,
11479
- plotBackgroundColor = optionsChart.plotBackgroundColor,
11480
- plotBackgroundImage = optionsChart.plotBackgroundImage,
11481
- plotBorderWidth = optionsChart.plotBorderWidth || 0,
11482
- mgn,
11483
- bgAttr,
11484
- plotLeft = chart.plotLeft,
11485
- plotTop = chart.plotTop,
11486
- plotWidth = chart.plotWidth,
11487
- plotHeight = chart.plotHeight,
11488
- plotBox = chart.plotBox,
11489
- clipRect = chart.clipRect,
11490
- clipBox = chart.clipBox;
11491
-
11492
- // Chart area
11493
- mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
11494
-
11495
- if (chartBorderWidth || chartBackgroundColor) {
11496
- if (!chartBackground) {
11497
-
11498
- bgAttr = {
11499
- fill: chartBackgroundColor || NONE
11500
- };
11501
- if (chartBorderWidth) { // #980
11502
- bgAttr.stroke = optionsChart.borderColor;
11503
- bgAttr['stroke-width'] = chartBorderWidth;
11504
- }
11505
- chart.chartBackground = renderer.rect(mgn / 2, mgn / 2, chartWidth - mgn, chartHeight - mgn,
11506
- optionsChart.borderRadius, chartBorderWidth)
11507
- .attr(bgAttr)
11508
- .add()
11509
- .shadow(optionsChart.shadow);
11510
-
11511
- } else { // resize
11512
- chartBackground.animate(
11513
- chartBackground.crisp(null, null, null, chartWidth - mgn, chartHeight - mgn)
11514
- );
11515
- }
11516
- }
11517
-
11518
-
11519
- // Plot background
11520
- if (plotBackgroundColor) {
11521
- if (!plotBackground) {
11522
- chart.plotBackground = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0)
11523
- .attr({
11524
- fill: plotBackgroundColor
11525
- })
11526
- .add()
11527
- .shadow(optionsChart.plotShadow);
11528
- } else {
11529
- plotBackground.animate(plotBox);
11530
- }
11531
- }
11532
- if (plotBackgroundImage) {
11533
- if (!plotBGImage) {
11534
- chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight)
11535
- .add();
11536
- } else {
11537
- plotBGImage.animate(plotBox);
11538
- }
11539
- }
11540
-
11541
- // Plot clip
11542
- if (!clipRect) {
11543
- chart.clipRect = renderer.clipRect(clipBox);
11544
- } else {
11545
- clipRect.animate({
11546
- width: clipBox.width,
11547
- height: clipBox.height
11548
- });
11549
- }
11550
-
11551
- // Plot area border
11552
- if (plotBorderWidth) {
11553
- if (!plotBorder) {
11554
- chart.plotBorder = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0, plotBorderWidth)
11555
- .attr({
11556
- stroke: optionsChart.plotBorderColor,
11557
- 'stroke-width': plotBorderWidth,
11558
- zIndex: 1
11559
- })
11560
- .add();
11561
- } else {
11562
- plotBorder.animate(
11563
- plotBorder.crisp(null, plotLeft, plotTop, plotWidth, plotHeight)
11564
- );
11565
- }
11566
- }
11567
-
11568
- // reset
11569
- chart.isDirtyBox = false;
11570
- },
11571
-
11572
- /**
11573
- * Detect whether a certain chart property is needed based on inspecting its options
11574
- * and series. This mainly applies to the chart.invert property, and in extensions to
11575
- * the chart.angular and chart.polar properties.
11576
- */
11577
- propFromSeries: function () {
11578
- var chart = this,
11579
- optionsChart = chart.options.chart,
11580
- klass,
11581
- seriesOptions = chart.options.series,
11582
- i,
11583
- value;
11584
-
11585
-
11586
- each(['inverted', 'angular', 'polar'], function (key) {
11587
-
11588
- // The default series type's class
11589
- klass = seriesTypes[optionsChart.type || optionsChart.defaultSeriesType];
11590
-
11591
- // Get the value from available chart-wide properties
11592
- value = (
11593
- chart[key] || // 1. it is set before
11594
- optionsChart[key] || // 2. it is set in the options
11595
- (klass && klass.prototype[key]) // 3. it's default series class requires it
11596
- );
11597
-
11598
- // 4. Check if any the chart's series require it
11599
- i = seriesOptions && seriesOptions.length;
11600
- while (!value && i--) {
11601
- klass = seriesTypes[seriesOptions[i].type];
11602
- if (klass && klass.prototype[key]) {
11603
- value = true;
11604
- }
11605
- }
11606
-
11607
- // Set the chart property
11608
- chart[key] = value;
11609
- });
11610
-
11611
- },
11612
-
11613
- /**
11614
- * Render all graphics for the chart
11615
- */
11616
- render: function () {
11617
- var chart = this,
11618
- axes = chart.axes,
11619
- renderer = chart.renderer,
11620
- options = chart.options;
11621
-
11622
- var labels = options.labels,
11623
- credits = options.credits,
11624
- creditsHref;
11625
-
11626
- // Title
11627
- chart.setTitle();
11628
-
11629
-
11630
- // Legend
11631
- chart.legend = new Legend(chart, options.legend);
11632
-
11633
- // Get margins by pre-rendering axes
11634
- // set axes scales
11635
- each(axes, function (axis) {
11636
- axis.setScale();
11637
- });
11638
- chart.getMargins();
11639
-
11640
- chart.maxTicks = null; // reset for second pass
11641
- each(axes, function (axis) {
11642
- axis.setTickPositions(true); // update to reflect the new margins
11643
- axis.setMaxTicks();
11644
- });
11645
- chart.adjustTickAmounts();
11646
- chart.getMargins(); // second pass to check for new labels
11647
-
11648
-
11649
- // Draw the borders and backgrounds
11650
- chart.drawChartBox();
11651
-
11652
-
11653
- // Axes
11654
- if (chart.hasCartesianSeries) {
11655
- each(axes, function (axis) {
11656
- axis.render();
11657
- });
11658
- }
11659
-
11660
- // The series
11661
- if (!chart.seriesGroup) {
11662
- chart.seriesGroup = renderer.g('series-group')
11663
- .attr({ zIndex: 3 })
11664
- .add();
11665
- }
11666
- each(chart.series, function (serie) {
11667
- serie.translate();
11668
- serie.setTooltipPoints();
11669
- serie.render();
11670
- });
11671
-
11672
- // Labels
11673
- if (labels.items) {
11674
- each(labels.items, function (label) {
11675
- var style = extend(labels.style, label.style),
11676
- x = pInt(style.left) + chart.plotLeft,
11677
- y = pInt(style.top) + chart.plotTop + 12;
11678
-
11679
- // delete to prevent rewriting in IE
11680
- delete style.left;
11681
- delete style.top;
11682
-
11683
- renderer.text(
11684
- label.html,
11685
- x,
11686
- y
11687
- )
11688
- .attr({ zIndex: 2 })
11689
- .css(style)
11690
- .add();
11691
-
11692
- });
11693
- }
11694
-
11695
- // Credits
11696
- if (credits.enabled && !chart.credits) {
11697
- creditsHref = credits.href;
11698
- chart.credits = renderer.text(
11699
- credits.text,
11700
- 0,
11701
- 0
11702
- )
11703
- .on('click', function () {
11704
- if (creditsHref) {
11705
- location.href = creditsHref;
11706
- }
11707
- })
11708
- .attr({
11709
- align: credits.position.align,
11710
- zIndex: 8
11711
- })
11712
- .css(credits.style)
11713
- .add()
11714
- .align(credits.position);
11715
- }
11716
-
11717
- // Set flag
11718
- chart.hasRendered = true;
11719
-
11720
- },
11721
-
11722
- /**
11723
- * Clean up memory usage
11724
- */
11725
- destroy: function () {
11726
- var chart = this,
11727
- axes = chart.axes,
11728
- series = chart.series,
11729
- container = chart.container,
11730
- i,
11731
- parentNode = container && container.parentNode;
11732
-
11733
- // fire the chart.destoy event
11734
- fireEvent(chart, 'destroy');
11735
-
11736
- // Delete the chart from charts lookup array
11737
- charts[chart.index] = UNDEFINED;
11738
- chart.renderTo.removeAttribute('data-highcharts-chart');
11739
-
11740
- // remove events
11741
- removeEvent(chart);
11742
-
11743
- // ==== Destroy collections:
11744
- // Destroy axes
11745
- i = axes.length;
11746
- while (i--) {
11747
- axes[i] = axes[i].destroy();
11748
- }
11749
-
11750
- // Destroy each series
11751
- i = series.length;
11752
- while (i--) {
11753
- series[i] = series[i].destroy();
11754
- }
11755
-
11756
- // ==== Destroy chart properties:
11757
- each(['title', 'subtitle', 'chartBackground', 'plotBackground', 'plotBGImage',
11758
- 'plotBorder', 'seriesGroup', 'clipRect', 'credits', 'pointer', 'scroller',
11759
- 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip', 'renderer'], function (name) {
11760
- var prop = chart[name];
11761
-
11762
- if (prop && prop.destroy) {
11763
- chart[name] = prop.destroy();
11764
- }
11765
- });
11766
-
11767
- // remove container and all SVG
11768
- if (container) { // can break in IE when destroyed before finished loading
11769
- container.innerHTML = '';
11770
- removeEvent(container);
11771
- if (parentNode) {
11772
- discardElement(container);
11773
- }
11774
-
11775
- }
11776
-
11777
- // clean it all up
11778
- for (i in chart) {
11779
- delete chart[i];
11780
- }
11781
-
11782
- },
11783
-
11784
-
11785
- /**
11786
- * VML namespaces can't be added until after complete. Listening
11787
- * for Perini's doScroll hack is not enough.
11788
- */
11789
- isReadyToRender: function () {
11790
- var chart = this;
11791
-
11792
- // Note: in spite of JSLint's complaints, win == win.top is required
11793
- /*jslint eqeq: true*/
11794
- if ((!hasSVG && (win == win.top && doc.readyState !== 'complete')) || (useCanVG && !win.canvg)) {
11795
- /*jslint eqeq: false*/
11796
- if (useCanVG) {
11797
- // Delay rendering until canvg library is downloaded and ready
11798
- CanVGController.push(function () { chart.firstRender(); }, chart.options.global.canvasToolsURL);
11799
- } else {
11800
- doc.attachEvent('onreadystatechange', function () {
11801
- doc.detachEvent('onreadystatechange', chart.firstRender);
11802
- if (doc.readyState === 'complete') {
11803
- chart.firstRender();
11804
- }
11805
- });
11806
- }
11807
- return false;
11808
- }
11809
- return true;
11810
- },
11811
-
11812
- /**
11813
- * Prepare for first rendering after all data are loaded
11814
- */
11815
- firstRender: function () {
11816
- var chart = this,
11817
- options = chart.options,
11818
- callback = chart.callback;
11819
-
11820
- // Check whether the chart is ready to render
11821
- if (!chart.isReadyToRender()) {
11822
- return;
11823
- }
11824
-
11825
- // Create the container
11826
- chart.getContainer();
11827
-
11828
- // Run an early event after the container and renderer are established
11829
- fireEvent(chart, 'init');
11830
-
11831
-
11832
- chart.resetMargins();
11833
- chart.setChartSize();
11834
-
11835
- // Set the common chart properties (mainly invert) from the given series
11836
- chart.propFromSeries();
11837
-
11838
- // get axes
11839
- chart.getAxes();
11840
-
11841
- // Initialize the series
11842
- each(options.series || [], function (serieOptions) {
11843
- chart.initSeries(serieOptions);
11844
- });
11845
-
11846
- // Run an event after axes and series are initialized, but before render. At this stage,
11847
- // the series data is indexed and cached in the xData and yData arrays, so we can access
11848
- // those before rendering. Used in Highstock.
11849
- fireEvent(chart, 'beforeRender');
11850
-
11851
- // depends on inverted and on margins being set
11852
- chart.pointer = new Pointer(chart, options);
11853
-
11854
- chart.render();
11855
-
11856
- // add canvas
11857
- chart.renderer.draw();
11858
- // run callbacks
11859
- if (callback) {
11860
- callback.apply(chart, [chart]);
11861
- }
11862
- each(chart.callbacks, function (fn) {
11863
- fn.apply(chart, [chart]);
11864
- });
11865
-
11866
-
11867
- // If the chart was rendered outside the top container, put it back in
11868
- chart.cloneRenderTo(true);
11869
-
11870
- fireEvent(chart, 'load');
11871
-
11872
- }
11873
- }; // end Chart
11874
-
11875
- // Hook for exporting module
11876
- Chart.prototype.callbacks = [];
11877
- /**
11878
- * The Point object and prototype. Inheritable and used as base for PiePoint
11879
- */
11880
- var Point = function () {};
11881
- Point.prototype = {
11882
-
11883
- /**
11884
- * Initialize the point
11885
- * @param {Object} series The series object containing this point
11886
- * @param {Object} options The data in either number, array or object format
11887
- */
11888
- init: function (series, options, x) {
11889
-
11890
- var point = this,
11891
- colors;
11892
- point.series = series;
11893
- point.applyOptions(options, x);
11894
- point.pointAttr = {};
11895
-
11896
- if (series.options.colorByPoint) {
11897
- colors = series.options.colors || series.chart.options.colors;
11898
- point.color = point.color || colors[series.colorCounter++];
11899
- // loop back to zero
11900
- if (series.colorCounter === colors.length) {
11901
- series.colorCounter = 0;
11902
- }
11903
- }
11904
-
11905
- series.chart.pointCount++;
11906
- return point;
11907
- },
11908
- /**
11909
- * Apply the options containing the x and y data and possible some extra properties.
11910
- * This is called on point init or from point.update.
11911
- *
11912
- * @param {Object} options
11913
- */
11914
- applyOptions: function (options, x) {
11915
- var point = this,
11916
- series = point.series,
11917
- pointValKey = series.pointValKey;
11918
-
11919
- options = Point.prototype.optionsToObject.call(this, options);
11920
-
11921
- // copy options directly to point
11922
- extend(point, options);
11923
- point.options = point.options ? extend(point.options, options) : options;
11924
-
11925
- // For higher dimension series types. For instance, for ranges, point.y is mapped to point.low.
11926
- if (pointValKey) {
11927
- point.y = point[pointValKey];
11928
- }
11929
-
11930
- // If no x is set by now, get auto incremented value. All points must have an
11931
- // x value, however the y value can be null to create a gap in the series
11932
- if (point.x === UNDEFINED && series) {
11933
- point.x = x === UNDEFINED ? series.autoIncrement() : x;
11934
- }
11935
-
11936
- return point;
11937
- },
11938
-
11939
- /**
11940
- * Transform number or array configs into objects
11941
- */
11942
- optionsToObject: function (options) {
11943
- var ret,
11944
- series = this.series,
11945
- pointArrayMap = series.pointArrayMap || ['y'],
11946
- valueCount = pointArrayMap.length,
11947
- firstItemType,
11948
- i = 0,
11949
- j = 0;
11950
-
11951
- if (typeof options === 'number' || options === null) {
11952
- ret = { y: options };
11953
-
11954
- } else if (isArray(options)) {
11955
- ret = {};
11956
- // with leading x value
11957
- if (options.length > valueCount) {
11958
- firstItemType = typeof options[0];
11959
- if (firstItemType === 'string') {
11960
- ret.name = options[0];
11961
- } else if (firstItemType === 'number') {
11962
- ret.x = options[0];
11963
- }
11964
- i++;
11965
- }
11966
- while (j < valueCount) {
11967
- ret[pointArrayMap[j++]] = options[i++];
11968
- }
11969
- } else if (typeof options === 'object') {
11970
- ret = options;
11971
-
11972
- // This is the fastest way to detect if there are individual point dataLabels that need
11973
- // to be considered in drawDataLabels. These can only occur in object configs.
11974
- if (options.dataLabels) {
11975
- series._hasPointLabels = true;
11976
- }
11977
-
11978
- // Same approach as above for markers
11979
- if (options.marker) {
11980
- series._hasPointMarkers = true;
11981
- }
11982
- }
11983
- return ret;
11984
- },
11985
-
11986
- /**
11987
- * Destroy a point to clear memory. Its reference still stays in series.data.
11988
- */
11989
- destroy: function () {
11990
- var point = this,
11991
- series = point.series,
11992
- chart = series.chart,
11993
- hoverPoints = chart.hoverPoints,
11994
- prop;
11995
-
11996
- chart.pointCount--;
11997
-
11998
- if (hoverPoints) {
11999
- point.setState();
12000
- erase(hoverPoints, point);
12001
- if (!hoverPoints.length) {
12002
- chart.hoverPoints = null;
12003
- }
12004
-
12005
- }
12006
- if (point === chart.hoverPoint) {
12007
- point.onMouseOut();
12008
- }
12009
-
12010
- // remove all events
12011
- if (point.graphic || point.dataLabel) { // removeEvent and destroyElements are performance expensive
12012
- removeEvent(point);
12013
- point.destroyElements();
12014
- }
12015
-
12016
- if (point.legendItem) { // pies have legend items
12017
- chart.legend.destroyItem(point);
12018
- }
12019
-
12020
- for (prop in point) {
12021
- point[prop] = null;
12022
- }
12023
-
12024
-
12025
- },
12026
-
12027
- /**
12028
- * Destroy SVG elements associated with the point
12029
- */
12030
- destroyElements: function () {
12031
- var point = this,
12032
- props = ['graphic', 'dataLabel', 'dataLabelUpper', 'group', 'connector', 'shadowGroup'],
12033
- prop,
12034
- i = 6;
12035
- while (i--) {
12036
- prop = props[i];
12037
- if (point[prop]) {
12038
- point[prop] = point[prop].destroy();
12039
- }
12040
- }
12041
- },
12042
-
12043
- /**
12044
- * Return the configuration hash needed for the data label and tooltip formatters
12045
- */
12046
- getLabelConfig: function () {
12047
- var point = this;
12048
- return {
12049
- x: point.category,
12050
- y: point.y,
12051
- key: point.name || point.category,
12052
- series: point.series,
12053
- point: point,
12054
- percentage: point.percentage,
12055
- total: point.total || point.stackTotal
12056
- };
12057
- },
12058
-
12059
- /**
12060
- * Toggle the selection status of a point
12061
- * @param {Boolean} selected Whether to select or unselect the point.
12062
- * @param {Boolean} accumulate Whether to add to the previous selection. By default,
12063
- * this happens if the control key (Cmd on Mac) was pressed during clicking.
12064
- */
12065
- select: function (selected, accumulate) {
12066
- var point = this,
12067
- series = point.series,
12068
- chart = series.chart;
12069
-
12070
- selected = pick(selected, !point.selected);
12071
-
12072
- // fire the event with the defalut handler
12073
- point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
12074
- point.selected = point.options.selected = selected;
12075
- series.options.data[inArray(point, series.data)] = point.options;
12076
-
12077
- point.setState(selected && SELECT_STATE);
12078
-
12079
- // unselect all other points unless Ctrl or Cmd + click
12080
- if (!accumulate) {
12081
- each(chart.getSelectedPoints(), function (loopPoint) {
12082
- if (loopPoint.selected && loopPoint !== point) {
12083
- loopPoint.selected = loopPoint.options.selected = false;
12084
- series.options.data[inArray(loopPoint, series.data)] = loopPoint.options;
12085
- loopPoint.setState(NORMAL_STATE);
12086
- loopPoint.firePointEvent('unselect');
12087
- }
12088
- });
12089
- }
12090
- });
12091
- },
12092
-
12093
- /**
12094
- * Runs on mouse over the point
12095
- */
12096
- onMouseOver: function (e) {
12097
- var point = this,
12098
- series = point.series,
12099
- chart = series.chart,
12100
- tooltip = chart.tooltip,
12101
- hoverPoint = chart.hoverPoint;
12102
-
12103
- // set normal state to previous series
12104
- if (hoverPoint && hoverPoint !== point) {
12105
- hoverPoint.onMouseOut();
12106
- }
12107
-
12108
- // trigger the event
12109
- point.firePointEvent('mouseOver');
12110
-
12111
- // update the tooltip
12112
- if (tooltip && (!tooltip.shared || series.noSharedTooltip)) {
12113
- tooltip.refresh(point, e);
12114
- }
12115
-
12116
- // hover this
12117
- point.setState(HOVER_STATE);
12118
- chart.hoverPoint = point;
12119
- },
12120
-
12121
- /**
12122
- * Runs on mouse out from the point
12123
- */
12124
- onMouseOut: function () {
12125
- var chart = this.series.chart,
12126
- hoverPoints = chart.hoverPoints;
12127
-
12128
- if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887
12129
- this.firePointEvent('mouseOut');
12130
-
12131
- this.setState();
12132
- chart.hoverPoint = null;
12133
- }
12134
- },
12135
-
12136
- /**
12137
- * Extendable method for formatting each point's tooltip line
12138
- *
12139
- * @return {String} A string to be concatenated in to the common tooltip text
12140
- */
12141
- tooltipFormatter: function (pointFormat) {
12142
-
12143
- // Insert options for valueDecimals, valuePrefix, and valueSuffix
12144
- var series = this.series,
12145
- seriesTooltipOptions = series.tooltipOptions,
12146
- valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''),
12147
- valuePrefix = seriesTooltipOptions.valuePrefix || '',
12148
- valueSuffix = seriesTooltipOptions.valueSuffix || '';
12149
-
12150
- // Loop over the point array map and replace unformatted values with sprintf formatting markup
12151
- each(series.pointArrayMap || ['y'], function (key) {
12152
- key = '{point.' + key; // without the closing bracket
12153
- if (valuePrefix || valueSuffix) {
12154
- pointFormat = pointFormat.replace(key + '}', valuePrefix + key + '}' + valueSuffix);
12155
- }
12156
- pointFormat = pointFormat.replace(key + '}', key + ':,.' + valueDecimals + 'f}');
12157
- });
12158
-
12159
- return format(pointFormat, {
12160
- point: this,
12161
- series: this.series
12162
- });
12163
- },
12164
-
12165
- /**
12166
- * Update the point with new options (typically x/y data) and optionally redraw the series.
12167
- *
12168
- * @param {Object} options Point options as defined in the series.data array
12169
- * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
12170
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
12171
- * configuration
12172
- *
12173
- */
12174
- update: function (options, redraw, animation) {
12175
- var point = this,
12176
- series = point.series,
12177
- graphic = point.graphic,
12178
- i,
12179
- data = series.data,
12180
- chart = series.chart;
12181
-
12182
- redraw = pick(redraw, true);
12183
-
12184
- // fire the event with a default handler of doing the update
12185
- point.firePointEvent('update', { options: options }, function () {
12186
-
12187
- point.applyOptions(options);
12188
-
12189
- // update visuals
12190
- if (isObject(options)) {
12191
- series.getAttribs();
12192
- if (graphic) {
12193
- graphic.attr(point.pointAttr[series.state]);
12194
- }
12195
- }
12196
-
12197
- // record changes in the parallel arrays
12198
- i = inArray(point, data);
12199
- series.xData[i] = point.x;
12200
- series.yData[i] = series.toYData ? series.toYData(point) : point.y;
12201
- series.zData[i] = point.z;
12202
- series.options.data[i] = point.options;
12203
-
12204
- // redraw
12205
- series.isDirty = true;
12206
- series.isDirtyData = true;
12207
- if (redraw) {
12208
- chart.redraw(animation);
12209
- }
12210
- });
12211
- },
12212
-
12213
- /**
12214
- * Remove a point and optionally redraw the series and if necessary the axes
12215
- * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
12216
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
12217
- * configuration
12218
- */
12219
- remove: function (redraw, animation) {
12220
- var point = this,
12221
- series = point.series,
12222
- chart = series.chart,
12223
- i,
12224
- data = series.data;
12225
-
12226
- setAnimation(animation, chart);
12227
- redraw = pick(redraw, true);
12228
-
12229
- // fire the event with a default handler of removing the point
12230
- point.firePointEvent('remove', null, function () {
12231
-
12232
- // splice all the parallel arrays
12233
- i = inArray(point, data);
12234
- data.splice(i, 1);
12235
- series.options.data.splice(i, 1);
12236
- series.xData.splice(i, 1);
12237
- series.yData.splice(i, 1);
12238
- series.zData.splice(i, 1);
12239
-
12240
- point.destroy();
12241
-
12242
-
12243
- // redraw
12244
- series.isDirty = true;
12245
- series.isDirtyData = true;
12246
- if (redraw) {
12247
- chart.redraw();
12248
- }
12249
- });
12250
-
12251
-
12252
- },
12253
-
12254
- /**
12255
- * Fire an event on the Point object. Must not be renamed to fireEvent, as this
12256
- * causes a name clash in MooTools
12257
- * @param {String} eventType
12258
- * @param {Object} eventArgs Additional event arguments
12259
- * @param {Function} defaultFunction Default event handler
12260
- */
12261
- firePointEvent: function (eventType, eventArgs, defaultFunction) {
12262
- var point = this,
12263
- series = this.series,
12264
- seriesOptions = series.options;
12265
-
12266
- // load event handlers on demand to save time on mouseover/out
12267
- if (seriesOptions.point.events[eventType] || (point.options && point.options.events && point.options.events[eventType])) {
12268
- this.importEvents();
12269
- }
12270
-
12271
- // add default handler if in selection mode
12272
- if (eventType === 'click' && seriesOptions.allowPointSelect) {
12273
- defaultFunction = function (event) {
12274
- // Control key is for Windows, meta (= Cmd key) for Mac, Shift for Opera
12275
- point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
12276
- };
12277
- }
12278
-
12279
- fireEvent(this, eventType, eventArgs, defaultFunction);
12280
- },
12281
- /**
12282
- * Import events from the series' and point's options. Only do it on
12283
- * demand, to save processing time on hovering.
12284
- */
12285
- importEvents: function () {
12286
- if (!this.hasImportedEvents) {
12287
- var point = this,
12288
- options = merge(point.series.options.point, point.options),
12289
- events = options.events,
12290
- eventType;
12291
-
12292
- point.events = events;
12293
-
12294
- for (eventType in events) {
12295
- addEvent(point, eventType, events[eventType]);
12296
- }
12297
- this.hasImportedEvents = true;
12298
-
12299
- }
12300
- },
12301
-
12302
- /**
12303
- * Set the point's state
12304
- * @param {String} state
12305
- */
12306
- setState: function (state) {
12307
- var point = this,
12308
- plotX = point.plotX,
12309
- plotY = point.plotY,
12310
- series = point.series,
12311
- stateOptions = series.options.states,
12312
- markerOptions = defaultPlotOptions[series.type].marker && series.options.marker,
12313
- normalDisabled = markerOptions && !markerOptions.enabled,
12314
- markerStateOptions = markerOptions && markerOptions.states[state],
12315
- stateDisabled = markerStateOptions && markerStateOptions.enabled === false,
12316
- stateMarkerGraphic = series.stateMarkerGraphic,
12317
- pointMarker = point.marker || {},
12318
- chart = series.chart,
12319
- radius,
12320
- newSymbol,
12321
- pointAttr = point.pointAttr;
12322
-
12323
- state = state || NORMAL_STATE; // empty string
12324
-
12325
- if (
12326
- // already has this state
12327
- state === point.state ||
12328
- // selected points don't respond to hover
12329
- (point.selected && state !== SELECT_STATE) ||
12330
- // series' state options is disabled
12331
- (stateOptions[state] && stateOptions[state].enabled === false) ||
12332
- // point marker's state options is disabled
12333
- (state && (stateDisabled || (normalDisabled && !markerStateOptions.enabled)))
12334
-
12335
- ) {
12336
- return;
12337
- }
12338
-
12339
- // apply hover styles to the existing point
12340
- if (point.graphic) {
12341
- radius = markerOptions && point.graphic.symbolName && pointAttr[state].r;
12342
- point.graphic.attr(merge(
12343
- pointAttr[state],
12344
- radius ? { // new symbol attributes (#507, #612)
12345
- x: plotX - radius,
12346
- y: plotY - radius,
12347
- width: 2 * radius,
12348
- height: 2 * radius
12349
- } : {}
12350
- ));
12351
- } else {
12352
- // if a graphic is not applied to each point in the normal state, create a shared
12353
- // graphic for the hover state
12354
- if (state && markerStateOptions) {
12355
- radius = markerStateOptions.radius;
12356
- newSymbol = pointMarker.symbol || series.symbol;
12357
-
12358
- // If the point has another symbol than the previous one, throw away the
12359
- // state marker graphic and force a new one (#1459)
12360
- if (stateMarkerGraphic && stateMarkerGraphic.currentSymbol !== newSymbol) {
12361
- stateMarkerGraphic = stateMarkerGraphic.destroy();
12362
- }
12363
-
12364
- // Add a new state marker graphic
12365
- if (!stateMarkerGraphic) {
12366
- series.stateMarkerGraphic = stateMarkerGraphic = chart.renderer.symbol(
12367
- newSymbol,
12368
- plotX - radius,
12369
- plotY - radius,
12370
- 2 * radius,
12371
- 2 * radius
12372
- )
12373
- .attr(pointAttr[state])
12374
- .add(series.markerGroup);
12375
- stateMarkerGraphic.currentSymbol = newSymbol;
12376
-
12377
- // Move the existing graphic
12378
- } else {
12379
- stateMarkerGraphic.attr({ // #1054
12380
- x: plotX - radius,
12381
- y: plotY - radius
12382
- });
12383
- }
12384
- }
12385
-
12386
- if (stateMarkerGraphic) {
12387
- stateMarkerGraphic[state && chart.isInsidePlot(plotX, plotY) ? 'show' : 'hide']();
12388
- }
12389
- }
12390
-
12391
- point.state = state;
12392
- }
12393
- };
12394
-
12395
- /**
12396
- * @classDescription The base function which all other series types inherit from. The data in the series is stored
12397
- * in various arrays.
12398
- *
12399
- * - First, series.options.data contains all the original config options for
12400
- * each point whether added by options or methods like series.addPoint.
12401
- * - Next, series.data contains those values converted to points, but in case the series data length
12402
- * exceeds the cropThreshold, or if the data is grouped, series.data doesn't contain all the points. It
12403
- * only contains the points that have been created on demand.
12404
- * - Then there's series.points that contains all currently visible point objects. In case of cropping,
12405
- * the cropped-away points are not part of this array. The series.points array starts at series.cropStart
12406
- * compared to series.data and series.options.data. If however the series data is grouped, these can't
12407
- * be correlated one to one.
12408
- * - series.xData and series.processedXData contain clean x values, equivalent to series.data and series.points.
12409
- * - series.yData and series.processedYData contain clean x values, equivalent to series.data and series.points.
12410
- *
12411
- * @param {Object} chart
12412
- * @param {Object} options
12413
- */
12414
- var Series = function () {};
12415
-
12416
- Series.prototype = {
12417
-
12418
- isCartesian: true,
12419
- type: 'line',
12420
- pointClass: Point,
12421
- sorted: true, // requires the data to be sorted
12422
- requireSorting: true,
12423
- pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
12424
- stroke: 'lineColor',
12425
- 'stroke-width': 'lineWidth',
12426
- fill: 'fillColor',
12427
- r: 'radius'
12428
- },
12429
- colorCounter: 0,
12430
- init: function (chart, options) {
12431
- var series = this,
12432
- eventType,
12433
- events,
12434
- linkedTo,
12435
- chartSeries = chart.series;
12436
-
12437
- series.chart = chart;
12438
- series.options = options = series.setOptions(options); // merge with plotOptions
12439
-
12440
- // bind the axes
12441
- series.bindAxes();
12442
-
12443
- // set some variables
12444
- extend(series, {
12445
- name: options.name,
12446
- state: NORMAL_STATE,
12447
- pointAttr: {},
12448
- visible: options.visible !== false, // true by default
12449
- selected: options.selected === true // false by default
12450
- });
12451
-
12452
- // special
12453
- if (useCanVG) {
12454
- options.animation = false;
12455
- }
12456
-
12457
- // register event listeners
12458
- events = options.events;
12459
- for (eventType in events) {
12460
- addEvent(series, eventType, events[eventType]);
12461
- }
12462
- if (
12463
- (events && events.click) ||
12464
- (options.point && options.point.events && options.point.events.click) ||
12465
- options.allowPointSelect
12466
- ) {
12467
- chart.runTrackerClick = true;
12468
- }
12469
-
12470
- series.getColor();
12471
- series.getSymbol();
12472
-
12473
- // set the data
12474
- series.setData(options.data, false);
12475
-
12476
- // Mark cartesian
12477
- if (series.isCartesian) {
12478
- chart.hasCartesianSeries = true;
12479
- }
12480
-
12481
- // Register it in the chart
12482
- chartSeries.push(series);
12483
- series._i = chartSeries.length - 1;
12484
-
12485
- // Sort series according to index option (#248, #1123)
12486
- stableSort(chartSeries, function (a, b) {
12487
- return pick(a.options.index, a._i) - pick(b.options.index, a._i);
12488
- });
12489
- each(chartSeries, function (series, i) {
12490
- series.index = i;
12491
- series.name = series.name || 'Series ' + (i + 1);
12492
- });
12493
-
12494
- // Linked series
12495
- linkedTo = options.linkedTo;
12496
- series.linkedSeries = [];
12497
- if (isString(linkedTo)) {
12498
- if (linkedTo === ':previous') {
12499
- linkedTo = chartSeries[series.index - 1];
12500
- } else {
12501
- linkedTo = chart.get(linkedTo);
12502
- }
12503
- if (linkedTo) {
12504
- linkedTo.linkedSeries.push(series);
12505
- series.linkedParent = linkedTo;
12506
- }
12507
- }
12508
- },
12509
-
12510
- /**
12511
- * Set the xAxis and yAxis properties of cartesian series, and register the series
12512
- * in the axis.series array
12513
- */
12514
- bindAxes: function () {
12515
- var series = this,
12516
- seriesOptions = series.options,
12517
- chart = series.chart,
12518
- axisOptions;
12519
-
12520
- if (series.isCartesian) {
12521
-
12522
- each(['xAxis', 'yAxis'], function (AXIS) { // repeat for xAxis and yAxis
12523
-
12524
- each(chart[AXIS], function (axis) { // loop through the chart's axis objects
12525
-
12526
- axisOptions = axis.options;
12527
-
12528
- // apply if the series xAxis or yAxis option mathches the number of the
12529
- // axis, or if undefined, use the first axis
12530
- if ((seriesOptions[AXIS] === axisOptions.index) ||
12531
- (seriesOptions[AXIS] !== UNDEFINED && seriesOptions[AXIS] === axisOptions.id) ||
12532
- (seriesOptions[AXIS] === UNDEFINED && axisOptions.index === 0)) {
12533
-
12534
- // register this series in the axis.series lookup
12535
- axis.series.push(series);
12536
-
12537
- // set this series.xAxis or series.yAxis reference
12538
- series[AXIS] = axis;
12539
-
12540
- // mark dirty for redraw
12541
- axis.isDirty = true;
12542
- }
12543
- });
12544
-
12545
- // The series needs an X and an Y axis
12546
- if (!series[AXIS]) {
12547
- error(18, true);
12548
- }
12549
-
12550
- });
12551
- }
12552
- },
12553
-
12554
-
12555
- /**
12556
- * Return an auto incremented x value based on the pointStart and pointInterval options.
12557
- * This is only used if an x value is not given for the point that calls autoIncrement.
12558
- */
12559
- autoIncrement: function () {
12560
- var series = this,
12561
- options = series.options,
12562
- xIncrement = series.xIncrement;
12563
-
12564
- xIncrement = pick(xIncrement, options.pointStart, 0);
12565
-
12566
- series.pointInterval = pick(series.pointInterval, options.pointInterval, 1);
12567
-
12568
- series.xIncrement = xIncrement + series.pointInterval;
12569
- return xIncrement;
12570
- },
12571
-
12572
- /**
12573
- * Divide the series data into segments divided by null values.
12574
- */
12575
- getSegments: function () {
12576
- var series = this,
12577
- lastNull = -1,
12578
- segments = [],
12579
- i,
12580
- points = series.points,
12581
- pointsLength = points.length;
12582
-
12583
- if (pointsLength) { // no action required for []
12584
-
12585
- // if connect nulls, just remove null points
12586
- if (series.options.connectNulls) {
12587
- i = pointsLength;
12588
- while (i--) {
12589
- if (points[i].y === null) {
12590
- points.splice(i, 1);
12591
- }
12592
- }
12593
- if (points.length) {
12594
- segments = [points];
12595
- }
12596
-
12597
- // else, split on null points
12598
- } else {
12599
- each(points, function (point, i) {
12600
- if (point.y === null) {
12601
- if (i > lastNull + 1) {
12602
- segments.push(points.slice(lastNull + 1, i));
12603
- }
12604
- lastNull = i;
12605
- } else if (i === pointsLength - 1) { // last value
12606
- segments.push(points.slice(lastNull + 1, i + 1));
12607
- }
12608
- });
12609
- }
12610
- }
12611
-
12612
- // register it
12613
- series.segments = segments;
12614
- },
12615
- /**
12616
- * Set the series options by merging from the options tree
12617
- * @param {Object} itemOptions
12618
- */
12619
- setOptions: function (itemOptions) {
12620
- var chart = this.chart,
12621
- chartOptions = chart.options,
12622
- plotOptions = chartOptions.plotOptions,
12623
- typeOptions = plotOptions[this.type],
12624
- options;
12625
-
12626
- this.userOptions = itemOptions;
12627
-
12628
- options = merge(
12629
- typeOptions,
12630
- plotOptions.series,
12631
- itemOptions
12632
- );
12633
-
12634
- // the tooltip options are merged between global and series specific options
12635
- this.tooltipOptions = merge(chartOptions.tooltip, options.tooltip);
12636
-
12637
- // Delte marker object if not allowed (#1125)
12638
- if (typeOptions.marker === null) {
12639
- delete options.marker;
12640
- }
12641
-
12642
- return options;
12643
-
12644
- },
12645
- /**
12646
- * Get the series' color
12647
- */
12648
- getColor: function () {
12649
- var options = this.options,
12650
- userOptions = this.userOptions,
12651
- defaultColors = this.chart.options.colors,
12652
- counters = this.chart.counters,
12653
- color,
12654
- colorIndex;
12655
-
12656
- color = options.color || defaultPlotOptions[this.type].color;
12657
-
12658
- if (!color && !options.colorByPoint) {
12659
- if (defined(userOptions._colorIndex)) { // after Series.update()
12660
- colorIndex = userOptions._colorIndex;
12661
- } else {
12662
- userOptions._colorIndex = counters.color;
12663
- colorIndex = counters.color++;
12664
- }
12665
- color = defaultColors[colorIndex];
12666
- }
12667
-
12668
- this.color = color;
12669
- counters.wrapColor(defaultColors.length);
12670
- },
12671
- /**
12672
- * Get the series' symbol
12673
- */
12674
- getSymbol: function () {
12675
- var series = this,
12676
- userOptions = series.userOptions,
12677
- seriesMarkerOption = series.options.marker,
12678
- chart = series.chart,
12679
- defaultSymbols = chart.options.symbols,
12680
- counters = chart.counters,
12681
- symbolIndex;
12682
-
12683
- series.symbol = seriesMarkerOption.symbol;
12684
- if (!series.symbol) {
12685
- if (defined(userOptions._symbolIndex)) { // after Series.update()
12686
- symbolIndex = userOptions._symbolIndex;
12687
- } else {
12688
- userOptions._symbolIndex = counters.symbol;
12689
- symbolIndex = counters.symbol++;
12690
- }
12691
- series.symbol = defaultSymbols[symbolIndex];
12692
- }
12693
-
12694
- // don't substract radius in image symbols (#604)
12695
- if (/^url/.test(series.symbol)) {
12696
- seriesMarkerOption.radius = 0;
12697
- }
12698
- counters.wrapSymbol(defaultSymbols.length);
12699
- },
12700
-
12701
- /**
12702
- * Get the series' symbol in the legend. This method should be overridable to create custom
12703
- * symbols through Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
12704
- *
12705
- * @param {Object} legend The legend object
12706
- */
12707
- drawLegendSymbol: function (legend) {
12708
-
12709
- var options = this.options,
12710
- markerOptions = options.marker,
12711
- radius,
12712
- legendOptions = legend.options,
12713
- legendSymbol,
12714
- symbolWidth = legendOptions.symbolWidth,
12715
- renderer = this.chart.renderer,
12716
- legendItemGroup = this.legendGroup,
12717
- baseline = legend.baseline,
12718
- attr;
12719
-
12720
- // Draw the line
12721
- if (options.lineWidth) {
12722
- attr = {
12723
- 'stroke-width': options.lineWidth
12724
- };
12725
- if (options.dashStyle) {
12726
- attr.dashstyle = options.dashStyle;
12727
- }
12728
- this.legendLine = renderer.path([
12729
- M,
12730
- 0,
12731
- baseline - 4,
12732
- L,
12733
- symbolWidth,
12734
- baseline - 4
12735
- ])
12736
- .attr(attr)
12737
- .add(legendItemGroup);
12738
- }
12739
-
12740
- // Draw the marker
12741
- if (markerOptions && markerOptions.enabled) {
12742
- radius = markerOptions.radius;
12743
- this.legendSymbol = legendSymbol = renderer.symbol(
12744
- this.symbol,
12745
- (symbolWidth / 2) - radius,
12746
- baseline - 4 - radius,
12747
- 2 * radius,
12748
- 2 * radius
12749
- )
12750
- .add(legendItemGroup);
12751
- }
12752
- },
12753
-
12754
- /**
12755
- * Add a point dynamically after chart load time
12756
- * @param {Object} options Point options as given in series.data
12757
- * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
12758
- * @param {Boolean} shift If shift is true, a point is shifted off the start
12759
- * of the series as one is appended to the end.
12760
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
12761
- * configuration
12762
- */
12763
- addPoint: function (options, redraw, shift, animation) {
12764
- var series = this,
12765
- seriesOptions = series.options,
12766
- data = series.data,
12767
- graph = series.graph,
12768
- area = series.area,
12769
- chart = series.chart,
12770
- xData = series.xData,
12771
- yData = series.yData,
12772
- zData = series.zData,
12773
- names = series.names,
12774
- currentShift = (graph && graph.shift) || 0,
12775
- dataOptions = seriesOptions.data,
12776
- point;
12777
-
12778
- setAnimation(animation, chart);
12779
-
12780
- // Make graph animate sideways
12781
- if (graph && shift) {
12782
- graph.shift = currentShift + 1;
12783
- }
12784
- if (area) {
12785
- if (shift) { // #780
12786
- area.shift = currentShift + 1;
12787
- }
12788
- area.isArea = true; // needed in animation, both with and without shift
12789
- }
12790
-
12791
- // Optional redraw, defaults to true
12792
- redraw = pick(redraw, true);
12793
-
12794
- // Get options and push the point to xData, yData and series.options. In series.generatePoints
12795
- // the Point instance will be created on demand and pushed to the series.data array.
12796
- point = { series: series };
12797
- series.pointClass.prototype.applyOptions.apply(point, [options]);
12798
- xData.push(point.x);
12799
- yData.push(series.toYData ? series.toYData(point) : point.y);
12800
- zData.push(point.z);
12801
- if (names) {
12802
- names[point.x] = point.name;
12803
- }
12804
- dataOptions.push(options);
12805
-
12806
- // Generate points to be added to the legend (#1329)
12807
- if (seriesOptions.legendType === 'point') {
12808
- series.generatePoints();
12809
- }
12810
-
12811
- // Shift the first point off the parallel arrays
12812
- // todo: consider series.removePoint(i) method
12813
- if (shift) {
12814
- if (data[0] && data[0].remove) {
12815
- data[0].remove(false);
12816
- } else {
12817
- data.shift();
12818
- xData.shift();
12819
- yData.shift();
12820
- zData.shift();
12821
- dataOptions.shift();
12822
- }
12823
- }
12824
- series.getAttribs();
12825
-
12826
- // redraw
12827
- series.isDirty = true;
12828
- series.isDirtyData = true;
12829
- if (redraw) {
12830
- chart.redraw();
12831
- }
12832
- },
12833
-
12834
- /**
12835
- * Replace the series data with a new set of data
12836
- * @param {Object} data
12837
- * @param {Object} redraw
12838
- */
12839
- setData: function (data, redraw) {
12840
- var series = this,
12841
- oldData = series.points,
12842
- options = series.options,
12843
- chart = series.chart,
12844
- firstPoint = null,
12845
- xAxis = series.xAxis,
12846
- names = xAxis && xAxis.categories && !xAxis.categories.length ? [] : null,
12847
- i;
12848
-
12849
- // reset properties
12850
- series.xIncrement = null;
12851
- series.pointRange = xAxis && xAxis.categories ? 1 : options.pointRange;
12852
-
12853
- series.colorCounter = 0; // for series with colorByPoint (#1547)
12854
-
12855
- // parallel arrays
12856
- var xData = [],
12857
- yData = [],
12858
- zData = [],
12859
- dataLength = data ? data.length : [],
12860
- turboThreshold = options.turboThreshold || 1000,
12861
- pt,
12862
- pointArrayMap = series.pointArrayMap,
12863
- valueCount = pointArrayMap && pointArrayMap.length,
12864
- hasToYData = !!series.toYData;
12865
-
12866
- // In turbo mode, only one- or twodimensional arrays of numbers are allowed. The
12867
- // first value is tested, and we assume that all the rest are defined the same
12868
- // way. Although the 'for' loops are similar, they are repeated inside each
12869
- // if-else conditional for max performance.
12870
- if (dataLength > turboThreshold) {
12871
-
12872
- // find the first non-null point
12873
- i = 0;
12874
- while (firstPoint === null && i < dataLength) {
12875
- firstPoint = data[i];
12876
- i++;
12877
- }
12878
-
12879
-
12880
- if (isNumber(firstPoint)) { // assume all points are numbers
12881
- var x = pick(options.pointStart, 0),
12882
- pointInterval = pick(options.pointInterval, 1);
12883
-
12884
- for (i = 0; i < dataLength; i++) {
12885
- xData[i] = x;
12886
- yData[i] = data[i];
12887
- x += pointInterval;
12888
- }
12889
- series.xIncrement = x;
12890
- } else if (isArray(firstPoint)) { // assume all points are arrays
12891
- if (valueCount) { // [x, low, high] or [x, o, h, l, c]
12892
- for (i = 0; i < dataLength; i++) {
12893
- pt = data[i];
12894
- xData[i] = pt[0];
12895
- yData[i] = pt.slice(1, valueCount + 1);
12896
- }
12897
- } else { // [x, y]
12898
- for (i = 0; i < dataLength; i++) {
12899
- pt = data[i];
12900
- xData[i] = pt[0];
12901
- yData[i] = pt[1];
12902
- }
12903
- }
12904
- } /* else {
12905
- error(12); // Highcharts expects configs to be numbers or arrays in turbo mode
12906
- }*/
12907
- } else {
12908
- for (i = 0; i < dataLength; i++) {
12909
- if (data[i] !== UNDEFINED) { // stray commas in oldIE
12910
- pt = { series: series };
12911
- series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
12912
- xData[i] = pt.x;
12913
- yData[i] = hasToYData ? series.toYData(pt) : pt.y;
12914
- zData[i] = pt.z;
12915
- if (names && pt.name) {
12916
- names[i] = pt.name;
12917
- }
12918
- }
12919
- }
12920
- }
12921
-
12922
- // Unsorted data is not supported by the line tooltip as well as data grouping and
12923
- // navigation in Stock charts (#725)
12924
- if (series.requireSorting && xData.length > 1 && xData[1] < xData[0]) {
12925
- error(15);
12926
- }
12927
-
12928
- // Forgetting to cast strings to numbers is a common caveat when handling CSV or JSON
12929
- if (isString(yData[0])) {
12930
- error(14, true);
12931
- }
12932
-
12933
- series.data = [];
12934
- series.options.data = data;
12935
- series.xData = xData;
12936
- series.yData = yData;
12937
- series.zData = zData;
12938
- series.names = names;
12939
-
12940
- // destroy old points
12941
- i = (oldData && oldData.length) || 0;
12942
- while (i--) {
12943
- if (oldData[i] && oldData[i].destroy) {
12944
- oldData[i].destroy();
12945
- }
12946
- }
12947
-
12948
- // reset minRange (#878)
12949
- if (xAxis) {
12950
- xAxis.minRange = xAxis.userMinRange;
12951
- }
12952
-
12953
- // redraw
12954
- series.isDirty = series.isDirtyData = chart.isDirtyBox = true;
12955
- if (pick(redraw, true)) {
12956
- chart.redraw(false);
12957
- }
12958
- },
12959
-
12960
- /**
12961
- * Remove a series and optionally redraw the chart
12962
- *
12963
- * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
12964
- * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
12965
- * configuration
12966
- */
12967
-
12968
- remove: function (redraw, animation) {
12969
- var series = this,
12970
- chart = series.chart;
12971
- redraw = pick(redraw, true);
12972
-
12973
- if (!series.isRemoving) { /* prevent triggering native event in jQuery
12974
- (calling the remove function from the remove event) */
12975
- series.isRemoving = true;
12976
-
12977
- // fire the event with a default handler of removing the point
12978
- fireEvent(series, 'remove', null, function () {
12979
-
12980
-
12981
- // destroy elements
12982
- series.destroy();
12983
-
12984
-
12985
- // redraw
12986
- chart.isDirtyLegend = chart.isDirtyBox = true;
12987
- if (redraw) {
12988
- chart.redraw(animation);
12989
- }
12990
- });
12991
-
12992
- }
12993
- series.isRemoving = false;
12994
- },
12995
-
12996
- /**
12997
- * Process the data by cropping away unused data points if the series is longer
12998
- * than the crop threshold. This saves computing time for lage series.
12999
- */
13000
- processData: function (force) {
13001
- var series = this,
13002
- processedXData = series.xData, // copied during slice operation below
13003
- processedYData = series.yData,
13004
- dataLength = processedXData.length,
13005
- cropStart = 0,
13006
- cropEnd = dataLength,
13007
- cropped,
13008
- distance,
13009
- closestPointRange,
13010
- xAxis = series.xAxis,
13011
- i, // loop variable
13012
- options = series.options,
13013
- cropThreshold = options.cropThreshold,
13014
- isCartesian = series.isCartesian;
13015
-
13016
- // If the series data or axes haven't changed, don't go through this. Return false to pass
13017
- // the message on to override methods like in data grouping.
13018
- if (isCartesian && !series.isDirty && !xAxis.isDirty && !series.yAxis.isDirty && !force) {
13019
- return false;
13020
- }
13021
-
13022
- // optionally filter out points outside the plot area
13023
- if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
13024
- var extremes = xAxis.getExtremes(),
13025
- min = extremes.min,
13026
- max = extremes.max;
13027
-
13028
- // it's outside current extremes
13029
- if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
13030
- processedXData = [];
13031
- processedYData = [];
13032
-
13033
- // only crop if it's actually spilling out
13034
- } else if (processedXData[0] < min || processedXData[dataLength - 1] > max) {
13035
-
13036
- // iterate up to find slice start
13037
- for (i = 0; i < dataLength; i++) {
13038
- if (processedXData[i] >= min) {
13039
- cropStart = mathMax(0, i - 1);
13040
- break;
13041
- }
13042
- }
13043
- // proceed to find slice end
13044
- for (; i < dataLength; i++) {
13045
- if (processedXData[i] > max) {
13046
- cropEnd = i + 1;
13047
- break;
13048
- }
13049
-
13050
- }
13051
- processedXData = processedXData.slice(cropStart, cropEnd);
13052
- processedYData = processedYData.slice(cropStart, cropEnd);
13053
- cropped = true;
13054
- }
13055
- }
13056
-
13057
-
13058
- // Find the closest distance between processed points
13059
- for (i = processedXData.length - 1; i > 0; i--) {
13060
- distance = processedXData[i] - processedXData[i - 1];
13061
- if (distance > 0 && (closestPointRange === UNDEFINED || distance < closestPointRange)) {
13062
- closestPointRange = distance;
13063
- }
13064
- }
13065
-
13066
- // Record the properties
13067
- series.cropped = cropped; // undefined or true
13068
- series.cropStart = cropStart;
13069
- series.processedXData = processedXData;
13070
- series.processedYData = processedYData;
13071
-
13072
- if (options.pointRange === null) { // null means auto, as for columns, candlesticks and OHLC
13073
- series.pointRange = closestPointRange || 1;
13074
- }
13075
- series.closestPointRange = closestPointRange;
13076
-
13077
- },
13078
-
13079
- /**
13080
- * Generate the data point after the data has been processed by cropping away
13081
- * unused points and optionally grouped in Highcharts Stock.
13082
- */
13083
- generatePoints: function () {
13084
- var series = this,
13085
- options = series.options,
13086
- dataOptions = options.data,
13087
- data = series.data,
13088
- dataLength,
13089
- processedXData = series.processedXData,
13090
- processedYData = series.processedYData,
13091
- pointClass = series.pointClass,
13092
- processedDataLength = processedXData.length,
13093
- cropStart = series.cropStart || 0,
13094
- cursor,
13095
- hasGroupedData = series.hasGroupedData,
13096
- point,
13097
- points = [],
13098
- i;
13099
-
13100
- if (!data && !hasGroupedData) {
13101
- var arr = [];
13102
- arr.length = dataOptions.length;
13103
- data = series.data = arr;
13104
- }
13105
-
13106
- for (i = 0; i < processedDataLength; i++) {
13107
- cursor = cropStart + i;
13108
- if (!hasGroupedData) {
13109
- if (data[cursor]) {
13110
- point = data[cursor];
13111
- } else if (dataOptions[cursor] !== UNDEFINED) { // #970
13112
- data[cursor] = point = (new pointClass()).init(series, dataOptions[cursor], processedXData[i]);
13113
- }
13114
- points[i] = point;
13115
- } else {
13116
- // splat the y data in case of ohlc data array
13117
- points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
13118
- }
13119
- }
13120
-
13121
- // Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when
13122
- // swithching view from non-grouped data to grouped data (#637)
13123
- if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) {
13124
- for (i = 0; i < dataLength; i++) {
13125
- if (i === cropStart && !hasGroupedData) { // when has grouped data, clear all points
13126
- i += processedDataLength;
13127
- }
13128
- if (data[i]) {
13129
- data[i].destroyElements();
13130
- data[i].plotX = UNDEFINED; // #1003
13131
- }
13132
- }
13133
- }
13134
-
13135
- series.data = data;
13136
- series.points = points;
13137
- },
13138
-
13139
- /**
13140
- * Translate data points from raw data values to chart specific positioning data
13141
- * needed later in drawPoints, drawGraph and drawTracker.
13142
- */
13143
- translate: function () {
13144
- if (!this.processedXData) { // hidden series
13145
- this.processData();
13146
- }
13147
- this.generatePoints();
13148
- var series = this,
13149
- options = series.options,
13150
- stacking = options.stacking,
13151
- xAxis = series.xAxis,
13152
- categories = xAxis.categories,
13153
- yAxis = series.yAxis,
13154
- points = series.points,
13155
- dataLength = points.length,
13156
- hasModifyValue = !!series.modifyValue,
13157
- isBottomSeries,
13158
- allStackSeries,
13159
- i,
13160
- placeBetween = options.pointPlacement === 'between',
13161
- threshold = options.threshold;
13162
- //nextSeriesDown;
13163
-
13164
- // Is it the last visible series? (#809, #1722).
13165
- // TODO: After merging in the 'stacking' branch, this logic should probably be moved to Chart.getStacks
13166
- allStackSeries = yAxis.series.sort(function (a, b) {
13167
- return a.index - b.index;
13168
- });
13169
- i = allStackSeries.length;
13170
- while (i--) {
13171
- if (allStackSeries[i].visible) {
13172
- if (allStackSeries[i] === series) { // #809
13173
- isBottomSeries = true;
13174
- }
13175
- break;
13176
- }
13177
- }
13178
-
13179
- // Translate each point
13180
- for (i = 0; i < dataLength; i++) {
13181
- var point = points[i],
13182
- xValue = point.x,
13183
- yValue = point.y,
13184
- yBottom = point.low,
13185
- stack = yAxis.stacks[(yValue < threshold ? '-' : '') + series.stackKey],
13186
- pointStack,
13187
- pointStackTotal;
13188
-
13189
- // Discard disallowed y values for log axes
13190
- if (yAxis.isLog && yValue <= 0) {
13191
- point.y = yValue = null;
13192
- }
13193
-
13194
- // Get the plotX translation
13195
- point.plotX = xAxis.translate(xValue, 0, 0, 0, 1, placeBetween); // Math.round fixes #591
13196
-
13197
- // Calculate the bottom y value for stacked series
13198
- if (stacking && series.visible && stack && stack[xValue]) {
13199
- pointStack = stack[xValue];
13200
- pointStackTotal = pointStack.total;
13201
- pointStack.cum = yBottom = pointStack.cum - yValue; // start from top
13202
- yValue = yBottom + yValue;
13203
-
13204
- if (isBottomSeries) {
13205
- yBottom = pick(threshold, yAxis.min);
13206
- }
13207
-
13208
- if (yAxis.isLog && yBottom <= 0) { // #1200, #1232
13209
- yBottom = null;
13210
- }
13211
-
13212
- if (stacking === 'percent') {
13213
- yBottom = pointStackTotal ? yBottom * 100 / pointStackTotal : 0;
13214
- yValue = pointStackTotal ? yValue * 100 / pointStackTotal : 0;
13215
- }
13216
-
13217
- point.percentage = pointStackTotal ? point.y * 100 / pointStackTotal : 0;
13218
- point.total = point.stackTotal = pointStackTotal;
13219
- point.stackY = yValue;
13220
- }
13221
-
13222
- // Set translated yBottom or remove it
13223
- point.yBottom = defined(yBottom) ?
13224
- yAxis.translate(yBottom, 0, 1, 0, 1) :
13225
- null;
13226
-
13227
- // general hook, used for Highstock compare mode
13228
- if (hasModifyValue) {
13229
- yValue = series.modifyValue(yValue, point);
13230
- }
13231
-
13232
- // Set the the plotY value, reset it for redraws
13233
- point.plotY = (typeof yValue === 'number' && yValue !== Infinity) ?
13234
- mathRound(yAxis.translate(yValue, 0, 1, 0, 1) * 10) / 10 : // Math.round fixes #591
13235
- UNDEFINED;
13236
-
13237
- // Set client related positions for mouse tracking
13238
- point.clientX = placeBetween ? xAxis.translate(xValue, 0, 0, 0, 1) : point.plotX; // #1514
13239
-
13240
- point.negative = point.y < (threshold || 0);
13241
-
13242
- // some API data
13243
- point.category = categories && categories[point.x] !== UNDEFINED ?
13244
- categories[point.x] : point.x;
13245
-
13246
-
13247
- }
13248
-
13249
- // now that we have the cropped data, build the segments
13250
- series.getSegments();
13251
- },
13252
- /**
13253
- * Memoize tooltip texts and positions
13254
- */
13255
- setTooltipPoints: function (renew) {
13256
- var series = this,
13257
- points = [],
13258
- pointsLength,
13259
- low,
13260
- high,
13261
- xAxis = series.xAxis,
13262
- axisLength = xAxis ? (xAxis.tooltipLen || xAxis.len) : series.chart.plotSizeX, // tooltipLen and tooltipPosName used in polar
13263
- point,
13264
- i,
13265
- tooltipPoints = []; // a lookup array for each pixel in the x dimension
13266
-
13267
- // don't waste resources if tracker is disabled
13268
- if (series.options.enableMouseTracking === false) {
13269
- return;
13270
- }
13271
-
13272
- // renew
13273
- if (renew) {
13274
- series.tooltipPoints = null;
13275
- }
13276
-
13277
- // concat segments to overcome null values
13278
- each(series.segments || series.points, function (segment) {
13279
- points = points.concat(segment);
13280
- });
13281
-
13282
- // Reverse the points in case the X axis is reversed
13283
- if (xAxis && xAxis.reversed) {
13284
- points = points.reverse();
13285
- }
13286
-
13287
- // Assign each pixel position to the nearest point
13288
- pointsLength = points.length;
13289
- for (i = 0; i < pointsLength; i++) {
13290
- point = points[i];
13291
- // Set this range's low to the last range's high plus one
13292
- low = points[i - 1] ? high + 1 : 0;
13293
- // Now find the new high
13294
- high = points[i + 1] ?
13295
- mathMax(0, mathFloor((point.clientX + (points[i + 1] ? points[i + 1].clientX : axisLength)) / 2)) :
13296
- axisLength;
13297
-
13298
- while (low >= 0 && low <= high) {
13299
- tooltipPoints[low++] = point;
13300
- }
13301
- }
13302
- series.tooltipPoints = tooltipPoints;
13303
- },
13304
-
13305
- /**
13306
- * Format the header of the tooltip
13307
- */
13308
- tooltipHeaderFormatter: function (point) {
13309
- var series = this,
13310
- tooltipOptions = series.tooltipOptions,
13311
- xDateFormat = tooltipOptions.xDateFormat,
13312
- dateTimeLabelFormats = tooltipOptions.dateTimeLabelFormats,
13313
- xAxis = series.xAxis,
13314
- isDateTime = xAxis && xAxis.options.type === 'datetime',
13315
- headerFormat = tooltipOptions.headerFormat,
13316
- closestPointRange = xAxis && xAxis.closestPointRange,
13317
- n;
13318
-
13319
- // Guess the best date format based on the closest point distance (#568)
13320
- if (isDateTime && !xDateFormat) {
13321
- if (closestPointRange) {
13322
- for (n in timeUnits) {
13323
- if (timeUnits[n] >= closestPointRange) {
13324
- xDateFormat = dateTimeLabelFormats[n];
13325
- break;
13326
- }
13327
- }
13328
- } else {
13329
- xDateFormat = dateTimeLabelFormats.day;
13330
- }
13331
- }
13332
-
13333
- // Insert the header date format if any
13334
- if (isDateTime && xDateFormat && isNumber(point.key)) {
13335
- headerFormat = headerFormat.replace('{point.key}', '{point.key:' + xDateFormat + '}');
13336
- }
13337
-
13338
- return format(headerFormat, {
13339
- point: point,
13340
- series: series
13341
- });
13342
- },
13343
-
13344
- /**
13345
- * Series mouse over handler
13346
- */
13347
- onMouseOver: function () {
13348
- var series = this,
13349
- chart = series.chart,
13350
- hoverSeries = chart.hoverSeries;
13351
-
13352
- // set normal state to previous series
13353
- if (hoverSeries && hoverSeries !== series) {
13354
- hoverSeries.onMouseOut();
13355
- }
13356
-
13357
- // trigger the event, but to save processing time,
13358
- // only if defined
13359
- if (series.options.events.mouseOver) {
13360
- fireEvent(series, 'mouseOver');
13361
- }
13362
-
13363
- // hover this
13364
- series.setState(HOVER_STATE);
13365
- chart.hoverSeries = series;
13366
- },
13367
-
13368
- /**
13369
- * Series mouse out handler
13370
- */
13371
- onMouseOut: function () {
13372
- // trigger the event only if listeners exist
13373
- var series = this,
13374
- options = series.options,
13375
- chart = series.chart,
13376
- tooltip = chart.tooltip,
13377
- hoverPoint = chart.hoverPoint;
13378
-
13379
- // trigger mouse out on the point, which must be in this series
13380
- if (hoverPoint) {
13381
- hoverPoint.onMouseOut();
13382
- }
13383
-
13384
- // fire the mouse out event
13385
- if (series && options.events.mouseOut) {
13386
- fireEvent(series, 'mouseOut');
13387
- }
13388
-
13389
-
13390
- // hide the tooltip
13391
- if (tooltip && !options.stickyTracking && (!tooltip.shared || series.noSharedTooltip)) {
13392
- tooltip.hide();
13393
- }
13394
-
13395
- // set normal state
13396
- series.setState();
13397
- chart.hoverSeries = null;
13398
- },
13399
-
13400
- /**
13401
- * Animate in the series
13402
- */
13403
- animate: function (init) {
13404
- var series = this,
13405
- chart = series.chart,
13406
- renderer = chart.renderer,
13407
- clipRect,
13408
- markerClipRect,
13409
- animation = series.options.animation,
13410
- clipBox = chart.clipBox,
13411
- inverted = chart.inverted,
13412
- sharedClipKey;
13413
-
13414
- // Animation option is set to true
13415
- if (animation && !isObject(animation)) {
13416
- animation = defaultPlotOptions[series.type].animation;
13417
- }
13418
- sharedClipKey = '_sharedClip' + animation.duration + animation.easing;
13419
-
13420
- // Initialize the animation. Set up the clipping rectangle.
13421
- if (init) {
13422
-
13423
- // If a clipping rectangle with the same properties is currently present in the chart, use that.
13424
- clipRect = chart[sharedClipKey];
13425
- markerClipRect = chart[sharedClipKey + 'm'];
13426
- if (!clipRect) {
13427
- chart[sharedClipKey] = clipRect = renderer.clipRect(
13428
- extend(clipBox, { width: 0 })
13429
- );
13430
-
13431
- chart[sharedClipKey + 'm'] = markerClipRect = renderer.clipRect(
13432
- -99, // include the width of the first marker
13433
- inverted ? -chart.plotLeft : -chart.plotTop,
13434
- 99,
13435
- inverted ? chart.chartWidth : chart.chartHeight
13436
- );
13437
- }
13438
- series.group.clip(clipRect);
13439
- series.markerGroup.clip(markerClipRect);
13440
- series.sharedClipKey = sharedClipKey;
13441
-
13442
- // Run the animation
13443
- } else {
13444
- clipRect = chart[sharedClipKey];
13445
- if (clipRect) {
13446
- clipRect.animate({
13447
- width: chart.plotSizeX
13448
- }, animation);
13449
- chart[sharedClipKey + 'm'].animate({
13450
- width: chart.plotSizeX + 99
13451
- }, animation);
13452
- }
13453
-
13454
- // Delete this function to allow it only once
13455
- series.animate = null;
13456
-
13457
- // Call the afterAnimate function on animation complete (but don't overwrite the animation.complete option
13458
- // which should be available to the user).
13459
- series.animationTimeout = setTimeout(function () {
13460
- series.afterAnimate();
13461
- }, animation.duration);
13462
- }
13463
- },
13464
-
13465
- /**
13466
- * This runs after animation to land on the final plot clipping
13467
- */
13468
- afterAnimate: function () {
13469
- var chart = this.chart,
13470
- sharedClipKey = this.sharedClipKey,
13471
- group = this.group;
13472
-
13473
- if (group && this.options.clip !== false) {
13474
- group.clip(chart.clipRect);
13475
- this.markerGroup.clip(); // no clip
13476
- }
13477
-
13478
- // Remove the shared clipping rectancgle when all series are shown
13479
- setTimeout(function () {
13480
- if (sharedClipKey && chart[sharedClipKey]) {
13481
- chart[sharedClipKey] = chart[sharedClipKey].destroy();
13482
- chart[sharedClipKey + 'm'] = chart[sharedClipKey + 'm'].destroy();
13483
- }
13484
- }, 100);
13485
- },
13486
-
13487
- /**
13488
- * Draw the markers
13489
- */
13490
- drawPoints: function () {
13491
- var series = this,
13492
- pointAttr,
13493
- points = series.points,
13494
- chart = series.chart,
13495
- plotX,
13496
- plotY,
13497
- i,
13498
- point,
13499
- radius,
13500
- symbol,
13501
- isImage,
13502
- graphic,
13503
- options = series.options,
13504
- seriesMarkerOptions = options.marker,
13505
- pointMarkerOptions,
13506
- enabled,
13507
- isInside,
13508
- markerGroup = series.markerGroup;
13509
-
13510
- if (seriesMarkerOptions.enabled || series._hasPointMarkers) {
13511
-
13512
- i = points.length;
13513
- while (i--) {
13514
- point = points[i];
13515
- plotX = point.plotX;
13516
- plotY = point.plotY;
13517
- graphic = point.graphic;
13518
- pointMarkerOptions = point.marker || {};
13519
- enabled = (seriesMarkerOptions.enabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled;
13520
- isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858
13521
-
13522
- // only draw the point if y is defined
13523
- if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
13524
-
13525
- // shortcuts
13526
- pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE];
13527
- radius = pointAttr.r;
13528
- symbol = pick(pointMarkerOptions.symbol, series.symbol);
13529
- isImage = symbol.indexOf('url') === 0;
13530
-
13531
- if (graphic) { // update
13532
- graphic
13533
- .attr({ // Since the marker group isn't clipped, each individual marker must be toggled
13534
- visibility: isInside ? (hasSVG ? 'inherit' : VISIBLE) : HIDDEN
13535
- })
13536
- .animate(extend({
13537
- x: plotX - radius,
13538
- y: plotY - radius
13539
- }, graphic.symbolName ? { // don't apply to image symbols #507
13540
- width: 2 * radius,
13541
- height: 2 * radius
13542
- } : {}));
13543
- } else if (isInside && (radius > 0 || isImage)) {
13544
- point.graphic = graphic = chart.renderer.symbol(
13545
- symbol,
13546
- plotX - radius,
13547
- plotY - radius,
13548
- 2 * radius,
13549
- 2 * radius
13550
- )
13551
- .attr(pointAttr)
13552
- .add(markerGroup);
13553
- }
13554
-
13555
- } else if (graphic) {
13556
- point.graphic = graphic.destroy(); // #1269
13557
- }
13558
- }
13559
- }
13560
-
13561
- },
13562
-
13563
- /**
13564
- * Convert state properties from API naming conventions to SVG attributes
13565
- *
13566
- * @param {Object} options API options object
13567
- * @param {Object} base1 SVG attribute object to inherit from
13568
- * @param {Object} base2 Second level SVG attribute object to inherit from
13569
- */
13570
- convertAttribs: function (options, base1, base2, base3) {
13571
- var conversion = this.pointAttrToOptions,
13572
- attr,
13573
- option,
13574
- obj = {};
13575
-
13576
- options = options || {};
13577
- base1 = base1 || {};
13578
- base2 = base2 || {};
13579
- base3 = base3 || {};
13580
-
13581
- for (attr in conversion) {
13582
- option = conversion[attr];
13583
- obj[attr] = pick(options[option], base1[attr], base2[attr], base3[attr]);
13584
- }
13585
- return obj;
13586
- },
13587
-
13588
- /**
13589
- * Get the state attributes. Each series type has its own set of attributes
13590
- * that are allowed to change on a point's state change. Series wide attributes are stored for
13591
- * all series, and additionally point specific attributes are stored for all
13592
- * points with individual marker options. If such options are not defined for the point,
13593
- * a reference to the series wide attributes is stored in point.pointAttr.
13594
- */
13595
- getAttribs: function () {
13596
- var series = this,
13597
- seriesOptions = series.options,
13598
- normalOptions = defaultPlotOptions[series.type].marker ? seriesOptions.marker : seriesOptions,
13599
- stateOptions = normalOptions.states,
13600
- stateOptionsHover = stateOptions[HOVER_STATE],
13601
- pointStateOptionsHover,
13602
- seriesColor = series.color,
13603
- normalDefaults = {
13604
- stroke: seriesColor,
13605
- fill: seriesColor
13606
- },
13607
- points = series.points || [], // #927
13608
- i,
13609
- point,
13610
- seriesPointAttr = [],
13611
- pointAttr,
13612
- pointAttrToOptions = series.pointAttrToOptions,
13613
- hasPointSpecificOptions,
13614
- negativeColor = seriesOptions.negativeColor,
13615
- key;
13616
-
13617
- // series type specific modifications
13618
- if (seriesOptions.marker) { // line, spline, area, areaspline, scatter
13619
-
13620
- // if no hover radius is given, default to normal radius + 2
13621
- stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + 2;
13622
- stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + 1;
13623
-
13624
- } else { // column, bar, pie
13625
-
13626
- // if no hover color is given, brighten the normal color
13627
- stateOptionsHover.color = stateOptionsHover.color ||
13628
- Color(stateOptionsHover.color || seriesColor)
13629
- .brighten(stateOptionsHover.brightness).get();
13630
- }
13631
-
13632
- // general point attributes for the series normal state
13633
- seriesPointAttr[NORMAL_STATE] = series.convertAttribs(normalOptions, normalDefaults);
13634
-
13635
- // HOVER_STATE and SELECT_STATE states inherit from normal state except the default radius
13636
- each([HOVER_STATE, SELECT_STATE], function (state) {
13637
- seriesPointAttr[state] =
13638
- series.convertAttribs(stateOptions[state], seriesPointAttr[NORMAL_STATE]);
13639
- });
13640
-
13641
- // set it
13642
- series.pointAttr = seriesPointAttr;
13643
-
13644
-
13645
- // Generate the point-specific attribute collections if specific point
13646
- // options are given. If not, create a referance to the series wide point
13647
- // attributes
13648
- i = points.length;
13649
- while (i--) {
13650
- point = points[i];
13651
- normalOptions = (point.options && point.options.marker) || point.options;
13652
- if (normalOptions && normalOptions.enabled === false) {
13653
- normalOptions.radius = 0;
13654
- }
13655
-
13656
- if (point.negative && negativeColor) {
13657
- point.color = point.fillColor = negativeColor;
13658
- }
13659
-
13660
- hasPointSpecificOptions = seriesOptions.colorByPoint || point.color; // #868
13661
-
13662
- // check if the point has specific visual options
13663
- if (point.options) {
13664
- for (key in pointAttrToOptions) {
13665
- if (defined(normalOptions[pointAttrToOptions[key]])) {
13666
- hasPointSpecificOptions = true;
13667
- }
13668
- }
13669
- }
13670
-
13671
- // a specific marker config object is defined for the individual point:
13672
- // create it's own attribute collection
13673
- if (hasPointSpecificOptions) {
13674
- normalOptions = normalOptions || {};
13675
- pointAttr = [];
13676
- stateOptions = normalOptions.states || {}; // reassign for individual point
13677
- pointStateOptionsHover = stateOptions[HOVER_STATE] = stateOptions[HOVER_STATE] || {};
13678
-
13679
- // Handle colors for column and pies
13680
- if (!seriesOptions.marker) { // column, bar, point
13681
- // if no hover color is given, brighten the normal color
13682
- pointStateOptionsHover.color =
13683
- Color(pointStateOptionsHover.color || point.color)
13684
- .brighten(pointStateOptionsHover.brightness ||
13685
- stateOptionsHover.brightness).get();
13686
-
13687
- }
13688
-
13689
- // normal point state inherits series wide normal state
13690
- pointAttr[NORMAL_STATE] = series.convertAttribs(extend({
13691
- color: point.color // #868
13692
- }, normalOptions), seriesPointAttr[NORMAL_STATE]);
13693
-
13694
- // inherit from point normal and series hover
13695
- pointAttr[HOVER_STATE] = series.convertAttribs(
13696
- stateOptions[HOVER_STATE],
13697
- seriesPointAttr[HOVER_STATE],
13698
- pointAttr[NORMAL_STATE]
13699
- );
13700
-
13701
- // inherit from point normal and series hover
13702
- pointAttr[SELECT_STATE] = series.convertAttribs(
13703
- stateOptions[SELECT_STATE],
13704
- seriesPointAttr[SELECT_STATE],
13705
- pointAttr[NORMAL_STATE]
13706
- );
13707
-
13708
- // Force the fill to negativeColor on markers
13709
- if (point.negative && seriesOptions.marker && negativeColor) {
13710
- pointAttr[NORMAL_STATE].fill = pointAttr[HOVER_STATE].fill = pointAttr[SELECT_STATE].fill =
13711
- series.convertAttribs({ fillColor: negativeColor }).fill;
13712
- }
13713
-
13714
-
13715
- // no marker config object is created: copy a reference to the series-wide
13716
- // attribute collection
13717
- } else {
13718
- pointAttr = seriesPointAttr;
13719
- }
13720
-
13721
- point.pointAttr = pointAttr;
13722
-
13723
- }
13724
-
13725
- },
13726
- /**
13727
- * Update the series with a new set of options
13728
- */
13729
- update: function (newOptions, redraw) {
13730
- var chart = this.chart,
13731
- // must use user options when changing type because this.options is merged
13732
- // in with type specific plotOptions
13733
- oldOptions = this.userOptions,
13734
- oldType = this.type;
13735
-
13736
- // Do the merge, with some forced options
13737
- newOptions = merge(oldOptions, {
13738
- animation: false,
13739
- index: this.index,
13740
- pointStart: this.xData[0] // when updating after addPoint
13741
- }, newOptions);
13742
-
13743
- // Destroy the series and reinsert methods from the type prototype
13744
- this.remove(false);
13745
- extend(this, seriesTypes[newOptions.type || oldType].prototype);
13746
-
13747
-
13748
- this.init(chart, newOptions);
13749
- if (pick(redraw, true)) {
13750
- chart.redraw(false);
13751
- }
13752
- },
13753
-
13754
- /**
13755
- * Clear DOM objects and free up memory
13756
- */
13757
- destroy: function () {
13758
- var series = this,
13759
- chart = series.chart,
13760
- issue134 = /AppleWebKit\/533/.test(userAgent),
13761
- destroy,
13762
- i,
13763
- data = series.data || [],
13764
- point,
13765
- prop,
13766
- axis;
13767
-
13768
- // add event hook
13769
- fireEvent(series, 'destroy');
13770
-
13771
- // remove all events
13772
- removeEvent(series);
13773
-
13774
- // erase from axes
13775
- each(['xAxis', 'yAxis'], function (AXIS) {
13776
- axis = series[AXIS];
13777
- if (axis) {
13778
- erase(axis.series, series);
13779
- axis.isDirty = axis.forceRedraw = true;
13780
- }
13781
- });
13782
-
13783
- // remove legend items
13784
- if (series.legendItem) {
13785
- series.chart.legend.destroyItem(series);
13786
- }
13787
-
13788
- // destroy all points with their elements
13789
- i = data.length;
13790
- while (i--) {
13791
- point = data[i];
13792
- if (point && point.destroy) {
13793
- point.destroy();
13794
- }
13795
- }
13796
- series.points = null;
13797
-
13798
- // Clear the animation timeout if we are destroying the series during initial animation
13799
- clearTimeout(series.animationTimeout);
13800
-
13801
- // destroy all SVGElements associated to the series
13802
- each(['area', 'graph', 'dataLabelsGroup', 'group', 'markerGroup', 'tracker',
13803
- 'graphNeg', 'areaNeg', 'posClip', 'negClip'], function (prop) {
13804
- if (series[prop]) {
13805
-
13806
- // issue 134 workaround
13807
- destroy = issue134 && prop === 'group' ?
13808
- 'hide' :
13809
- 'destroy';
13810
-
13811
- series[prop][destroy]();
13812
- }
13813
- });
13814
-
13815
- // remove from hoverSeries
13816
- if (chart.hoverSeries === series) {
13817
- chart.hoverSeries = null;
13818
- }
13819
- erase(chart.series, series);
13820
-
13821
- // clear all members
13822
- for (prop in series) {
13823
- delete series[prop];
13824
- }
13825
- },
13826
-
13827
- /**
13828
- * Draw the data labels
13829
- */
13830
- drawDataLabels: function () {
13831
-
13832
- var series = this,
13833
- seriesOptions = series.options,
13834
- options = seriesOptions.dataLabels,
13835
- points = series.points,
13836
- pointOptions,
13837
- generalOptions,
13838
- str,
13839
- dataLabelsGroup;
13840
-
13841
- if (options.enabled || series._hasPointLabels) {
13842
-
13843
- // Process default alignment of data labels for columns
13844
- if (series.dlProcessOptions) {
13845
- series.dlProcessOptions(options);
13846
- }
13847
-
13848
- // Create a separate group for the data labels to avoid rotation
13849
- dataLabelsGroup = series.plotGroup(
13850
- 'dataLabelsGroup',
13851
- 'data-labels',
13852
- series.visible ? VISIBLE : HIDDEN,
13853
- options.zIndex || 6
13854
- );
13855
-
13856
- // Make the labels for each point
13857
- generalOptions = options;
13858
- each(points, function (point) {
13859
-
13860
- var enabled,
13861
- dataLabel = point.dataLabel,
13862
- labelConfig,
13863
- attr,
13864
- name,
13865
- rotation,
13866
- connector = point.connector,
13867
- isNew = true;
13868
-
13869
- // Determine if each data label is enabled
13870
- pointOptions = point.options && point.options.dataLabels;
13871
- enabled = generalOptions.enabled || (pointOptions && pointOptions.enabled);
13872
-
13873
-
13874
- // If the point is outside the plot area, destroy it. #678, #820
13875
- if (dataLabel && !enabled) {
13876
- point.dataLabel = dataLabel.destroy();
13877
-
13878
- // Individual labels are disabled if the are explicitly disabled
13879
- // in the point options, or if they fall outside the plot area.
13880
- } else if (enabled) {
13881
-
13882
- rotation = options.rotation;
13883
-
13884
- // Create individual options structure that can be extended without
13885
- // affecting others
13886
- options = merge(generalOptions, pointOptions);
13887
-
13888
- // Get the string
13889
- labelConfig = point.getLabelConfig();
13890
- str = options.format ?
13891
- format(options.format, labelConfig) :
13892
- options.formatter.call(labelConfig, options);
13893
-
13894
- // Determine the color
13895
- options.style.color = pick(options.color, options.style.color, series.color, 'black');
13896
-
13897
-
13898
- // update existing label
13899
- if (dataLabel) {
13900
-
13901
- if (defined(str)) {
13902
- dataLabel
13903
- .attr({
13904
- text: str
13905
- });
13906
- isNew = false;
13907
-
13908
- } else { // #1437 - the label is shown conditionally
13909
- point.dataLabel = dataLabel = dataLabel.destroy();
13910
- if (connector) {
13911
- point.connector = connector.destroy();
13912
- }
13913
- }
13914
-
13915
- // create new label
13916
- } else if (defined(str)) {
13917
- attr = {
13918
- //align: align,
13919
- fill: options.backgroundColor,
13920
- stroke: options.borderColor,
13921
- 'stroke-width': options.borderWidth,
13922
- r: options.borderRadius || 0,
13923
- rotation: rotation,
13924
- padding: options.padding,
13925
- zIndex: 1
13926
- };
13927
- // Remove unused attributes (#947)
13928
- for (name in attr) {
13929
- if (attr[name] === UNDEFINED) {
13930
- delete attr[name];
13931
- }
13932
- }
13933
-
13934
- dataLabel = point.dataLabel = series.chart.renderer[rotation ? 'text' : 'label']( // labels don't support rotation
13935
- str,
13936
- 0,
13937
- -999,
13938
- null,
13939
- null,
13940
- null,
13941
- options.useHTML
13942
- )
13943
- .attr(attr)
13944
- .css(options.style)
13945
- .add(dataLabelsGroup)
13946
- .shadow(options.shadow);
13947
-
13948
- }
13949
-
13950
- if (dataLabel) {
13951
- // Now the data label is created and placed at 0,0, so we need to align it
13952
- series.alignDataLabel(point, dataLabel, options, null, isNew);
13953
- }
13954
- }
13955
- });
13956
- }
13957
- },
13958
-
13959
- /**
13960
- * Align each individual data label
13961
- */
13962
- alignDataLabel: function (point, dataLabel, options, alignTo, isNew) {
13963
- var chart = this.chart,
13964
- inverted = chart.inverted,
13965
- plotX = pick(point.plotX, -999),
13966
- plotY = pick(point.plotY, -999),
13967
- bBox = dataLabel.getBBox(),
13968
- alignAttr; // the final position;
13969
-
13970
- // The alignment box is a singular point
13971
- alignTo = extend({
13972
- x: inverted ? chart.plotWidth - plotY : plotX,
13973
- y: mathRound(inverted ? chart.plotHeight - plotX : plotY),
13974
- width: 0,
13975
- height: 0
13976
- }, alignTo);
13977
-
13978
- // Add the text size for alignment calculation
13979
- extend(options, {
13980
- width: bBox.width,
13981
- height: bBox.height
13982
- });
13983
-
13984
- // Allow a hook for changing alignment in the last moment, then do the alignment
13985
- if (options.rotation) { // Fancy box alignment isn't supported for rotated text
13986
- alignAttr = {
13987
- align: options.align,
13988
- x: alignTo.x + options.x + alignTo.width / 2,
13989
- y: alignTo.y + options.y + alignTo.height / 2
13990
- };
13991
- dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
13992
- } else {
13993
- dataLabel.align(options, null, alignTo);
13994
- alignAttr = dataLabel.alignAttr;
13995
- }
13996
-
13997
- // Show or hide based on the final aligned position
13998
- dataLabel.attr({
13999
- visibility: options.crop === false || /*chart.isInsidePlot(alignAttr.x, alignAttr.y) || */chart.isInsidePlot(plotX, plotY, inverted) ?
14000
- (chart.renderer.isSVG ? 'inherit' : VISIBLE) :
14001
- HIDDEN
14002
- });
14003
-
14004
- },
14005
-
14006
- /**
14007
- * Return the graph path of a segment
14008
- */
14009
- getSegmentPath: function (segment) {
14010
- var series = this,
14011
- segmentPath = [],
14012
- step = series.options.step;
14013
-
14014
- // build the segment line
14015
- each(segment, function (point, i) {
14016
-
14017
- var plotX = point.plotX,
14018
- plotY = point.plotY,
14019
- lastPoint;
14020
-
14021
- if (series.getPointSpline) { // generate the spline as defined in the SplineSeries object
14022
- segmentPath.push.apply(segmentPath, series.getPointSpline(segment, point, i));
14023
-
14024
- } else {
14025
-
14026
- // moveTo or lineTo
14027
- segmentPath.push(i ? L : M);
14028
-
14029
- // step line?
14030
- if (step && i) {
14031
- lastPoint = segment[i - 1];
14032
- if (step === 'right') {
14033
- segmentPath.push(
14034
- lastPoint.plotX,
14035
- plotY
14036
- );
14037
-
14038
- } else if (step === 'center') {
14039
- segmentPath.push(
14040
- (lastPoint.plotX + plotX) / 2,
14041
- lastPoint.plotY,
14042
- (lastPoint.plotX + plotX) / 2,
14043
- plotY
14044
- );
14045
-
14046
- } else {
14047
- segmentPath.push(
14048
- plotX,
14049
- lastPoint.plotY
14050
- );
14051
- }
14052
- }
14053
-
14054
- // normal line to next point
14055
- segmentPath.push(
14056
- point.plotX,
14057
- point.plotY
14058
- );
14059
- }
14060
- });
14061
-
14062
- return segmentPath;
14063
- },
14064
-
14065
- /**
14066
- * Get the graph path
14067
- */
14068
- getGraphPath: function () {
14069
- var series = this,
14070
- graphPath = [],
14071
- segmentPath,
14072
- singlePoints = []; // used in drawTracker
14073
-
14074
- // Divide into segments and build graph and area paths
14075
- each(series.segments, function (segment) {
14076
-
14077
- segmentPath = series.getSegmentPath(segment);
14078
-
14079
- // add the segment to the graph, or a single point for tracking
14080
- if (segment.length > 1) {
14081
- graphPath = graphPath.concat(segmentPath);
14082
- } else {
14083
- singlePoints.push(segment[0]);
14084
- }
14085
- });
14086
-
14087
- // Record it for use in drawGraph and drawTracker, and return graphPath
14088
- series.singlePoints = singlePoints;
14089
- series.graphPath = graphPath;
14090
-
14091
- return graphPath;
14092
-
14093
- },
14094
-
14095
- /**
14096
- * Draw the actual graph
14097
- */
14098
- drawGraph: function () {
14099
- var series = this,
14100
- options = this.options,
14101
- props = [['graph', options.lineColor || this.color]],
14102
- lineWidth = options.lineWidth,
14103
- dashStyle = options.dashStyle,
14104
- graphPath = this.getGraphPath(),
14105
- negativeColor = options.negativeColor;
14106
-
14107
- if (negativeColor) {
14108
- props.push(['graphNeg', negativeColor]);
14109
- }
14110
-
14111
- // draw the graph
14112
- each(props, function (prop, i) {
14113
- var graphKey = prop[0],
14114
- graph = series[graphKey],
14115
- attribs;
14116
-
14117
- if (graph) {
14118
- stop(graph); // cancel running animations, #459
14119
- graph.animate({ d: graphPath });
14120
-
14121
- } else if (lineWidth && graphPath.length) { // #1487
14122
- attribs = {
14123
- stroke: prop[1],
14124
- 'stroke-width': lineWidth,
14125
- zIndex: 1 // #1069
14126
- };
14127
- if (dashStyle) {
14128
- attribs.dashstyle = dashStyle;
14129
- }
14130
-
14131
- series[graphKey] = series.chart.renderer.path(graphPath)
14132
- .attr(attribs)
14133
- .add(series.group)
14134
- .shadow(!i && options.shadow);
14135
- }
14136
- });
14137
- },
14138
-
14139
- /**
14140
- * Clip the graphs into the positive and negative coloured graphs
14141
- */
14142
- clipNeg: function () {
14143
- var options = this.options,
14144
- chart = this.chart,
14145
- renderer = chart.renderer,
14146
- negativeColor = options.negativeColor,
14147
- translatedThreshold,
14148
- posAttr,
14149
- negAttr,
14150
- graph = this.graph,
14151
- area = this.area,
14152
- posClip = this.posClip,
14153
- negClip = this.negClip,
14154
- chartWidth = chart.chartWidth,
14155
- chartHeight = chart.chartHeight,
14156
- chartSizeMax = mathMax(chartWidth, chartHeight),
14157
- above,
14158
- below;
14159
-
14160
- if (negativeColor && (graph || area)) {
14161
- translatedThreshold = mathCeil(this.yAxis.len - this.yAxis.translate(options.threshold || 0));
14162
- above = {
14163
- x: 0,
14164
- y: 0,
14165
- width: chartSizeMax,
14166
- height: translatedThreshold
14167
- };
14168
- below = {
14169
- x: 0,
14170
- y: translatedThreshold,
14171
- width: chartSizeMax,
14172
- height: chartSizeMax - translatedThreshold
14173
- };
14174
-
14175
- if (chart.inverted && renderer.isVML) {
14176
- above = {
14177
- x: chart.plotWidth - translatedThreshold - chart.plotLeft,
14178
- y: 0,
14179
- width: chartWidth,
14180
- height: chartHeight
14181
- };
14182
- below = {
14183
- x: translatedThreshold + chart.plotLeft - chartWidth,
14184
- y: 0,
14185
- width: chart.plotLeft + translatedThreshold,
14186
- height: chartWidth
14187
- };
14188
- }
14189
-
14190
- if (this.yAxis.reversed) {
14191
- posAttr = below;
14192
- negAttr = above;
14193
- } else {
14194
- posAttr = above;
14195
- negAttr = below;
14196
- }
14197
-
14198
- if (posClip) { // update
14199
- posClip.animate(posAttr);
14200
- negClip.animate(negAttr);
14201
- } else {
14202
-
14203
- this.posClip = posClip = renderer.clipRect(posAttr);
14204
- this.negClip = negClip = renderer.clipRect(negAttr);
14205
-
14206
- if (graph) {
14207
- graph.clip(posClip);
14208
- this.graphNeg.clip(negClip);
14209
- }
14210
-
14211
- if (area) {
14212
- area.clip(posClip);
14213
- this.areaNeg.clip(negClip);
14214
- }
14215
- }
14216
- }
14217
- },
14218
-
14219
- /**
14220
- * Initialize and perform group inversion on series.group and series.markerGroup
14221
- */
14222
- invertGroups: function () {
14223
- var series = this,
14224
- chart = series.chart;
14225
-
14226
- // Pie, go away (#1736)
14227
- if (!series.xAxis) {
14228
- return;
14229
- }
14230
-
14231
- // A fixed size is needed for inversion to work
14232
- function setInvert() {
14233
- var size = {
14234
- width: series.yAxis.len,
14235
- height: series.xAxis.len
14236
- };
14237
-
14238
- each(['group', 'markerGroup'], function (groupName) {
14239
- if (series[groupName]) {
14240
- series[groupName].attr(size).invert();
14241
- }
14242
- });
14243
- }
14244
-
14245
- addEvent(chart, 'resize', setInvert); // do it on resize
14246
- addEvent(series, 'destroy', function () {
14247
- removeEvent(chart, 'resize', setInvert);
14248
- });
14249
-
14250
- // Do it now
14251
- setInvert(); // do it now
14252
-
14253
- // On subsequent render and redraw, just do setInvert without setting up events again
14254
- series.invertGroups = setInvert;
14255
- },
14256
-
14257
- /**
14258
- * General abstraction for creating plot groups like series.group, series.dataLabelsGroup and
14259
- * series.markerGroup. On subsequent calls, the group will only be adjusted to the updated plot size.
14260
- */
14261
- plotGroup: function (prop, name, visibility, zIndex, parent) {
14262
- var group = this[prop],
14263
- isNew = !group,
14264
- chart = this.chart,
14265
- xAxis = this.xAxis,
14266
- yAxis = this.yAxis;
14267
-
14268
- // Generate it on first call
14269
- if (isNew) {
14270
- this[prop] = group = chart.renderer.g(name)
14271
- .attr({
14272
- visibility: visibility,
14273
- zIndex: zIndex || 0.1 // IE8 needs this
14274
- })
14275
- .add(parent);
14276
- }
14277
- // Place it on first and subsequent (redraw) calls
14278
- group[isNew ? 'attr' : 'animate']({
14279
- translateX: xAxis ? xAxis.left : chart.plotLeft,
14280
- translateY: yAxis ? yAxis.top : chart.plotTop,
14281
- scaleX: 1, // #1623
14282
- scaleY: 1
14283
- });
14284
- return group;
14285
-
14286
- },
14287
-
14288
- /**
14289
- * Render the graph and markers
14290
- */
14291
- render: function () {
14292
- var series = this,
14293
- chart = series.chart,
14294
- group,
14295
- options = series.options,
14296
- animation = options.animation,
14297
- doAnimation = animation && !!series.animate &&
14298
- chart.renderer.isSVG, // this animation doesn't work in IE8 quirks when the group div is hidden,
14299
- // and looks bad in other oldIE
14300
- visibility = series.visible ? VISIBLE : HIDDEN,
14301
- zIndex = options.zIndex,
14302
- hasRendered = series.hasRendered,
14303
- chartSeriesGroup = chart.seriesGroup;
14304
-
14305
- // the group
14306
- group = series.plotGroup(
14307
- 'group',
14308
- 'series',
14309
- visibility,
14310
- zIndex,
14311
- chartSeriesGroup
14312
- );
14313
-
14314
- series.markerGroup = series.plotGroup(
14315
- 'markerGroup',
14316
- 'markers',
14317
- visibility,
14318
- zIndex,
14319
- chartSeriesGroup
14320
- );
14321
-
14322
- // initiate the animation
14323
- if (doAnimation) {
14324
- series.animate(true);
14325
- }
14326
-
14327
- // cache attributes for shapes
14328
- series.getAttribs();
14329
-
14330
- // SVGRenderer needs to know this before drawing elements (#1089, #1795)
14331
- group.inverted = series.isCartesian ? chart.inverted : false;
14332
-
14333
- // draw the graph if any
14334
- if (series.drawGraph) {
14335
- series.drawGraph();
14336
- series.clipNeg();
14337
- }
14338
-
14339
- // draw the data labels (inn pies they go before the points)
14340
- series.drawDataLabels();
14341
-
14342
- // draw the points
14343
- series.drawPoints();
14344
-
14345
-
14346
- // draw the mouse tracking area
14347
- if (series.options.enableMouseTracking !== false) {
14348
- series.drawTracker();
14349
- }
14350
-
14351
- // Handle inverted series and tracker groups
14352
- if (chart.inverted) {
14353
- series.invertGroups();
14354
- }
14355
-
14356
- // Initial clipping, must be defined after inverting groups for VML
14357
- if (options.clip !== false && !series.sharedClipKey && !hasRendered) {
14358
- group.clip(chart.clipRect);
14359
- }
14360
-
14361
- // Run the animation
14362
- if (doAnimation) {
14363
- series.animate();
14364
- } else if (!hasRendered) {
14365
- series.afterAnimate();
14366
- }
14367
-
14368
- series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
14369
- // (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
14370
- series.hasRendered = true;
14371
- },
14372
-
14373
- /**
14374
- * Redraw the series after an update in the axes.
14375
- */
14376
- redraw: function () {
14377
- var series = this,
14378
- chart = series.chart,
14379
- wasDirtyData = series.isDirtyData, // cache it here as it is set to false in render, but used after
14380
- group = series.group,
14381
- xAxis = series.xAxis,
14382
- yAxis = series.yAxis;
14383
-
14384
- // reposition on resize
14385
- if (group) {
14386
- if (chart.inverted) {
14387
- group.attr({
14388
- width: chart.plotWidth,
14389
- height: chart.plotHeight
14390
- });
14391
- }
14392
-
14393
- group.animate({
14394
- translateX: pick(xAxis && xAxis.left, chart.plotLeft),
14395
- translateY: pick(yAxis && yAxis.top, chart.plotTop)
14396
- });
14397
- }
14398
-
14399
- series.translate();
14400
- series.setTooltipPoints(true);
14401
-
14402
- series.render();
14403
- if (wasDirtyData) {
14404
- fireEvent(series, 'updatedData');
14405
- }
14406
- },
14407
-
14408
- /**
14409
- * Set the state of the graph
14410
- */
14411
- setState: function (state) {
14412
- var series = this,
14413
- options = series.options,
14414
- graph = series.graph,
14415
- graphNeg = series.graphNeg,
14416
- stateOptions = options.states,
14417
- lineWidth = options.lineWidth,
14418
- attribs;
14419
-
14420
- state = state || NORMAL_STATE;
14421
-
14422
- if (series.state !== state) {
14423
- series.state = state;
14424
-
14425
- if (stateOptions[state] && stateOptions[state].enabled === false) {
14426
- return;
14427
- }
14428
-
14429
- if (state) {
14430
- lineWidth = stateOptions[state].lineWidth || lineWidth + 1;
14431
- }
14432
-
14433
- if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML
14434
- attribs = {
14435
- 'stroke-width': lineWidth
14436
- };
14437
- // use attr because animate will cause any other animation on the graph to stop
14438
- graph.attr(attribs);
14439
- if (graphNeg) {
14440
- graphNeg.attr(attribs);
14441
- }
14442
- }
14443
- }
14444
- },
14445
-
14446
- /**
14447
- * Set the visibility of the graph
14448
- *
14449
- * @param vis {Boolean} True to show the series, false to hide. If UNDEFINED,
14450
- * the visibility is toggled.
14451
- */
14452
- setVisible: function (vis, redraw) {
14453
- var series = this,
14454
- chart = series.chart,
14455
- legendItem = series.legendItem,
14456
- showOrHide,
14457
- ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
14458
- oldVisibility = series.visible;
14459
-
14460
- // if called without an argument, toggle visibility
14461
- series.visible = vis = series.userOptions.visible = vis === UNDEFINED ? !oldVisibility : vis;
14462
- showOrHide = vis ? 'show' : 'hide';
14463
-
14464
- // show or hide elements
14465
- each(['group', 'dataLabelsGroup', 'markerGroup', 'tracker'], function (key) {
14466
- if (series[key]) {
14467
- series[key][showOrHide]();
14468
- }
14469
- });
14470
-
14471
-
14472
- // hide tooltip (#1361)
14473
- if (chart.hoverSeries === series) {
14474
- series.onMouseOut();
14475
- }
14476
-
14477
-
14478
- if (legendItem) {
14479
- chart.legend.colorizeItem(series, vis);
14480
- }
14481
-
14482
-
14483
- // rescale or adapt to resized chart
14484
- series.isDirty = true;
14485
- // in a stack, all other series are affected
14486
- if (series.options.stacking) {
14487
- each(chart.series, function (otherSeries) {
14488
- if (otherSeries.options.stacking && otherSeries.visible) {
14489
- otherSeries.isDirty = true;
14490
- }
14491
- });
14492
- }
14493
-
14494
- // show or hide linked series
14495
- each(series.linkedSeries, function (otherSeries) {
14496
- otherSeries.setVisible(vis, false);
14497
- });
14498
-
14499
- if (ignoreHiddenSeries) {
14500
- chart.isDirtyBox = true;
14501
- }
14502
- if (redraw !== false) {
14503
- chart.redraw();
14504
- }
14505
-
14506
- fireEvent(series, showOrHide);
14507
- },
14508
-
14509
- /**
14510
- * Show the graph
14511
- */
14512
- show: function () {
14513
- this.setVisible(true);
14514
- },
14515
-
14516
- /**
14517
- * Hide the graph
14518
- */
14519
- hide: function () {
14520
- this.setVisible(false);
14521
- },
14522
-
14523
-
14524
- /**
14525
- * Set the selected state of the graph
14526
- *
14527
- * @param selected {Boolean} True to select the series, false to unselect. If
14528
- * UNDEFINED, the selection state is toggled.
14529
- */
14530
- select: function (selected) {
14531
- var series = this;
14532
- // if called without an argument, toggle
14533
- series.selected = selected = (selected === UNDEFINED) ? !series.selected : selected;
14534
-
14535
- if (series.checkbox) {
14536
- series.checkbox.checked = selected;
14537
- }
14538
-
14539
- fireEvent(series, selected ? 'select' : 'unselect');
14540
- },
14541
-
14542
- /**
14543
- * Draw the tracker object that sits above all data labels and markers to
14544
- * track mouse events on the graph or points. For the line type charts
14545
- * the tracker uses the same graphPath, but with a greater stroke width
14546
- * for better control.
14547
- */
14548
- drawTracker: function () {
14549
- var series = this,
14550
- options = series.options,
14551
- trackByArea = options.trackByArea,
14552
- trackerPath = [].concat(trackByArea ? series.areaPath : series.graphPath),
14553
- trackerPathLength = trackerPath.length,
14554
- chart = series.chart,
14555
- pointer = chart.pointer,
14556
- renderer = chart.renderer,
14557
- snap = chart.options.tooltip.snap,
14558
- tracker = series.tracker,
14559
- cursor = options.cursor,
14560
- css = cursor && { cursor: cursor },
14561
- singlePoints = series.singlePoints,
14562
- singlePoint,
14563
- i,
14564
- onMouseOver = function () {
14565
- if (chart.hoverSeries !== series) {
14566
- series.onMouseOver();
14567
- }
14568
- };
14569
-
14570
- // Extend end points. A better way would be to use round linecaps,
14571
- // but those are not clickable in VML.
14572
- if (trackerPathLength && !trackByArea) {
14573
- i = trackerPathLength + 1;
14574
- while (i--) {
14575
- if (trackerPath[i] === M) { // extend left side
14576
- trackerPath.splice(i + 1, 0, trackerPath[i + 1] - snap, trackerPath[i + 2], L);
14577
- }
14578
- if ((i && trackerPath[i] === M) || i === trackerPathLength) { // extend right side
14579
- trackerPath.splice(i, 0, L, trackerPath[i - 2] + snap, trackerPath[i - 1]);
14580
- }
14581
- }
14582
- }
14583
-
14584
- // handle single points
14585
- for (i = 0; i < singlePoints.length; i++) {
14586
- singlePoint = singlePoints[i];
14587
- trackerPath.push(M, singlePoint.plotX - snap, singlePoint.plotY,
14588
- L, singlePoint.plotX + snap, singlePoint.plotY);
14589
- }
14590
-
14591
-
14592
-
14593
- // draw the tracker
14594
- if (tracker) {
14595
- tracker.attr({ d: trackerPath });
14596
-
14597
- } else { // create
14598
-
14599
- series.tracker = tracker = renderer.path(trackerPath)
14600
- .attr({
14601
- 'class': PREFIX + 'tracker',
14602
- 'stroke-linejoin': 'round', // #1225
14603
- visibility: series.visible ? VISIBLE : HIDDEN,
14604
- stroke: TRACKER_FILL,
14605
- fill: trackByArea ? TRACKER_FILL : NONE,
14606
- 'stroke-width' : options.lineWidth + (trackByArea ? 0 : 2 * snap),
14607
- zIndex: 2
14608
- })
14609
- .addClass(PREFIX + 'tracker')
14610
- .on('mouseover', onMouseOver)
14611
- .on('mouseout', function (e) { pointer.onTrackerMouseOut(e); })
14612
- .css(css)
14613
- .add(series.markerGroup);
14614
-
14615
- if (hasTouch) {
14616
- tracker.on('touchstart', onMouseOver);
14617
- }
14618
- }
14619
-
14620
- }
14621
-
14622
- }; // end Series prototype
14623
-
14624
-
14625
- /**
14626
- * LineSeries object
14627
- */
14628
- var LineSeries = extendClass(Series);
14629
- seriesTypes.line = LineSeries;
14630
-
14631
- /**
14632
- * Set the default options for area
14633
- */
14634
- defaultPlotOptions.area = merge(defaultSeriesOptions, {
14635
- threshold: 0
14636
- // trackByArea: false,
14637
- // lineColor: null, // overrides color, but lets fillColor be unaltered
14638
- // fillOpacity: 0.75,
14639
- // fillColor: null
14640
- });
14641
-
14642
- /**
14643
- * AreaSeries object
14644
- */
14645
- var AreaSeries = extendClass(Series, {
14646
- type: 'area',
14647
-
14648
- /**
14649
- * For stacks, don't split segments on null values. Instead, draw null values with
14650
- * no marker. Also insert dummy points for any X position that exists in other series
14651
- * in the stack.
14652
- */
14653
- getSegments: function () {
14654
- var segments = [],
14655
- segment = [],
14656
- keys = [],
14657
- xAxis = this.xAxis,
14658
- yAxis = this.yAxis,
14659
- stack = yAxis.stacks[this.stackKey],
14660
- pointMap = {},
14661
- plotX,
14662
- plotY,
14663
- points = this.points,
14664
- i,
14665
- x;
14666
-
14667
- if (this.options.stacking && !this.cropped) { // cropped causes artefacts in Stock, and perf issue
14668
- // Create a map where we can quickly look up the points by their X value.
14669
- for (i = 0; i < points.length; i++) {
14670
- pointMap[points[i].x] = points[i];
14671
- }
14672
-
14673
- // Sort the keys (#1651)
14674
- for (x in stack) {
14675
- keys.push(+x);
14676
- }
14677
- keys.sort(function (a, b) {
14678
- return a - b;
14679
- });
14680
-
14681
- each(keys, function (x) {
14682
- // The point exists, push it to the segment
14683
- if (pointMap[x]) {
14684
- segment.push(pointMap[x]);
14685
-
14686
- // There is no point for this X value in this series, so we
14687
- // insert a dummy point in order for the areas to be drawn
14688
- // correctly.
14689
- } else {
14690
- plotX = xAxis.translate(x);
14691
- plotY = yAxis.toPixels(stack[x].cum, true);
14692
- segment.push({
14693
- y: null,
14694
- plotX: plotX,
14695
- clientX: plotX,
14696
- plotY: plotY,
14697
- yBottom: plotY,
14698
- onMouseOver: noop
14699
- });
14700
- }
14701
- });
14702
-
14703
- if (segment.length) {
14704
- segments.push(segment);
14705
- }
14706
-
14707
- } else {
14708
- Series.prototype.getSegments.call(this);
14709
- segments = this.segments;
14710
- }
14711
-
14712
- this.segments = segments;
14713
- },
14714
-
14715
- /**
14716
- * Extend the base Series getSegmentPath method by adding the path for the area.
14717
- * This path is pushed to the series.areaPath property.
14718
- */
14719
- getSegmentPath: function (segment) {
14720
-
14721
- var segmentPath = Series.prototype.getSegmentPath.call(this, segment), // call base method
14722
- areaSegmentPath = [].concat(segmentPath), // work on a copy for the area path
14723
- i,
14724
- options = this.options,
14725
- segLength = segmentPath.length;
14726
-
14727
- if (segLength === 3) { // for animation from 1 to two points
14728
- areaSegmentPath.push(L, segmentPath[1], segmentPath[2]);
14729
- }
14730
- if (options.stacking && !this.closedStacks) {
14731
-
14732
- // Follow stack back. Todo: implement areaspline. A general solution could be to
14733
- // reverse the entire graphPath of the previous series, though may be hard with
14734
- // splines and with series with different extremes
14735
- for (i = segment.length - 1; i >= 0; i--) {
14736
-
14737
- // step line?
14738
- if (i < segment.length - 1 && options.step) {
14739
- areaSegmentPath.push(segment[i + 1].plotX, segment[i].yBottom);
14740
- }
14741
-
14742
- areaSegmentPath.push(segment[i].plotX, segment[i].yBottom);
14743
- }
14744
-
14745
- } else { // follow zero line back
14746
- this.closeSegment(areaSegmentPath, segment);
14747
- }
14748
- this.areaPath = this.areaPath.concat(areaSegmentPath);
14749
-
14750
- return segmentPath;
14751
- },
14752
-
14753
- /**
14754
- * Extendable method to close the segment path of an area. This is overridden in polar
14755
- * charts.
14756
- */
14757
- closeSegment: function (path, segment) {
14758
- var translatedThreshold = this.yAxis.getThreshold(this.options.threshold);
14759
- path.push(
14760
- L,
14761
- segment[segment.length - 1].plotX,
14762
- translatedThreshold,
14763
- L,
14764
- segment[0].plotX,
14765
- translatedThreshold
14766
- );
14767
- },
14768
-
14769
- /**
14770
- * Draw the graph and the underlying area. This method calls the Series base
14771
- * function and adds the area. The areaPath is calculated in the getSegmentPath
14772
- * method called from Series.prototype.drawGraph.
14773
- */
14774
- drawGraph: function () {
14775
-
14776
- // Define or reset areaPath
14777
- this.areaPath = [];
14778
-
14779
- // Call the base method
14780
- Series.prototype.drawGraph.apply(this);
14781
-
14782
- // Define local variables
14783
- var series = this,
14784
- areaPath = this.areaPath,
14785
- options = this.options,
14786
- negativeColor = options.negativeColor,
14787
- props = [['area', this.color, options.fillColor]]; // area name, main color, fill color
14788
-
14789
- if (negativeColor) {
14790
- props.push(['areaNeg', options.negativeColor, options.negativeFillColor]);
14791
- }
14792
-
14793
- each(props, function (prop) {
14794
- var areaKey = prop[0],
14795
- area = series[areaKey];
14796
-
14797
- // Create or update the area
14798
- if (area) { // update
14799
- area.animate({ d: areaPath });
14800
-
14801
- } else { // create
14802
- series[areaKey] = series.chart.renderer.path(areaPath)
14803
- .attr({
14804
- fill: pick(
14805
- prop[2],
14806
- Color(prop[1]).setOpacity(options.fillOpacity || 0.75).get()
14807
- ),
14808
- zIndex: 0 // #1069
14809
- }).add(series.group);
14810
- }
14811
- });
14812
- },
14813
-
14814
- /**
14815
- * Get the series' symbol in the legend
14816
- *
14817
- * @param {Object} legend The legend object
14818
- * @param {Object} item The series (this) or point
14819
- */
14820
- drawLegendSymbol: function (legend, item) {
14821
-
14822
- item.legendSymbol = this.chart.renderer.rect(
14823
- 0,
14824
- legend.baseline - 11,
14825
- legend.options.symbolWidth,
14826
- 12,
14827
- 2
14828
- ).attr({
14829
- zIndex: 3
14830
- }).add(item.legendGroup);
14831
-
14832
- }
14833
- });
14834
-
14835
- seriesTypes.area = AreaSeries;/**
14836
- * Set the default options for spline
14837
- */
14838
- defaultPlotOptions.spline = merge(defaultSeriesOptions);
14839
-
14840
- /**
14841
- * SplineSeries object
14842
- */
14843
- var SplineSeries = extendClass(Series, {
14844
- type: 'spline',
14845
-
14846
- /**
14847
- * Get the spline segment from a given point's previous neighbour to the given point
14848
- */
14849
- getPointSpline: function (segment, point, i) {
14850
- var smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc
14851
- denom = smoothing + 1,
14852
- plotX = point.plotX,
14853
- plotY = point.plotY,
14854
- lastPoint = segment[i - 1],
14855
- nextPoint = segment[i + 1],
14856
- leftContX,
14857
- leftContY,
14858
- rightContX,
14859
- rightContY,
14860
- ret;
14861
-
14862
- // find control points
14863
- if (lastPoint && nextPoint) {
14864
-
14865
- var lastX = lastPoint.plotX,
14866
- lastY = lastPoint.plotY,
14867
- nextX = nextPoint.plotX,
14868
- nextY = nextPoint.plotY,
14869
- correction;
14870
-
14871
- leftContX = (smoothing * plotX + lastX) / denom;
14872
- leftContY = (smoothing * plotY + lastY) / denom;
14873
- rightContX = (smoothing * plotX + nextX) / denom;
14874
- rightContY = (smoothing * plotY + nextY) / denom;
14875
-
14876
- // have the two control points make a straight line through main point
14877
- correction = ((rightContY - leftContY) * (rightContX - plotX)) /
14878
- (rightContX - leftContX) + plotY - rightContY;
14879
-
14880
- leftContY += correction;
14881
- rightContY += correction;
14882
-
14883
- // to prevent false extremes, check that control points are between
14884
- // neighbouring points' y values
14885
- if (leftContY > lastY && leftContY > plotY) {
14886
- leftContY = mathMax(lastY, plotY);
14887
- rightContY = 2 * plotY - leftContY; // mirror of left control point
14888
- } else if (leftContY < lastY && leftContY < plotY) {
14889
- leftContY = mathMin(lastY, plotY);
14890
- rightContY = 2 * plotY - leftContY;
14891
- }
14892
- if (rightContY > nextY && rightContY > plotY) {
14893
- rightContY = mathMax(nextY, plotY);
14894
- leftContY = 2 * plotY - rightContY;
14895
- } else if (rightContY < nextY && rightContY < plotY) {
14896
- rightContY = mathMin(nextY, plotY);
14897
- leftContY = 2 * plotY - rightContY;
14898
- }
14899
-
14900
- // record for drawing in next point
14901
- point.rightContX = rightContX;
14902
- point.rightContY = rightContY;
14903
-
14904
- }
14905
-
14906
- // Visualize control points for debugging
14907
- /*
14908
- if (leftContX) {
14909
- this.chart.renderer.circle(leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop, 2)
14910
- .attr({
14911
- stroke: 'red',
14912
- 'stroke-width': 1,
14913
- fill: 'none'
14914
- })
14915
- .add();
14916
- this.chart.renderer.path(['M', leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop,
14917
- 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
14918
- .attr({
14919
- stroke: 'red',
14920
- 'stroke-width': 1
14921
- })
14922
- .add();
14923
- this.chart.renderer.circle(rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop, 2)
14924
- .attr({
14925
- stroke: 'green',
14926
- 'stroke-width': 1,
14927
- fill: 'none'
14928
- })
14929
- .add();
14930
- this.chart.renderer.path(['M', rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop,
14931
- 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
14932
- .attr({
14933
- stroke: 'green',
14934
- 'stroke-width': 1
14935
- })
14936
- .add();
14937
- }
14938
- */
14939
-
14940
- // moveTo or lineTo
14941
- if (!i) {
14942
- ret = [M, plotX, plotY];
14943
- } else { // curve from last point to this
14944
- ret = [
14945
- 'C',
14946
- lastPoint.rightContX || lastPoint.plotX,
14947
- lastPoint.rightContY || lastPoint.plotY,
14948
- leftContX || plotX,
14949
- leftContY || plotY,
14950
- plotX,
14951
- plotY
14952
- ];
14953
- lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
14954
- }
14955
- return ret;
14956
- }
14957
- });
14958
- seriesTypes.spline = SplineSeries;
14959
-
14960
- /**
14961
- * Set the default options for areaspline
14962
- */
14963
- defaultPlotOptions.areaspline = merge(defaultPlotOptions.area);
14964
-
14965
- /**
14966
- * AreaSplineSeries object
14967
- */
14968
- var areaProto = AreaSeries.prototype,
14969
- AreaSplineSeries = extendClass(SplineSeries, {
14970
- type: 'areaspline',
14971
- closedStacks: true, // instead of following the previous graph back, follow the threshold back
14972
-
14973
- // Mix in methods from the area series
14974
- getSegmentPath: areaProto.getSegmentPath,
14975
- closeSegment: areaProto.closeSegment,
14976
- drawGraph: areaProto.drawGraph
14977
- });
14978
- seriesTypes.areaspline = AreaSplineSeries;
14979
-
14980
- /**
14981
- * Set the default options for column
14982
- */
14983
- defaultPlotOptions.column = merge(defaultSeriesOptions, {
14984
- borderColor: '#FFFFFF',
14985
- borderWidth: 1,
14986
- borderRadius: 0,
14987
- //colorByPoint: undefined,
14988
- groupPadding: 0.2,
14989
- //grouping: true,
14990
- marker: null, // point options are specified in the base options
14991
- pointPadding: 0.1,
14992
- //pointWidth: null,
14993
- minPointLength: 0,
14994
- cropThreshold: 50, // when there are more points, they will not animate out of the chart on xAxis.setExtremes
14995
- pointRange: null, // null means auto, meaning 1 in a categorized axis and least distance between points if not categories
14996
- states: {
14997
- hover: {
14998
- brightness: 0.1,
14999
- shadow: false
15000
- },
15001
- select: {
15002
- color: '#C0C0C0',
15003
- borderColor: '#000000',
15004
- shadow: false
15005
- }
15006
- },
15007
- dataLabels: {
15008
- align: null, // auto
15009
- verticalAlign: null, // auto
15010
- y: null
15011
- },
15012
- stickyTracking: false,
15013
- threshold: 0
15014
- });
15015
-
15016
- /**
15017
- * ColumnSeries object
15018
- */
15019
- var ColumnSeries = extendClass(Series, {
15020
- type: 'column',
15021
- tooltipOutsidePlot: true,
15022
- requireSorting: false,
15023
- pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
15024
- stroke: 'borderColor',
15025
- 'stroke-width': 'borderWidth',
15026
- fill: 'color',
15027
- r: 'borderRadius'
15028
- },
15029
- trackerGroups: ['group', 'dataLabelsGroup'],
15030
-
15031
- /**
15032
- * Initialize the series
15033
- */
15034
- init: function () {
15035
- Series.prototype.init.apply(this, arguments);
15036
-
15037
- var series = this,
15038
- chart = series.chart;
15039
-
15040
- // if the series is added dynamically, force redraw of other
15041
- // series affected by a new column
15042
- if (chart.hasRendered) {
15043
- each(chart.series, function (otherSeries) {
15044
- if (otherSeries.type === series.type) {
15045
- otherSeries.isDirty = true;
15046
- }
15047
- });
15048
- }
15049
- },
15050
-
15051
- /**
15052
- * Return the width and x offset of the columns adjusted for grouping, groupPadding, pointPadding,
15053
- * pointWidth etc.
15054
- */
15055
- getColumnMetrics: function () {
15056
-
15057
- var series = this,
15058
- chart = series.chart,
15059
- options = series.options,
15060
- xAxis = this.xAxis,
15061
- reversedXAxis = xAxis.reversed,
15062
- stackKey,
15063
- stackGroups = {},
15064
- columnIndex,
15065
- columnCount = 0;
15066
-
15067
- // Get the total number of column type series.
15068
- // This is called on every series. Consider moving this logic to a
15069
- // chart.orderStacks() function and call it on init, addSeries and removeSeries
15070
- if (options.grouping === false) {
15071
- columnCount = 1;
15072
- } else {
15073
- each(chart.series, function (otherSeries) {
15074
- var otherOptions = otherSeries.options;
15075
- if (otherSeries.type === series.type && otherSeries.visible &&
15076
- series.options.group === otherOptions.group) { // used in Stock charts navigator series
15077
- if (otherOptions.stacking) {
15078
- stackKey = otherSeries.stackKey;
15079
- if (stackGroups[stackKey] === UNDEFINED) {
15080
- stackGroups[stackKey] = columnCount++;
15081
- }
15082
- columnIndex = stackGroups[stackKey];
15083
- } else if (otherOptions.grouping !== false) { // #1162
15084
- columnIndex = columnCount++;
15085
- }
15086
- otherSeries.columnIndex = columnIndex;
15087
- }
15088
- });
15089
- }
15090
-
15091
- var categoryWidth = mathMin(
15092
- mathAbs(xAxis.transA) * (xAxis.ordinalSlope || options.pointRange || xAxis.closestPointRange || 1),
15093
- xAxis.len // #1535
15094
- ),
15095
- groupPadding = categoryWidth * options.groupPadding,
15096
- groupWidth = categoryWidth - 2 * groupPadding,
15097
- pointOffsetWidth = groupWidth / columnCount,
15098
- optionPointWidth = options.pointWidth,
15099
- pointPadding = defined(optionPointWidth) ? (pointOffsetWidth - optionPointWidth) / 2 :
15100
- pointOffsetWidth * options.pointPadding,
15101
- pointWidth = pick(optionPointWidth, pointOffsetWidth - 2 * pointPadding), // exact point width, used in polar charts
15102
- colIndex = (reversedXAxis ?
15103
- columnCount - (series.columnIndex || 0) : // #1251
15104
- series.columnIndex) || 0,
15105
- pointXOffset = pointPadding + (groupPadding + colIndex *
15106
- pointOffsetWidth - (categoryWidth / 2)) *
15107
- (reversedXAxis ? -1 : 1);
15108
-
15109
- // Save it for reading in linked series (Error bars particularly)
15110
- return (series.columnMetrics = {
15111
- width: pointWidth,
15112
- offset: pointXOffset
15113
- });
15114
-
15115
- },
15116
-
15117
- /**
15118
- * Translate each point to the plot area coordinate system and find shape positions
15119
- */
15120
- translate: function () {
15121
- var series = this,
15122
- chart = series.chart,
15123
- options = series.options,
15124
- stacking = options.stacking,
15125
- borderWidth = options.borderWidth,
15126
- yAxis = series.yAxis,
15127
- threshold = options.threshold,
15128
- translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold),
15129
- minPointLength = pick(options.minPointLength, 5),
15130
- metrics = series.getColumnMetrics(),
15131
- pointWidth = metrics.width,
15132
- barW = mathCeil(mathMax(pointWidth, 1 + 2 * borderWidth)), // rounded and postprocessed for border width
15133
- pointXOffset = metrics.offset;
15134
-
15135
- Series.prototype.translate.apply(series);
15136
-
15137
- // record the new values
15138
- each(series.points, function (point) {
15139
- var plotY = mathMin(mathMax(-999, point.plotY), yAxis.len + 999), // Don't draw too far outside plot area (#1303)
15140
- yBottom = pick(point.yBottom, translatedThreshold),
15141
- barX = point.plotX + pointXOffset,
15142
- barY = mathCeil(mathMin(plotY, yBottom)),
15143
- barH = mathCeil(mathMax(plotY, yBottom) - barY),
15144
- stack = yAxis.stacks[(point.y < 0 ? '-' : '') + series.stackKey],
15145
- shapeArgs;
15146
-
15147
- // Record the offset'ed position and width of the bar to be able to align the stacking total correctly
15148
- if (stacking && series.visible && stack && stack[point.x]) {
15149
- stack[point.x].setOffset(pointXOffset, barW);
15150
- }
15151
-
15152
- // handle options.minPointLength
15153
- if (mathAbs(barH) < minPointLength) {
15154
- if (minPointLength) {
15155
- barH = minPointLength;
15156
- barY =
15157
- mathAbs(barY - translatedThreshold) > minPointLength ? // stacked
15158
- yBottom - minPointLength : // keep position
15159
- translatedThreshold - (yAxis.translate(point.y, 0, 1, 0, 1) <= translatedThreshold ? minPointLength : 0); // use exact yAxis.translation (#1485)
15160
- }
15161
- }
15162
-
15163
- point.barX = barX;
15164
- point.pointWidth = pointWidth;
15165
-
15166
- // create shape type and shape args that are reused in drawPoints and drawTracker
15167
- point.shapeType = 'rect';
15168
- point.shapeArgs = shapeArgs = chart.renderer.Element.prototype.crisp.call(0, borderWidth, barX, barY, barW, barH);
15169
-
15170
- if (borderWidth % 2) { // correct for shorting in crisp method, visible in stacked columns with 1px border
15171
- shapeArgs.y -= 1;
15172
- shapeArgs.height += 1;
15173
- }
15174
-
15175
- });
15176
-
15177
- },
15178
-
15179
- getSymbol: noop,
15180
-
15181
- /**
15182
- * Use a solid rectangle like the area series types
15183
- */
15184
- drawLegendSymbol: AreaSeries.prototype.drawLegendSymbol,
15185
-
15186
-
15187
- /**
15188
- * Columns have no graph
15189
- */
15190
- drawGraph: noop,
15191
-
15192
- /**
15193
- * Draw the columns. For bars, the series.group is rotated, so the same coordinates
15194
- * apply for columns and bars. This method is inherited by scatter series.
15195
- *
15196
- */
15197
- drawPoints: function () {
15198
- var series = this,
15199
- options = series.options,
15200
- renderer = series.chart.renderer,
15201
- shapeArgs;
15202
-
15203
-
15204
- // draw the columns
15205
- each(series.points, function (point) {
15206
- var plotY = point.plotY,
15207
- graphic = point.graphic;
15208
-
15209
- if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
15210
- shapeArgs = point.shapeArgs;
15211
-
15212
- if (graphic) { // update
15213
- stop(graphic);
15214
- graphic.animate(merge(shapeArgs));
15215
-
15216
- } else {
15217
- point.graphic = graphic = renderer[point.shapeType](shapeArgs)
15218
- .attr(point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE])
15219
- .add(series.group)
15220
- .shadow(options.shadow, null, options.stacking && !options.borderRadius);
15221
- }
15222
-
15223
- } else if (graphic) {
15224
- point.graphic = graphic.destroy(); // #1269
15225
- }
15226
- });
15227
- },
15228
-
15229
- /**
15230
- * Add tracking event listener to the series group, so the point graphics
15231
- * themselves act as trackers
15232
- */
15233
- drawTracker: function () {
15234
- var series = this,
15235
- pointer = series.chart.pointer,
15236
- cursor = series.options.cursor,
15237
- css = cursor && { cursor: cursor },
15238
- onMouseOver = function (e) {
15239
- var target = e.target,
15240
- point;
15241
-
15242
- series.onMouseOver();
15243
-
15244
- while (target && !point) {
15245
- point = target.point;
15246
- target = target.parentNode;
15247
- }
15248
- if (point !== UNDEFINED) { // undefined on graph in scatterchart
15249
- point.onMouseOver(e);
15250
- }
15251
- };
15252
-
15253
- // Add reference to the point
15254
- each(series.points, function (point) {
15255
- if (point.graphic) {
15256
- point.graphic.element.point = point;
15257
- }
15258
- if (point.dataLabel) {
15259
- point.dataLabel.element.point = point;
15260
- }
15261
- });
15262
-
15263
- // Add the event listeners, we need to do this only once
15264
- if (!series._hasTracking) {
15265
- each(series.trackerGroups, function (key) {
15266
- if (series[key]) { // we don't always have dataLabelsGroup
15267
- series[key]
15268
- .addClass(PREFIX + 'tracker')
15269
- .on('mouseover', onMouseOver)
15270
- .on('mouseout', function (e) { pointer.onTrackerMouseOut(e); })
15271
- .css(css);
15272
- if (hasTouch) {
15273
- series[key].on('touchstart', onMouseOver);
15274
- }
15275
- }
15276
- });
15277
-
15278
- } else {
15279
- series._hasTracking = true;
15280
- }
15281
- },
15282
-
15283
- /**
15284
- * Override the basic data label alignment by adjusting for the position of the column
15285
- */
15286
- alignDataLabel: function (point, dataLabel, options, alignTo, isNew) {
15287
- var chart = this.chart,
15288
- inverted = chart.inverted,
15289
- dlBox = point.dlBox || point.shapeArgs, // data label box for alignment
15290
- below = point.below || (point.plotY > pick(this.translatedThreshold, chart.plotSizeY)),
15291
- inside = pick(options.inside, !!this.options.stacking); // draw it inside the box?
15292
-
15293
- // Align to the column itself, or the top of it
15294
- if (dlBox) { // Area range uses this method but not alignTo
15295
- alignTo = merge(dlBox);
15296
- if (inverted) {
15297
- alignTo = {
15298
- x: chart.plotWidth - alignTo.y - alignTo.height,
15299
- y: chart.plotHeight - alignTo.x - alignTo.width,
15300
- width: alignTo.height,
15301
- height: alignTo.width
15302
- };
15303
- }
15304
-
15305
- // Compute the alignment box
15306
- if (!inside) {
15307
- if (inverted) {
15308
- alignTo.x += below ? 0 : alignTo.width;
15309
- alignTo.width = 0;
15310
- } else {
15311
- alignTo.y += below ? alignTo.height : 0;
15312
- alignTo.height = 0;
15313
- }
15314
- }
15315
- }
15316
-
15317
- // When alignment is undefined (typically columns and bars), display the individual
15318
- // point below or above the point depending on the threshold
15319
- options.align = pick(
15320
- options.align,
15321
- !inverted || inside ? 'center' : below ? 'right' : 'left'
15322
- );
15323
- options.verticalAlign = pick(
15324
- options.verticalAlign,
15325
- inverted || inside ? 'middle' : below ? 'top' : 'bottom'
15326
- );
15327
-
15328
- // Call the parent method
15329
- Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
15330
- },
15331
-
15332
-
15333
- /**
15334
- * Animate the column heights one by one from zero
15335
- * @param {Boolean} init Whether to initialize the animation or run it
15336
- */
15337
- animate: function (init) {
15338
- var series = this,
15339
- yAxis = this.yAxis,
15340
- options = series.options,
15341
- inverted = this.chart.inverted,
15342
- attr = {},
15343
- translatedThreshold;
15344
-
15345
- if (hasSVG) { // VML is too slow anyway
15346
- if (init) {
15347
- attr.scaleY = 0.001;
15348
- translatedThreshold = mathMin(yAxis.pos + yAxis.len, mathMax(yAxis.pos, yAxis.toPixels(options.threshold)));
15349
- if (inverted) {
15350
- attr.translateX = translatedThreshold - yAxis.len;
15351
- } else {
15352
- attr.translateY = translatedThreshold;
15353
- }
15354
- series.group.attr(attr);
15355
-
15356
- } else { // run the animation
15357
-
15358
- attr.scaleY = 1;
15359
- attr[inverted ? 'translateX' : 'translateY'] = yAxis.pos;
15360
- series.group.animate(attr, series.options.animation);
15361
-
15362
- // delete this function to allow it only once
15363
- series.animate = null;
15364
- }
15365
- }
15366
- },
15367
-
15368
- /**
15369
- * Remove this series from the chart
15370
- */
15371
- remove: function () {
15372
- var series = this,
15373
- chart = series.chart;
15374
-
15375
- // column and bar series affects other series of the same type
15376
- // as they are either stacked or grouped
15377
- if (chart.hasRendered) {
15378
- each(chart.series, function (otherSeries) {
15379
- if (otherSeries.type === series.type) {
15380
- otherSeries.isDirty = true;
15381
- }
15382
- });
15383
- }
15384
-
15385
- Series.prototype.remove.apply(series, arguments);
15386
- }
15387
- });
15388
- seriesTypes.column = ColumnSeries;
15389
- /**
15390
- * Set the default options for bar
15391
- */
15392
- defaultPlotOptions.bar = merge(defaultPlotOptions.column);
15393
- /**
15394
- * The Bar series class
15395
- */
15396
- var BarSeries = extendClass(ColumnSeries, {
15397
- type: 'bar',
15398
- inverted: true
15399
- });
15400
- seriesTypes.bar = BarSeries;
15401
-
15402
- /**
15403
- * Set the default options for scatter
15404
- */
15405
- defaultPlotOptions.scatter = merge(defaultSeriesOptions, {
15406
- lineWidth: 0,
15407
- tooltip: {
15408
- headerFormat: '<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',
15409
- pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>',
15410
- followPointer: true
15411
- },
15412
- stickyTracking: false
15413
- });
15414
-
15415
- /**
15416
- * The scatter series class
15417
- */
15418
- var ScatterSeries = extendClass(Series, {
15419
- type: 'scatter',
15420
- sorted: false,
15421
- requireSorting: false,
15422
- noSharedTooltip: true,
15423
- trackerGroups: ['markerGroup'],
15424
-
15425
- drawTracker: ColumnSeries.prototype.drawTracker,
15426
-
15427
- setTooltipPoints: noop
15428
- });
15429
- seriesTypes.scatter = ScatterSeries;
15430
-
15431
- /**
15432
- * Set the default options for pie
15433
- */
15434
- defaultPlotOptions.pie = merge(defaultSeriesOptions, {
15435
- borderColor: '#FFFFFF',
15436
- borderWidth: 1,
15437
- center: [null, null],
15438
- clip: false,
15439
- colorByPoint: true, // always true for pies
15440
- dataLabels: {
15441
- // align: null,
15442
- // connectorWidth: 1,
15443
- // connectorColor: point.color,
15444
- // connectorPadding: 5,
15445
- distance: 30,
15446
- enabled: true,
15447
- formatter: function () {
15448
- return this.point.name;
15449
- }
15450
- // softConnector: true,
15451
- //y: 0
15452
- },
15453
- ignoreHiddenPoint: true,
15454
- //innerSize: 0,
15455
- legendType: 'point',
15456
- marker: null, // point options are specified in the base options
15457
- size: null,
15458
- showInLegend: false,
15459
- slicedOffset: 10,
15460
- states: {
15461
- hover: {
15462
- brightness: 0.1,
15463
- shadow: false
15464
- }
15465
- },
15466
- stickyTracking: false,
15467
- tooltip: {
15468
- followPointer: true
15469
- }
15470
- });
15471
-
15472
- /**
15473
- * Extended point object for pies
15474
- */
15475
- var PiePoint = extendClass(Point, {
15476
- /**
15477
- * Initiate the pie slice
15478
- */
15479
- init: function () {
15480
-
15481
- Point.prototype.init.apply(this, arguments);
15482
-
15483
- var point = this,
15484
- toggleSlice;
15485
-
15486
- // Disallow negative values (#1530)
15487
- if (point.y < 0) {
15488
- point.y = null;
15489
- }
15490
-
15491
- //visible: options.visible !== false,
15492
- extend(point, {
15493
- visible: point.visible !== false,
15494
- name: pick(point.name, 'Slice')
15495
- });
15496
-
15497
- // add event listener for select
15498
- toggleSlice = function () {
15499
- point.slice();
15500
- };
15501
- addEvent(point, 'select', toggleSlice);
15502
- addEvent(point, 'unselect', toggleSlice);
15503
-
15504
- return point;
15505
- },
15506
-
15507
- /**
15508
- * Toggle the visibility of the pie slice
15509
- * @param {Boolean} vis Whether to show the slice or not. If undefined, the
15510
- * visibility is toggled
15511
- */
15512
- setVisible: function (vis) {
15513
- var point = this,
15514
- series = point.series,
15515
- chart = series.chart,
15516
- method;
15517
-
15518
- // if called without an argument, toggle visibility
15519
- point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis;
15520
- series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
15521
-
15522
- method = vis ? 'show' : 'hide';
15523
-
15524
- // Show and hide associated elements
15525
- each(['graphic', 'dataLabel', 'connector', 'shadowGroup'], function (key) {
15526
- if (point[key]) {
15527
- point[key][method]();
15528
- }
15529
- });
15530
-
15531
- if (point.legendItem) {
15532
- chart.legend.colorizeItem(point, vis);
15533
- }
15534
-
15535
- // Handle ignore hidden slices
15536
- if (!series.isDirty && series.options.ignoreHiddenPoint) {
15537
- series.isDirty = true;
15538
- chart.redraw();
15539
- }
15540
- },
15541
-
15542
- /**
15543
- * Set or toggle whether the slice is cut out from the pie
15544
- * @param {Boolean} sliced When undefined, the slice state is toggled
15545
- * @param {Boolean} redraw Whether to redraw the chart. True by default.
15546
- */
15547
- slice: function (sliced, redraw, animation) {
15548
- var point = this,
15549
- series = point.series,
15550
- chart = series.chart,
15551
- translation;
15552
-
15553
- setAnimation(animation, chart);
15554
-
15555
- // redraw is true by default
15556
- redraw = pick(redraw, true);
15557
-
15558
- // if called without an argument, toggle
15559
- point.sliced = point.options.sliced = sliced = defined(sliced) ? sliced : !point.sliced;
15560
- series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
15561
-
15562
- translation = sliced ? point.slicedTranslation : {
15563
- translateX: 0,
15564
- translateY: 0
15565
- };
15566
-
15567
- point.graphic.animate(translation);
15568
-
15569
- if (point.shadowGroup) {
15570
- point.shadowGroup.animate(translation);
15571
- }
15572
-
15573
- }
15574
- });
15575
-
15576
- /**
15577
- * The Pie series class
15578
- */
15579
- var PieSeries = {
15580
- type: 'pie',
15581
- isCartesian: false,
15582
- pointClass: PiePoint,
15583
- requireSorting: false,
15584
- noSharedTooltip: true,
15585
- trackerGroups: ['group', 'dataLabelsGroup'],
15586
- pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
15587
- stroke: 'borderColor',
15588
- 'stroke-width': 'borderWidth',
15589
- fill: 'color'
15590
- },
15591
-
15592
- /**
15593
- * Pies have one color each point
15594
- */
15595
- getColor: noop,
15596
-
15597
- /**
15598
- * Animate the pies in
15599
- */
15600
- animate: function (init) {
15601
- var series = this,
15602
- points = series.points,
15603
- startAngleRad = series.startAngleRad;
15604
-
15605
- if (!init) {
15606
- each(points, function (point) {
15607
- var graphic = point.graphic,
15608
- args = point.shapeArgs;
15609
-
15610
- if (graphic) {
15611
- // start values
15612
- graphic.attr({
15613
- r: series.center[3] / 2, // animate from inner radius (#779)
15614
- start: startAngleRad,
15615
- end: startAngleRad
15616
- });
15617
-
15618
- // animate
15619
- graphic.animate({
15620
- r: args.r,
15621
- start: args.start,
15622
- end: args.end
15623
- }, series.options.animation);
15624
- }
15625
- });
15626
-
15627
- // delete this function to allow it only once
15628
- series.animate = null;
15629
- }
15630
- },
15631
-
15632
- /**
15633
- * Extend the basic setData method by running processData and generatePoints immediately,
15634
- * in order to access the points from the legend.
15635
- */
15636
- setData: function (data, redraw) {
15637
- Series.prototype.setData.call(this, data, false);
15638
- this.processData();
15639
- this.generatePoints();
15640
- if (pick(redraw, true)) {
15641
- this.chart.redraw();
15642
- }
15643
- },
15644
-
15645
- /**
15646
- * Get the center of the pie based on the size and center options relative to the
15647
- * plot area. Borrowed by the polar and gauge series types.
15648
- */
15649
- getCenter: function () {
15650
-
15651
- var options = this.options,
15652
- chart = this.chart,
15653
- slicingRoom = 2 * (options.slicedOffset || 0),
15654
- handleSlicingRoom,
15655
- plotWidth = chart.plotWidth - 2 * slicingRoom,
15656
- plotHeight = chart.plotHeight - 2 * slicingRoom,
15657
- centerOption = options.center,
15658
- positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0],
15659
- smallestSize = mathMin(plotWidth, plotHeight),
15660
- isPercent;
15661
-
15662
- return map(positions, function (length, i) {
15663
- isPercent = /%$/.test(length);
15664
- handleSlicingRoom = i < 2 || (i === 2 && isPercent);
15665
- return (isPercent ?
15666
- // i == 0: centerX, relative to width
15667
- // i == 1: centerY, relative to height
15668
- // i == 2: size, relative to smallestSize
15669
- // i == 4: innerSize, relative to smallestSize
15670
- [plotWidth, plotHeight, smallestSize, smallestSize][i] *
15671
- pInt(length) / 100 :
15672
- length) + (handleSlicingRoom ? slicingRoom : 0);
15673
- });
15674
- },
15675
-
15676
- /**
15677
- * Do translation for pie slices
15678
- */
15679
- translate: function (positions) {
15680
- this.generatePoints();
15681
-
15682
- var total = 0,
15683
- series = this,
15684
- cumulative = 0,
15685
- precision = 1000, // issue #172
15686
- options = series.options,
15687
- slicedOffset = options.slicedOffset,
15688
- connectorOffset = slicedOffset + options.borderWidth,
15689
- start,
15690
- end,
15691
- angle,
15692
- startAngleRad = series.startAngleRad = mathPI / 180 * ((options.startAngle || 0) % 360 - 90),
15693
- points = series.points,
15694
- circ = 2 * mathPI,
15695
- fraction,
15696
- radiusX, // the x component of the radius vector for a given point
15697
- radiusY,
15698
- labelDistance = options.dataLabels.distance,
15699
- ignoreHiddenPoint = options.ignoreHiddenPoint,
15700
- i,
15701
- len = points.length,
15702
- point;
15703
-
15704
- // Get positions - either an integer or a percentage string must be given.
15705
- // If positions are passed as a parameter, we're in a recursive loop for adjusting
15706
- // space for data labels.
15707
- if (!positions) {
15708
- series.center = positions = series.getCenter();
15709
- }
15710
-
15711
- // utility for getting the x value from a given y, used for anticollision logic in data labels
15712
- series.getX = function (y, left) {
15713
-
15714
- angle = math.asin((y - positions[1]) / (positions[2] / 2 + labelDistance));
15715
-
15716
- return positions[0] +
15717
- (left ? -1 : 1) *
15718
- (mathCos(angle) * (positions[2] / 2 + labelDistance));
15719
- };
15720
-
15721
- // get the total sum
15722
- for (i = 0; i < len; i++) {
15723
- point = points[i];
15724
- total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y;
15725
- }
15726
-
15727
- // Calculate the geometry for each point
15728
- for (i = 0; i < len; i++) {
15729
-
15730
- point = points[i];
15731
-
15732
- // set start and end angle
15733
- fraction = total ? point.y / total : 0;
15734
- start = mathRound((startAngleRad + (cumulative * circ)) * precision) / precision;
15735
- if (!ignoreHiddenPoint || point.visible) {
15736
- cumulative += fraction;
15737
- }
15738
- end = mathRound((startAngleRad + (cumulative * circ)) * precision) / precision;
15739
-
15740
- // set the shape
15741
- point.shapeType = 'arc';
15742
- point.shapeArgs = {
15743
- x: positions[0],
15744
- y: positions[1],
15745
- r: positions[2] / 2,
15746
- innerR: positions[3] / 2,
15747
- start: start,
15748
- end: end
15749
- };
15750
-
15751
- // center for the sliced out slice
15752
- angle = (end + start) / 2;
15753
- if (angle > 0.75 * circ) {
15754
- angle -= 2 * mathPI;
15755
- }
15756
- point.slicedTranslation = {
15757
- translateX: mathRound(mathCos(angle) * slicedOffset),
15758
- translateY: mathRound(mathSin(angle) * slicedOffset)
15759
- };
15760
-
15761
- // set the anchor point for tooltips
15762
- radiusX = mathCos(angle) * positions[2] / 2;
15763
- radiusY = mathSin(angle) * positions[2] / 2;
15764
- point.tooltipPos = [
15765
- positions[0] + radiusX * 0.7,
15766
- positions[1] + radiusY * 0.7
15767
- ];
15768
-
15769
- point.half = angle < circ / 4 ? 0 : 1;
15770
- point.angle = angle;
15771
-
15772
- // set the anchor point for data labels
15773
- connectorOffset = mathMin(connectorOffset, labelDistance / 2); // #1678
15774
- point.labelPos = [
15775
- positions[0] + radiusX + mathCos(angle) * labelDistance, // first break of connector
15776
- positions[1] + radiusY + mathSin(angle) * labelDistance, // a/a
15777
- positions[0] + radiusX + mathCos(angle) * connectorOffset, // second break, right outside pie
15778
- positions[1] + radiusY + mathSin(angle) * connectorOffset, // a/a
15779
- positions[0] + radiusX, // landing point for connector
15780
- positions[1] + radiusY, // a/a
15781
- labelDistance < 0 ? // alignment
15782
- 'center' :
15783
- point.half ? 'right' : 'left', // alignment
15784
- angle // center angle
15785
- ];
15786
-
15787
- // API properties
15788
- point.percentage = fraction * 100;
15789
- point.total = total;
15790
-
15791
- }
15792
-
15793
-
15794
- this.setTooltipPoints();
15795
- },
15796
-
15797
- drawGraph: null,
15798
-
15799
- /**
15800
- * Draw the data points
15801
- */
15802
- drawPoints: function () {
15803
- var series = this,
15804
- chart = series.chart,
15805
- renderer = chart.renderer,
15806
- groupTranslation,
15807
- //center,
15808
- graphic,
15809
- //group,
15810
- shadow = series.options.shadow,
15811
- shadowGroup,
15812
- shapeArgs;
15813
-
15814
- if (shadow && !series.shadowGroup) {
15815
- series.shadowGroup = renderer.g('shadow')
15816
- .add(series.group);
15817
- }
15818
-
15819
- // draw the slices
15820
- each(series.points, function (point) {
15821
- graphic = point.graphic;
15822
- shapeArgs = point.shapeArgs;
15823
- shadowGroup = point.shadowGroup;
15824
-
15825
- // put the shadow behind all points
15826
- if (shadow && !shadowGroup) {
15827
- shadowGroup = point.shadowGroup = renderer.g('shadow')
15828
- .add(series.shadowGroup);
15829
- }
15830
-
15831
- // if the point is sliced, use special translation, else use plot area traslation
15832
- groupTranslation = point.sliced ? point.slicedTranslation : {
15833
- translateX: 0,
15834
- translateY: 0
15835
- };
15836
-
15837
- //group.translate(groupTranslation[0], groupTranslation[1]);
15838
- if (shadowGroup) {
15839
- shadowGroup.attr(groupTranslation);
15840
- }
15841
-
15842
- // draw the slice
15843
- if (graphic) {
15844
- graphic.animate(extend(shapeArgs, groupTranslation));
15845
- } else {
15846
- point.graphic = graphic = renderer.arc(shapeArgs)
15847
- .setRadialReference(series.center)
15848
- .attr(
15849
- point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]
15850
- )
15851
- .attr({ 'stroke-linejoin': 'round' })
15852
- .attr(groupTranslation)
15853
- .add(series.group)
15854
- .shadow(shadow, shadowGroup);
15855
- }
15856
-
15857
- // detect point specific visibility
15858
- if (point.visible === false) {
15859
- point.setVisible(false);
15860
- }
15861
-
15862
- });
15863
-
15864
- },
15865
-
15866
- /**
15867
- * Override the base drawDataLabels method by pie specific functionality
15868
- */
15869
- drawDataLabels: function () {
15870
- var series = this,
15871
- data = series.data,
15872
- point,
15873
- chart = series.chart,
15874
- options = series.options.dataLabels,
15875
- connectorPadding = pick(options.connectorPadding, 10),
15876
- connectorWidth = pick(options.connectorWidth, 1),
15877
- plotWidth = chart.plotWidth,
15878
- plotHeight = chart.plotHeight,
15879
- connector,
15880
- connectorPath,
15881
- softConnector = pick(options.softConnector, true),
15882
- distanceOption = options.distance,
15883
- seriesCenter = series.center,
15884
- radius = seriesCenter[2] / 2,
15885
- centerY = seriesCenter[1],
15886
- outside = distanceOption > 0,
15887
- dataLabel,
15888
- dataLabelWidth,
15889
- labelPos,
15890
- labelHeight,
15891
- halves = [// divide the points into right and left halves for anti collision
15892
- [], // right
15893
- [] // left
15894
- ],
15895
- x,
15896
- y,
15897
- visibility,
15898
- rankArr,
15899
- i,
15900
- j,
15901
- overflow = [0, 0, 0, 0], // top, right, bottom, left
15902
- sort = function (a, b) {
15903
- return b.y - a.y;
15904
- },
15905
- sortByAngle = function (points, sign) {
15906
- points.sort(function (a, b) {
15907
- return a.angle !== undefined && (b.angle - a.angle) * sign;
15908
- });
15909
- };
15910
-
15911
- // get out if not enabled
15912
- if (!options.enabled && !series._hasPointLabels) {
15913
- return;
15914
- }
15915
-
15916
- // run parent method
15917
- Series.prototype.drawDataLabels.apply(series);
15918
-
15919
- // arrange points for detection collision
15920
- each(data, function (point) {
15921
- if (point.dataLabel) { // it may have been cancelled in the base method (#407)
15922
- halves[point.half].push(point);
15923
- }
15924
- });
15925
-
15926
- // assume equal label heights
15927
- i = 0;
15928
- while (!labelHeight && data[i]) { // #1569
15929
- labelHeight = data[i] && data[i].dataLabel && (data[i].dataLabel.getBBox().height || 21); // 21 is for #968
15930
- i++;
15931
- }
15932
-
15933
- /* Loop over the points in each half, starting from the top and bottom
15934
- * of the pie to detect overlapping labels.
15935
- */
15936
- i = 2;
15937
- while (i--) {
15938
-
15939
- var slots = [],
15940
- slotsLength,
15941
- usedSlots = [],
15942
- points = halves[i],
15943
- pos,
15944
- length = points.length,
15945
- slotIndex;
15946
-
15947
- // Sort by angle
15948
- sortByAngle(points, i - 0.5);
15949
-
15950
- // Only do anti-collision when we are outside the pie and have connectors (#856)
15951
- if (distanceOption > 0) {
15952
-
15953
- // build the slots
15954
- for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
15955
- slots.push(pos);
15956
-
15957
- // visualize the slot
15958
- /*
15959
- var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
15960
- slotY = pos + chart.plotTop;
15961
- if (!isNaN(slotX)) {
15962
- chart.renderer.rect(slotX, slotY - 7, 100, labelHeight, 1)
15963
- .attr({
15964
- 'stroke-width': 1,
15965
- stroke: 'silver'
15966
- })
15967
- .add();
15968
- chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
15969
- .attr({
15970
- fill: 'silver'
15971
- }).add();
15972
- }
15973
- */
15974
- }
15975
- slotsLength = slots.length;
15976
-
15977
- // if there are more values than available slots, remove lowest values
15978
- if (length > slotsLength) {
15979
- // create an array for sorting and ranking the points within each quarter
15980
- rankArr = [].concat(points);
15981
- rankArr.sort(sort);
15982
- j = length;
15983
- while (j--) {
15984
- rankArr[j].rank = j;
15985
- }
15986
- j = length;
15987
- while (j--) {
15988
- if (points[j].rank >= slotsLength) {
15989
- points.splice(j, 1);
15990
- }
15991
- }
15992
- length = points.length;
15993
- }
15994
-
15995
- // The label goes to the nearest open slot, but not closer to the edge than
15996
- // the label's index.
15997
- for (j = 0; j < length; j++) {
15998
-
15999
- point = points[j];
16000
- labelPos = point.labelPos;
16001
-
16002
- var closest = 9999,
16003
- distance,
16004
- slotI;
16005
-
16006
- // find the closest slot index
16007
- for (slotI = 0; slotI < slotsLength; slotI++) {
16008
- distance = mathAbs(slots[slotI] - labelPos[1]);
16009
- if (distance < closest) {
16010
- closest = distance;
16011
- slotIndex = slotI;
16012
- }
16013
- }
16014
-
16015
- // if that slot index is closer to the edges of the slots, move it
16016
- // to the closest appropriate slot
16017
- if (slotIndex < j && slots[j] !== null) { // cluster at the top
16018
- slotIndex = j;
16019
- } else if (slotsLength < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom
16020
- slotIndex = slotsLength - length + j;
16021
- while (slots[slotIndex] === null) { // make sure it is not taken
16022
- slotIndex++;
16023
- }
16024
- } else {
16025
- // Slot is taken, find next free slot below. In the next run, the next slice will find the
16026
- // slot above these, because it is the closest one
16027
- while (slots[slotIndex] === null) { // make sure it is not taken
16028
- slotIndex++;
16029
- }
16030
- }
16031
-
16032
- usedSlots.push({ i: slotIndex, y: slots[slotIndex] });
16033
- slots[slotIndex] = null; // mark as taken
16034
- }
16035
- // sort them in order to fill in from the top
16036
- usedSlots.sort(sort);
16037
- }
16038
-
16039
- // now the used slots are sorted, fill them up sequentially
16040
- for (j = 0; j < length; j++) {
16041
-
16042
- var slot, naturalY;
16043
-
16044
- point = points[j];
16045
- labelPos = point.labelPos;
16046
- dataLabel = point.dataLabel;
16047
- visibility = point.visible === false ? HIDDEN : VISIBLE;
16048
- naturalY = labelPos[1];
16049
-
16050
- if (distanceOption > 0) {
16051
- slot = usedSlots.pop();
16052
- slotIndex = slot.i;
16053
-
16054
- // if the slot next to currrent slot is free, the y value is allowed
16055
- // to fall back to the natural position
16056
- y = slot.y;
16057
- if ((naturalY > y && slots[slotIndex + 1] !== null) ||
16058
- (naturalY < y && slots[slotIndex - 1] !== null)) {
16059
- y = naturalY;
16060
- }
16061
-
16062
- } else {
16063
- y = naturalY;
16064
- }
16065
-
16066
- // get the x - use the natural x position for first and last slot, to prevent the top
16067
- // and botton slice connectors from touching each other on either side
16068
- x = options.justify ?
16069
- seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) :
16070
- series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
16071
-
16072
-
16073
- // Record the placement and visibility
16074
- dataLabel._attr = {
16075
- visibility: visibility,
16076
- align: labelPos[6]
16077
- };
16078
- dataLabel._pos = {
16079
- x: x + options.x +
16080
- ({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0),
16081
- y: y + options.y - 10 // 10 is for the baseline (label vs text)
16082
- };
16083
- dataLabel.connX = x;
16084
- dataLabel.connY = y;
16085
-
16086
-
16087
- // Detect overflowing data labels
16088
- if (this.options.size === null) {
16089
- dataLabelWidth = dataLabel.width;
16090
- // Overflow left
16091
- if (x - dataLabelWidth < connectorPadding) {
16092
- overflow[3] = mathMax(mathRound(dataLabelWidth - x + connectorPadding), overflow[3]);
16093
-
16094
- // Overflow right
16095
- } else if (x + dataLabelWidth > plotWidth - connectorPadding) {
16096
- overflow[1] = mathMax(mathRound(x + dataLabelWidth - plotWidth + connectorPadding), overflow[1]);
16097
- }
16098
-
16099
- // Overflow top
16100
- if (y - labelHeight / 2 < 0) {
16101
- overflow[0] = mathMax(mathRound(-y + labelHeight / 2), overflow[0]);
16102
-
16103
- // Overflow left
16104
- } else if (y + labelHeight / 2 > plotHeight) {
16105
- overflow[2] = mathMax(mathRound(y + labelHeight / 2 - plotHeight), overflow[2]);
16106
- }
16107
- }
16108
- } // for each point
16109
- } // for each half
16110
-
16111
- // Do not apply the final placement and draw the connectors until we have verified
16112
- // that labels are not spilling over.
16113
- if (arrayMax(overflow) === 0 || this.verifyDataLabelOverflow(overflow)) {
16114
-
16115
- // Place the labels in the final position
16116
- this.placeDataLabels();
16117
-
16118
- // Draw the connectors
16119
- if (outside && connectorWidth) {
16120
- each(this.points, function (point) {
16121
- connector = point.connector;
16122
- labelPos = point.labelPos;
16123
- dataLabel = point.dataLabel;
16124
-
16125
- if (dataLabel && dataLabel._pos) {
16126
- visibility = dataLabel._attr.visibility;
16127
- x = dataLabel.connX;
16128
- y = dataLabel.connY;
16129
- connectorPath = softConnector ? [
16130
- M,
16131
- x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
16132
- 'C',
16133
- x, y, // first break, next to the label
16134
- 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
16135
- labelPos[2], labelPos[3], // second break
16136
- L,
16137
- labelPos[4], labelPos[5] // base
16138
- ] : [
16139
- M,
16140
- x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
16141
- L,
16142
- labelPos[2], labelPos[3], // second break
16143
- L,
16144
- labelPos[4], labelPos[5] // base
16145
- ];
16146
-
16147
- if (connector) {
16148
- connector.animate({ d: connectorPath });
16149
- connector.attr('visibility', visibility);
16150
-
16151
- } else {
16152
- point.connector = connector = series.chart.renderer.path(connectorPath).attr({
16153
- 'stroke-width': connectorWidth,
16154
- stroke: options.connectorColor || point.color || '#606060',
16155
- visibility: visibility
16156
- })
16157
- .add(series.group);
16158
- }
16159
- } else if (connector) {
16160
- point.connector = connector.destroy();
16161
- }
16162
- });
16163
- }
16164
- }
16165
- },
16166
-
16167
- /**
16168
- * Verify whether the data labels are allowed to draw, or we should run more translation and data
16169
- * label positioning to keep them inside the plot area. Returns true when data labels are ready
16170
- * to draw.
16171
- */
16172
- verifyDataLabelOverflow: function (overflow) {
16173
-
16174
- var center = this.center,
16175
- options = this.options,
16176
- centerOption = options.center,
16177
- minSize = options.minSize || 80,
16178
- newSize = minSize,
16179
- ret;
16180
-
16181
- // Handle horizontal size and center
16182
- if (centerOption[0] !== null) { // Fixed center
16183
- newSize = mathMax(center[2] - mathMax(overflow[1], overflow[3]), minSize);
16184
-
16185
- } else { // Auto center
16186
- newSize = mathMax(
16187
- center[2] - overflow[1] - overflow[3], // horizontal overflow
16188
- minSize
16189
- );
16190
- center[0] += (overflow[3] - overflow[1]) / 2; // horizontal center
16191
- }
16192
-
16193
- // Handle vertical size and center
16194
- if (centerOption[1] !== null) { // Fixed center
16195
- newSize = mathMax(mathMin(newSize, center[2] - mathMax(overflow[0], overflow[2])), minSize);
16196
-
16197
- } else { // Auto center
16198
- newSize = mathMax(
16199
- mathMin(
16200
- newSize,
16201
- center[2] - overflow[0] - overflow[2] // vertical overflow
16202
- ),
16203
- minSize
16204
- );
16205
- center[1] += (overflow[0] - overflow[2]) / 2; // vertical center
16206
- }
16207
-
16208
- // If the size must be decreased, we need to run translate and drawDataLabels again
16209
- if (newSize < center[2]) {
16210
- center[2] = newSize;
16211
- this.translate(center);
16212
- each(this.points, function (point) {
16213
- if (point.dataLabel) {
16214
- point.dataLabel._pos = null; // reset
16215
- }
16216
- });
16217
- this.drawDataLabels();
16218
-
16219
- // Else, return true to indicate that the pie and its labels is within the plot area
16220
- } else {
16221
- ret = true;
16222
- }
16223
- return ret;
16224
- },
16225
-
16226
- /**
16227
- * Perform the final placement of the data labels after we have verified that they
16228
- * fall within the plot area.
16229
- */
16230
- placeDataLabels: function () {
16231
- each(this.points, function (point) {
16232
- var dataLabel = point.dataLabel,
16233
- _pos;
16234
-
16235
- if (dataLabel) {
16236
- _pos = dataLabel._pos;
16237
- if (_pos) {
16238
- dataLabel.attr(dataLabel._attr);
16239
- dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
16240
- dataLabel.moved = true;
16241
- } else if (dataLabel) {
16242
- dataLabel.attr({ y: -999 });
16243
- }
16244
- }
16245
- });
16246
- },
16247
-
16248
- alignDataLabel: noop,
16249
-
16250
- /**
16251
- * Draw point specific tracker objects. Inherit directly from column series.
16252
- */
16253
- drawTracker: ColumnSeries.prototype.drawTracker,
16254
-
16255
- /**
16256
- * Use a simple symbol from column prototype
16257
- */
16258
- drawLegendSymbol: AreaSeries.prototype.drawLegendSymbol,
16259
-
16260
- /**
16261
- * Pies don't have point marker symbols
16262
- */
16263
- getSymbol: noop
16264
-
16265
- };
16266
- PieSeries = extendClass(Series, PieSeries);
16267
- seriesTypes.pie = PieSeries;
16268
-
16269
-
16270
- // global variables
16271
- extend(Highcharts, {
16272
-
16273
- // Constructors
16274
- Axis: Axis,
16275
- Chart: Chart,
16276
- Color: Color,
16277
- Legend: Legend,
16278
- Pointer: Pointer,
16279
- Point: Point,
16280
- Tick: Tick,
16281
- Tooltip: Tooltip,
16282
- Renderer: Renderer,
16283
- Series: Series,
16284
- SVGElement: SVGElement,
16285
- SVGRenderer: SVGRenderer,
16286
-
16287
- // Various
16288
- arrayMin: arrayMin,
16289
- arrayMax: arrayMax,
16290
- charts: charts,
16291
- dateFormat: dateFormat,
16292
- format: format,
16293
- pathAnim: pathAnim,
16294
- getOptions: getOptions,
16295
- hasBidiBug: hasBidiBug,
16296
- isTouchDevice: isTouchDevice,
16297
- numberFormat: numberFormat,
16298
- seriesTypes: seriesTypes,
16299
- setOptions: setOptions,
16300
- addEvent: addEvent,
16301
- removeEvent: removeEvent,
16302
- createElement: createElement,
16303
- discardElement: discardElement,
16304
- css: css,
16305
- each: each,
16306
- extend: extend,
16307
- map: map,
16308
- merge: merge,
16309
- pick: pick,
16310
- splat: splat,
16311
- extendClass: extendClass,
16312
- pInt: pInt,
16313
- wrap: wrap,
16314
- svg: hasSVG,
16315
- canvas: useCanVG,
16316
- vml: !hasSVG && !useCanVG,
16317
- product: PRODUCT,
16318
- version: VERSION
16319
- });
16320
- }());
1
+ // ==ClosureCompiler==
2
+ // @compilation_level SIMPLE_OPTIMIZATIONS
3
+
4
+ /**
5
+ * @license Highcharts JS v3.0.2 (2013-06-05)
6
+ *
7
+ * (c) 2009-2013 Torstein Hønsi
8
+ *
9
+ * License: www.highcharts.com/license
10
+ */
11
+
12
+ // JSLint options:
13
+ /*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */
14
+
15
+ (function () {
16
+ // encapsulated variables
17
+ var UNDEFINED,
18
+ doc = document,
19
+ win = window,
20
+ math = Math,
21
+ mathRound = math.round,
22
+ mathFloor = math.floor,
23
+ mathCeil = math.ceil,
24
+ mathMax = math.max,
25
+ mathMin = math.min,
26
+ mathAbs = math.abs,
27
+ mathCos = math.cos,
28
+ mathSin = math.sin,
29
+ mathPI = math.PI,
30
+ deg2rad = mathPI * 2 / 360,
31
+
32
+
33
+ // some variables
34
+ userAgent = navigator.userAgent,
35
+ isOpera = win.opera,
36
+ isIE = /msie/i.test(userAgent) && !isOpera,
37
+ docMode8 = doc.documentMode === 8,
38
+ isWebKit = /AppleWebKit/.test(userAgent),
39
+ isFirefox = /Firefox/.test(userAgent),
40
+ isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent),
41
+ SVG_NS = 'http://www.w3.org/2000/svg',
42
+ hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect,
43
+ hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38
44
+ useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext,
45
+ Renderer,
46
+ hasTouch = doc.documentElement.ontouchstart !== UNDEFINED,
47
+ symbolSizes = {},
48
+ idCounter = 0,
49
+ garbageBin,
50
+ defaultOptions,
51
+ dateFormat, // function
52
+ globalAnimation,
53
+ pathAnim,
54
+ timeUnits,
55
+ noop = function () {},
56
+ charts = [],
57
+ PRODUCT = 'Highcharts',
58
+ VERSION = '3.0.2',
59
+
60
+ // some constants for frequently used strings
61
+ DIV = 'div',
62
+ ABSOLUTE = 'absolute',
63
+ RELATIVE = 'relative',
64
+ HIDDEN = 'hidden',
65
+ PREFIX = 'highcharts-',
66
+ VISIBLE = 'visible',
67
+ PX = 'px',
68
+ NONE = 'none',
69
+ M = 'M',
70
+ L = 'L',
71
+ /*
72
+ * Empirical lowest possible opacities for TRACKER_FILL
73
+ * IE6: 0.002
74
+ * IE7: 0.002
75
+ * IE8: 0.002
76
+ * IE9: 0.00000000001 (unlimited)
77
+ * IE10: 0.0001 (exporting only)
78
+ * FF: 0.00000000001 (unlimited)
79
+ * Chrome: 0.000001
80
+ * Safari: 0.000001
81
+ * Opera: 0.00000000001 (unlimited)
82
+ */
83
+ TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')', // invisible but clickable
84
+ //TRACKER_FILL = 'rgba(192,192,192,0.5)',
85
+ NORMAL_STATE = '',
86
+ HOVER_STATE = 'hover',
87
+ SELECT_STATE = 'select',
88
+ MILLISECOND = 'millisecond',
89
+ SECOND = 'second',
90
+ MINUTE = 'minute',
91
+ HOUR = 'hour',
92
+ DAY = 'day',
93
+ WEEK = 'week',
94
+ MONTH = 'month',
95
+ YEAR = 'year',
96
+
97
+ // constants for attributes
98
+ LINEAR_GRADIENT = 'linearGradient',
99
+ STOPS = 'stops',
100
+ STROKE_WIDTH = 'stroke-width',
101
+
102
+ // time methods, changed based on whether or not UTC is used
103
+ makeTime,
104
+ getMinutes,
105
+ getHours,
106
+ getDay,
107
+ getDate,
108
+ getMonth,
109
+ getFullYear,
110
+ setMinutes,
111
+ setHours,
112
+ setDate,
113
+ setMonth,
114
+ setFullYear,
115
+
116
+
117
+ // lookup over the types and the associated classes
118
+ seriesTypes = {};
119
+
120
+ // The Highcharts namespace
121
+ win.Highcharts = win.Highcharts ? error(16, true) : {};
122
+
123
+ /**
124
+ * Extend an object with the members of another
125
+ * @param {Object} a The object to be extended
126
+ * @param {Object} b The object to add to the first one
127
+ */
128
+ function extend(a, b) {
129
+ var n;
130
+ if (!a) {
131
+ a = {};
132
+ }
133
+ for (n in b) {
134
+ a[n] = b[n];
135
+ }
136
+ return a;
137
+ }
138
+
139
+ /**
140
+ * Deep merge two or more objects and return a third object.
141
+ * Previously this function redirected to jQuery.extend(true), but this had two limitations.
142
+ * First, it deep merged arrays, which lead to workarounds in Highcharts. Second,
143
+ * it copied properties from extended prototypes.
144
+ */
145
+ function merge() {
146
+ var i,
147
+ len = arguments.length,
148
+ ret = {},
149
+ doCopy = function (copy, original) {
150
+ var value, key;
151
+
152
+ for (key in original) {
153
+ if (original.hasOwnProperty(key)) {
154
+ value = original[key];
155
+
156
+ // An object is replacing a primitive
157
+ if (typeof copy !== 'object') {
158
+ copy = {};
159
+ }
160
+
161
+ // Copy the contents of objects, but not arrays or DOM nodes
162
+ if (value && typeof value === 'object' && Object.prototype.toString.call(value) !== '[object Array]'
163
+ && typeof value.nodeType !== 'number') {
164
+ copy[key] = doCopy(copy[key] || {}, value);
165
+
166
+ // Primitives and arrays are copied over directly
167
+ } else {
168
+ copy[key] = original[key];
169
+ }
170
+ }
171
+ }
172
+ return copy;
173
+ };
174
+
175
+ // For each argument, extend the return
176
+ for (i = 0; i < len; i++) {
177
+ ret = doCopy(ret, arguments[i]);
178
+ }
179
+
180
+ return ret;
181
+ }
182
+
183
+ /**
184
+ * Take an array and turn into a hash with even number arguments as keys and odd numbers as
185
+ * values. Allows creating constants for commonly used style properties, attributes etc.
186
+ * Avoid it in performance critical situations like looping
187
+ */
188
+ function hash() {
189
+ var i = 0,
190
+ args = arguments,
191
+ length = args.length,
192
+ obj = {};
193
+ for (; i < length; i++) {
194
+ obj[args[i++]] = args[i];
195
+ }
196
+ return obj;
197
+ }
198
+
199
+ /**
200
+ * Shortcut for parseInt
201
+ * @param {Object} s
202
+ * @param {Number} mag Magnitude
203
+ */
204
+ function pInt(s, mag) {
205
+ return parseInt(s, mag || 10);
206
+ }
207
+
208
+ /**
209
+ * Check for string
210
+ * @param {Object} s
211
+ */
212
+ function isString(s) {
213
+ return typeof s === 'string';
214
+ }
215
+
216
+ /**
217
+ * Check for object
218
+ * @param {Object} obj
219
+ */
220
+ function isObject(obj) {
221
+ return typeof obj === 'object';
222
+ }
223
+
224
+ /**
225
+ * Check for array
226
+ * @param {Object} obj
227
+ */
228
+ function isArray(obj) {
229
+ return Object.prototype.toString.call(obj) === '[object Array]';
230
+ }
231
+
232
+ /**
233
+ * Check for number
234
+ * @param {Object} n
235
+ */
236
+ function isNumber(n) {
237
+ return typeof n === 'number';
238
+ }
239
+
240
+ function log2lin(num) {
241
+ return math.log(num) / math.LN10;
242
+ }
243
+ function lin2log(num) {
244
+ return math.pow(10, num);
245
+ }
246
+
247
+ /**
248
+ * Remove last occurence of an item from an array
249
+ * @param {Array} arr
250
+ * @param {Mixed} item
251
+ */
252
+ function erase(arr, item) {
253
+ var i = arr.length;
254
+ while (i--) {
255
+ if (arr[i] === item) {
256
+ arr.splice(i, 1);
257
+ break;
258
+ }
259
+ }
260
+ //return arr;
261
+ }
262
+
263
+ /**
264
+ * Returns true if the object is not null or undefined. Like MooTools' $.defined.
265
+ * @param {Object} obj
266
+ */
267
+ function defined(obj) {
268
+ return obj !== UNDEFINED && obj !== null;
269
+ }
270
+
271
+ /**
272
+ * Set or get an attribute or an object of attributes. Can't use jQuery attr because
273
+ * it attempts to set expando properties on the SVG element, which is not allowed.
274
+ *
275
+ * @param {Object} elem The DOM element to receive the attribute(s)
276
+ * @param {String|Object} prop The property or an abject of key-value pairs
277
+ * @param {String} value The value if a single property is set
278
+ */
279
+ function attr(elem, prop, value) {
280
+ var key,
281
+ setAttribute = 'setAttribute',
282
+ ret;
283
+
284
+ // if the prop is a string
285
+ if (isString(prop)) {
286
+ // set the value
287
+ if (defined(value)) {
288
+
289
+ elem[setAttribute](prop, value);
290
+
291
+ // get the value
292
+ } else if (elem && elem.getAttribute) { // elem not defined when printing pie demo...
293
+ ret = elem.getAttribute(prop);
294
+ }
295
+
296
+ // else if prop is defined, it is a hash of key/value pairs
297
+ } else if (defined(prop) && isObject(prop)) {
298
+ for (key in prop) {
299
+ elem[setAttribute](key, prop[key]);
300
+ }
301
+ }
302
+ return ret;
303
+ }
304
+ /**
305
+ * Check if an element is an array, and if not, make it into an array. Like
306
+ * MooTools' $.splat.
307
+ */
308
+ function splat(obj) {
309
+ return isArray(obj) ? obj : [obj];
310
+ }
311
+
312
+
313
+ /**
314
+ * Return the first value that is defined. Like MooTools' $.pick.
315
+ */
316
+ function pick() {
317
+ var args = arguments,
318
+ i,
319
+ arg,
320
+ length = args.length;
321
+ for (i = 0; i < length; i++) {
322
+ arg = args[i];
323
+ if (typeof arg !== 'undefined' && arg !== null) {
324
+ return arg;
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Set CSS on a given element
331
+ * @param {Object} el
332
+ * @param {Object} styles Style object with camel case property names
333
+ */
334
+ function css(el, styles) {
335
+ if (isIE) {
336
+ if (styles && styles.opacity !== UNDEFINED) {
337
+ styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
338
+ }
339
+ }
340
+ extend(el.style, styles);
341
+ }
342
+
343
+ /**
344
+ * Utility function to create element with attributes and styles
345
+ * @param {Object} tag
346
+ * @param {Object} attribs
347
+ * @param {Object} styles
348
+ * @param {Object} parent
349
+ * @param {Object} nopad
350
+ */
351
+ function createElement(tag, attribs, styles, parent, nopad) {
352
+ var el = doc.createElement(tag);
353
+ if (attribs) {
354
+ extend(el, attribs);
355
+ }
356
+ if (nopad) {
357
+ css(el, {padding: 0, border: NONE, margin: 0});
358
+ }
359
+ if (styles) {
360
+ css(el, styles);
361
+ }
362
+ if (parent) {
363
+ parent.appendChild(el);
364
+ }
365
+ return el;
366
+ }
367
+
368
+ /**
369
+ * Extend a prototyped class by new members
370
+ * @param {Object} parent
371
+ * @param {Object} members
372
+ */
373
+ function extendClass(parent, members) {
374
+ var object = function () {};
375
+ object.prototype = new parent();
376
+ extend(object.prototype, members);
377
+ return object;
378
+ }
379
+
380
+ /**
381
+ * Format a number and return a string based on input settings
382
+ * @param {Number} number The input number to format
383
+ * @param {Number} decimals The amount of decimals
384
+ * @param {String} decPoint The decimal point, defaults to the one given in the lang options
385
+ * @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options
386
+ */
387
+ function numberFormat(number, decimals, decPoint, thousandsSep) {
388
+ var lang = defaultOptions.lang,
389
+ // http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/
390
+ n = number,
391
+ c = decimals === -1 ?
392
+ ((n || 0).toString().split('.')[1] || '').length : // preserve decimals
393
+ (isNaN(decimals = mathAbs(decimals)) ? 2 : decimals),
394
+ d = decPoint === undefined ? lang.decimalPoint : decPoint,
395
+ t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep,
396
+ s = n < 0 ? "-" : "",
397
+ i = String(pInt(n = mathAbs(+n || 0).toFixed(c))),
398
+ j = i.length > 3 ? i.length % 3 : 0;
399
+
400
+ return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
401
+ (c ? d + mathAbs(n - i).toFixed(c).slice(2) : "");
402
+ }
403
+
404
+ /**
405
+ * Pad a string to a given length by adding 0 to the beginning
406
+ * @param {Number} number
407
+ * @param {Number} length
408
+ */
409
+ function pad(number, length) {
410
+ // Create an array of the remaining length +1 and join it with 0's
411
+ return new Array((length || 2) + 1 - String(number).length).join(0) + number;
412
+ }
413
+
414
+ /**
415
+ * Wrap a method with extended functionality, preserving the original function
416
+ * @param {Object} obj The context object that the method belongs to
417
+ * @param {String} method The name of the method to extend
418
+ * @param {Function} func A wrapper function callback. This function is called with the same arguments
419
+ * as the original function, except that the original function is unshifted and passed as the first
420
+ * argument.
421
+ */
422
+ function wrap(obj, method, func) {
423
+ var proceed = obj[method];
424
+ obj[method] = function () {
425
+ var args = Array.prototype.slice.call(arguments);
426
+ args.unshift(proceed);
427
+ return func.apply(this, args);
428
+ };
429
+ }
430
+
431
+ /**
432
+ * Based on http://www.php.net/manual/en/function.strftime.php
433
+ * @param {String} format
434
+ * @param {Number} timestamp
435
+ * @param {Boolean} capitalize
436
+ */
437
+ dateFormat = function (format, timestamp, capitalize) {
438
+ if (!defined(timestamp) || isNaN(timestamp)) {
439
+ return 'Invalid date';
440
+ }
441
+ format = pick(format, '%Y-%m-%d %H:%M:%S');
442
+
443
+ var date = new Date(timestamp),
444
+ key, // used in for constuct below
445
+ // get the basic time values
446
+ hours = date[getHours](),
447
+ day = date[getDay](),
448
+ dayOfMonth = date[getDate](),
449
+ month = date[getMonth](),
450
+ fullYear = date[getFullYear](),
451
+ lang = defaultOptions.lang,
452
+ langWeekdays = lang.weekdays,
453
+
454
+ // List all format keys. Custom formats can be added from the outside.
455
+ replacements = extend({
456
+
457
+ // Day
458
+ 'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon'
459
+ 'A': langWeekdays[day], // Long weekday, like 'Monday'
460
+ 'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31
461
+ 'e': dayOfMonth, // Day of the month, 1 through 31
462
+
463
+ // Week (none implemented)
464
+ //'W': weekNumber(),
465
+
466
+ // Month
467
+ 'b': lang.shortMonths[month], // Short month, like 'Jan'
468
+ 'B': lang.months[month], // Long month, like 'January'
469
+ 'm': pad(month + 1), // Two digit month number, 01 through 12
470
+
471
+ // Year
472
+ 'y': fullYear.toString().substr(2, 2), // Two digits year, like 09 for 2009
473
+ 'Y': fullYear, // Four digits year, like 2009
474
+
475
+ // Time
476
+ 'H': pad(hours), // Two digits hours in 24h format, 00 through 23
477
+ 'I': pad((hours % 12) || 12), // Two digits hours in 12h format, 00 through 11
478
+ 'l': (hours % 12) || 12, // Hours in 12h format, 1 through 12
479
+ 'M': pad(date[getMinutes]()), // Two digits minutes, 00 through 59
480
+ 'p': hours < 12 ? 'AM' : 'PM', // Upper case AM or PM
481
+ 'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM
482
+ 'S': pad(date.getSeconds()), // Two digits seconds, 00 through 59
483
+ 'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby)
484
+ }, Highcharts.dateFormats);
485
+
486
+
487
+ // do the replaces
488
+ for (key in replacements) {
489
+ while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster
490
+ format = format.replace('%' + key, typeof replacements[key] === 'function' ? replacements[key](timestamp) : replacements[key]);
491
+ }
492
+ }
493
+
494
+ // Optionally capitalize the string and return
495
+ return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format;
496
+ };
497
+
498
+ /**
499
+ * Format a single variable. Similar to sprintf, without the % prefix.
500
+ */
501
+ function formatSingle(format, val) {
502
+ var floatRegex = /f$/,
503
+ decRegex = /\.([0-9])/,
504
+ lang = defaultOptions.lang,
505
+ decimals;
506
+
507
+ if (floatRegex.test(format)) { // float
508
+ decimals = format.match(decRegex);
509
+ decimals = decimals ? decimals[1] : -1;
510
+ val = numberFormat(
511
+ val,
512
+ decimals,
513
+ lang.decimalPoint,
514
+ format.indexOf(',') > -1 ? lang.thousandsSep : ''
515
+ );
516
+ } else {
517
+ val = dateFormat(format, val);
518
+ }
519
+ return val;
520
+ }
521
+
522
+ /**
523
+ * Format a string according to a subset of the rules of Python's String.format method.
524
+ */
525
+ function format(str, ctx) {
526
+ var splitter = '{',
527
+ isInside = false,
528
+ segment,
529
+ valueAndFormat,
530
+ path,
531
+ i,
532
+ len,
533
+ ret = [],
534
+ val,
535
+ index;
536
+
537
+ while ((index = str.indexOf(splitter)) !== -1) {
538
+
539
+ segment = str.slice(0, index);
540
+ if (isInside) { // we're on the closing bracket looking back
541
+
542
+ valueAndFormat = segment.split(':');
543
+ path = valueAndFormat.shift().split('.'); // get first and leave format
544
+ len = path.length;
545
+ val = ctx;
546
+
547
+ // Assign deeper paths
548
+ for (i = 0; i < len; i++) {
549
+ val = val[path[i]];
550
+ }
551
+
552
+ // Format the replacement
553
+ if (valueAndFormat.length) {
554
+ val = formatSingle(valueAndFormat.join(':'), val);
555
+ }
556
+
557
+ // Push the result and advance the cursor
558
+ ret.push(val);
559
+
560
+ } else {
561
+ ret.push(segment);
562
+
563
+ }
564
+ str = str.slice(index + 1); // the rest
565
+ isInside = !isInside; // toggle
566
+ splitter = isInside ? '}' : '{'; // now look for next matching bracket
567
+ }
568
+ ret.push(str);
569
+ return ret.join('');
570
+ }
571
+
572
+ /**
573
+ * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5
574
+ * @param {Number} interval
575
+ * @param {Array} multiples
576
+ * @param {Number} magnitude
577
+ * @param {Object} options
578
+ */
579
+ function normalizeTickInterval(interval, multiples, magnitude, options) {
580
+ var normalized, i;
581
+
582
+ // round to a tenfold of 1, 2, 2.5 or 5
583
+ magnitude = pick(magnitude, 1);
584
+ normalized = interval / magnitude;
585
+
586
+ // multiples for a linear scale
587
+ if (!multiples) {
588
+ multiples = [1, 2, 2.5, 5, 10];
589
+
590
+ // the allowDecimals option
591
+ if (options && options.allowDecimals === false) {
592
+ if (magnitude === 1) {
593
+ multiples = [1, 2, 5, 10];
594
+ } else if (magnitude <= 0.1) {
595
+ multiples = [1 / magnitude];
596
+ }
597
+ }
598
+ }
599
+
600
+ // normalize the interval to the nearest multiple
601
+ for (i = 0; i < multiples.length; i++) {
602
+ interval = multiples[i];
603
+ if (normalized <= (multiples[i] + (multiples[i + 1] || multiples[i])) / 2) {
604
+ break;
605
+ }
606
+ }
607
+
608
+ // multiply back to the correct magnitude
609
+ interval *= magnitude;
610
+
611
+ return interval;
612
+ }
613
+
614
+ /**
615
+ * Get a normalized tick interval for dates. Returns a configuration object with
616
+ * unit range (interval), count and name. Used to prepare data for getTimeTicks.
617
+ * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
618
+ * of segments in stock charts, the normalizing logic was extracted in order to
619
+ * prevent it for running over again for each segment having the same interval.
620
+ * #662, #697.
621
+ */
622
+ function normalizeTimeTickInterval(tickInterval, unitsOption) {
623
+ var units = unitsOption || [[
624
+ MILLISECOND, // unit name
625
+ [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
626
+ ], [
627
+ SECOND,
628
+ [1, 2, 5, 10, 15, 30]
629
+ ], [
630
+ MINUTE,
631
+ [1, 2, 5, 10, 15, 30]
632
+ ], [
633
+ HOUR,
634
+ [1, 2, 3, 4, 6, 8, 12]
635
+ ], [
636
+ DAY,
637
+ [1, 2]
638
+ ], [
639
+ WEEK,
640
+ [1, 2]
641
+ ], [
642
+ MONTH,
643
+ [1, 2, 3, 4, 6]
644
+ ], [
645
+ YEAR,
646
+ null
647
+ ]],
648
+ unit = units[units.length - 1], // default unit is years
649
+ interval = timeUnits[unit[0]],
650
+ multiples = unit[1],
651
+ count,
652
+ i;
653
+
654
+ // loop through the units to find the one that best fits the tickInterval
655
+ for (i = 0; i < units.length; i++) {
656
+ unit = units[i];
657
+ interval = timeUnits[unit[0]];
658
+ multiples = unit[1];
659
+
660
+
661
+ if (units[i + 1]) {
662
+ // lessThan is in the middle between the highest multiple and the next unit.
663
+ var lessThan = (interval * multiples[multiples.length - 1] +
664
+ timeUnits[units[i + 1][0]]) / 2;
665
+
666
+ // break and keep the current unit
667
+ if (tickInterval <= lessThan) {
668
+ break;
669
+ }
670
+ }
671
+ }
672
+
673
+ // prevent 2.5 years intervals, though 25, 250 etc. are allowed
674
+ if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
675
+ multiples = [1, 2, 5];
676
+ }
677
+
678
+ // prevent 2.5 years intervals, though 25, 250 etc. are allowed
679
+ if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
680
+ multiples = [1, 2, 5];
681
+ }
682
+
683
+ // get the count
684
+ count = normalizeTickInterval(tickInterval / interval, multiples);
685
+
686
+ return {
687
+ unitRange: interval,
688
+ count: count,
689
+ unitName: unit[0]
690
+ };
691
+ }
692
+
693
+ /**
694
+ * Set the tick positions to a time unit that makes sense, for example
695
+ * on the first of each month or on every Monday. Return an array
696
+ * with the time positions. Used in datetime axes as well as for grouping
697
+ * data on a datetime axis.
698
+ *
699
+ * @param {Object} normalizedInterval The interval in axis values (ms) and the count
700
+ * @param {Number} min The minimum in axis values
701
+ * @param {Number} max The maximum in axis values
702
+ * @param {Number} startOfWeek
703
+ */
704
+ function getTimeTicks(normalizedInterval, min, max, startOfWeek) {
705
+ var tickPositions = [],
706
+ i,
707
+ higherRanks = {},
708
+ useUTC = defaultOptions.global.useUTC,
709
+ minYear, // used in months and years as a basis for Date.UTC()
710
+ minDate = new Date(min),
711
+ interval = normalizedInterval.unitRange,
712
+ count = normalizedInterval.count;
713
+
714
+ if (defined(min)) { // #1300
715
+ if (interval >= timeUnits[SECOND]) { // second
716
+ minDate.setMilliseconds(0);
717
+ minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 :
718
+ count * mathFloor(minDate.getSeconds() / count));
719
+ }
720
+
721
+ if (interval >= timeUnits[MINUTE]) { // minute
722
+ minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 :
723
+ count * mathFloor(minDate[getMinutes]() / count));
724
+ }
725
+
726
+ if (interval >= timeUnits[HOUR]) { // hour
727
+ minDate[setHours](interval >= timeUnits[DAY] ? 0 :
728
+ count * mathFloor(minDate[getHours]() / count));
729
+ }
730
+
731
+ if (interval >= timeUnits[DAY]) { // day
732
+ minDate[setDate](interval >= timeUnits[MONTH] ? 1 :
733
+ count * mathFloor(minDate[getDate]() / count));
734
+ }
735
+
736
+ if (interval >= timeUnits[MONTH]) { // month
737
+ minDate[setMonth](interval >= timeUnits[YEAR] ? 0 :
738
+ count * mathFloor(minDate[getMonth]() / count));
739
+ minYear = minDate[getFullYear]();
740
+ }
741
+
742
+ if (interval >= timeUnits[YEAR]) { // year
743
+ minYear -= minYear % count;
744
+ minDate[setFullYear](minYear);
745
+ }
746
+
747
+ // week is a special case that runs outside the hierarchy
748
+ if (interval === timeUnits[WEEK]) {
749
+ // get start of current week, independent of count
750
+ minDate[setDate](minDate[getDate]() - minDate[getDay]() +
751
+ pick(startOfWeek, 1));
752
+ }
753
+
754
+
755
+ // get tick positions
756
+ i = 1;
757
+ minYear = minDate[getFullYear]();
758
+ var time = minDate.getTime(),
759
+ minMonth = minDate[getMonth](),
760
+ minDateDate = minDate[getDate](),
761
+ timezoneOffset = useUTC ?
762
+ 0 :
763
+ (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950
764
+
765
+ // iterate and add tick positions at appropriate values
766
+ while (time < max) {
767
+ tickPositions.push(time);
768
+
769
+ // if the interval is years, use Date.UTC to increase years
770
+ if (interval === timeUnits[YEAR]) {
771
+ time = makeTime(minYear + i * count, 0);
772
+
773
+ // if the interval is months, use Date.UTC to increase months
774
+ } else if (interval === timeUnits[MONTH]) {
775
+ time = makeTime(minYear, minMonth + i * count);
776
+
777
+ // if we're using global time, the interval is not fixed as it jumps
778
+ // one hour at the DST crossover
779
+ } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) {
780
+ time = makeTime(minYear, minMonth, minDateDate +
781
+ i * count * (interval === timeUnits[DAY] ? 1 : 7));
782
+
783
+ // else, the interval is fixed and we use simple addition
784
+ } else {
785
+ time += interval * count;
786
+ }
787
+
788
+ i++;
789
+ }
790
+
791
+ // push the last time
792
+ tickPositions.push(time);
793
+
794
+
795
+ // mark new days if the time is dividible by day (#1649, #1760)
796
+ each(grep(tickPositions, function (time) {
797
+ return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset;
798
+ }), function (time) {
799
+ higherRanks[time] = DAY;
800
+ });
801
+ }
802
+
803
+
804
+ // record information on the chosen unit - for dynamic label formatter
805
+ tickPositions.info = extend(normalizedInterval, {
806
+ higherRanks: higherRanks,
807
+ totalRange: interval * count
808
+ });
809
+
810
+ return tickPositions;
811
+ }
812
+
813
+ /**
814
+ * Helper class that contains variuos counters that are local to the chart.
815
+ */
816
+ function ChartCounters() {
817
+ this.color = 0;
818
+ this.symbol = 0;
819
+ }
820
+
821
+ ChartCounters.prototype = {
822
+ /**
823
+ * Wraps the color counter if it reaches the specified length.
824
+ */
825
+ wrapColor: function (length) {
826
+ if (this.color >= length) {
827
+ this.color = 0;
828
+ }
829
+ },
830
+
831
+ /**
832
+ * Wraps the symbol counter if it reaches the specified length.
833
+ */
834
+ wrapSymbol: function (length) {
835
+ if (this.symbol >= length) {
836
+ this.symbol = 0;
837
+ }
838
+ }
839
+ };
840
+
841
+
842
+ /**
843
+ * Utility method that sorts an object array and keeping the order of equal items.
844
+ * ECMA script standard does not specify the behaviour when items are equal.
845
+ */
846
+ function stableSort(arr, sortFunction) {
847
+ var length = arr.length,
848
+ sortValue,
849
+ i;
850
+
851
+ // Add index to each item
852
+ for (i = 0; i < length; i++) {
853
+ arr[i].ss_i = i; // stable sort index
854
+ }
855
+
856
+ arr.sort(function (a, b) {
857
+ sortValue = sortFunction(a, b);
858
+ return sortValue === 0 ? a.ss_i - b.ss_i : sortValue;
859
+ });
860
+
861
+ // Remove index from items
862
+ for (i = 0; i < length; i++) {
863
+ delete arr[i].ss_i; // stable sort index
864
+ }
865
+ }
866
+
867
+ /**
868
+ * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
869
+ * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
870
+ * method is slightly slower, but safe.
871
+ */
872
+ function arrayMin(data) {
873
+ var i = data.length,
874
+ min = data[0];
875
+
876
+ while (i--) {
877
+ if (data[i] < min) {
878
+ min = data[i];
879
+ }
880
+ }
881
+ return min;
882
+ }
883
+
884
+ /**
885
+ * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
886
+ * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
887
+ * method is slightly slower, but safe.
888
+ */
889
+ function arrayMax(data) {
890
+ var i = data.length,
891
+ max = data[0];
892
+
893
+ while (i--) {
894
+ if (data[i] > max) {
895
+ max = data[i];
896
+ }
897
+ }
898
+ return max;
899
+ }
900
+
901
+ /**
902
+ * Utility method that destroys any SVGElement or VMLElement that are properties on the given object.
903
+ * It loops all properties and invokes destroy if there is a destroy method. The property is
904
+ * then delete'ed.
905
+ * @param {Object} The object to destroy properties on
906
+ * @param {Object} Exception, do not destroy this property, only delete it.
907
+ */
908
+ function destroyObjectProperties(obj, except) {
909
+ var n;
910
+ for (n in obj) {
911
+ // If the object is non-null and destroy is defined
912
+ if (obj[n] && obj[n] !== except && obj[n].destroy) {
913
+ // Invoke the destroy
914
+ obj[n].destroy();
915
+ }
916
+
917
+ // Delete the property from the object.
918
+ delete obj[n];
919
+ }
920
+ }
921
+
922
+
923
+ /**
924
+ * Discard an element by moving it to the bin and delete
925
+ * @param {Object} The HTML node to discard
926
+ */
927
+ function discardElement(element) {
928
+ // create a garbage bin element, not part of the DOM
929
+ if (!garbageBin) {
930
+ garbageBin = createElement(DIV);
931
+ }
932
+
933
+ // move the node and empty bin
934
+ if (element) {
935
+ garbageBin.appendChild(element);
936
+ }
937
+ garbageBin.innerHTML = '';
938
+ }
939
+
940
+ /**
941
+ * Provide error messages for debugging, with links to online explanation
942
+ */
943
+ function error(code, stop) {
944
+ var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code;
945
+ if (stop) {
946
+ throw msg;
947
+ } else if (win.console) {
948
+ console.log(msg);
949
+ }
950
+ }
951
+
952
+ /**
953
+ * Fix JS round off float errors
954
+ * @param {Number} num
955
+ */
956
+ function correctFloat(num) {
957
+ return parseFloat(
958
+ num.toPrecision(14)
959
+ );
960
+ }
961
+
962
+ /**
963
+ * Set the global animation to either a given value, or fall back to the
964
+ * given chart's animation option
965
+ * @param {Object} animation
966
+ * @param {Object} chart
967
+ */
968
+ function setAnimation(animation, chart) {
969
+ globalAnimation = pick(animation, chart.animation);
970
+ }
971
+
972
+ /**
973
+ * The time unit lookup
974
+ */
975
+ /*jslint white: true*/
976
+ timeUnits = hash(
977
+ MILLISECOND, 1,
978
+ SECOND, 1000,
979
+ MINUTE, 60000,
980
+ HOUR, 3600000,
981
+ DAY, 24 * 3600000,
982
+ WEEK, 7 * 24 * 3600000,
983
+ MONTH, 31 * 24 * 3600000,
984
+ YEAR, 31556952000
985
+ );
986
+ /*jslint white: false*/
987
+ /**
988
+ * Path interpolation algorithm used across adapters
989
+ */
990
+ pathAnim = {
991
+ /**
992
+ * Prepare start and end values so that the path can be animated one to one
993
+ */
994
+ init: function (elem, fromD, toD) {
995
+ fromD = fromD || '';
996
+ var shift = elem.shift,
997
+ bezier = fromD.indexOf('C') > -1,
998
+ numParams = bezier ? 7 : 3,
999
+ endLength,
1000
+ slice,
1001
+ i,
1002
+ start = fromD.split(' '),
1003
+ end = [].concat(toD), // copy
1004
+ startBaseLine,
1005
+ endBaseLine,
1006
+ sixify = function (arr) { // in splines make move points have six parameters like bezier curves
1007
+ i = arr.length;
1008
+ while (i--) {
1009
+ if (arr[i] === M) {
1010
+ arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]);
1011
+ }
1012
+ }
1013
+ };
1014
+
1015
+ if (bezier) {
1016
+ sixify(start);
1017
+ sixify(end);
1018
+ }
1019
+
1020
+ // pull out the base lines before padding
1021
+ if (elem.isArea) {
1022
+ startBaseLine = start.splice(start.length - 6, 6);
1023
+ endBaseLine = end.splice(end.length - 6, 6);
1024
+ }
1025
+
1026
+ // if shifting points, prepend a dummy point to the end path
1027
+ if (shift <= end.length / numParams) {
1028
+ while (shift--) {
1029
+ end = [].concat(end).splice(0, numParams).concat(end);
1030
+ }
1031
+ }
1032
+ elem.shift = 0; // reset for following animations
1033
+
1034
+ // copy and append last point until the length matches the end length
1035
+ if (start.length) {
1036
+ endLength = end.length;
1037
+ while (start.length < endLength) {
1038
+
1039
+ //bezier && sixify(start);
1040
+ slice = [].concat(start).splice(start.length - numParams, numParams);
1041
+ if (bezier) { // disable first control point
1042
+ slice[numParams - 6] = slice[numParams - 2];
1043
+ slice[numParams - 5] = slice[numParams - 1];
1044
+ }
1045
+ start = start.concat(slice);
1046
+ }
1047
+ }
1048
+
1049
+ if (startBaseLine) { // append the base lines for areas
1050
+ start = start.concat(startBaseLine);
1051
+ end = end.concat(endBaseLine);
1052
+ }
1053
+ return [start, end];
1054
+ },
1055
+
1056
+ /**
1057
+ * Interpolate each value of the path and return the array
1058
+ */
1059
+ step: function (start, end, pos, complete) {
1060
+ var ret = [],
1061
+ i = start.length,
1062
+ startVal;
1063
+
1064
+ if (pos === 1) { // land on the final path without adjustment points appended in the ends
1065
+ ret = complete;
1066
+
1067
+ } else if (i === end.length && pos < 1) {
1068
+ while (i--) {
1069
+ startVal = parseFloat(start[i]);
1070
+ ret[i] =
1071
+ isNaN(startVal) ? // a letter instruction like M or L
1072
+ start[i] :
1073
+ pos * (parseFloat(end[i] - startVal)) + startVal;
1074
+
1075
+ }
1076
+ } else { // if animation is finished or length not matching, land on right value
1077
+ ret = end;
1078
+ }
1079
+ return ret;
1080
+ }
1081
+ };
1082
+
1083
+ (function ($) {
1084
+ /**
1085
+ * The default HighchartsAdapter for jQuery
1086
+ */
1087
+ win.HighchartsAdapter = win.HighchartsAdapter || ($ && {
1088
+
1089
+ /**
1090
+ * Initialize the adapter by applying some extensions to jQuery
1091
+ */
1092
+ init: function (pathAnim) {
1093
+
1094
+ // extend the animate function to allow SVG animations
1095
+ var Fx = $.fx,
1096
+ Step = Fx.step,
1097
+ dSetter,
1098
+ Tween = $.Tween,
1099
+ propHooks = Tween && Tween.propHooks,
1100
+ opacityHook = $.cssHooks.opacity;
1101
+
1102
+ /*jslint unparam: true*//* allow unused param x in this function */
1103
+ $.extend($.easing, {
1104
+ easeOutQuad: function (x, t, b, c, d) {
1105
+ return -c * (t /= d) * (t - 2) + b;
1106
+ }
1107
+ });
1108
+ /*jslint unparam: false*/
1109
+
1110
+ // extend some methods to check for elem.attr, which means it is a Highcharts SVG object
1111
+ $.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) {
1112
+ var obj = Step,
1113
+ base,
1114
+ elem;
1115
+
1116
+ // Handle different parent objects
1117
+ if (fn === 'cur') {
1118
+ obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype
1119
+
1120
+ } else if (fn === '_default' && Tween) { // jQuery 1.8 model
1121
+ obj = propHooks[fn];
1122
+ fn = 'set';
1123
+ }
1124
+
1125
+ // Overwrite the method
1126
+ base = obj[fn];
1127
+ if (base) { // step.width and step.height don't exist in jQuery < 1.7
1128
+
1129
+ // create the extended function replacement
1130
+ obj[fn] = function (fx) {
1131
+
1132
+ // Fx.prototype.cur does not use fx argument
1133
+ fx = i ? fx : this;
1134
+
1135
+ // shortcut
1136
+ elem = fx.elem;
1137
+
1138
+ // Fx.prototype.cur returns the current value. The other ones are setters
1139
+ // and returning a value has no effect.
1140
+ return elem.attr ? // is SVG element wrapper
1141
+ elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method
1142
+ base.apply(this, arguments); // use jQuery's built-in method
1143
+ };
1144
+ }
1145
+ });
1146
+
1147
+ // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+
1148
+ wrap(opacityHook, 'get', function (proceed, elem, computed) {
1149
+ return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed);
1150
+ });
1151
+
1152
+
1153
+ // Define the setter function for d (path definitions)
1154
+ dSetter = function (fx) {
1155
+ var elem = fx.elem,
1156
+ ends;
1157
+
1158
+ // Normally start and end should be set in state == 0, but sometimes,
1159
+ // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped
1160
+ // in these cases
1161
+ if (!fx.started) {
1162
+ ends = pathAnim.init(elem, elem.d, elem.toD);
1163
+ fx.start = ends[0];
1164
+ fx.end = ends[1];
1165
+ fx.started = true;
1166
+ }
1167
+
1168
+
1169
+ // interpolate each value of the path
1170
+ elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD));
1171
+ };
1172
+
1173
+ // jQuery 1.8 style
1174
+ if (Tween) {
1175
+ propHooks.d = {
1176
+ set: dSetter
1177
+ };
1178
+ // pre 1.8
1179
+ } else {
1180
+ // animate paths
1181
+ Step.d = dSetter;
1182
+ }
1183
+
1184
+ /**
1185
+ * Utility for iterating over an array. Parameters are reversed compared to jQuery.
1186
+ * @param {Array} arr
1187
+ * @param {Function} fn
1188
+ */
1189
+ this.each = Array.prototype.forEach ?
1190
+ function (arr, fn) { // modern browsers
1191
+ return Array.prototype.forEach.call(arr, fn);
1192
+
1193
+ } :
1194
+ function (arr, fn) { // legacy
1195
+ var i = 0,
1196
+ len = arr.length;
1197
+ for (; i < len; i++) {
1198
+ if (fn.call(arr[i], arr[i], i, arr) === false) {
1199
+ return i;
1200
+ }
1201
+ }
1202
+ };
1203
+
1204
+ /**
1205
+ * Register Highcharts as a plugin in the respective framework
1206
+ */
1207
+ $.fn.highcharts = function () {
1208
+ var constr = 'Chart', // default constructor
1209
+ args = arguments,
1210
+ options,
1211
+ ret,
1212
+ chart;
1213
+
1214
+ if (isString(args[0])) {
1215
+ constr = args[0];
1216
+ args = Array.prototype.slice.call(args, 1);
1217
+ }
1218
+ options = args[0];
1219
+
1220
+ // Create the chart
1221
+ if (options !== UNDEFINED) {
1222
+ /*jslint unused:false*/
1223
+ options.chart = options.chart || {};
1224
+ options.chart.renderTo = this[0];
1225
+ chart = new Highcharts[constr](options, args[1]);
1226
+ ret = this;
1227
+ /*jslint unused:true*/
1228
+ }
1229
+
1230
+ // When called without parameters or with the return argument, get a predefined chart
1231
+ if (options === UNDEFINED) {
1232
+ ret = charts[attr(this[0], 'data-highcharts-chart')];
1233
+ }
1234
+
1235
+ return ret;
1236
+ };
1237
+
1238
+ },
1239
+
1240
+
1241
+ /**
1242
+ * Downloads a script and executes a callback when done.
1243
+ * @param {String} scriptLocation
1244
+ * @param {Function} callback
1245
+ */
1246
+ getScript: $.getScript,
1247
+
1248
+ /**
1249
+ * Return the index of an item in an array, or -1 if not found
1250
+ */
1251
+ inArray: $.inArray,
1252
+
1253
+ /**
1254
+ * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method.
1255
+ * @param {Object} elem The HTML element
1256
+ * @param {String} method Which method to run on the wrapped element
1257
+ */
1258
+ adapterRun: function (elem, method) {
1259
+ return $(elem)[method]();
1260
+ },
1261
+
1262
+ /**
1263
+ * Filter an array
1264
+ */
1265
+ grep: $.grep,
1266
+
1267
+ /**
1268
+ * Map an array
1269
+ * @param {Array} arr
1270
+ * @param {Function} fn
1271
+ */
1272
+ map: function (arr, fn) {
1273
+ //return jQuery.map(arr, fn);
1274
+ var results = [],
1275
+ i = 0,
1276
+ len = arr.length;
1277
+ for (; i < len; i++) {
1278
+ results[i] = fn.call(arr[i], arr[i], i, arr);
1279
+ }
1280
+ return results;
1281
+
1282
+ },
1283
+
1284
+ /**
1285
+ * Get the position of an element relative to the top left of the page
1286
+ */
1287
+ offset: function (el) {
1288
+ return $(el).offset();
1289
+ },
1290
+
1291
+ /**
1292
+ * Add an event listener
1293
+ * @param {Object} el A HTML element or custom object
1294
+ * @param {String} event The event type
1295
+ * @param {Function} fn The event handler
1296
+ */
1297
+ addEvent: function (el, event, fn) {
1298
+ $(el).bind(event, fn);
1299
+ },
1300
+
1301
+ /**
1302
+ * Remove event added with addEvent
1303
+ * @param {Object} el The object
1304
+ * @param {String} eventType The event type. Leave blank to remove all events.
1305
+ * @param {Function} handler The function to remove
1306
+ */
1307
+ removeEvent: function (el, eventType, handler) {
1308
+ // workaround for jQuery issue with unbinding custom events:
1309
+ // http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2
1310
+ var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent';
1311
+ if (doc[func] && el && !el[func]) {
1312
+ el[func] = function () {};
1313
+ }
1314
+
1315
+ $(el).unbind(eventType, handler);
1316
+ },
1317
+
1318
+ /**
1319
+ * Fire an event on a custom object
1320
+ * @param {Object} el
1321
+ * @param {String} type
1322
+ * @param {Object} eventArguments
1323
+ * @param {Function} defaultFunction
1324
+ */
1325
+ fireEvent: function (el, type, eventArguments, defaultFunction) {
1326
+ var event = $.Event(type),
1327
+ detachedType = 'detached' + type,
1328
+ defaultPrevented;
1329
+
1330
+ // Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts
1331
+ // never uses these properties, Chrome includes them in the default click event and
1332
+ // raises the warning when they are copied over in the extend statement below.
1333
+ //
1334
+ // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid
1335
+ // testing if they are there (warning in chrome) the only option is to test if running IE.
1336
+ if (!isIE && eventArguments) {
1337
+ delete eventArguments.layerX;
1338
+ delete eventArguments.layerY;
1339
+ }
1340
+
1341
+ extend(event, eventArguments);
1342
+
1343
+ // Prevent jQuery from triggering the object method that is named the
1344
+ // same as the event. For example, if the event is 'select', jQuery
1345
+ // attempts calling el.select and it goes into a loop.
1346
+ if (el[type]) {
1347
+ el[detachedType] = el[type];
1348
+ el[type] = null;
1349
+ }
1350
+
1351
+ // Wrap preventDefault and stopPropagation in try/catch blocks in
1352
+ // order to prevent JS errors when cancelling events on non-DOM
1353
+ // objects. #615.
1354
+ /*jslint unparam: true*/
1355
+ $.each(['preventDefault', 'stopPropagation'], function (i, fn) {
1356
+ var base = event[fn];
1357
+ event[fn] = function () {
1358
+ try {
1359
+ base.call(event);
1360
+ } catch (e) {
1361
+ if (fn === 'preventDefault') {
1362
+ defaultPrevented = true;
1363
+ }
1364
+ }
1365
+ };
1366
+ });
1367
+ /*jslint unparam: false*/
1368
+
1369
+ // trigger it
1370
+ $(el).trigger(event);
1371
+
1372
+ // attach the method
1373
+ if (el[detachedType]) {
1374
+ el[type] = el[detachedType];
1375
+ el[detachedType] = null;
1376
+ }
1377
+
1378
+ if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) {
1379
+ defaultFunction(event);
1380
+ }
1381
+ },
1382
+
1383
+ /**
1384
+ * Extension method needed for MooTools
1385
+ */
1386
+ washMouseEvent: function (e) {
1387
+ var ret = e.originalEvent || e;
1388
+
1389
+ // computed by jQuery, needed by IE8
1390
+ if (ret.pageX === UNDEFINED) { // #1236
1391
+ ret.pageX = e.pageX;
1392
+ ret.pageY = e.pageY;
1393
+ }
1394
+
1395
+ return ret;
1396
+ },
1397
+
1398
+ /**
1399
+ * Animate a HTML element or SVG element wrapper
1400
+ * @param {Object} el
1401
+ * @param {Object} params
1402
+ * @param {Object} options jQuery-like animation options: duration, easing, callback
1403
+ */
1404
+ animate: function (el, params, options) {
1405
+ var $el = $(el);
1406
+ if (!el.style) {
1407
+ el.style = {}; // #1881
1408
+ }
1409
+ if (params.d) {
1410
+ el.toD = params.d; // keep the array form for paths, used in $.fx.step.d
1411
+ params.d = 1; // because in jQuery, animating to an array has a different meaning
1412
+ }
1413
+
1414
+ $el.stop();
1415
+ $el.animate(params, options);
1416
+
1417
+ },
1418
+ /**
1419
+ * Stop running animation
1420
+ */
1421
+ stop: function (el) {
1422
+ $(el).stop();
1423
+ }
1424
+ });
1425
+ }(win.jQuery));
1426
+
1427
+
1428
+ // check for a custom HighchartsAdapter defined prior to this file
1429
+ var globalAdapter = win.HighchartsAdapter,
1430
+ adapter = globalAdapter || {};
1431
+
1432
+ // Initialize the adapter
1433
+ if (globalAdapter) {
1434
+ globalAdapter.init.call(globalAdapter, pathAnim);
1435
+ }
1436
+
1437
+
1438
+ // Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object
1439
+ // and all the utility functions will be null. In that case they are populated by the
1440
+ // default adapters below.
1441
+ var adapterRun = adapter.adapterRun,
1442
+ getScript = adapter.getScript,
1443
+ inArray = adapter.inArray,
1444
+ each = adapter.each,
1445
+ grep = adapter.grep,
1446
+ offset = adapter.offset,
1447
+ map = adapter.map,
1448
+ addEvent = adapter.addEvent,
1449
+ removeEvent = adapter.removeEvent,
1450
+ fireEvent = adapter.fireEvent,
1451
+ washMouseEvent = adapter.washMouseEvent,
1452
+ animate = adapter.animate,
1453
+ stop = adapter.stop;
1454
+
1455
+
1456
+
1457
+ /* ****************************************************************************
1458
+ * Handle the options *
1459
+ *****************************************************************************/
1460
+ var
1461
+
1462
+ defaultLabelOptions = {
1463
+ enabled: true,
1464
+ // rotation: 0,
1465
+ align: 'center',
1466
+ x: 0,
1467
+ y: 15,
1468
+ /*formatter: function () {
1469
+ return this.value;
1470
+ },*/
1471
+ style: {
1472
+ color: '#666',
1473
+ cursor: 'default',
1474
+ fontSize: '11px',
1475
+ lineHeight: '14px'
1476
+ }
1477
+ };
1478
+
1479
+ defaultOptions = {
1480
+ colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce', '#492970',
1481
+ '#f28f43', '#77a1e5', '#c42525', '#a6c96a'],
1482
+ symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
1483
+ lang: {
1484
+ loading: 'Loading...',
1485
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
1486
+ 'August', 'September', 'October', 'November', 'December'],
1487
+ shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
1488
+ weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1489
+ decimalPoint: '.',
1490
+ numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels
1491
+ resetZoom: 'Reset zoom',
1492
+ resetZoomTitle: 'Reset zoom level 1:1',
1493
+ thousandsSep: ','
1494
+ },
1495
+ global: {
1496
+ useUTC: true,
1497
+ canvasToolsURL: 'http://code.highcharts.com/3.0.2/modules/canvas-tools.js',
1498
+ VMLRadialGradientURL: 'http://code.highcharts.com/3.0.2/gfx/vml-radial-gradient.png'
1499
+ },
1500
+ chart: {
1501
+ //animation: true,
1502
+ //alignTicks: false,
1503
+ //reflow: true,
1504
+ //className: null,
1505
+ //events: { load, selection },
1506
+ //margin: [null],
1507
+ //marginTop: null,
1508
+ //marginRight: null,
1509
+ //marginBottom: null,
1510
+ //marginLeft: null,
1511
+ borderColor: '#4572A7',
1512
+ //borderWidth: 0,
1513
+ borderRadius: 5,
1514
+ defaultSeriesType: 'line',
1515
+ ignoreHiddenSeries: true,
1516
+ //inverted: false,
1517
+ //shadow: false,
1518
+ spacingTop: 10,
1519
+ spacingRight: 10,
1520
+ spacingBottom: 15,
1521
+ spacingLeft: 10,
1522
+ style: {
1523
+ fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font
1524
+ fontSize: '12px'
1525
+ },
1526
+ backgroundColor: '#FFFFFF',
1527
+ //plotBackgroundColor: null,
1528
+ plotBorderColor: '#C0C0C0',
1529
+ //plotBorderWidth: 0,
1530
+ //plotShadow: false,
1531
+ //zoomType: ''
1532
+ resetZoomButton: {
1533
+ theme: {
1534
+ zIndex: 20
1535
+ },
1536
+ position: {
1537
+ align: 'right',
1538
+ x: -10,
1539
+ //verticalAlign: 'top',
1540
+ y: 10
1541
+ }
1542
+ // relativeTo: 'plot'
1543
+ }
1544
+ },
1545
+ title: {
1546
+ text: 'Chart title',
1547
+ align: 'center',
1548
+ // floating: false,
1549
+ // margin: 15,
1550
+ // x: 0,
1551
+ // verticalAlign: 'top',
1552
+ y: 15,
1553
+ style: {
1554
+ color: '#274b6d',//#3E576F',
1555
+ fontSize: '16px'
1556
+ }
1557
+
1558
+ },
1559
+ subtitle: {
1560
+ text: '',
1561
+ align: 'center',
1562
+ // floating: false
1563
+ // x: 0,
1564
+ // verticalAlign: 'top',
1565
+ y: 30,
1566
+ style: {
1567
+ color: '#4d759e'
1568
+ }
1569
+ },
1570
+
1571
+ plotOptions: {
1572
+ line: { // base series options
1573
+ allowPointSelect: false,
1574
+ showCheckbox: false,
1575
+ animation: {
1576
+ duration: 1000
1577
+ },
1578
+ //connectNulls: false,
1579
+ //cursor: 'default',
1580
+ //clip: true,
1581
+ //dashStyle: null,
1582
+ //enableMouseTracking: true,
1583
+ events: {},
1584
+ //legendIndex: 0,
1585
+ lineWidth: 2,
1586
+ //shadow: false,
1587
+ // stacking: null,
1588
+ marker: {
1589
+ enabled: true,
1590
+ //symbol: null,
1591
+ lineWidth: 0,
1592
+ radius: 4,
1593
+ lineColor: '#FFFFFF',
1594
+ //fillColor: null,
1595
+ states: { // states for a single point
1596
+ hover: {
1597
+ enabled: true
1598
+ //radius: base + 2
1599
+ },
1600
+ select: {
1601
+ fillColor: '#FFFFFF',
1602
+ lineColor: '#000000',
1603
+ lineWidth: 2
1604
+ }
1605
+ }
1606
+ },
1607
+ point: {
1608
+ events: {}
1609
+ },
1610
+ dataLabels: merge(defaultLabelOptions, {
1611
+ enabled: false,
1612
+ formatter: function () {
1613
+ return numberFormat(this.y, -1);
1614
+ },
1615
+ verticalAlign: 'bottom', // above singular point
1616
+ y: 0
1617
+ // backgroundColor: undefined,
1618
+ // borderColor: undefined,
1619
+ // borderRadius: undefined,
1620
+ // borderWidth: undefined,
1621
+ // padding: 3,
1622
+ // shadow: false
1623
+ }),
1624
+ cropThreshold: 300, // draw points outside the plot area when the number of points is less than this
1625
+ pointRange: 0,
1626
+ //pointStart: 0,
1627
+ //pointInterval: 1,
1628
+ showInLegend: true,
1629
+ states: { // states for the entire series
1630
+ hover: {
1631
+ //enabled: false,
1632
+ //lineWidth: base + 1,
1633
+ marker: {
1634
+ // lineWidth: base + 1,
1635
+ // radius: base + 1
1636
+ }
1637
+ },
1638
+ select: {
1639
+ marker: {}
1640
+ }
1641
+ },
1642
+ stickyTracking: true
1643
+ //tooltip: {
1644
+ //pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b>'
1645
+ //valueDecimals: null,
1646
+ //xDateFormat: '%A, %b %e, %Y',
1647
+ //valuePrefix: '',
1648
+ //ySuffix: ''
1649
+ //}
1650
+ // turboThreshold: 1000
1651
+ // zIndex: null
1652
+ }
1653
+ },
1654
+ labels: {
1655
+ //items: [],
1656
+ style: {
1657
+ //font: defaultFont,
1658
+ position: ABSOLUTE,
1659
+ color: '#3E576F'
1660
+ }
1661
+ },
1662
+ legend: {
1663
+ enabled: true,
1664
+ align: 'center',
1665
+ //floating: false,
1666
+ layout: 'horizontal',
1667
+ labelFormatter: function () {
1668
+ return this.name;
1669
+ },
1670
+ borderWidth: 1,
1671
+ borderColor: '#909090',
1672
+ borderRadius: 5,
1673
+ navigation: {
1674
+ // animation: true,
1675
+ activeColor: '#274b6d',
1676
+ // arrowSize: 12
1677
+ inactiveColor: '#CCC'
1678
+ // style: {} // text styles
1679
+ },
1680
+ // margin: 10,
1681
+ // reversed: false,
1682
+ shadow: false,
1683
+ // backgroundColor: null,
1684
+ /*style: {
1685
+ padding: '5px'
1686
+ },*/
1687
+ itemStyle: {
1688
+ cursor: 'pointer',
1689
+ color: '#274b6d',
1690
+ fontSize: '12px'
1691
+ },
1692
+ itemHoverStyle: {
1693
+ //cursor: 'pointer', removed as of #601
1694
+ color: '#000'
1695
+ },
1696
+ itemHiddenStyle: {
1697
+ color: '#CCC'
1698
+ },
1699
+ itemCheckboxStyle: {
1700
+ position: ABSOLUTE,
1701
+ width: '13px', // for IE precision
1702
+ height: '13px'
1703
+ },
1704
+ // itemWidth: undefined,
1705
+ symbolWidth: 16,
1706
+ symbolPadding: 5,
1707
+ verticalAlign: 'bottom',
1708
+ // width: undefined,
1709
+ x: 0,
1710
+ y: 0,
1711
+ title: {
1712
+ //text: null,
1713
+ style: {
1714
+ fontWeight: 'bold'
1715
+ }
1716
+ }
1717
+ },
1718
+
1719
+ loading: {
1720
+ // hideDuration: 100,
1721
+ labelStyle: {
1722
+ fontWeight: 'bold',
1723
+ position: RELATIVE,
1724
+ top: '1em'
1725
+ },
1726
+ // showDuration: 0,
1727
+ style: {
1728
+ position: ABSOLUTE,
1729
+ backgroundColor: 'white',
1730
+ opacity: 0.5,
1731
+ textAlign: 'center'
1732
+ }
1733
+ },
1734
+
1735
+ tooltip: {
1736
+ enabled: true,
1737
+ animation: hasSVG,
1738
+ //crosshairs: null,
1739
+ backgroundColor: 'rgba(255, 255, 255, .85)',
1740
+ borderWidth: 1,
1741
+ borderRadius: 3,
1742
+ dateTimeLabelFormats: {
1743
+ millisecond: '%A, %b %e, %H:%M:%S.%L',
1744
+ second: '%A, %b %e, %H:%M:%S',
1745
+ minute: '%A, %b %e, %H:%M',
1746
+ hour: '%A, %b %e, %H:%M',
1747
+ day: '%A, %b %e, %Y',
1748
+ week: 'Week from %A, %b %e, %Y',
1749
+ month: '%B %Y',
1750
+ year: '%Y'
1751
+ },
1752
+ //formatter: defaultFormatter,
1753
+ headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
1754
+ pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',
1755
+ shadow: true,
1756
+ //shared: false,
1757
+ snap: isTouchDevice ? 25 : 10,
1758
+ style: {
1759
+ color: '#333333',
1760
+ cursor: 'default',
1761
+ fontSize: '12px',
1762
+ padding: '8px',
1763
+ whiteSpace: 'nowrap'
1764
+ }
1765
+ //xDateFormat: '%A, %b %e, %Y',
1766
+ //valueDecimals: null,
1767
+ //valuePrefix: '',
1768
+ //valueSuffix: ''
1769
+ },
1770
+
1771
+ credits: {
1772
+ enabled: true,
1773
+ text: 'Highcharts.com',
1774
+ href: 'http://www.highcharts.com',
1775
+ position: {
1776
+ align: 'right',
1777
+ x: -10,
1778
+ verticalAlign: 'bottom',
1779
+ y: -5
1780
+ },
1781
+ style: {
1782
+ cursor: 'pointer',
1783
+ color: '#909090',
1784
+ fontSize: '9px'
1785
+ }
1786
+ }
1787
+ };
1788
+
1789
+
1790
+
1791
+
1792
+ // Series defaults
1793
+ var defaultPlotOptions = defaultOptions.plotOptions,
1794
+ defaultSeriesOptions = defaultPlotOptions.line;
1795
+
1796
+ // set the default time methods
1797
+ setTimeMethods();
1798
+
1799
+
1800
+
1801
+ /**
1802
+ * Set the time methods globally based on the useUTC option. Time method can be either
1803
+ * local time or UTC (default).
1804
+ */
1805
+ function setTimeMethods() {
1806
+ var useUTC = defaultOptions.global.useUTC,
1807
+ GET = useUTC ? 'getUTC' : 'get',
1808
+ SET = useUTC ? 'setUTC' : 'set';
1809
+
1810
+ makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) {
1811
+ return new Date(
1812
+ year,
1813
+ month,
1814
+ pick(date, 1),
1815
+ pick(hours, 0),
1816
+ pick(minutes, 0),
1817
+ pick(seconds, 0)
1818
+ ).getTime();
1819
+ };
1820
+ getMinutes = GET + 'Minutes';
1821
+ getHours = GET + 'Hours';
1822
+ getDay = GET + 'Day';
1823
+ getDate = GET + 'Date';
1824
+ getMonth = GET + 'Month';
1825
+ getFullYear = GET + 'FullYear';
1826
+ setMinutes = SET + 'Minutes';
1827
+ setHours = SET + 'Hours';
1828
+ setDate = SET + 'Date';
1829
+ setMonth = SET + 'Month';
1830
+ setFullYear = SET + 'FullYear';
1831
+
1832
+ }
1833
+
1834
+ /**
1835
+ * Merge the default options with custom options and return the new options structure
1836
+ * @param {Object} options The new custom options
1837
+ */
1838
+ function setOptions(options) {
1839
+
1840
+ // Pull out axis options and apply them to the respective default axis options
1841
+ /*defaultXAxisOptions = merge(defaultXAxisOptions, options.xAxis);
1842
+ defaultYAxisOptions = merge(defaultYAxisOptions, options.yAxis);
1843
+ options.xAxis = options.yAxis = UNDEFINED;*/
1844
+
1845
+ // Merge in the default options
1846
+ defaultOptions = merge(defaultOptions, options);
1847
+
1848
+ // Apply UTC
1849
+ setTimeMethods();
1850
+
1851
+ return defaultOptions;
1852
+ }
1853
+
1854
+ /**
1855
+ * Get the updated default options. Merely exposing defaultOptions for outside modules
1856
+ * isn't enough because the setOptions method creates a new object.
1857
+ */
1858
+ function getOptions() {
1859
+ return defaultOptions;
1860
+ }
1861
+
1862
+
1863
+ /**
1864
+ * Handle color operations. The object methods are chainable.
1865
+ * @param {String} input The input color in either rbga or hex format
1866
+ */
1867
+ var Color = function (input) {
1868
+ // declare variables
1869
+ var rgba = [], result, stops;
1870
+
1871
+ /**
1872
+ * Parse the input color to rgba array
1873
+ * @param {String} input
1874
+ */
1875
+ function init(input) {
1876
+
1877
+ // Gradients
1878
+ if (input && input.stops) {
1879
+ stops = map(input.stops, function (stop) {
1880
+ return Color(stop[1]);
1881
+ });
1882
+
1883
+ // Solid colors
1884
+ } else {
1885
+ // rgba
1886
+ result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(input);
1887
+ if (result) {
1888
+ rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)];
1889
+ } else {
1890
+ // hex
1891
+ result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(input);
1892
+ if (result) {
1893
+ rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1];
1894
+ } else {
1895
+ // rgb
1896
+ result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(input);
1897
+ if (result) {
1898
+ rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
1899
+ }
1900
+ }
1901
+ }
1902
+ }
1903
+
1904
+ }
1905
+ /**
1906
+ * Return the color a specified format
1907
+ * @param {String} format
1908
+ */
1909
+ function get(format) {
1910
+ var ret;
1911
+
1912
+ if (stops) {
1913
+ ret = merge(input);
1914
+ ret.stops = [].concat(ret.stops);
1915
+ each(stops, function (stop, i) {
1916
+ ret.stops[i] = [ret.stops[i][0], stop.get(format)];
1917
+ });
1918
+
1919
+ // it's NaN if gradient colors on a column chart
1920
+ } else if (rgba && !isNaN(rgba[0])) {
1921
+ if (format === 'rgb') {
1922
+ ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
1923
+ } else if (format === 'a') {
1924
+ ret = rgba[3];
1925
+ } else {
1926
+ ret = 'rgba(' + rgba.join(',') + ')';
1927
+ }
1928
+ } else {
1929
+ ret = input;
1930
+ }
1931
+ return ret;
1932
+ }
1933
+
1934
+ /**
1935
+ * Brighten the color
1936
+ * @param {Number} alpha
1937
+ */
1938
+ function brighten(alpha) {
1939
+ if (stops) {
1940
+ each(stops, function (stop) {
1941
+ stop.brighten(alpha);
1942
+ });
1943
+
1944
+ } else if (isNumber(alpha) && alpha !== 0) {
1945
+ var i;
1946
+ for (i = 0; i < 3; i++) {
1947
+ rgba[i] += pInt(alpha * 255);
1948
+
1949
+ if (rgba[i] < 0) {
1950
+ rgba[i] = 0;
1951
+ }
1952
+ if (rgba[i] > 255) {
1953
+ rgba[i] = 255;
1954
+ }
1955
+ }
1956
+ }
1957
+ return this;
1958
+ }
1959
+ /**
1960
+ * Set the color's opacity to a given alpha value
1961
+ * @param {Number} alpha
1962
+ */
1963
+ function setOpacity(alpha) {
1964
+ rgba[3] = alpha;
1965
+ return this;
1966
+ }
1967
+
1968
+ // initialize: parse the input
1969
+ init(input);
1970
+
1971
+ // public methods
1972
+ return {
1973
+ get: get,
1974
+ brighten: brighten,
1975
+ rgba: rgba,
1976
+ setOpacity: setOpacity
1977
+ };
1978
+ };
1979
+
1980
+
1981
+ /**
1982
+ * A wrapper object for SVG elements
1983
+ */
1984
+ function SVGElement() {}
1985
+
1986
+ SVGElement.prototype = {
1987
+ /**
1988
+ * Initialize the SVG renderer
1989
+ * @param {Object} renderer
1990
+ * @param {String} nodeName
1991
+ */
1992
+ init: function (renderer, nodeName) {
1993
+ var wrapper = this;
1994
+ wrapper.element = nodeName === 'span' ?
1995
+ createElement(nodeName) :
1996
+ doc.createElementNS(SVG_NS, nodeName);
1997
+ wrapper.renderer = renderer;
1998
+ /**
1999
+ * A collection of attribute setters. These methods, if defined, are called right before a certain
2000
+ * attribute is set on an element wrapper. Returning false prevents the default attribute
2001
+ * setter to run. Returning a value causes the default setter to set that value. Used in
2002
+ * Renderer.label.
2003
+ */
2004
+ wrapper.attrSetters = {};
2005
+ },
2006
+ /**
2007
+ * Default base for animation
2008
+ */
2009
+ opacity: 1,
2010
+ /**
2011
+ * Animate a given attribute
2012
+ * @param {Object} params
2013
+ * @param {Number} options The same options as in jQuery animation
2014
+ * @param {Function} complete Function to perform at the end of animation
2015
+ */
2016
+ animate: function (params, options, complete) {
2017
+ var animOptions = pick(options, globalAnimation, true);
2018
+ stop(this); // stop regardless of animation actually running, or reverting to .attr (#607)
2019
+ if (animOptions) {
2020
+ animOptions = merge(animOptions);
2021
+ if (complete) { // allows using a callback with the global animation without overwriting it
2022
+ animOptions.complete = complete;
2023
+ }
2024
+ animate(this, params, animOptions);
2025
+ } else {
2026
+ this.attr(params);
2027
+ if (complete) {
2028
+ complete();
2029
+ }
2030
+ }
2031
+ },
2032
+ /**
2033
+ * Set or get a given attribute
2034
+ * @param {Object|String} hash
2035
+ * @param {Mixed|Undefined} val
2036
+ */
2037
+ attr: function (hash, val) {
2038
+ var wrapper = this,
2039
+ key,
2040
+ value,
2041
+ result,
2042
+ i,
2043
+ child,
2044
+ element = wrapper.element,
2045
+ nodeName = element.nodeName.toLowerCase(), // Android2 requires lower for "text"
2046
+ renderer = wrapper.renderer,
2047
+ skipAttr,
2048
+ titleNode,
2049
+ attrSetters = wrapper.attrSetters,
2050
+ shadows = wrapper.shadows,
2051
+ hasSetSymbolSize,
2052
+ doTransform,
2053
+ ret = wrapper;
2054
+
2055
+ // single key-value pair
2056
+ if (isString(hash) && defined(val)) {
2057
+ key = hash;
2058
+ hash = {};
2059
+ hash[key] = val;
2060
+ }
2061
+
2062
+ // used as a getter: first argument is a string, second is undefined
2063
+ if (isString(hash)) {
2064
+ key = hash;
2065
+ if (nodeName === 'circle') {
2066
+ key = { x: 'cx', y: 'cy' }[key] || key;
2067
+ } else if (key === 'strokeWidth') {
2068
+ key = 'stroke-width';
2069
+ }
2070
+ ret = attr(element, key) || wrapper[key] || 0;
2071
+ if (key !== 'd' && key !== 'visibility') { // 'd' is string in animation step
2072
+ ret = parseFloat(ret);
2073
+ }
2074
+
2075
+ // setter
2076
+ } else {
2077
+
2078
+ for (key in hash) {
2079
+ skipAttr = false; // reset
2080
+ value = hash[key];
2081
+
2082
+ // check for a specific attribute setter
2083
+ result = attrSetters[key] && attrSetters[key].call(wrapper, value, key);
2084
+
2085
+ if (result !== false) {
2086
+ if (result !== UNDEFINED) {
2087
+ value = result; // the attribute setter has returned a new value to set
2088
+ }
2089
+
2090
+
2091
+ // paths
2092
+ if (key === 'd') {
2093
+ if (value && value.join) { // join path
2094
+ value = value.join(' ');
2095
+ }
2096
+ if (/(NaN| {2}|^$)/.test(value)) {
2097
+ value = 'M 0 0';
2098
+ }
2099
+ //wrapper.d = value; // shortcut for animations
2100
+
2101
+ // update child tspans x values
2102
+ } else if (key === 'x' && nodeName === 'text') {
2103
+ for (i = 0; i < element.childNodes.length; i++) {
2104
+ child = element.childNodes[i];
2105
+ // if the x values are equal, the tspan represents a linebreak
2106
+ if (attr(child, 'x') === attr(element, 'x')) {
2107
+ //child.setAttribute('x', value);
2108
+ attr(child, 'x', value);
2109
+ }
2110
+ }
2111
+
2112
+ } else if (wrapper.rotation && (key === 'x' || key === 'y')) {
2113
+ doTransform = true;
2114
+
2115
+ // apply gradients
2116
+ } else if (key === 'fill') {
2117
+ value = renderer.color(value, element, key);
2118
+
2119
+ // circle x and y
2120
+ } else if (nodeName === 'circle' && (key === 'x' || key === 'y')) {
2121
+ key = { x: 'cx', y: 'cy' }[key] || key;
2122
+
2123
+ // rectangle border radius
2124
+ } else if (nodeName === 'rect' && key === 'r') {
2125
+ attr(element, {
2126
+ rx: value,
2127
+ ry: value
2128
+ });
2129
+ skipAttr = true;
2130
+
2131
+ // translation and text rotation
2132
+ } else if (key === 'translateX' || key === 'translateY' || key === 'rotation' ||
2133
+ key === 'verticalAlign' || key === 'scaleX' || key === 'scaleY') {
2134
+ doTransform = true;
2135
+ skipAttr = true;
2136
+
2137
+ // apply opacity as subnode (required by legacy WebKit and Batik)
2138
+ } else if (key === 'stroke') {
2139
+ value = renderer.color(value, element, key);
2140
+
2141
+ // emulate VML's dashstyle implementation
2142
+ } else if (key === 'dashstyle') {
2143
+ key = 'stroke-dasharray';
2144
+ value = value && value.toLowerCase();
2145
+ if (value === 'solid') {
2146
+ value = NONE;
2147
+ } else if (value) {
2148
+ value = value
2149
+ .replace('shortdashdotdot', '3,1,1,1,1,1,')
2150
+ .replace('shortdashdot', '3,1,1,1')
2151
+ .replace('shortdot', '1,1,')
2152
+ .replace('shortdash', '3,1,')
2153
+ .replace('longdash', '8,3,')
2154
+ .replace(/dot/g, '1,3,')
2155
+ .replace('dash', '4,3,')
2156
+ .replace(/,$/, '')
2157
+ .split(','); // ending comma
2158
+
2159
+ i = value.length;
2160
+ while (i--) {
2161
+ value[i] = pInt(value[i]) * hash['stroke-width'];
2162
+ }
2163
+ value = value.join(',');
2164
+ }
2165
+
2166
+ // IE9/MooTools combo: MooTools returns objects instead of numbers and IE9 Beta 2
2167
+ // is unable to cast them. Test again with final IE9.
2168
+ } else if (key === 'width') {
2169
+ value = pInt(value);
2170
+
2171
+ // Text alignment
2172
+ } else if (key === 'align') {
2173
+ key = 'text-anchor';
2174
+ value = { left: 'start', center: 'middle', right: 'end' }[value];
2175
+
2176
+ // Title requires a subnode, #431
2177
+ } else if (key === 'title') {
2178
+ titleNode = element.getElementsByTagName('title')[0];
2179
+ if (!titleNode) {
2180
+ titleNode = doc.createElementNS(SVG_NS, 'title');
2181
+ element.appendChild(titleNode);
2182
+ }
2183
+ titleNode.textContent = value;
2184
+ }
2185
+
2186
+ // jQuery animate changes case
2187
+ if (key === 'strokeWidth') {
2188
+ key = 'stroke-width';
2189
+ }
2190
+
2191
+ // In Chrome/Win < 6 as well as Batik, the stroke attribute can't be set when the stroke-
2192
+ // width is 0. #1369
2193
+ if (key === 'stroke-width' || key === 'stroke') {
2194
+ wrapper[key] = value;
2195
+ // Only apply the stroke attribute if the stroke width is defined and larger than 0
2196
+ if (wrapper.stroke && wrapper['stroke-width']) {
2197
+ attr(element, 'stroke', wrapper.stroke);
2198
+ attr(element, 'stroke-width', wrapper['stroke-width']);
2199
+ wrapper.hasStroke = true;
2200
+ } else if (key === 'stroke-width' && value === 0 && wrapper.hasStroke) {
2201
+ element.removeAttribute('stroke');
2202
+ wrapper.hasStroke = false;
2203
+ }
2204
+ skipAttr = true;
2205
+ }
2206
+
2207
+ // symbols
2208
+ if (wrapper.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
2209
+
2210
+
2211
+ if (!hasSetSymbolSize) {
2212
+ wrapper.symbolAttr(hash);
2213
+ hasSetSymbolSize = true;
2214
+ }
2215
+ skipAttr = true;
2216
+ }
2217
+
2218
+ // let the shadow follow the main element
2219
+ if (shadows && /^(width|height|visibility|x|y|d|transform)$/.test(key)) {
2220
+ i = shadows.length;
2221
+ while (i--) {
2222
+ attr(
2223
+ shadows[i],
2224
+ key,
2225
+ key === 'height' ?
2226
+ mathMax(value - (shadows[i].cutHeight || 0), 0) :
2227
+ value
2228
+ );
2229
+ }
2230
+ }
2231
+
2232
+ // validate heights
2233
+ if ((key === 'width' || key === 'height') && nodeName === 'rect' && value < 0) {
2234
+ value = 0;
2235
+ }
2236
+
2237
+ // Record for animation and quick access without polling the DOM
2238
+ wrapper[key] = value;
2239
+
2240
+
2241
+ if (key === 'text') {
2242
+ // Delete bBox memo when the text changes
2243
+ if (value !== wrapper.textStr) {
2244
+ delete wrapper.bBox;
2245
+ }
2246
+ wrapper.textStr = value;
2247
+ if (wrapper.added) {
2248
+ renderer.buildText(wrapper);
2249
+ }
2250
+ } else if (!skipAttr) {
2251
+ attr(element, key, value);
2252
+ }
2253
+
2254
+ }
2255
+
2256
+ }
2257
+
2258
+ // Update transform. Do this outside the loop to prevent redundant updating for batch setting
2259
+ // of attributes.
2260
+ if (doTransform) {
2261
+ wrapper.updateTransform();
2262
+ }
2263
+
2264
+ }
2265
+
2266
+ return ret;
2267
+ },
2268
+
2269
+
2270
+ /**
2271
+ * Add a class name to an element
2272
+ */
2273
+ addClass: function (className) {
2274
+ attr(this.element, 'class', attr(this.element, 'class') + ' ' + className);
2275
+ return this;
2276
+ },
2277
+ /* hasClass and removeClass are not (yet) needed
2278
+ hasClass: function (className) {
2279
+ return attr(this.element, 'class').indexOf(className) !== -1;
2280
+ },
2281
+ removeClass: function (className) {
2282
+ attr(this.element, 'class', attr(this.element, 'class').replace(className, ''));
2283
+ return this;
2284
+ },
2285
+ */
2286
+
2287
+ /**
2288
+ * If one of the symbol size affecting parameters are changed,
2289
+ * check all the others only once for each call to an element's
2290
+ * .attr() method
2291
+ * @param {Object} hash
2292
+ */
2293
+ symbolAttr: function (hash) {
2294
+ var wrapper = this;
2295
+
2296
+ each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) {
2297
+ wrapper[key] = pick(hash[key], wrapper[key]);
2298
+ });
2299
+
2300
+ wrapper.attr({
2301
+ d: wrapper.renderer.symbols[wrapper.symbolName](
2302
+ wrapper.x,
2303
+ wrapper.y,
2304
+ wrapper.width,
2305
+ wrapper.height,
2306
+ wrapper
2307
+ )
2308
+ });
2309
+ },
2310
+
2311
+ /**
2312
+ * Apply a clipping path to this object
2313
+ * @param {String} id
2314
+ */
2315
+ clip: function (clipRect) {
2316
+ return this.attr('clip-path', clipRect ? 'url(' + this.renderer.url + '#' + clipRect.id + ')' : NONE);
2317
+ },
2318
+
2319
+ /**
2320
+ * Calculate the coordinates needed for drawing a rectangle crisply and return the
2321
+ * calculated attributes
2322
+ * @param {Number} strokeWidth
2323
+ * @param {Number} x
2324
+ * @param {Number} y
2325
+ * @param {Number} width
2326
+ * @param {Number} height
2327
+ */
2328
+ crisp: function (strokeWidth, x, y, width, height) {
2329
+
2330
+ var wrapper = this,
2331
+ key,
2332
+ attribs = {},
2333
+ values = {},
2334
+ normalizer;
2335
+
2336
+ strokeWidth = strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0;
2337
+ normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors
2338
+
2339
+ // normalize for crisp edges
2340
+ values.x = mathFloor(x || wrapper.x || 0) + normalizer;
2341
+ values.y = mathFloor(y || wrapper.y || 0) + normalizer;
2342
+ values.width = mathFloor((width || wrapper.width || 0) - 2 * normalizer);
2343
+ values.height = mathFloor((height || wrapper.height || 0) - 2 * normalizer);
2344
+ values.strokeWidth = strokeWidth;
2345
+
2346
+ for (key in values) {
2347
+ if (wrapper[key] !== values[key]) { // only set attribute if changed
2348
+ wrapper[key] = attribs[key] = values[key];
2349
+ }
2350
+ }
2351
+
2352
+ return attribs;
2353
+ },
2354
+
2355
+ /**
2356
+ * Set styles for the element
2357
+ * @param {Object} styles
2358
+ */
2359
+ css: function (styles) {
2360
+ /*jslint unparam: true*//* allow unused param a in the regexp function below */
2361
+ var elemWrapper = this,
2362
+ elem = elemWrapper.element,
2363
+ textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text',
2364
+ n,
2365
+ serializedCss = '',
2366
+ hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
2367
+ /*jslint unparam: false*/
2368
+
2369
+ // convert legacy
2370
+ if (styles && styles.color) {
2371
+ styles.fill = styles.color;
2372
+ }
2373
+
2374
+ // Merge the new styles with the old ones
2375
+ styles = extend(
2376
+ elemWrapper.styles,
2377
+ styles
2378
+ );
2379
+
2380
+ // store object
2381
+ elemWrapper.styles = styles;
2382
+
2383
+
2384
+ // Don't handle line wrap on canvas
2385
+ if (useCanVG && textWidth) {
2386
+ delete styles.width;
2387
+ }
2388
+
2389
+ // serialize and set style attribute
2390
+ if (isIE && !hasSVG) { // legacy IE doesn't support setting style attribute
2391
+ if (textWidth) {
2392
+ delete styles.width;
2393
+ }
2394
+ css(elemWrapper.element, styles);
2395
+ } else {
2396
+ for (n in styles) {
2397
+ serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';';
2398
+ }
2399
+ attr(elem, 'style', serializedCss); // #1881
2400
+ }
2401
+
2402
+
2403
+ // re-build text
2404
+ if (textWidth && elemWrapper.added) {
2405
+ elemWrapper.renderer.buildText(elemWrapper);
2406
+ }
2407
+
2408
+ return elemWrapper;
2409
+ },
2410
+
2411
+ /**
2412
+ * Add an event listener
2413
+ * @param {String} eventType
2414
+ * @param {Function} handler
2415
+ */
2416
+ on: function (eventType, handler) {
2417
+ // touch
2418
+ if (hasTouch && eventType === 'click') {
2419
+ this.element.ontouchstart = function (e) {
2420
+ e.preventDefault();
2421
+ handler();
2422
+ };
2423
+ }
2424
+ // simplest possible event model for internal use
2425
+ this.element['on' + eventType] = handler;
2426
+ return this;
2427
+ },
2428
+
2429
+ /**
2430
+ * Set the coordinates needed to draw a consistent radial gradient across
2431
+ * pie slices regardless of positioning inside the chart. The format is
2432
+ * [centerX, centerY, diameter] in pixels.
2433
+ */
2434
+ setRadialReference: function (coordinates) {
2435
+ this.element.radialReference = coordinates;
2436
+ return this;
2437
+ },
2438
+
2439
+ /**
2440
+ * Move an object and its children by x and y values
2441
+ * @param {Number} x
2442
+ * @param {Number} y
2443
+ */
2444
+ translate: function (x, y) {
2445
+ return this.attr({
2446
+ translateX: x,
2447
+ translateY: y
2448
+ });
2449
+ },
2450
+
2451
+ /**
2452
+ * Invert a group, rotate and flip
2453
+ */
2454
+ invert: function () {
2455
+ var wrapper = this;
2456
+ wrapper.inverted = true;
2457
+ wrapper.updateTransform();
2458
+ return wrapper;
2459
+ },
2460
+
2461
+ /**
2462
+ * Apply CSS to HTML elements. This is used in text within SVG rendering and
2463
+ * by the VML renderer
2464
+ */
2465
+ htmlCss: function (styles) {
2466
+ var wrapper = this,
2467
+ element = wrapper.element,
2468
+ textWidth = styles && element.tagName === 'SPAN' && styles.width;
2469
+
2470
+ if (textWidth) {
2471
+ delete styles.width;
2472
+ wrapper.textWidth = textWidth;
2473
+ wrapper.updateTransform();
2474
+ }
2475
+
2476
+ wrapper.styles = extend(wrapper.styles, styles);
2477
+ css(wrapper.element, styles);
2478
+
2479
+ return wrapper;
2480
+ },
2481
+
2482
+
2483
+
2484
+ /**
2485
+ * VML and useHTML method for calculating the bounding box based on offsets
2486
+ * @param {Boolean} refresh Whether to force a fresh value from the DOM or to
2487
+ * use the cached value
2488
+ *
2489
+ * @return {Object} A hash containing values for x, y, width and height
2490
+ */
2491
+
2492
+ htmlGetBBox: function () {
2493
+ var wrapper = this,
2494
+ element = wrapper.element,
2495
+ bBox = wrapper.bBox;
2496
+
2497
+ // faking getBBox in exported SVG in legacy IE
2498
+ if (!bBox) {
2499
+ // faking getBBox in exported SVG in legacy IE (is this a duplicate of the fix for #1079?)
2500
+ if (element.nodeName === 'text') {
2501
+ element.style.position = ABSOLUTE;
2502
+ }
2503
+
2504
+ bBox = wrapper.bBox = {
2505
+ x: element.offsetLeft,
2506
+ y: element.offsetTop,
2507
+ width: element.offsetWidth,
2508
+ height: element.offsetHeight
2509
+ };
2510
+ }
2511
+
2512
+ return bBox;
2513
+ },
2514
+
2515
+ /**
2516
+ * VML override private method to update elements based on internal
2517
+ * properties based on SVG transform
2518
+ */
2519
+ htmlUpdateTransform: function () {
2520
+ // aligning non added elements is expensive
2521
+ if (!this.added) {
2522
+ this.alignOnAdd = true;
2523
+ return;
2524
+ }
2525
+
2526
+ var wrapper = this,
2527
+ renderer = wrapper.renderer,
2528
+ elem = wrapper.element,
2529
+ translateX = wrapper.translateX || 0,
2530
+ translateY = wrapper.translateY || 0,
2531
+ x = wrapper.x || 0,
2532
+ y = wrapper.y || 0,
2533
+ align = wrapper.textAlign || 'left',
2534
+ alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
2535
+ nonLeft = align && align !== 'left',
2536
+ shadows = wrapper.shadows;
2537
+
2538
+ // apply translate
2539
+ css(elem, {
2540
+ marginLeft: translateX,
2541
+ marginTop: translateY
2542
+ });
2543
+ if (shadows) { // used in labels/tooltip
2544
+ each(shadows, function (shadow) {
2545
+ css(shadow, {
2546
+ marginLeft: translateX + 1,
2547
+ marginTop: translateY + 1
2548
+ });
2549
+ });
2550
+ }
2551
+
2552
+ // apply inversion
2553
+ if (wrapper.inverted) { // wrapper is a group
2554
+ each(elem.childNodes, function (child) {
2555
+ renderer.invertChild(child, elem);
2556
+ });
2557
+ }
2558
+
2559
+ if (elem.tagName === 'SPAN') {
2560
+
2561
+ var width, height,
2562
+ rotation = wrapper.rotation,
2563
+ baseline,
2564
+ radians = 0,
2565
+ costheta = 1,
2566
+ sintheta = 0,
2567
+ quad,
2568
+ textWidth = pInt(wrapper.textWidth),
2569
+ xCorr = wrapper.xCorr || 0,
2570
+ yCorr = wrapper.yCorr || 0,
2571
+ currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','),
2572
+ rotationStyle = {},
2573
+ cssTransformKey;
2574
+
2575
+ if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed
2576
+
2577
+ if (defined(rotation)) {
2578
+
2579
+ if (renderer.isSVG) { // #916
2580
+ cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
2581
+ rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
2582
+
2583
+ } else {
2584
+ radians = rotation * deg2rad; // deg to rad
2585
+ costheta = mathCos(radians);
2586
+ sintheta = mathSin(radians);
2587
+
2588
+ // Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented
2589
+ // but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+
2590
+ // has support for CSS3 transform. The getBBox method also needs to be updated
2591
+ // to compensate for the rotation, like it currently does for SVG.
2592
+ // Test case: http://highcharts.com/tests/?file=text-rotation
2593
+ rotationStyle.filter = rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta,
2594
+ ', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
2595
+ ', sizingMethod=\'auto expand\')'].join('') : NONE;
2596
+ }
2597
+ css(elem, rotationStyle);
2598
+ }
2599
+
2600
+ width = pick(wrapper.elemWidth, elem.offsetWidth);
2601
+ height = pick(wrapper.elemHeight, elem.offsetHeight);
2602
+
2603
+ // update textWidth
2604
+ if (width > textWidth && /[ \-]/.test(elem.textContent || elem.innerText)) { // #983, #1254
2605
+ css(elem, {
2606
+ width: textWidth + PX,
2607
+ display: 'block',
2608
+ whiteSpace: 'normal'
2609
+ });
2610
+ width = textWidth;
2611
+ }
2612
+
2613
+ // correct x and y
2614
+ baseline = renderer.fontMetrics(elem.style.fontSize).b;
2615
+ xCorr = costheta < 0 && -width;
2616
+ yCorr = sintheta < 0 && -height;
2617
+
2618
+ // correct for baseline and corners spilling out after rotation
2619
+ quad = costheta * sintheta < 0;
2620
+ xCorr += sintheta * baseline * (quad ? 1 - alignCorrection : alignCorrection);
2621
+ yCorr -= costheta * baseline * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
2622
+
2623
+ // correct for the length/height of the text
2624
+ if (nonLeft) {
2625
+ xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
2626
+ if (rotation) {
2627
+ yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1);
2628
+ }
2629
+ css(elem, {
2630
+ textAlign: align
2631
+ });
2632
+ }
2633
+
2634
+ // record correction
2635
+ wrapper.xCorr = xCorr;
2636
+ wrapper.yCorr = yCorr;
2637
+ }
2638
+
2639
+ // apply position with correction
2640
+ css(elem, {
2641
+ left: (x + xCorr) + PX,
2642
+ top: (y + yCorr) + PX
2643
+ });
2644
+
2645
+ // force reflow in webkit to apply the left and top on useHTML element (#1249)
2646
+ if (isWebKit) {
2647
+ height = elem.offsetHeight; // assigned to height for JSLint purpose
2648
+ }
2649
+
2650
+ // record current text transform
2651
+ wrapper.cTT = currentTextTransform;
2652
+ }
2653
+ },
2654
+
2655
+ /**
2656
+ * Private method to update the transform attribute based on internal
2657
+ * properties
2658
+ */
2659
+ updateTransform: function () {
2660
+ var wrapper = this,
2661
+ translateX = wrapper.translateX || 0,
2662
+ translateY = wrapper.translateY || 0,
2663
+ scaleX = wrapper.scaleX,
2664
+ scaleY = wrapper.scaleY,
2665
+ inverted = wrapper.inverted,
2666
+ rotation = wrapper.rotation,
2667
+ transform;
2668
+
2669
+ // flipping affects translate as adjustment for flipping around the group's axis
2670
+ if (inverted) {
2671
+ translateX += wrapper.attr('width');
2672
+ translateY += wrapper.attr('height');
2673
+ }
2674
+
2675
+ // Apply translate. Nearly all transformed elements have translation, so instead
2676
+ // of checking for translate = 0, do it always (#1767, #1846).
2677
+ transform = ['translate(' + translateX + ',' + translateY + ')'];
2678
+
2679
+ // apply rotation
2680
+ if (inverted) {
2681
+ transform.push('rotate(90) scale(-1,1)');
2682
+ } else if (rotation) { // text rotation
2683
+ transform.push('rotate(' + rotation + ' ' + (wrapper.x || 0) + ' ' + (wrapper.y || 0) + ')');
2684
+ }
2685
+
2686
+ // apply scale
2687
+ if (defined(scaleX) || defined(scaleY)) {
2688
+ transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
2689
+ }
2690
+
2691
+ if (transform.length) {
2692
+ attr(wrapper.element, 'transform', transform.join(' '));
2693
+ }
2694
+ },
2695
+ /**
2696
+ * Bring the element to the front
2697
+ */
2698
+ toFront: function () {
2699
+ var element = this.element;
2700
+ element.parentNode.appendChild(element);
2701
+ return this;
2702
+ },
2703
+
2704
+
2705
+ /**
2706
+ * Break down alignment options like align, verticalAlign, x and y
2707
+ * to x and y relative to the chart.
2708
+ *
2709
+ * @param {Object} alignOptions
2710
+ * @param {Boolean} alignByTranslate
2711
+ * @param {String[Object} box The box to align to, needs a width and height. When the
2712
+ * box is a string, it refers to an object in the Renderer. For example, when
2713
+ * box is 'spacingBox', it refers to Renderer.spacingBox which holds width, height
2714
+ * x and y properties.
2715
+ *
2716
+ */
2717
+ align: function (alignOptions, alignByTranslate, box) {
2718
+ var align,
2719
+ vAlign,
2720
+ x,
2721
+ y,
2722
+ attribs = {},
2723
+ alignTo,
2724
+ renderer = this.renderer,
2725
+ alignedObjects = renderer.alignedObjects;
2726
+
2727
+ // First call on instanciate
2728
+ if (alignOptions) {
2729
+ this.alignOptions = alignOptions;
2730
+ this.alignByTranslate = alignByTranslate;
2731
+ if (!box || isString(box)) { // boxes other than renderer handle this internally
2732
+ this.alignTo = alignTo = box || 'renderer';
2733
+ erase(alignedObjects, this); // prevent duplicates, like legendGroup after resize
2734
+ alignedObjects.push(this);
2735
+ box = null; // reassign it below
2736
+ }
2737
+
2738
+ // When called on resize, no arguments are supplied
2739
+ } else {
2740
+ alignOptions = this.alignOptions;
2741
+ alignByTranslate = this.alignByTranslate;
2742
+ alignTo = this.alignTo;
2743
+ }
2744
+
2745
+ box = pick(box, renderer[alignTo], renderer);
2746
+
2747
+ // Assign variables
2748
+ align = alignOptions.align;
2749
+ vAlign = alignOptions.verticalAlign;
2750
+ x = (box.x || 0) + (alignOptions.x || 0); // default: left align
2751
+ y = (box.y || 0) + (alignOptions.y || 0); // default: top align
2752
+
2753
+ // Align
2754
+ if (align === 'right' || align === 'center') {
2755
+ x += (box.width - (alignOptions.width || 0)) /
2756
+ { right: 1, center: 2 }[align];
2757
+ }
2758
+ attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x);
2759
+
2760
+
2761
+ // Vertical align
2762
+ if (vAlign === 'bottom' || vAlign === 'middle') {
2763
+ y += (box.height - (alignOptions.height || 0)) /
2764
+ ({ bottom: 1, middle: 2 }[vAlign] || 1);
2765
+
2766
+ }
2767
+ attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y);
2768
+
2769
+ // Animate only if already placed
2770
+ this[this.placed ? 'animate' : 'attr'](attribs);
2771
+ this.placed = true;
2772
+ this.alignAttr = attribs;
2773
+
2774
+ return this;
2775
+ },
2776
+
2777
+ /**
2778
+ * Get the bounding box (width, height, x and y) for the element
2779
+ */
2780
+ getBBox: function () {
2781
+ var wrapper = this,
2782
+ bBox = wrapper.bBox,
2783
+ renderer = wrapper.renderer,
2784
+ width,
2785
+ height,
2786
+ rotation = wrapper.rotation,
2787
+ element = wrapper.element,
2788
+ styles = wrapper.styles,
2789
+ rad = rotation * deg2rad;
2790
+
2791
+ if (!bBox) {
2792
+ // SVG elements
2793
+ if (element.namespaceURI === SVG_NS || renderer.forExport) {
2794
+ try { // Fails in Firefox if the container has display: none.
2795
+
2796
+ bBox = element.getBBox ?
2797
+ // SVG: use extend because IE9 is not allowed to change width and height in case
2798
+ // of rotation (below)
2799
+ extend({}, element.getBBox()) :
2800
+ // Canvas renderer and legacy IE in export mode
2801
+ {
2802
+ width: element.offsetWidth,
2803
+ height: element.offsetHeight
2804
+ };
2805
+ } catch (e) {}
2806
+
2807
+ // If the bBox is not set, the try-catch block above failed. The other condition
2808
+ // is for Opera that returns a width of -Infinity on hidden elements.
2809
+ if (!bBox || bBox.width < 0) {
2810
+ bBox = { width: 0, height: 0 };
2811
+ }
2812
+
2813
+
2814
+ // VML Renderer or useHTML within SVG
2815
+ } else {
2816
+
2817
+ bBox = wrapper.htmlGetBBox();
2818
+
2819
+ }
2820
+
2821
+ // True SVG elements as well as HTML elements in modern browsers using the .useHTML option
2822
+ // need to compensated for rotation
2823
+ if (renderer.isSVG) {
2824
+ width = bBox.width;
2825
+ height = bBox.height;
2826
+
2827
+ // Workaround for wrong bounding box in IE9 and IE10 (#1101, #1505, #1669)
2828
+ if (isIE && styles && styles.fontSize === '11px' && height.toPrecision(3) === '22.7') {
2829
+ bBox.height = height = 14;
2830
+ }
2831
+
2832
+ // Adjust for rotated text
2833
+ if (rotation) {
2834
+ bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad));
2835
+ bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad));
2836
+ }
2837
+ }
2838
+
2839
+ wrapper.bBox = bBox;
2840
+ }
2841
+ return bBox;
2842
+ },
2843
+
2844
+ /**
2845
+ * Show the element
2846
+ */
2847
+ show: function () {
2848
+ return this.attr({ visibility: VISIBLE });
2849
+ },
2850
+
2851
+ /**
2852
+ * Hide the element
2853
+ */
2854
+ hide: function () {
2855
+ return this.attr({ visibility: HIDDEN });
2856
+ },
2857
+
2858
+ fadeOut: function (duration) {
2859
+ var elemWrapper = this;
2860
+ elemWrapper.animate({
2861
+ opacity: 0
2862
+ }, {
2863
+ duration: duration || 150,
2864
+ complete: function () {
2865
+ elemWrapper.hide();
2866
+ }
2867
+ });
2868
+ },
2869
+
2870
+ /**
2871
+ * Add the element
2872
+ * @param {Object|Undefined} parent Can be an element, an element wrapper or undefined
2873
+ * to append the element to the renderer.box.
2874
+ */
2875
+ add: function (parent) {
2876
+
2877
+ var renderer = this.renderer,
2878
+ parentWrapper = parent || renderer,
2879
+ parentNode = parentWrapper.element || renderer.box,
2880
+ childNodes = parentNode.childNodes,
2881
+ element = this.element,
2882
+ zIndex = attr(element, 'zIndex'),
2883
+ otherElement,
2884
+ otherZIndex,
2885
+ i,
2886
+ inserted;
2887
+
2888
+ if (parent) {
2889
+ this.parentGroup = parent;
2890
+ }
2891
+
2892
+ // mark as inverted
2893
+ this.parentInverted = parent && parent.inverted;
2894
+
2895
+ // build formatted text
2896
+ if (this.textStr !== undefined) {
2897
+ renderer.buildText(this);
2898
+ }
2899
+
2900
+ // mark the container as having z indexed children
2901
+ if (zIndex) {
2902
+ parentWrapper.handleZ = true;
2903
+ zIndex = pInt(zIndex);
2904
+ }
2905
+
2906
+ // insert according to this and other elements' zIndex
2907
+ if (parentWrapper.handleZ) { // this element or any of its siblings has a z index
2908
+ for (i = 0; i < childNodes.length; i++) {
2909
+ otherElement = childNodes[i];
2910
+ otherZIndex = attr(otherElement, 'zIndex');
2911
+ if (otherElement !== element && (
2912
+ // insert before the first element with a higher zIndex
2913
+ pInt(otherZIndex) > zIndex ||
2914
+ // if no zIndex given, insert before the first element with a zIndex
2915
+ (!defined(zIndex) && defined(otherZIndex))
2916
+
2917
+ )) {
2918
+ parentNode.insertBefore(element, otherElement);
2919
+ inserted = true;
2920
+ break;
2921
+ }
2922
+ }
2923
+ }
2924
+
2925
+ // default: append at the end
2926
+ if (!inserted) {
2927
+ parentNode.appendChild(element);
2928
+ }
2929
+
2930
+ // mark as added
2931
+ this.added = true;
2932
+
2933
+ // fire an event for internal hooks
2934
+ fireEvent(this, 'add');
2935
+
2936
+ return this;
2937
+ },
2938
+
2939
+ /**
2940
+ * Removes a child either by removeChild or move to garbageBin.
2941
+ * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
2942
+ */
2943
+ safeRemoveChild: function (element) {
2944
+ var parentNode = element.parentNode;
2945
+ if (parentNode) {
2946
+ parentNode.removeChild(element);
2947
+ }
2948
+ },
2949
+
2950
+ /**
2951
+ * Destroy the element and element wrapper
2952
+ */
2953
+ destroy: function () {
2954
+ var wrapper = this,
2955
+ element = wrapper.element || {},
2956
+ shadows = wrapper.shadows,
2957
+ key,
2958
+ i;
2959
+
2960
+ // remove events
2961
+ element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null;
2962
+ stop(wrapper); // stop running animations
2963
+
2964
+ if (wrapper.clipPath) {
2965
+ wrapper.clipPath = wrapper.clipPath.destroy();
2966
+ }
2967
+
2968
+ // Destroy stops in case this is a gradient object
2969
+ if (wrapper.stops) {
2970
+ for (i = 0; i < wrapper.stops.length; i++) {
2971
+ wrapper.stops[i] = wrapper.stops[i].destroy();
2972
+ }
2973
+ wrapper.stops = null;
2974
+ }
2975
+
2976
+ // remove element
2977
+ wrapper.safeRemoveChild(element);
2978
+
2979
+ // destroy shadows
2980
+ if (shadows) {
2981
+ each(shadows, function (shadow) {
2982
+ wrapper.safeRemoveChild(shadow);
2983
+ });
2984
+ }
2985
+
2986
+ // remove from alignObjects
2987
+ if (wrapper.alignTo) {
2988
+ erase(wrapper.renderer.alignedObjects, wrapper);
2989
+ }
2990
+
2991
+ for (key in wrapper) {
2992
+ delete wrapper[key];
2993
+ }
2994
+
2995
+ return null;
2996
+ },
2997
+
2998
+ /**
2999
+ * Add a shadow to the element. Must be done after the element is added to the DOM
3000
+ * @param {Boolean|Object} shadowOptions
3001
+ */
3002
+ shadow: function (shadowOptions, group, cutOff) {
3003
+ var shadows = [],
3004
+ i,
3005
+ shadow,
3006
+ element = this.element,
3007
+ strokeWidth,
3008
+ shadowWidth,
3009
+ shadowElementOpacity,
3010
+
3011
+ // compensate for inverted plot area
3012
+ transform;
3013
+
3014
+
3015
+ if (shadowOptions) {
3016
+ shadowWidth = pick(shadowOptions.width, 3);
3017
+ shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
3018
+ transform = this.parentInverted ?
3019
+ '(-1,-1)' :
3020
+ '(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')';
3021
+ for (i = 1; i <= shadowWidth; i++) {
3022
+ shadow = element.cloneNode(0);
3023
+ strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
3024
+ attr(shadow, {
3025
+ 'isShadow': 'true',
3026
+ 'stroke': shadowOptions.color || 'black',
3027
+ 'stroke-opacity': shadowElementOpacity * i,
3028
+ 'stroke-width': strokeWidth,
3029
+ 'transform': 'translate' + transform,
3030
+ 'fill': NONE
3031
+ });
3032
+ if (cutOff) {
3033
+ attr(shadow, 'height', mathMax(attr(shadow, 'height') - strokeWidth, 0));
3034
+ shadow.cutHeight = strokeWidth;
3035
+ }
3036
+
3037
+ if (group) {
3038
+ group.element.appendChild(shadow);
3039
+ } else {
3040
+ element.parentNode.insertBefore(shadow, element);
3041
+ }
3042
+
3043
+ shadows.push(shadow);
3044
+ }
3045
+
3046
+ this.shadows = shadows;
3047
+ }
3048
+ return this;
3049
+
3050
+ }
3051
+ };
3052
+
3053
+
3054
+ /**
3055
+ * The default SVG renderer
3056
+ */
3057
+ var SVGRenderer = function () {
3058
+ this.init.apply(this, arguments);
3059
+ };
3060
+ SVGRenderer.prototype = {
3061
+ Element: SVGElement,
3062
+
3063
+ /**
3064
+ * Initialize the SVGRenderer
3065
+ * @param {Object} container
3066
+ * @param {Number} width
3067
+ * @param {Number} height
3068
+ * @param {Boolean} forExport
3069
+ */
3070
+ init: function (container, width, height, forExport) {
3071
+ var renderer = this,
3072
+ loc = location,
3073
+ boxWrapper,
3074
+ desc;
3075
+
3076
+ boxWrapper = renderer.createElement('svg')
3077
+ .attr({
3078
+ xmlns: SVG_NS,
3079
+ version: '1.1'
3080
+ });
3081
+ container.appendChild(boxWrapper.element);
3082
+
3083
+ // object properties
3084
+ renderer.isSVG = true;
3085
+ renderer.box = boxWrapper.element;
3086
+ renderer.boxWrapper = boxWrapper;
3087
+ renderer.alignedObjects = [];
3088
+
3089
+ // Page url used for internal references. #24, #672, #1070
3090
+ renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ?
3091
+ loc.href
3092
+ .replace(/#.*?$/, '') // remove the hash
3093
+ .replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes
3094
+ .replace(/ /g, '%20') : // replace spaces (needed for Safari only)
3095
+ '';
3096
+
3097
+ // Add description
3098
+ desc = this.createElement('desc').add();
3099
+ desc.element.appendChild(doc.createTextNode('Created with ' + PRODUCT + ' ' + VERSION));
3100
+
3101
+
3102
+ renderer.defs = this.createElement('defs').add();
3103
+ renderer.forExport = forExport;
3104
+ renderer.gradients = {}; // Object where gradient SvgElements are stored
3105
+
3106
+ renderer.setSize(width, height, false);
3107
+
3108
+
3109
+
3110
+ // Issue 110 workaround:
3111
+ // In Firefox, if a div is positioned by percentage, its pixel position may land
3112
+ // between pixels. The container itself doesn't display this, but an SVG element
3113
+ // inside this container will be drawn at subpixel precision. In order to draw
3114
+ // sharp lines, this must be compensated for. This doesn't seem to work inside
3115
+ // iframes though (like in jsFiddle).
3116
+ var subPixelFix, rect;
3117
+ if (isFirefox && container.getBoundingClientRect) {
3118
+ renderer.subPixelFix = subPixelFix = function () {
3119
+ css(container, { left: 0, top: 0 });
3120
+ rect = container.getBoundingClientRect();
3121
+ css(container, {
3122
+ left: (mathCeil(rect.left) - rect.left) + PX,
3123
+ top: (mathCeil(rect.top) - rect.top) + PX
3124
+ });
3125
+ };
3126
+
3127
+ // run the fix now
3128
+ subPixelFix();
3129
+
3130
+ // run it on resize
3131
+ addEvent(win, 'resize', subPixelFix);
3132
+ }
3133
+ },
3134
+
3135
+ /**
3136
+ * Detect whether the renderer is hidden. This happens when one of the parent elements
3137
+ * has display: none. #608.
3138
+ */
3139
+ isHidden: function () {
3140
+ return !this.boxWrapper.getBBox().width;
3141
+ },
3142
+
3143
+ /**
3144
+ * Destroys the renderer and its allocated members.
3145
+ */
3146
+ destroy: function () {
3147
+ var renderer = this,
3148
+ rendererDefs = renderer.defs;
3149
+ renderer.box = null;
3150
+ renderer.boxWrapper = renderer.boxWrapper.destroy();
3151
+
3152
+ // Call destroy on all gradient elements
3153
+ destroyObjectProperties(renderer.gradients || {});
3154
+ renderer.gradients = null;
3155
+
3156
+ // Defs are null in VMLRenderer
3157
+ // Otherwise, destroy them here.
3158
+ if (rendererDefs) {
3159
+ renderer.defs = rendererDefs.destroy();
3160
+ }
3161
+
3162
+ // Remove sub pixel fix handler
3163
+ // We need to check that there is a handler, otherwise all functions that are registered for event 'resize' are removed
3164
+ // See issue #982
3165
+ if (renderer.subPixelFix) {
3166
+ removeEvent(win, 'resize', renderer.subPixelFix);
3167
+ }
3168
+
3169
+ renderer.alignedObjects = null;
3170
+
3171
+ return null;
3172
+ },
3173
+
3174
+ /**
3175
+ * Create a wrapper for an SVG element
3176
+ * @param {Object} nodeName
3177
+ */
3178
+ createElement: function (nodeName) {
3179
+ var wrapper = new this.Element();
3180
+ wrapper.init(this, nodeName);
3181
+ return wrapper;
3182
+ },
3183
+
3184
+ /**
3185
+ * Dummy function for use in canvas renderer
3186
+ */
3187
+ draw: function () {},
3188
+
3189
+ /**
3190
+ * Parse a simple HTML string into SVG tspans
3191
+ *
3192
+ * @param {Object} textNode The parent text SVG node
3193
+ */
3194
+ buildText: function (wrapper) {
3195
+ var textNode = wrapper.element,
3196
+ renderer = this,
3197
+ forExport = renderer.forExport,
3198
+ lines = pick(wrapper.textStr, '').toString()
3199
+ .replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
3200
+ .replace(/<(i|em)>/g, '<span style="font-style:italic">')
3201
+ .replace(/<a/g, '<span')
3202
+ .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
3203
+ .split(/<br.*?>/g),
3204
+ childNodes = textNode.childNodes,
3205
+ styleRegex = /style="([^"]+)"/,
3206
+ hrefRegex = /href="([^"]+)"/,
3207
+ parentX = attr(textNode, 'x'),
3208
+ textStyles = wrapper.styles,
3209
+ width = textStyles && textStyles.width && pInt(textStyles.width),
3210
+ textLineHeight = textStyles && textStyles.lineHeight,
3211
+ i = childNodes.length;
3212
+
3213
+ /// remove old text
3214
+ while (i--) {
3215
+ textNode.removeChild(childNodes[i]);
3216
+ }
3217
+
3218
+ if (width && !wrapper.added) {
3219
+ this.box.appendChild(textNode); // attach it to the DOM to read offset width
3220
+ }
3221
+
3222
+ // remove empty line at end
3223
+ if (lines[lines.length - 1] === '') {
3224
+ lines.pop();
3225
+ }
3226
+
3227
+ // build the lines
3228
+ each(lines, function (line, lineNo) {
3229
+ var spans, spanNo = 0;
3230
+
3231
+ line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||');
3232
+ spans = line.split('|||');
3233
+
3234
+ each(spans, function (span) {
3235
+ if (span !== '' || spans.length === 1) {
3236
+ var attributes = {},
3237
+ tspan = doc.createElementNS(SVG_NS, 'tspan'),
3238
+ spanStyle; // #390
3239
+ if (styleRegex.test(span)) {
3240
+ spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2');
3241
+ attr(tspan, 'style', spanStyle);
3242
+ }
3243
+ if (hrefRegex.test(span) && !forExport) { // Not for export - #1529
3244
+ attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"');
3245
+ css(tspan, { cursor: 'pointer' });
3246
+ }
3247
+
3248
+ span = (span.replace(/<(.|\n)*?>/g, '') || ' ')
3249
+ .replace(/&lt;/g, '<')
3250
+ .replace(/&gt;/g, '>');
3251
+
3252
+ // add the text node
3253
+ tspan.appendChild(doc.createTextNode(span));
3254
+
3255
+ if (!spanNo) { // first span in a line, align it to the left
3256
+ attributes.x = parentX;
3257
+ } else {
3258
+ attributes.dx = 0; // #16
3259
+ }
3260
+
3261
+ // add attributes
3262
+ attr(tspan, attributes);
3263
+
3264
+ // first span on subsequent line, add the line height
3265
+ if (!spanNo && lineNo) {
3266
+
3267
+ // allow getting the right offset height in exporting in IE
3268
+ if (!hasSVG && forExport) {
3269
+ css(tspan, { display: 'block' });
3270
+ }
3271
+
3272
+ // Set the line height based on the font size of either
3273
+ // the text element or the tspan element
3274
+ attr(
3275
+ tspan,
3276
+ 'dy',
3277
+ textLineHeight || renderer.fontMetrics(
3278
+ /px$/.test(tspan.style.fontSize) ?
3279
+ tspan.style.fontSize :
3280
+ textStyles.fontSize
3281
+ ).h,
3282
+ // Safari 6.0.2 - too optimized for its own good (#1539)
3283
+ // TODO: revisit this with future versions of Safari
3284
+ isWebKit && tspan.offsetHeight
3285
+ );
3286
+ }
3287
+
3288
+ // Append it
3289
+ textNode.appendChild(tspan);
3290
+
3291
+ spanNo++;
3292
+
3293
+ // check width and apply soft breaks
3294
+ if (width) {
3295
+ var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
3296
+ tooLong,
3297
+ actualWidth,
3298
+ rest = [];
3299
+
3300
+ while (words.length || rest.length) {
3301
+ delete wrapper.bBox; // delete cache
3302
+ actualWidth = wrapper.getBBox().width;
3303
+ tooLong = actualWidth > width;
3304
+ if (!tooLong || words.length === 1) { // new line needed
3305
+ words = rest;
3306
+ rest = [];
3307
+ if (words.length) {
3308
+ tspan = doc.createElementNS(SVG_NS, 'tspan');
3309
+ attr(tspan, {
3310
+ dy: textLineHeight || 16,
3311
+ x: parentX
3312
+ });
3313
+ if (spanStyle) { // #390
3314
+ attr(tspan, 'style', spanStyle);
3315
+ }
3316
+ textNode.appendChild(tspan);
3317
+
3318
+ if (actualWidth > width) { // a single word is pressing it out
3319
+ width = actualWidth;
3320
+ }
3321
+ }
3322
+ } else { // append to existing line tspan
3323
+ tspan.removeChild(tspan.firstChild);
3324
+ rest.unshift(words.pop());
3325
+ }
3326
+ if (words.length) {
3327
+ tspan.appendChild(doc.createTextNode(words.join(' ').replace(/- /g, '-')));
3328
+ }
3329
+ }
3330
+ }
3331
+ }
3332
+ });
3333
+ });
3334
+ },
3335
+
3336
+ /**
3337
+ * Create a button with preset states
3338
+ * @param {String} text
3339
+ * @param {Number} x
3340
+ * @param {Number} y
3341
+ * @param {Function} callback
3342
+ * @param {Object} normalState
3343
+ * @param {Object} hoverState
3344
+ * @param {Object} pressedState
3345
+ */
3346
+ button: function (text, x, y, callback, normalState, hoverState, pressedState) {
3347
+ var label = this.label(text, x, y, null, null, null, null, null, 'button'),
3348
+ curState = 0,
3349
+ stateOptions,
3350
+ stateStyle,
3351
+ normalStyle,
3352
+ hoverStyle,
3353
+ pressedStyle,
3354
+ STYLE = 'style',
3355
+ verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 };
3356
+
3357
+ // Normal state - prepare the attributes
3358
+ normalState = merge({
3359
+ 'stroke-width': 1,
3360
+ stroke: '#CCCCCC',
3361
+ fill: {
3362
+ linearGradient: verticalGradient,
3363
+ stops: [
3364
+ [0, '#FEFEFE'],
3365
+ [1, '#F6F6F6']
3366
+ ]
3367
+ },
3368
+ r: 2,
3369
+ padding: 5,
3370
+ style: {
3371
+ color: 'black'
3372
+ }
3373
+ }, normalState);
3374
+ normalStyle = normalState[STYLE];
3375
+ delete normalState[STYLE];
3376
+
3377
+ // Hover state
3378
+ hoverState = merge(normalState, {
3379
+ stroke: '#68A',
3380
+ fill: {
3381
+ linearGradient: verticalGradient,
3382
+ stops: [
3383
+ [0, '#FFF'],
3384
+ [1, '#ACF']
3385
+ ]
3386
+ }
3387
+ }, hoverState);
3388
+ hoverStyle = hoverState[STYLE];
3389
+ delete hoverState[STYLE];
3390
+
3391
+ // Pressed state
3392
+ pressedState = merge(normalState, {
3393
+ stroke: '#68A',
3394
+ fill: {
3395
+ linearGradient: verticalGradient,
3396
+ stops: [
3397
+ [0, '#9BD'],
3398
+ [1, '#CDF']
3399
+ ]
3400
+ }
3401
+ }, pressedState);
3402
+ pressedStyle = pressedState[STYLE];
3403
+ delete pressedState[STYLE];
3404
+
3405
+ // add the events
3406
+ addEvent(label.element, 'mouseenter', function () {
3407
+ label.attr(hoverState)
3408
+ .css(hoverStyle);
3409
+ });
3410
+ addEvent(label.element, 'mouseleave', function () {
3411
+ stateOptions = [normalState, hoverState, pressedState][curState];
3412
+ stateStyle = [normalStyle, hoverStyle, pressedStyle][curState];
3413
+ label.attr(stateOptions)
3414
+ .css(stateStyle);
3415
+ });
3416
+
3417
+ label.setState = function (state) {
3418
+ curState = state;
3419
+ if (!state) {
3420
+ label.attr(normalState)
3421
+ .css(normalStyle);
3422
+ } else if (state === 2) {
3423
+ label.attr(pressedState)
3424
+ .css(pressedStyle);
3425
+ }
3426
+ };
3427
+
3428
+ return label
3429
+ .on('click', function () {
3430
+ callback.call(label);
3431
+ })
3432
+ .attr(normalState)
3433
+ .css(extend({ cursor: 'default' }, normalStyle));
3434
+ },
3435
+
3436
+ /**
3437
+ * Make a straight line crisper by not spilling out to neighbour pixels
3438
+ * @param {Array} points
3439
+ * @param {Number} width
3440
+ */
3441
+ crispLine: function (points, width) {
3442
+ // points format: [M, 0, 0, L, 100, 0]
3443
+ // normalize to a crisp line
3444
+ if (points[1] === points[4]) {
3445
+ // Substract due to #1129. Now bottom and left axis gridlines behave the same.
3446
+ points[1] = points[4] = mathRound(points[1]) - (width % 2 / 2);
3447
+ }
3448
+ if (points[2] === points[5]) {
3449
+ points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2);
3450
+ }
3451
+ return points;
3452
+ },
3453
+
3454
+
3455
+ /**
3456
+ * Draw a path
3457
+ * @param {Array} path An SVG path in array form
3458
+ */
3459
+ path: function (path) {
3460
+ var attr = {
3461
+ fill: NONE
3462
+ };
3463
+ if (isArray(path)) {
3464
+ attr.d = path;
3465
+ } else if (isObject(path)) { // attributes
3466
+ extend(attr, path);
3467
+ }
3468
+ return this.createElement('path').attr(attr);
3469
+ },
3470
+
3471
+ /**
3472
+ * Draw and return an SVG circle
3473
+ * @param {Number} x The x position
3474
+ * @param {Number} y The y position
3475
+ * @param {Number} r The radius
3476
+ */
3477
+ circle: function (x, y, r) {
3478
+ var attr = isObject(x) ?
3479
+ x :
3480
+ {
3481
+ x: x,
3482
+ y: y,
3483
+ r: r
3484
+ };
3485
+
3486
+ return this.createElement('circle').attr(attr);
3487
+ },
3488
+
3489
+ /**
3490
+ * Draw and return an arc
3491
+ * @param {Number} x X position
3492
+ * @param {Number} y Y position
3493
+ * @param {Number} r Radius
3494
+ * @param {Number} innerR Inner radius like used in donut charts
3495
+ * @param {Number} start Starting angle
3496
+ * @param {Number} end Ending angle
3497
+ */
3498
+ arc: function (x, y, r, innerR, start, end) {
3499
+ // arcs are defined as symbols for the ability to set
3500
+ // attributes in attr and animate
3501
+
3502
+ if (isObject(x)) {
3503
+ y = x.y;
3504
+ r = x.r;
3505
+ innerR = x.innerR;
3506
+ start = x.start;
3507
+ end = x.end;
3508
+ x = x.x;
3509
+ }
3510
+ return this.symbol('arc', x || 0, y || 0, r || 0, r || 0, {
3511
+ innerR: innerR || 0,
3512
+ start: start || 0,
3513
+ end: end || 0
3514
+ });
3515
+ },
3516
+
3517
+ /**
3518
+ * Draw and return a rectangle
3519
+ * @param {Number} x Left position
3520
+ * @param {Number} y Top position
3521
+ * @param {Number} width
3522
+ * @param {Number} height
3523
+ * @param {Number} r Border corner radius
3524
+ * @param {Number} strokeWidth A stroke width can be supplied to allow crisp drawing
3525
+ */
3526
+ rect: function (x, y, width, height, r, strokeWidth) {
3527
+
3528
+ r = isObject(x) ? x.r : r;
3529
+
3530
+ var wrapper = this.createElement('rect').attr({
3531
+ rx: r,
3532
+ ry: r,
3533
+ fill: NONE
3534
+ });
3535
+ return wrapper.attr(
3536
+ isObject(x) ?
3537
+ x :
3538
+ // do not crispify when an object is passed in (as in column charts)
3539
+ wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0))
3540
+ );
3541
+ },
3542
+
3543
+ /**
3544
+ * Resize the box and re-align all aligned elements
3545
+ * @param {Object} width
3546
+ * @param {Object} height
3547
+ * @param {Boolean} animate
3548
+ *
3549
+ */
3550
+ setSize: function (width, height, animate) {
3551
+ var renderer = this,
3552
+ alignedObjects = renderer.alignedObjects,
3553
+ i = alignedObjects.length;
3554
+
3555
+ renderer.width = width;
3556
+ renderer.height = height;
3557
+
3558
+ renderer.boxWrapper[pick(animate, true) ? 'animate' : 'attr']({
3559
+ width: width,
3560
+ height: height
3561
+ });
3562
+
3563
+ while (i--) {
3564
+ alignedObjects[i].align();
3565
+ }
3566
+ },
3567
+
3568
+ /**
3569
+ * Create a group
3570
+ * @param {String} name The group will be given a class name of 'highcharts-{name}'.
3571
+ * This can be used for styling and scripting.
3572
+ */
3573
+ g: function (name) {
3574
+ var elem = this.createElement('g');
3575
+ return defined(name) ? elem.attr({ 'class': PREFIX + name }) : elem;
3576
+ },
3577
+
3578
+ /**
3579
+ * Display an image
3580
+ * @param {String} src
3581
+ * @param {Number} x
3582
+ * @param {Number} y
3583
+ * @param {Number} width
3584
+ * @param {Number} height
3585
+ */
3586
+ image: function (src, x, y, width, height) {
3587
+ var attribs = {
3588
+ preserveAspectRatio: NONE
3589
+ },
3590
+ elemWrapper;
3591
+
3592
+ // optional properties
3593
+ if (arguments.length > 1) {
3594
+ extend(attribs, {
3595
+ x: x,
3596
+ y: y,
3597
+ width: width,
3598
+ height: height
3599
+ });
3600
+ }
3601
+
3602
+ elemWrapper = this.createElement('image').attr(attribs);
3603
+
3604
+ // set the href in the xlink namespace
3605
+ if (elemWrapper.element.setAttributeNS) {
3606
+ elemWrapper.element.setAttributeNS('http://www.w3.org/1999/xlink',
3607
+ 'href', src);
3608
+ } else {
3609
+ // could be exporting in IE
3610
+ // using href throws "not supported" in ie7 and under, requries regex shim to fix later
3611
+ elemWrapper.element.setAttribute('hc-svg-href', src);
3612
+ }
3613
+
3614
+ return elemWrapper;
3615
+ },
3616
+
3617
+ /**
3618
+ * Draw a symbol out of pre-defined shape paths from the namespace 'symbol' object.
3619
+ *
3620
+ * @param {Object} symbol
3621
+ * @param {Object} x
3622
+ * @param {Object} y
3623
+ * @param {Object} radius
3624
+ * @param {Object} options
3625
+ */
3626
+ symbol: function (symbol, x, y, width, height, options) {
3627
+
3628
+ var obj,
3629
+
3630
+ // get the symbol definition function
3631
+ symbolFn = this.symbols[symbol],
3632
+
3633
+ // check if there's a path defined for this symbol
3634
+ path = symbolFn && symbolFn(
3635
+ mathRound(x),
3636
+ mathRound(y),
3637
+ width,
3638
+ height,
3639
+ options
3640
+ ),
3641
+
3642
+ imageElement,
3643
+ imageRegex = /^url\((.*?)\)$/,
3644
+ imageSrc,
3645
+ imageSize,
3646
+ centerImage;
3647
+
3648
+ if (path) {
3649
+
3650
+ obj = this.path(path);
3651
+ // expando properties for use in animate and attr
3652
+ extend(obj, {
3653
+ symbolName: symbol,
3654
+ x: x,
3655
+ y: y,
3656
+ width: width,
3657
+ height: height
3658
+ });
3659
+ if (options) {
3660
+ extend(obj, options);
3661
+ }
3662
+
3663
+
3664
+ // image symbols
3665
+ } else if (imageRegex.test(symbol)) {
3666
+
3667
+ // On image load, set the size and position
3668
+ centerImage = function (img, size) {
3669
+ if (img.element) { // it may be destroyed in the meantime (#1390)
3670
+ img.attr({
3671
+ width: size[0],
3672
+ height: size[1]
3673
+ });
3674
+
3675
+ if (!img.alignByTranslate) { // #185
3676
+ img.translate(
3677
+ mathRound((width - size[0]) / 2), // #1378
3678
+ mathRound((height - size[1]) / 2)
3679
+ );
3680
+ }
3681
+ }
3682
+ };
3683
+
3684
+ imageSrc = symbol.match(imageRegex)[1];
3685
+ imageSize = symbolSizes[imageSrc];
3686
+
3687
+ // Ireate the image synchronously, add attribs async
3688
+ obj = this.image(imageSrc)
3689
+ .attr({
3690
+ x: x,
3691
+ y: y
3692
+ });
3693
+ obj.isImg = true;
3694
+
3695
+ if (imageSize) {
3696
+ centerImage(obj, imageSize);
3697
+ } else {
3698
+ // Initialize image to be 0 size so export will still function if there's no cached sizes.
3699
+ //
3700
+ obj.attr({ width: 0, height: 0 });
3701
+
3702
+ // Create a dummy JavaScript image to get the width and height. Due to a bug in IE < 8,
3703
+ // the created element must be assigned to a variable in order to load (#292).
3704
+ imageElement = createElement('img', {
3705
+ onload: function () {
3706
+ centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]);
3707
+ },
3708
+ src: imageSrc
3709
+ });
3710
+ }
3711
+ }
3712
+
3713
+ return obj;
3714
+ },
3715
+
3716
+ /**
3717
+ * An extendable collection of functions for defining symbol paths.
3718
+ */
3719
+ symbols: {
3720
+ 'circle': function (x, y, w, h) {
3721
+ var cpw = 0.166 * w;
3722
+ return [
3723
+ M, x + w / 2, y,
3724
+ 'C', x + w + cpw, y, x + w + cpw, y + h, x + w / 2, y + h,
3725
+ 'C', x - cpw, y + h, x - cpw, y, x + w / 2, y,
3726
+ 'Z'
3727
+ ];
3728
+ },
3729
+
3730
+ 'square': function (x, y, w, h) {
3731
+ return [
3732
+ M, x, y,
3733
+ L, x + w, y,
3734
+ x + w, y + h,
3735
+ x, y + h,
3736
+ 'Z'
3737
+ ];
3738
+ },
3739
+
3740
+ 'triangle': function (x, y, w, h) {
3741
+ return [
3742
+ M, x + w / 2, y,
3743
+ L, x + w, y + h,
3744
+ x, y + h,
3745
+ 'Z'
3746
+ ];
3747
+ },
3748
+
3749
+ 'triangle-down': function (x, y, w, h) {
3750
+ return [
3751
+ M, x, y,
3752
+ L, x + w, y,
3753
+ x + w / 2, y + h,
3754
+ 'Z'
3755
+ ];
3756
+ },
3757
+ 'diamond': function (x, y, w, h) {
3758
+ return [
3759
+ M, x + w / 2, y,
3760
+ L, x + w, y + h / 2,
3761
+ x + w / 2, y + h,
3762
+ x, y + h / 2,
3763
+ 'Z'
3764
+ ];
3765
+ },
3766
+ 'arc': function (x, y, w, h, options) {
3767
+ var start = options.start,
3768
+ radius = options.r || w || h,
3769
+ end = options.end - 0.001, // to prevent cos and sin of start and end from becoming equal on 360 arcs (related: #1561)
3770
+ innerRadius = options.innerR,
3771
+ open = options.open,
3772
+ cosStart = mathCos(start),
3773
+ sinStart = mathSin(start),
3774
+ cosEnd = mathCos(end),
3775
+ sinEnd = mathSin(end),
3776
+ longArc = options.end - start < mathPI ? 0 : 1;
3777
+
3778
+ return [
3779
+ M,
3780
+ x + radius * cosStart,
3781
+ y + radius * sinStart,
3782
+ 'A', // arcTo
3783
+ radius, // x radius
3784
+ radius, // y radius
3785
+ 0, // slanting
3786
+ longArc, // long or short arc
3787
+ 1, // clockwise
3788
+ x + radius * cosEnd,
3789
+ y + radius * sinEnd,
3790
+ open ? M : L,
3791
+ x + innerRadius * cosEnd,
3792
+ y + innerRadius * sinEnd,
3793
+ 'A', // arcTo
3794
+ innerRadius, // x radius
3795
+ innerRadius, // y radius
3796
+ 0, // slanting
3797
+ longArc, // long or short arc
3798
+ 0, // clockwise
3799
+ x + innerRadius * cosStart,
3800
+ y + innerRadius * sinStart,
3801
+
3802
+ open ? '' : 'Z' // close
3803
+ ];
3804
+ }
3805
+ },
3806
+
3807
+ /**
3808
+ * Define a clipping rectangle
3809
+ * @param {String} id
3810
+ * @param {Number} x
3811
+ * @param {Number} y
3812
+ * @param {Number} width
3813
+ * @param {Number} height
3814
+ */
3815
+ clipRect: function (x, y, width, height) {
3816
+ var wrapper,
3817
+ id = PREFIX + idCounter++,
3818
+
3819
+ clipPath = this.createElement('clipPath').attr({
3820
+ id: id
3821
+ }).add(this.defs);
3822
+
3823
+ wrapper = this.rect(x, y, width, height, 0).add(clipPath);
3824
+ wrapper.id = id;
3825
+ wrapper.clipPath = clipPath;
3826
+
3827
+ return wrapper;
3828
+ },
3829
+
3830
+
3831
+ /**
3832
+ * Take a color and return it if it's a string, make it a gradient if it's a
3833
+ * gradient configuration object. Prior to Highstock, an array was used to define
3834
+ * a linear gradient with pixel positions relative to the SVG. In newer versions
3835
+ * we change the coordinates to apply relative to the shape, using coordinates
3836
+ * 0-1 within the shape. To preserve backwards compatibility, linearGradient
3837
+ * in this definition is an object of x1, y1, x2 and y2.
3838
+ *
3839
+ * @param {Object} color The color or config object
3840
+ */
3841
+ color: function (color, elem, prop) {
3842
+ var renderer = this,
3843
+ colorObject,
3844
+ regexRgba = /^rgba/,
3845
+ gradName,
3846
+ gradAttr,
3847
+ gradients,
3848
+ gradientObject,
3849
+ stops,
3850
+ stopColor,
3851
+ stopOpacity,
3852
+ radialReference,
3853
+ n,
3854
+ id,
3855
+ key = [];
3856
+
3857
+ // Apply linear or radial gradients
3858
+ if (color && color.linearGradient) {
3859
+ gradName = 'linearGradient';
3860
+ } else if (color && color.radialGradient) {
3861
+ gradName = 'radialGradient';
3862
+ }
3863
+
3864
+ if (gradName) {
3865
+ gradAttr = color[gradName];
3866
+ gradients = renderer.gradients;
3867
+ stops = color.stops;
3868
+ radialReference = elem.radialReference;
3869
+
3870
+ // Keep < 2.2 kompatibility
3871
+ if (isArray(gradAttr)) {
3872
+ color[gradName] = gradAttr = {
3873
+ x1: gradAttr[0],
3874
+ y1: gradAttr[1],
3875
+ x2: gradAttr[2],
3876
+ y2: gradAttr[3],
3877
+ gradientUnits: 'userSpaceOnUse'
3878
+ };
3879
+ }
3880
+
3881
+ // Correct the radial gradient for the radial reference system
3882
+ if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) {
3883
+ gradAttr = merge(gradAttr, {
3884
+ cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2],
3885
+ cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2],
3886
+ r: gradAttr.r * radialReference[2],
3887
+ gradientUnits: 'userSpaceOnUse'
3888
+ });
3889
+ }
3890
+
3891
+ // Build the unique key to detect whether we need to create a new element (#1282)
3892
+ for (n in gradAttr) {
3893
+ if (n !== 'id') {
3894
+ key.push(n, gradAttr[n]);
3895
+ }
3896
+ }
3897
+ for (n in stops) {
3898
+ key.push(stops[n]);
3899
+ }
3900
+ key = key.join(',');
3901
+
3902
+ // Check if a gradient object with the same config object is created within this renderer
3903
+ if (gradients[key]) {
3904
+ id = gradients[key].id;
3905
+
3906
+ } else {
3907
+
3908
+ // Set the id and create the element
3909
+ gradAttr.id = id = PREFIX + idCounter++;
3910
+ gradients[key] = gradientObject = renderer.createElement(gradName)
3911
+ .attr(gradAttr)
3912
+ .add(renderer.defs);
3913
+
3914
+
3915
+ // The gradient needs to keep a list of stops to be able to destroy them
3916
+ gradientObject.stops = [];
3917
+ each(stops, function (stop) {
3918
+ var stopObject;
3919
+ if (regexRgba.test(stop[1])) {
3920
+ colorObject = Color(stop[1]);
3921
+ stopColor = colorObject.get('rgb');
3922
+ stopOpacity = colorObject.get('a');
3923
+ } else {
3924
+ stopColor = stop[1];
3925
+ stopOpacity = 1;
3926
+ }
3927
+ stopObject = renderer.createElement('stop').attr({
3928
+ offset: stop[0],
3929
+ 'stop-color': stopColor,
3930
+ 'stop-opacity': stopOpacity
3931
+ }).add(gradientObject);
3932
+
3933
+ // Add the stop element to the gradient
3934
+ gradientObject.stops.push(stopObject);
3935
+ });
3936
+ }
3937
+
3938
+ // Return the reference to the gradient object
3939
+ return 'url(' + renderer.url + '#' + id + ')';
3940
+
3941
+ // Webkit and Batik can't show rgba.
3942
+ } else if (regexRgba.test(color)) {
3943
+ colorObject = Color(color);
3944
+ attr(elem, prop + '-opacity', colorObject.get('a'));
3945
+
3946
+ return colorObject.get('rgb');
3947
+
3948
+
3949
+ } else {
3950
+ // Remove the opacity attribute added above. Does not throw if the attribute is not there.
3951
+ elem.removeAttribute(prop + '-opacity');
3952
+
3953
+ return color;
3954
+ }
3955
+
3956
+ },
3957
+
3958
+
3959
+ /**
3960
+ * Add text to the SVG object
3961
+ * @param {String} str
3962
+ * @param {Number} x Left position
3963
+ * @param {Number} y Top position
3964
+ * @param {Boolean} useHTML Use HTML to render the text
3965
+ */
3966
+ text: function (str, x, y, useHTML) {
3967
+
3968
+ // declare variables
3969
+ var renderer = this,
3970
+ defaultChartStyle = defaultOptions.chart.style,
3971
+ fakeSVG = useCanVG || (!hasSVG && renderer.forExport),
3972
+ wrapper;
3973
+
3974
+ if (useHTML && !renderer.forExport) {
3975
+ return renderer.html(str, x, y);
3976
+ }
3977
+
3978
+ x = mathRound(pick(x, 0));
3979
+ y = mathRound(pick(y, 0));
3980
+
3981
+ wrapper = renderer.createElement('text')
3982
+ .attr({
3983
+ x: x,
3984
+ y: y,
3985
+ text: str
3986
+ })
3987
+ .css({
3988
+ fontFamily: defaultChartStyle.fontFamily,
3989
+ fontSize: defaultChartStyle.fontSize
3990
+ });
3991
+
3992
+ // Prevent wrapping from creating false offsetWidths in export in legacy IE (#1079, #1063)
3993
+ if (fakeSVG) {
3994
+ wrapper.css({
3995
+ position: ABSOLUTE
3996
+ });
3997
+ }
3998
+
3999
+ wrapper.x = x;
4000
+ wrapper.y = y;
4001
+ return wrapper;
4002
+ },
4003
+
4004
+
4005
+ /**
4006
+ * Create HTML text node. This is used by the VML renderer as well as the SVG
4007
+ * renderer through the useHTML option.
4008
+ *
4009
+ * @param {String} str
4010
+ * @param {Number} x
4011
+ * @param {Number} y
4012
+ */
4013
+ html: function (str, x, y) {
4014
+ var defaultChartStyle = defaultOptions.chart.style,
4015
+ wrapper = this.createElement('span'),
4016
+ attrSetters = wrapper.attrSetters,
4017
+ element = wrapper.element,
4018
+ renderer = wrapper.renderer;
4019
+
4020
+ // Text setter
4021
+ attrSetters.text = function (value) {
4022
+ if (value !== element.innerHTML) {
4023
+ delete this.bBox;
4024
+ }
4025
+ element.innerHTML = value;
4026
+ return false;
4027
+ };
4028
+
4029
+ // Various setters which rely on update transform
4030
+ attrSetters.x = attrSetters.y = attrSetters.align = function (value, key) {
4031
+ if (key === 'align') {
4032
+ key = 'textAlign'; // Do not overwrite the SVGElement.align method. Same as VML.
4033
+ }
4034
+ wrapper[key] = value;
4035
+ wrapper.htmlUpdateTransform();
4036
+ return false;
4037
+ };
4038
+
4039
+ // Set the default attributes
4040
+ wrapper.attr({
4041
+ text: str,
4042
+ x: mathRound(x),
4043
+ y: mathRound(y)
4044
+ })
4045
+ .css({
4046
+ position: ABSOLUTE,
4047
+ whiteSpace: 'nowrap',
4048
+ fontFamily: defaultChartStyle.fontFamily,
4049
+ fontSize: defaultChartStyle.fontSize
4050
+ });
4051
+
4052
+ // Use the HTML specific .css method
4053
+ wrapper.css = wrapper.htmlCss;
4054
+
4055
+ // This is specific for HTML within SVG
4056
+ if (renderer.isSVG) {
4057
+ wrapper.add = function (svgGroupWrapper) {
4058
+
4059
+ var htmlGroup,
4060
+ container = renderer.box.parentNode,
4061
+ parentGroup,
4062
+ parents = [];
4063
+
4064
+ // Create a mock group to hold the HTML elements
4065
+ if (svgGroupWrapper) {
4066
+ htmlGroup = svgGroupWrapper.div;
4067
+ if (!htmlGroup) {
4068
+
4069
+ // Read the parent chain into an array and read from top down
4070
+ parentGroup = svgGroupWrapper;
4071
+ while (parentGroup) {
4072
+
4073
+ parents.push(parentGroup);
4074
+
4075
+ // Move up to the next parent group
4076
+ parentGroup = parentGroup.parentGroup;
4077
+ }
4078
+
4079
+ // Ensure dynamically updating position when any parent is translated
4080
+ each(parents.reverse(), function (parentGroup) {
4081
+ var htmlGroupStyle;
4082
+
4083
+ // Create a HTML div and append it to the parent div to emulate
4084
+ // the SVG group structure
4085
+ htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, {
4086
+ className: attr(parentGroup.element, 'class')
4087
+ }, {
4088
+ position: ABSOLUTE,
4089
+ left: (parentGroup.translateX || 0) + PX,
4090
+ top: (parentGroup.translateY || 0) + PX
4091
+ }, htmlGroup || container); // the top group is appended to container
4092
+
4093
+ // Shortcut
4094
+ htmlGroupStyle = htmlGroup.s