Version Description
- Capability checking before display temporary dashboard widget
Download this release
Release Info
Developer | m_uysl |
Plugin | 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 +13 -13
- js/highcharts/adapters/mootools-adapter.src.js +313 -313
- js/highcharts/adapters/prototype-adapter.js +15 -15
- js/highcharts/adapters/prototype-adapter.src.js +316 -316
- js/highcharts/highcharts-more.js +51 -51
- js/highcharts/highcharts-more.src.js +2525 -2525
- js/highcharts/highcharts.js +272 -272
- js/highcharts/highcharts.src.js +4094 -16320
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(/</g,"<").replace(/>/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(/</g,"<").replace(/>/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(/</g, '<')
|
3250 |
-
.replace(/>/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.
|
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(/</g, '<')
|
3250 |
+
.replace(/>/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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|