|
1
|
|
|
/*! |
|
2
|
|
|
* Chart.js |
|
3
|
|
|
* http://chartjs.org/ |
|
4
|
|
|
* Version: 2.2.1 |
|
5
|
|
|
* |
|
6
|
|
|
* Copyright 2016 Nick Downie |
|
7
|
|
|
* Released under the MIT license |
|
8
|
|
|
* https://github.com/chartjs/Chart.js/blob/master/LICENSE.md |
|
9
|
|
|
*/ |
|
10
|
|
|
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
|
|
|
|
|
|
11
|
|
|
|
|
12
|
|
|
},{}],2:[function(require,module,exports){ |
|
13
|
|
|
/* MIT license */
|
|
14
|
|
|
var colorNames = require(6);
|
|
15
|
|
|
|
|
16
|
|
|
module.exports = {
|
|
17
|
|
|
getRgba: getRgba,
|
|
18
|
|
|
getHsla: getHsla,
|
|
19
|
|
|
getRgb: getRgb,
|
|
20
|
|
|
getHsl: getHsl,
|
|
21
|
|
|
getHwb: getHwb,
|
|
22
|
|
|
getAlpha: getAlpha,
|
|
23
|
|
|
|
|
24
|
|
|
hexString: hexString,
|
|
25
|
|
|
rgbString: rgbString,
|
|
26
|
|
|
rgbaString: rgbaString,
|
|
27
|
|
|
percentString: percentString,
|
|
28
|
|
|
percentaString: percentaString,
|
|
29
|
|
|
hslString: hslString,
|
|
30
|
|
|
hslaString: hslaString,
|
|
31
|
|
|
hwbString: hwbString,
|
|
32
|
|
|
keyword: keyword
|
|
33
|
|
|
}
|
|
34
|
|
|
|
|
35
|
|
|
function getRgba(string) {
|
|
36
|
|
|
if (!string) {
|
|
37
|
|
|
return;
|
|
38
|
|
|
}
|
|
39
|
|
|
var abbr = /^#([a-fA-F0-9]{3})$/,
|
|
40
|
|
|
hex = /^#([a-fA-F0-9]{6})$/,
|
|
41
|
|
|
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
|
|
42
|
|
|
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
|
|
43
|
|
|
keyword = /(\w+)/;
|
|
44
|
|
|
|
|
45
|
|
|
var rgb = [0, 0, 0],
|
|
46
|
|
|
a = 1,
|
|
47
|
|
|
match = string.match(abbr);
|
|
48
|
|
|
if (match) {
|
|
49
|
|
|
match = match[1];
|
|
50
|
|
|
for (var i = 0; i < rgb.length; i++) {
|
|
51
|
|
|
rgb[i] = parseInt(match[i] + match[i], 16);
|
|
52
|
|
|
}
|
|
53
|
|
|
}
|
|
54
|
|
|
else if (match = string.match(hex)) {
|
|
55
|
|
|
match = match[1];
|
|
56
|
|
|
for (var i = 0; i < rgb.length; i++) {
|
|
|
|
|
|
|
57
|
|
|
rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
|
|
58
|
|
|
}
|
|
59
|
|
|
}
|
|
60
|
|
|
else if (match = string.match(rgba)) {
|
|
61
|
|
|
for (var i = 0; i < rgb.length; i++) {
|
|
|
|
|
|
|
62
|
|
|
rgb[i] = parseInt(match[i + 1]);
|
|
63
|
|
|
}
|
|
64
|
|
|
a = parseFloat(match[4]);
|
|
65
|
|
|
}
|
|
66
|
|
|
else if (match = string.match(per)) {
|
|
67
|
|
|
for (var i = 0; i < rgb.length; i++) {
|
|
|
|
|
|
|
68
|
|
|
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
|
|
69
|
|
|
}
|
|
70
|
|
|
a = parseFloat(match[4]);
|
|
71
|
|
|
}
|
|
72
|
|
|
else if (match = string.match(keyword)) {
|
|
73
|
|
|
if (match[1] == "transparent") {
|
|
74
|
|
|
return [0, 0, 0, 0];
|
|
75
|
|
|
}
|
|
76
|
|
|
rgb = colorNames[match[1]];
|
|
77
|
|
|
if (!rgb) {
|
|
78
|
|
|
return;
|
|
79
|
|
|
}
|
|
80
|
|
|
}
|
|
81
|
|
|
|
|
82
|
|
|
for (var i = 0; i < rgb.length; i++) {
|
|
|
|
|
|
|
83
|
|
|
rgb[i] = scale(rgb[i], 0, 255);
|
|
84
|
|
|
}
|
|
85
|
|
|
if (!a && a != 0) {
|
|
86
|
|
|
a = 1;
|
|
87
|
|
|
}
|
|
88
|
|
|
else {
|
|
89
|
|
|
a = scale(a, 0, 1);
|
|
90
|
|
|
}
|
|
91
|
|
|
rgb[3] = a;
|
|
92
|
|
|
return rgb;
|
|
93
|
|
|
}
|
|
94
|
|
|
|
|
95
|
|
|
function getHsla(string) {
|
|
96
|
|
|
if (!string) {
|
|
97
|
|
|
return;
|
|
98
|
|
|
}
|
|
99
|
|
|
var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
|
|
100
|
|
|
var match = string.match(hsl);
|
|
101
|
|
|
if (match) {
|
|
|
|
|
|
|
102
|
|
|
var alpha = parseFloat(match[4]);
|
|
103
|
|
|
var h = scale(parseInt(match[1]), 0, 360),
|
|
104
|
|
|
s = scale(parseFloat(match[2]), 0, 100),
|
|
105
|
|
|
l = scale(parseFloat(match[3]), 0, 100),
|
|
106
|
|
|
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
|
|
107
|
|
|
return [h, s, l, a];
|
|
108
|
|
|
}
|
|
109
|
|
|
}
|
|
110
|
|
|
|
|
111
|
|
|
function getHwb(string) {
|
|
112
|
|
|
if (!string) {
|
|
113
|
|
|
return;
|
|
114
|
|
|
}
|
|
115
|
|
|
var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
|
|
116
|
|
|
var match = string.match(hwb);
|
|
117
|
|
|
if (match) {
|
|
|
|
|
|
|
118
|
|
|
var alpha = parseFloat(match[4]);
|
|
119
|
|
|
var h = scale(parseInt(match[1]), 0, 360),
|
|
120
|
|
|
w = scale(parseFloat(match[2]), 0, 100),
|
|
121
|
|
|
b = scale(parseFloat(match[3]), 0, 100),
|
|
122
|
|
|
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
|
|
123
|
|
|
return [h, w, b, a];
|
|
124
|
|
|
}
|
|
125
|
|
|
}
|
|
126
|
|
|
|
|
127
|
|
|
function getRgb(string) {
|
|
128
|
|
|
var rgba = getRgba(string);
|
|
129
|
|
|
return rgba && rgba.slice(0, 3);
|
|
130
|
|
|
}
|
|
131
|
|
|
|
|
132
|
|
|
function getHsl(string) {
|
|
133
|
|
|
var hsla = getHsla(string);
|
|
134
|
|
|
return hsla && hsla.slice(0, 3);
|
|
135
|
|
|
}
|
|
136
|
|
|
|
|
137
|
|
|
function getAlpha(string) {
|
|
138
|
|
|
var vals = getRgba(string);
|
|
139
|
|
|
if (vals) {
|
|
140
|
|
|
return vals[3];
|
|
141
|
|
|
}
|
|
142
|
|
|
else if (vals = getHsla(string)) {
|
|
143
|
|
|
return vals[3];
|
|
144
|
|
|
}
|
|
145
|
|
|
else if (vals = getHwb(string)) {
|
|
|
|
|
|
|
146
|
|
|
return vals[3];
|
|
147
|
|
|
}
|
|
148
|
|
|
}
|
|
149
|
|
|
|
|
150
|
|
|
// generators
|
|
151
|
|
|
function hexString(rgb) {
|
|
152
|
|
|
return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
|
|
153
|
|
|
+ hexDouble(rgb[2]);
|
|
154
|
|
|
}
|
|
155
|
|
|
|
|
156
|
|
|
function rgbString(rgba, alpha) {
|
|
157
|
|
|
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
|
|
158
|
|
|
return rgbaString(rgba, alpha);
|
|
159
|
|
|
}
|
|
160
|
|
|
return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
|
|
161
|
|
|
}
|
|
162
|
|
|
|
|
163
|
|
|
function rgbaString(rgba, alpha) {
|
|
164
|
|
|
if (alpha === undefined) {
|
|
165
|
|
|
alpha = (rgba[3] !== undefined ? rgba[3] : 1);
|
|
166
|
|
|
}
|
|
167
|
|
|
return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
|
|
168
|
|
|
+ ", " + alpha + ")";
|
|
169
|
|
|
}
|
|
170
|
|
|
|
|
171
|
|
|
function percentString(rgba, alpha) {
|
|
172
|
|
|
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
|
|
173
|
|
|
return percentaString(rgba, alpha);
|
|
174
|
|
|
}
|
|
175
|
|
|
var r = Math.round(rgba[0]/255 * 100),
|
|
176
|
|
|
g = Math.round(rgba[1]/255 * 100),
|
|
177
|
|
|
b = Math.round(rgba[2]/255 * 100);
|
|
178
|
|
|
|
|
179
|
|
|
return "rgb(" + r + "%, " + g + "%, " + b + "%)";
|
|
180
|
|
|
}
|
|
181
|
|
|
|
|
182
|
|
|
function percentaString(rgba, alpha) {
|
|
183
|
|
|
var r = Math.round(rgba[0]/255 * 100),
|
|
184
|
|
|
g = Math.round(rgba[1]/255 * 100),
|
|
185
|
|
|
b = Math.round(rgba[2]/255 * 100);
|
|
186
|
|
|
return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
|
|
187
|
|
|
}
|
|
188
|
|
|
|
|
189
|
|
|
function hslString(hsla, alpha) {
|
|
190
|
|
|
if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
|
|
191
|
|
|
return hslaString(hsla, alpha);
|
|
192
|
|
|
}
|
|
193
|
|
|
return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
|
|
194
|
|
|
}
|
|
195
|
|
|
|
|
196
|
|
|
function hslaString(hsla, alpha) {
|
|
197
|
|
|
if (alpha === undefined) {
|
|
198
|
|
|
alpha = (hsla[3] !== undefined ? hsla[3] : 1);
|
|
199
|
|
|
}
|
|
200
|
|
|
return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
|
|
201
|
|
|
+ alpha + ")";
|
|
202
|
|
|
}
|
|
203
|
|
|
|
|
204
|
|
|
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
|
|
205
|
|
|
// (hwb have alpha optional & 1 is default value)
|
|
206
|
|
|
function hwbString(hwb, alpha) {
|
|
207
|
|
|
if (alpha === undefined) {
|
|
208
|
|
|
alpha = (hwb[3] !== undefined ? hwb[3] : 1);
|
|
209
|
|
|
}
|
|
210
|
|
|
return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
|
|
211
|
|
|
+ (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
|
|
212
|
|
|
}
|
|
213
|
|
|
|
|
214
|
|
|
function keyword(rgb) {
|
|
215
|
|
|
return reverseNames[rgb.slice(0, 3)];
|
|
216
|
|
|
}
|
|
217
|
|
|
|
|
218
|
|
|
// helpers
|
|
219
|
|
|
function scale(num, min, max) {
|
|
220
|
|
|
return Math.min(Math.max(min, num), max);
|
|
221
|
|
|
}
|
|
222
|
|
|
|
|
223
|
|
|
function hexDouble(num) {
|
|
224
|
|
|
var str = num.toString(16).toUpperCase();
|
|
225
|
|
|
return (str.length < 2) ? "0" + str : str;
|
|
226
|
|
|
}
|
|
227
|
|
|
|
|
228
|
|
|
|
|
229
|
|
|
//create a list of reverse color names
|
|
230
|
|
|
var reverseNames = {};
|
|
231
|
|
|
for (var name in colorNames) {
|
|
|
|
|
|
|
232
|
|
|
reverseNames[colorNames[name]] = name;
|
|
233
|
|
|
}
|
|
234
|
|
|
|
|
235
|
|
|
},{"6":6}],3:[function(require,module,exports){ |
|
236
|
|
|
/* MIT license */
|
|
237
|
|
|
var convert = require(5);
|
|
238
|
|
|
var string = require(2);
|
|
239
|
|
|
|
|
240
|
|
|
var Color = function (obj) {
|
|
241
|
|
|
if (obj instanceof Color) {
|
|
242
|
|
|
return obj;
|
|
243
|
|
|
}
|
|
244
|
|
|
if (!(this instanceof Color)) {
|
|
245
|
|
|
return new Color(obj);
|
|
246
|
|
|
}
|
|
247
|
|
|
|
|
248
|
|
|
this.values = {
|
|
249
|
|
|
rgb: [0, 0, 0],
|
|
250
|
|
|
hsl: [0, 0, 0],
|
|
251
|
|
|
hsv: [0, 0, 0],
|
|
252
|
|
|
hwb: [0, 0, 0],
|
|
253
|
|
|
cmyk: [0, 0, 0, 0],
|
|
254
|
|
|
alpha: 1
|
|
255
|
|
|
};
|
|
256
|
|
|
|
|
257
|
|
|
// parse Color() argument
|
|
258
|
|
|
var vals;
|
|
259
|
|
|
if (typeof obj === 'string') {
|
|
260
|
|
|
vals = string.getRgba(obj);
|
|
261
|
|
|
if (vals) {
|
|
262
|
|
|
this.setValues('rgb', vals);
|
|
|
|
|
|
|
263
|
|
|
} else if (vals = string.getHsla(obj)) {
|
|
264
|
|
|
this.setValues('hsl', vals);
|
|
|
|
|
|
|
265
|
|
|
} else if (vals = string.getHwb(obj)) {
|
|
266
|
|
|
this.setValues('hwb', vals);
|
|
|
|
|
|
|
267
|
|
|
} else {
|
|
268
|
|
|
throw new Error('Unable to parse color from string "' + obj + '"');
|
|
269
|
|
|
}
|
|
270
|
|
|
} else if (typeof obj === 'object') {
|
|
|
|
|
|
|
271
|
|
|
vals = obj;
|
|
272
|
|
|
if (vals.r !== undefined || vals.red !== undefined) {
|
|
273
|
|
|
this.setValues('rgb', vals);
|
|
|
|
|
|
|
274
|
|
|
} else if (vals.l !== undefined || vals.lightness !== undefined) {
|
|
275
|
|
|
this.setValues('hsl', vals);
|
|
|
|
|
|
|
276
|
|
|
} else if (vals.v !== undefined || vals.value !== undefined) {
|
|
277
|
|
|
this.setValues('hsv', vals);
|
|
|
|
|
|
|
278
|
|
|
} else if (vals.w !== undefined || vals.whiteness !== undefined) {
|
|
279
|
|
|
this.setValues('hwb', vals);
|
|
|
|
|
|
|
280
|
|
|
} else if (vals.c !== undefined || vals.cyan !== undefined) {
|
|
281
|
|
|
this.setValues('cmyk', vals);
|
|
|
|
|
|
|
282
|
|
|
} else {
|
|
283
|
|
|
throw new Error('Unable to parse color from object ' + JSON.stringify(obj));
|
|
284
|
|
|
}
|
|
285
|
|
|
}
|
|
286
|
|
|
};
|
|
287
|
|
|
|
|
288
|
|
|
Color.prototype = {
|
|
289
|
|
|
rgb: function () {
|
|
290
|
|
|
return this.setSpace('rgb', arguments);
|
|
291
|
|
|
},
|
|
292
|
|
|
hsl: function () {
|
|
293
|
|
|
return this.setSpace('hsl', arguments);
|
|
294
|
|
|
},
|
|
295
|
|
|
hsv: function () {
|
|
296
|
|
|
return this.setSpace('hsv', arguments);
|
|
297
|
|
|
},
|
|
298
|
|
|
hwb: function () {
|
|
299
|
|
|
return this.setSpace('hwb', arguments);
|
|
300
|
|
|
},
|
|
301
|
|
|
cmyk: function () {
|
|
302
|
|
|
return this.setSpace('cmyk', arguments);
|
|
303
|
|
|
},
|
|
304
|
|
|
|
|
305
|
|
|
rgbArray: function () {
|
|
306
|
|
|
return this.values.rgb;
|
|
307
|
|
|
},
|
|
308
|
|
|
hslArray: function () {
|
|
309
|
|
|
return this.values.hsl;
|
|
310
|
|
|
},
|
|
311
|
|
|
hsvArray: function () {
|
|
312
|
|
|
return this.values.hsv;
|
|
313
|
|
|
},
|
|
314
|
|
|
hwbArray: function () {
|
|
315
|
|
|
var values = this.values;
|
|
316
|
|
|
if (values.alpha !== 1) {
|
|
317
|
|
|
return values.hwb.concat([values.alpha]);
|
|
318
|
|
|
}
|
|
319
|
|
|
return values.hwb;
|
|
320
|
|
|
},
|
|
321
|
|
|
cmykArray: function () {
|
|
322
|
|
|
return this.values.cmyk;
|
|
323
|
|
|
},
|
|
324
|
|
|
rgbaArray: function () {
|
|
325
|
|
|
var values = this.values;
|
|
326
|
|
|
return values.rgb.concat([values.alpha]);
|
|
327
|
|
|
},
|
|
328
|
|
|
hslaArray: function () {
|
|
329
|
|
|
var values = this.values;
|
|
330
|
|
|
return values.hsl.concat([values.alpha]);
|
|
331
|
|
|
},
|
|
332
|
|
|
alpha: function (val) {
|
|
333
|
|
|
if (val === undefined) {
|
|
334
|
|
|
return this.values.alpha;
|
|
335
|
|
|
}
|
|
336
|
|
|
this.setValues('alpha', val);
|
|
337
|
|
|
return this;
|
|
338
|
|
|
},
|
|
339
|
|
|
|
|
340
|
|
|
red: function (val) {
|
|
341
|
|
|
return this.setChannel('rgb', 0, val);
|
|
342
|
|
|
},
|
|
343
|
|
|
green: function (val) {
|
|
344
|
|
|
return this.setChannel('rgb', 1, val);
|
|
345
|
|
|
},
|
|
346
|
|
|
blue: function (val) {
|
|
347
|
|
|
return this.setChannel('rgb', 2, val);
|
|
348
|
|
|
},
|
|
349
|
|
|
hue: function (val) {
|
|
350
|
|
|
if (val) {
|
|
351
|
|
|
val %= 360;
|
|
352
|
|
|
val = val < 0 ? 360 + val : val;
|
|
353
|
|
|
}
|
|
354
|
|
|
return this.setChannel('hsl', 0, val);
|
|
355
|
|
|
},
|
|
356
|
|
|
saturation: function (val) {
|
|
357
|
|
|
return this.setChannel('hsl', 1, val);
|
|
358
|
|
|
},
|
|
359
|
|
|
lightness: function (val) {
|
|
360
|
|
|
return this.setChannel('hsl', 2, val);
|
|
361
|
|
|
},
|
|
362
|
|
|
saturationv: function (val) {
|
|
363
|
|
|
return this.setChannel('hsv', 1, val);
|
|
364
|
|
|
},
|
|
365
|
|
|
whiteness: function (val) {
|
|
366
|
|
|
return this.setChannel('hwb', 1, val);
|
|
367
|
|
|
},
|
|
368
|
|
|
blackness: function (val) {
|
|
369
|
|
|
return this.setChannel('hwb', 2, val);
|
|
370
|
|
|
},
|
|
371
|
|
|
value: function (val) {
|
|
372
|
|
|
return this.setChannel('hsv', 2, val);
|
|
373
|
|
|
},
|
|
374
|
|
|
cyan: function (val) {
|
|
375
|
|
|
return this.setChannel('cmyk', 0, val);
|
|
376
|
|
|
},
|
|
377
|
|
|
magenta: function (val) {
|
|
378
|
|
|
return this.setChannel('cmyk', 1, val);
|
|
379
|
|
|
},
|
|
380
|
|
|
yellow: function (val) {
|
|
381
|
|
|
return this.setChannel('cmyk', 2, val);
|
|
382
|
|
|
},
|
|
383
|
|
|
black: function (val) {
|
|
384
|
|
|
return this.setChannel('cmyk', 3, val);
|
|
385
|
|
|
},
|
|
386
|
|
|
|
|
387
|
|
|
hexString: function () {
|
|
388
|
|
|
return string.hexString(this.values.rgb);
|
|
389
|
|
|
},
|
|
390
|
|
|
rgbString: function () {
|
|
391
|
|
|
return string.rgbString(this.values.rgb, this.values.alpha);
|
|
392
|
|
|
},
|
|
393
|
|
|
rgbaString: function () {
|
|
394
|
|
|
return string.rgbaString(this.values.rgb, this.values.alpha);
|
|
395
|
|
|
},
|
|
396
|
|
|
percentString: function () {
|
|
397
|
|
|
return string.percentString(this.values.rgb, this.values.alpha);
|
|
398
|
|
|
},
|
|
399
|
|
|
hslString: function () {
|
|
400
|
|
|
return string.hslString(this.values.hsl, this.values.alpha);
|
|
401
|
|
|
},
|
|
402
|
|
|
hslaString: function () {
|
|
403
|
|
|
return string.hslaString(this.values.hsl, this.values.alpha);
|
|
404
|
|
|
},
|
|
405
|
|
|
hwbString: function () {
|
|
406
|
|
|
return string.hwbString(this.values.hwb, this.values.alpha);
|
|
407
|
|
|
},
|
|
408
|
|
|
keyword: function () {
|
|
409
|
|
|
return string.keyword(this.values.rgb, this.values.alpha);
|
|
410
|
|
|
},
|
|
411
|
|
|
|
|
412
|
|
|
rgbNumber: function () {
|
|
413
|
|
|
var rgb = this.values.rgb;
|
|
414
|
|
|
return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
|
415
|
|
|
},
|
|
416
|
|
|
|
|
417
|
|
|
luminosity: function () {
|
|
418
|
|
|
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
|
419
|
|
|
var rgb = this.values.rgb;
|
|
420
|
|
|
var lum = [];
|
|
421
|
|
|
for (var i = 0; i < rgb.length; i++) {
|
|
422
|
|
|
var chan = rgb[i] / 255;
|
|
423
|
|
|
lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
|
|
424
|
|
|
}
|
|
425
|
|
|
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
|
|
426
|
|
|
},
|
|
427
|
|
|
|
|
428
|
|
|
contrast: function (color2) {
|
|
429
|
|
|
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
|
|
430
|
|
|
var lum1 = this.luminosity();
|
|
431
|
|
|
var lum2 = color2.luminosity();
|
|
432
|
|
|
if (lum1 > lum2) {
|
|
433
|
|
|
return (lum1 + 0.05) / (lum2 + 0.05);
|
|
434
|
|
|
}
|
|
435
|
|
|
return (lum2 + 0.05) / (lum1 + 0.05);
|
|
436
|
|
|
},
|
|
437
|
|
|
|
|
438
|
|
|
level: function (color2) {
|
|
439
|
|
|
var contrastRatio = this.contrast(color2);
|
|
440
|
|
|
if (contrastRatio >= 7.1) {
|
|
441
|
|
|
return 'AAA';
|
|
442
|
|
|
}
|
|
443
|
|
|
|
|
444
|
|
|
return (contrastRatio >= 4.5) ? 'AA' : '';
|
|
445
|
|
|
},
|
|
446
|
|
|
|
|
447
|
|
|
dark: function () {
|
|
448
|
|
|
// YIQ equation from http://24ways.org/2010/calculating-color-contrast
|
|
449
|
|
|
var rgb = this.values.rgb;
|
|
450
|
|
|
var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
|
|
451
|
|
|
return yiq < 128;
|
|
452
|
|
|
},
|
|
453
|
|
|
|
|
454
|
|
|
light: function () {
|
|
455
|
|
|
return !this.dark();
|
|
456
|
|
|
},
|
|
457
|
|
|
|
|
458
|
|
|
negate: function () {
|
|
459
|
|
|
var rgb = [];
|
|
460
|
|
|
for (var i = 0; i < 3; i++) {
|
|
461
|
|
|
rgb[i] = 255 - this.values.rgb[i];
|
|
462
|
|
|
}
|
|
463
|
|
|
this.setValues('rgb', rgb);
|
|
464
|
|
|
return this;
|
|
465
|
|
|
},
|
|
466
|
|
|
|
|
467
|
|
|
lighten: function (ratio) {
|
|
468
|
|
|
var hsl = this.values.hsl;
|
|
469
|
|
|
hsl[2] += hsl[2] * ratio;
|
|
470
|
|
|
this.setValues('hsl', hsl);
|
|
471
|
|
|
return this;
|
|
472
|
|
|
},
|
|
473
|
|
|
|
|
474
|
|
|
darken: function (ratio) {
|
|
475
|
|
|
var hsl = this.values.hsl;
|
|
476
|
|
|
hsl[2] -= hsl[2] * ratio;
|
|
477
|
|
|
this.setValues('hsl', hsl);
|
|
478
|
|
|
return this;
|
|
479
|
|
|
},
|
|
480
|
|
|
|
|
481
|
|
|
saturate: function (ratio) {
|
|
482
|
|
|
var hsl = this.values.hsl;
|
|
483
|
|
|
hsl[1] += hsl[1] * ratio;
|
|
484
|
|
|
this.setValues('hsl', hsl);
|
|
485
|
|
|
return this;
|
|
486
|
|
|
},
|
|
487
|
|
|
|
|
488
|
|
|
desaturate: function (ratio) {
|
|
489
|
|
|
var hsl = this.values.hsl;
|
|
490
|
|
|
hsl[1] -= hsl[1] * ratio;
|
|
491
|
|
|
this.setValues('hsl', hsl);
|
|
492
|
|
|
return this;
|
|
493
|
|
|
},
|
|
494
|
|
|
|
|
495
|
|
|
whiten: function (ratio) {
|
|
496
|
|
|
var hwb = this.values.hwb;
|
|
497
|
|
|
hwb[1] += hwb[1] * ratio;
|
|
498
|
|
|
this.setValues('hwb', hwb);
|
|
499
|
|
|
return this;
|
|
500
|
|
|
},
|
|
501
|
|
|
|
|
502
|
|
|
blacken: function (ratio) {
|
|
503
|
|
|
var hwb = this.values.hwb;
|
|
504
|
|
|
hwb[2] += hwb[2] * ratio;
|
|
505
|
|
|
this.setValues('hwb', hwb);
|
|
506
|
|
|
return this;
|
|
507
|
|
|
},
|
|
508
|
|
|
|
|
509
|
|
|
greyscale: function () {
|
|
510
|
|
|
var rgb = this.values.rgb;
|
|
511
|
|
|
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
|
|
512
|
|
|
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
|
|
513
|
|
|
this.setValues('rgb', [val, val, val]);
|
|
514
|
|
|
return this;
|
|
515
|
|
|
},
|
|
516
|
|
|
|
|
517
|
|
|
clearer: function (ratio) {
|
|
518
|
|
|
var alpha = this.values.alpha;
|
|
519
|
|
|
this.setValues('alpha', alpha - (alpha * ratio));
|
|
520
|
|
|
return this;
|
|
521
|
|
|
},
|
|
522
|
|
|
|
|
523
|
|
|
opaquer: function (ratio) {
|
|
524
|
|
|
var alpha = this.values.alpha;
|
|
525
|
|
|
this.setValues('alpha', alpha + (alpha * ratio));
|
|
526
|
|
|
return this;
|
|
527
|
|
|
},
|
|
528
|
|
|
|
|
529
|
|
|
rotate: function (degrees) {
|
|
530
|
|
|
var hsl = this.values.hsl;
|
|
531
|
|
|
var hue = (hsl[0] + degrees) % 360;
|
|
532
|
|
|
hsl[0] = hue < 0 ? 360 + hue : hue;
|
|
533
|
|
|
this.setValues('hsl', hsl);
|
|
534
|
|
|
return this;
|
|
535
|
|
|
},
|
|
536
|
|
|
|
|
537
|
|
|
/**
|
|
538
|
|
|
* Ported from sass implementation in C
|
|
539
|
|
|
* https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
|
|
540
|
|
|
*/
|
|
541
|
|
|
mix: function (mixinColor, weight) {
|
|
542
|
|
|
var color1 = this;
|
|
543
|
|
|
var color2 = mixinColor;
|
|
544
|
|
|
var p = weight === undefined ? 0.5 : weight;
|
|
545
|
|
|
|
|
546
|
|
|
var w = 2 * p - 1;
|
|
547
|
|
|
var a = color1.alpha() - color2.alpha();
|
|
548
|
|
|
|
|
549
|
|
|
var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
|
|
550
|
|
|
var w2 = 1 - w1;
|
|
551
|
|
|
|
|
552
|
|
|
return this
|
|
553
|
|
|
.rgb(
|
|
554
|
|
|
w1 * color1.red() + w2 * color2.red(),
|
|
555
|
|
|
w1 * color1.green() + w2 * color2.green(),
|
|
556
|
|
|
w1 * color1.blue() + w2 * color2.blue()
|
|
557
|
|
|
)
|
|
558
|
|
|
.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
|
|
559
|
|
|
},
|
|
560
|
|
|
|
|
561
|
|
|
toJSON: function () {
|
|
562
|
|
|
return this.rgb();
|
|
563
|
|
|
},
|
|
564
|
|
|
|
|
565
|
|
|
clone: function () {
|
|
566
|
|
|
// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
|
|
567
|
|
|
// making the final build way to big to embed in Chart.js. So let's do it manually,
|
|
568
|
|
|
// assuming that values to clone are 1 dimension arrays containing only numbers,
|
|
569
|
|
|
// except 'alpha' which is a number.
|
|
570
|
|
|
var result = new Color();
|
|
571
|
|
|
var source = this.values;
|
|
572
|
|
|
var target = result.values;
|
|
573
|
|
|
var value, type;
|
|
574
|
|
|
|
|
575
|
|
|
for (var prop in source) {
|
|
576
|
|
|
if (source.hasOwnProperty(prop)) {
|
|
577
|
|
|
value = source[prop];
|
|
578
|
|
|
type = ({}).toString.call(value);
|
|
579
|
|
|
if (type === '[object Array]') {
|
|
580
|
|
|
target[prop] = value.slice(0);
|
|
581
|
|
|
} else if (type === '[object Number]') {
|
|
582
|
|
|
target[prop] = value;
|
|
583
|
|
|
} else {
|
|
584
|
|
|
console.error('unexpected color value:', value);
|
|
585
|
|
|
}
|
|
586
|
|
|
}
|
|
587
|
|
|
}
|
|
588
|
|
|
|
|
589
|
|
|
return result;
|
|
590
|
|
|
}
|
|
591
|
|
|
};
|
|
592
|
|
|
|
|
593
|
|
|
Color.prototype.spaces = {
|
|
594
|
|
|
rgb: ['red', 'green', 'blue'],
|
|
595
|
|
|
hsl: ['hue', 'saturation', 'lightness'],
|
|
596
|
|
|
hsv: ['hue', 'saturation', 'value'],
|
|
597
|
|
|
hwb: ['hue', 'whiteness', 'blackness'],
|
|
598
|
|
|
cmyk: ['cyan', 'magenta', 'yellow', 'black']
|
|
599
|
|
|
};
|
|
600
|
|
|
|
|
601
|
|
|
Color.prototype.maxes = {
|
|
602
|
|
|
rgb: [255, 255, 255],
|
|
603
|
|
|
hsl: [360, 100, 100],
|
|
604
|
|
|
hsv: [360, 100, 100],
|
|
605
|
|
|
hwb: [360, 100, 100],
|
|
606
|
|
|
cmyk: [100, 100, 100, 100]
|
|
607
|
|
|
};
|
|
608
|
|
|
|
|
609
|
|
|
Color.prototype.getValues = function (space) {
|
|
610
|
|
|
var values = this.values;
|
|
611
|
|
|
var vals = {};
|
|
612
|
|
|
|
|
613
|
|
|
for (var i = 0; i < space.length; i++) {
|
|
614
|
|
|
vals[space.charAt(i)] = values[space][i];
|
|
615
|
|
|
}
|
|
616
|
|
|
|
|
617
|
|
|
if (values.alpha !== 1) {
|
|
618
|
|
|
vals.a = values.alpha;
|
|
619
|
|
|
}
|
|
620
|
|
|
|
|
621
|
|
|
// {r: 255, g: 255, b: 255, a: 0.4}
|
|
622
|
|
|
return vals;
|
|
623
|
|
|
};
|
|
624
|
|
|
|
|
625
|
|
|
Color.prototype.setValues = function (space, vals) {
|
|
626
|
|
|
var values = this.values;
|
|
627
|
|
|
var spaces = this.spaces;
|
|
628
|
|
|
var maxes = this.maxes;
|
|
629
|
|
|
var alpha = 1;
|
|
630
|
|
|
var i;
|
|
631
|
|
|
|
|
632
|
|
|
if (space === 'alpha') {
|
|
633
|
|
|
alpha = vals;
|
|
634
|
|
|
} else if (vals.length) {
|
|
635
|
|
|
// [10, 10, 10]
|
|
636
|
|
|
values[space] = vals.slice(0, space.length);
|
|
637
|
|
|
alpha = vals[space.length];
|
|
638
|
|
|
} else if (vals[space.charAt(0)] !== undefined) {
|
|
639
|
|
|
// {r: 10, g: 10, b: 10}
|
|
640
|
|
|
for (i = 0; i < space.length; i++) {
|
|
641
|
|
|
values[space][i] = vals[space.charAt(i)];
|
|
642
|
|
|
}
|
|
643
|
|
|
|
|
644
|
|
|
alpha = vals.a;
|
|
645
|
|
|
} else if (vals[spaces[space][0]] !== undefined) {
|
|
646
|
|
|
// {red: 10, green: 10, blue: 10}
|
|
647
|
|
|
var chans = spaces[space];
|
|
648
|
|
|
|
|
649
|
|
|
for (i = 0; i < space.length; i++) {
|
|
650
|
|
|
values[space][i] = vals[chans[i]];
|
|
651
|
|
|
}
|
|
652
|
|
|
|
|
653
|
|
|
alpha = vals.alpha;
|
|
654
|
|
|
}
|
|
655
|
|
|
|
|
656
|
|
|
values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
|
|
657
|
|
|
|
|
658
|
|
|
if (space === 'alpha') {
|
|
659
|
|
|
return false;
|
|
660
|
|
|
}
|
|
661
|
|
|
|
|
662
|
|
|
var capped;
|
|
663
|
|
|
|
|
664
|
|
|
// cap values of the space prior converting all values
|
|
665
|
|
|
for (i = 0; i < space.length; i++) {
|
|
666
|
|
|
capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
|
|
667
|
|
|
values[space][i] = Math.round(capped);
|
|
668
|
|
|
}
|
|
669
|
|
|
|
|
670
|
|
|
// convert to all the other color spaces
|
|
671
|
|
|
for (var sname in spaces) {
|
|
|
|
|
|
|
672
|
|
|
if (sname !== space) {
|
|
673
|
|
|
values[sname] = convert[space][sname](values[space]);
|
|
674
|
|
|
}
|
|
675
|
|
|
}
|
|
676
|
|
|
|
|
677
|
|
|
return true;
|
|
678
|
|
|
};
|
|
679
|
|
|
|
|
680
|
|
|
Color.prototype.setSpace = function (space, args) {
|
|
681
|
|
|
var vals = args[0];
|
|
682
|
|
|
|
|
683
|
|
|
if (vals === undefined) {
|
|
684
|
|
|
// color.rgb()
|
|
685
|
|
|
return this.getValues(space);
|
|
686
|
|
|
}
|
|
687
|
|
|
|
|
688
|
|
|
// color.rgb(10, 10, 10)
|
|
689
|
|
|
if (typeof vals === 'number') {
|
|
690
|
|
|
vals = Array.prototype.slice.call(args);
|
|
691
|
|
|
}
|
|
692
|
|
|
|
|
693
|
|
|
this.setValues(space, vals);
|
|
694
|
|
|
return this;
|
|
695
|
|
|
};
|
|
696
|
|
|
|
|
697
|
|
|
Color.prototype.setChannel = function (space, index, val) {
|
|
698
|
|
|
var svalues = this.values[space];
|
|
699
|
|
|
if (val === undefined) {
|
|
700
|
|
|
// color.red()
|
|
701
|
|
|
return svalues[index];
|
|
702
|
|
|
} else if (val === svalues[index]) {
|
|
703
|
|
|
// color.red(color.red())
|
|
704
|
|
|
return this;
|
|
705
|
|
|
}
|
|
706
|
|
|
|
|
707
|
|
|
// color.red(100)
|
|
708
|
|
|
svalues[index] = val;
|
|
709
|
|
|
this.setValues(space, svalues);
|
|
710
|
|
|
|
|
711
|
|
|
return this;
|
|
712
|
|
|
};
|
|
713
|
|
|
|
|
714
|
|
|
if (typeof window !== 'undefined') {
|
|
715
|
|
|
window.Color = Color;
|
|
716
|
|
|
}
|
|
717
|
|
|
|
|
718
|
|
|
module.exports = Color;
|
|
719
|
|
|
|
|
720
|
|
|
},{"2":2,"5":5}],4:[function(require,module,exports){ |
|
721
|
|
|
/* MIT license */ |
|
722
|
|
|
|
|
723
|
|
|
module.exports = { |
|
724
|
|
|
rgb2hsl: rgb2hsl, |
|
725
|
|
|
rgb2hsv: rgb2hsv, |
|
726
|
|
|
rgb2hwb: rgb2hwb, |
|
727
|
|
|
rgb2cmyk: rgb2cmyk, |
|
728
|
|
|
rgb2keyword: rgb2keyword, |
|
729
|
|
|
rgb2xyz: rgb2xyz, |
|
730
|
|
|
rgb2lab: rgb2lab, |
|
731
|
|
|
rgb2lch: rgb2lch, |
|
732
|
|
|
|
|
733
|
|
|
hsl2rgb: hsl2rgb, |
|
734
|
|
|
hsl2hsv: hsl2hsv, |
|
735
|
|
|
hsl2hwb: hsl2hwb, |
|
736
|
|
|
hsl2cmyk: hsl2cmyk, |
|
737
|
|
|
hsl2keyword: hsl2keyword, |
|
738
|
|
|
|
|
739
|
|
|
hsv2rgb: hsv2rgb, |
|
740
|
|
|
hsv2hsl: hsv2hsl, |
|
741
|
|
|
hsv2hwb: hsv2hwb, |
|
742
|
|
|
hsv2cmyk: hsv2cmyk, |
|
743
|
|
|
hsv2keyword: hsv2keyword, |
|
744
|
|
|
|
|
745
|
|
|
hwb2rgb: hwb2rgb, |
|
746
|
|
|
hwb2hsl: hwb2hsl, |
|
747
|
|
|
hwb2hsv: hwb2hsv, |
|
748
|
|
|
hwb2cmyk: hwb2cmyk, |
|
749
|
|
|
hwb2keyword: hwb2keyword, |
|
750
|
|
|
|
|
751
|
|
|
cmyk2rgb: cmyk2rgb, |
|
752
|
|
|
cmyk2hsl: cmyk2hsl, |
|
753
|
|
|
cmyk2hsv: cmyk2hsv, |
|
754
|
|
|
cmyk2hwb: cmyk2hwb, |
|
755
|
|
|
cmyk2keyword: cmyk2keyword, |
|
756
|
|
|
|
|
757
|
|
|
keyword2rgb: keyword2rgb, |
|
758
|
|
|
keyword2hsl: keyword2hsl, |
|
759
|
|
|
keyword2hsv: keyword2hsv, |
|
760
|
|
|
keyword2hwb: keyword2hwb, |
|
761
|
|
|
keyword2cmyk: keyword2cmyk, |
|
762
|
|
|
keyword2lab: keyword2lab, |
|
763
|
|
|
keyword2xyz: keyword2xyz, |
|
764
|
|
|
|
|
765
|
|
|
xyz2rgb: xyz2rgb, |
|
766
|
|
|
xyz2lab: xyz2lab, |
|
767
|
|
|
xyz2lch: xyz2lch, |
|
768
|
|
|
|
|
769
|
|
|
lab2xyz: lab2xyz, |
|
770
|
|
|
lab2rgb: lab2rgb, |
|
771
|
|
|
lab2lch: lab2lch, |
|
772
|
|
|
|
|
773
|
|
|
lch2lab: lch2lab, |
|
774
|
|
|
lch2xyz: lch2xyz, |
|
775
|
|
|
lch2rgb: lch2rgb |
|
776
|
|
|
} |
|
777
|
|
|
|
|
778
|
|
|
|
|
779
|
|
|
function rgb2hsl(rgb) { |
|
|
|
|
|
|
780
|
|
|
var r = rgb[0]/255, |
|
781
|
|
|
g = rgb[1]/255, |
|
782
|
|
|
b = rgb[2]/255, |
|
783
|
|
|
min = Math.min(r, g, b), |
|
784
|
|
|
max = Math.max(r, g, b), |
|
785
|
|
|
delta = max - min, |
|
786
|
|
|
h, s, l; |
|
787
|
|
|
|
|
788
|
|
|
if (max == min) |
|
789
|
|
|
h = 0; |
|
|
|
|
|
|
790
|
|
|
else if (r == max) |
|
791
|
|
|
h = (g - b) / delta; |
|
|
|
|
|
|
792
|
|
|
else if (g == max) |
|
793
|
|
|
h = 2 + (b - r) / delta; |
|
|
|
|
|
|
794
|
|
|
else if (b == max) |
|
795
|
|
|
h = 4 + (r - g)/ delta; |
|
|
|
|
|
|
796
|
|
|
|
|
797
|
|
|
h = Math.min(h * 60, 360); |
|
|
|
|
|
|
798
|
|
|
|
|
799
|
|
|
if (h < 0) |
|
800
|
|
|
h += 360; |
|
|
|
|
|
|
801
|
|
|
|
|
802
|
|
|
l = (min + max) / 2; |
|
803
|
|
|
|
|
804
|
|
|
if (max == min) |
|
805
|
|
|
s = 0; |
|
|
|
|
|
|
806
|
|
|
else if (l <= 0.5) |
|
807
|
|
|
s = delta / (max + min); |
|
|
|
|
|
|
808
|
|
|
else |
|
809
|
|
|
s = delta / (2 - max - min); |
|
810
|
|
|
|
|
811
|
|
|
return [h, s * 100, l * 100]; |
|
812
|
|
|
} |
|
813
|
|
|
|
|
814
|
|
|
function rgb2hsv(rgb) { |
|
|
|
|
|
|
815
|
|
|
var r = rgb[0], |
|
816
|
|
|
g = rgb[1], |
|
817
|
|
|
b = rgb[2], |
|
818
|
|
|
min = Math.min(r, g, b), |
|
819
|
|
|
max = Math.max(r, g, b), |
|
820
|
|
|
delta = max - min, |
|
821
|
|
|
h, s, v; |
|
822
|
|
|
|
|
823
|
|
|
if (max == 0) |
|
824
|
|
|
s = 0; |
|
|
|
|
|
|
825
|
|
|
else |
|
826
|
|
|
s = (delta/max * 1000)/10; |
|
827
|
|
|
|
|
828
|
|
|
if (max == min) |
|
829
|
|
|
h = 0; |
|
|
|
|
|
|
830
|
|
|
else if (r == max) |
|
831
|
|
|
h = (g - b) / delta; |
|
|
|
|
|
|
832
|
|
|
else if (g == max) |
|
833
|
|
|
h = 2 + (b - r) / delta; |
|
|
|
|
|
|
834
|
|
|
else if (b == max) |
|
835
|
|
|
h = 4 + (r - g) / delta; |
|
|
|
|
|
|
836
|
|
|
|
|
837
|
|
|
h = Math.min(h * 60, 360); |
|
|
|
|
|
|
838
|
|
|
|
|
839
|
|
|
if (h < 0) |
|
840
|
|
|
h += 360; |
|
|
|
|
|
|
841
|
|
|
|
|
842
|
|
|
v = ((max / 255) * 1000) / 10; |
|
843
|
|
|
|
|
844
|
|
|
return [h, s, v]; |
|
845
|
|
|
} |
|
846
|
|
|
|
|
847
|
|
|
function rgb2hwb(rgb) { |
|
848
|
|
|
var r = rgb[0], |
|
849
|
|
|
g = rgb[1], |
|
850
|
|
|
b = rgb[2], |
|
851
|
|
|
h = rgb2hsl(rgb)[0], |
|
852
|
|
|
w = 1/255 * Math.min(r, Math.min(g, b)), |
|
853
|
|
|
b = 1 - 1/255 * Math.max(r, Math.max(g, b)); |
|
|
|
|
|
|
854
|
|
|
|
|
855
|
|
|
return [h, w * 100, b * 100]; |
|
856
|
|
|
} |
|
857
|
|
|
|
|
858
|
|
|
function rgb2cmyk(rgb) { |
|
859
|
|
|
var r = rgb[0] / 255, |
|
860
|
|
|
g = rgb[1] / 255, |
|
861
|
|
|
b = rgb[2] / 255, |
|
862
|
|
|
c, m, y, k; |
|
863
|
|
|
|
|
864
|
|
|
k = Math.min(1 - r, 1 - g, 1 - b); |
|
865
|
|
|
c = (1 - r - k) / (1 - k) || 0; |
|
866
|
|
|
m = (1 - g - k) / (1 - k) || 0; |
|
867
|
|
|
y = (1 - b - k) / (1 - k) || 0; |
|
868
|
|
|
return [c * 100, m * 100, y * 100, k * 100]; |
|
869
|
|
|
} |
|
870
|
|
|
|
|
871
|
|
|
function rgb2keyword(rgb) { |
|
872
|
|
|
return reverseKeywords[JSON.stringify(rgb)]; |
|
873
|
|
|
} |
|
874
|
|
|
|
|
875
|
|
|
function rgb2xyz(rgb) { |
|
876
|
|
|
var r = rgb[0] / 255, |
|
877
|
|
|
g = rgb[1] / 255, |
|
878
|
|
|
b = rgb[2] / 255; |
|
879
|
|
|
|
|
880
|
|
|
// assume sRGB |
|
881
|
|
|
r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); |
|
882
|
|
|
g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); |
|
883
|
|
|
b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); |
|
884
|
|
|
|
|
885
|
|
|
var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); |
|
886
|
|
|
var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); |
|
887
|
|
|
var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); |
|
888
|
|
|
|
|
889
|
|
|
return [x * 100, y *100, z * 100]; |
|
890
|
|
|
} |
|
891
|
|
|
|
|
892
|
|
|
function rgb2lab(rgb) { |
|
893
|
|
|
var xyz = rgb2xyz(rgb), |
|
894
|
|
|
x = xyz[0], |
|
895
|
|
|
y = xyz[1], |
|
896
|
|
|
z = xyz[2], |
|
897
|
|
|
l, a, b; |
|
898
|
|
|
|
|
899
|
|
|
x /= 95.047; |
|
900
|
|
|
y /= 100; |
|
901
|
|
|
z /= 108.883; |
|
902
|
|
|
|
|
903
|
|
|
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); |
|
904
|
|
|
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); |
|
905
|
|
|
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); |
|
906
|
|
|
|
|
907
|
|
|
l = (116 * y) - 16; |
|
908
|
|
|
a = 500 * (x - y); |
|
909
|
|
|
b = 200 * (y - z); |
|
910
|
|
|
|
|
911
|
|
|
return [l, a, b]; |
|
912
|
|
|
} |
|
913
|
|
|
|
|
914
|
|
|
function rgb2lch(args) { |
|
915
|
|
|
return lab2lch(rgb2lab(args)); |
|
916
|
|
|
} |
|
917
|
|
|
|
|
918
|
|
|
function hsl2rgb(hsl) { |
|
919
|
|
|
var h = hsl[0] / 360, |
|
920
|
|
|
s = hsl[1] / 100, |
|
921
|
|
|
l = hsl[2] / 100, |
|
922
|
|
|
t1, t2, t3, rgb, val; |
|
923
|
|
|
|
|
924
|
|
|
if (s == 0) { |
|
925
|
|
|
val = l * 255; |
|
926
|
|
|
return [val, val, val]; |
|
927
|
|
|
} |
|
928
|
|
|
|
|
929
|
|
|
if (l < 0.5) |
|
930
|
|
|
t2 = l * (1 + s); |
|
|
|
|
|
|
931
|
|
|
else |
|
932
|
|
|
t2 = l + s - l * s; |
|
933
|
|
|
t1 = 2 * l - t2; |
|
934
|
|
|
|
|
935
|
|
|
rgb = [0, 0, 0]; |
|
936
|
|
|
for (var i = 0; i < 3; i++) { |
|
937
|
|
|
t3 = h + 1 / 3 * - (i - 1); |
|
938
|
|
|
t3 < 0 && t3++; |
|
939
|
|
|
t3 > 1 && t3--; |
|
940
|
|
|
|
|
941
|
|
|
if (6 * t3 < 1) |
|
942
|
|
|
val = t1 + (t2 - t1) * 6 * t3; |
|
|
|
|
|
|
943
|
|
|
else if (2 * t3 < 1) |
|
944
|
|
|
val = t2; |
|
|
|
|
|
|
945
|
|
|
else if (3 * t3 < 2) |
|
946
|
|
|
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; |
|
|
|
|
|
|
947
|
|
|
else |
|
948
|
|
|
val = t1; |
|
949
|
|
|
|
|
950
|
|
|
rgb[i] = val * 255; |
|
951
|
|
|
} |
|
952
|
|
|
|
|
953
|
|
|
return rgb; |
|
954
|
|
|
} |
|
955
|
|
|
|
|
956
|
|
|
function hsl2hsv(hsl) { |
|
957
|
|
|
var h = hsl[0], |
|
958
|
|
|
s = hsl[1] / 100, |
|
959
|
|
|
l = hsl[2] / 100, |
|
960
|
|
|
sv, v; |
|
961
|
|
|
|
|
962
|
|
|
if(l === 0) { |
|
963
|
|
|
// no need to do calc on black |
|
964
|
|
|
// also avoids divide by 0 error |
|
965
|
|
|
return [0, 0, 0]; |
|
966
|
|
|
} |
|
967
|
|
|
|
|
968
|
|
|
l *= 2; |
|
969
|
|
|
s *= (l <= 1) ? l : 2 - l; |
|
970
|
|
|
v = (l + s) / 2; |
|
971
|
|
|
sv = (2 * s) / (l + s); |
|
972
|
|
|
return [h, sv * 100, v * 100]; |
|
973
|
|
|
} |
|
974
|
|
|
|
|
975
|
|
|
function hsl2hwb(args) { |
|
976
|
|
|
return rgb2hwb(hsl2rgb(args)); |
|
977
|
|
|
} |
|
978
|
|
|
|
|
979
|
|
|
function hsl2cmyk(args) { |
|
980
|
|
|
return rgb2cmyk(hsl2rgb(args)); |
|
981
|
|
|
} |
|
982
|
|
|
|
|
983
|
|
|
function hsl2keyword(args) { |
|
984
|
|
|
return rgb2keyword(hsl2rgb(args)); |
|
985
|
|
|
} |
|
986
|
|
|
|
|
987
|
|
|
|
|
988
|
|
|
function hsv2rgb(hsv) { |
|
989
|
|
|
var h = hsv[0] / 60, |
|
990
|
|
|
s = hsv[1] / 100, |
|
991
|
|
|
v = hsv[2] / 100, |
|
992
|
|
|
hi = Math.floor(h) % 6; |
|
993
|
|
|
|
|
994
|
|
|
var f = h - Math.floor(h), |
|
995
|
|
|
p = 255 * v * (1 - s), |
|
996
|
|
|
q = 255 * v * (1 - (s * f)), |
|
997
|
|
|
t = 255 * v * (1 - (s * (1 - f))), |
|
998
|
|
|
v = 255 * v; |
|
|
|
|
|
|
999
|
|
|
|
|
1000
|
|
|
switch(hi) { |
|
|
|
|
|
|
1001
|
|
|
case 0: |
|
1002
|
|
|
return [v, t, p]; |
|
1003
|
|
|
case 1: |
|
1004
|
|
|
return [q, v, p]; |
|
1005
|
|
|
case 2: |
|
1006
|
|
|
return [p, v, t]; |
|
1007
|
|
|
case 3: |
|
1008
|
|
|
return [p, q, v]; |
|
1009
|
|
|
case 4: |
|
1010
|
|
|
return [t, p, v]; |
|
1011
|
|
|
case 5: |
|
1012
|
|
|
return [v, p, q]; |
|
1013
|
|
|
} |
|
|
|
|
|
|
1014
|
|
|
} |
|
1015
|
|
|
|
|
1016
|
|
|
function hsv2hsl(hsv) { |
|
1017
|
|
|
var h = hsv[0], |
|
1018
|
|
|
s = hsv[1] / 100, |
|
1019
|
|
|
v = hsv[2] / 100, |
|
1020
|
|
|
sl, l; |
|
1021
|
|
|
|
|
1022
|
|
|
l = (2 - s) * v; |
|
1023
|
|
|
sl = s * v; |
|
1024
|
|
|
sl /= (l <= 1) ? l : 2 - l; |
|
1025
|
|
|
sl = sl || 0; |
|
1026
|
|
|
l /= 2; |
|
1027
|
|
|
return [h, sl * 100, l * 100]; |
|
1028
|
|
|
} |
|
1029
|
|
|
|
|
1030
|
|
|
function hsv2hwb(args) { |
|
1031
|
|
|
return rgb2hwb(hsv2rgb(args)) |
|
1032
|
|
|
} |
|
1033
|
|
|
|
|
1034
|
|
|
function hsv2cmyk(args) { |
|
1035
|
|
|
return rgb2cmyk(hsv2rgb(args)); |
|
1036
|
|
|
} |
|
1037
|
|
|
|
|
1038
|
|
|
function hsv2keyword(args) { |
|
1039
|
|
|
return rgb2keyword(hsv2rgb(args)); |
|
1040
|
|
|
} |
|
1041
|
|
|
|
|
1042
|
|
|
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb |
|
1043
|
|
|
function hwb2rgb(hwb) { |
|
1044
|
|
|
var h = hwb[0] / 360, |
|
1045
|
|
|
wh = hwb[1] / 100, |
|
1046
|
|
|
bl = hwb[2] / 100, |
|
1047
|
|
|
ratio = wh + bl, |
|
1048
|
|
|
i, v, f, n; |
|
1049
|
|
|
|
|
1050
|
|
|
// wh + bl cant be > 1 |
|
1051
|
|
|
if (ratio > 1) { |
|
1052
|
|
|
wh /= ratio; |
|
1053
|
|
|
bl /= ratio; |
|
1054
|
|
|
} |
|
1055
|
|
|
|
|
1056
|
|
|
i = Math.floor(6 * h); |
|
1057
|
|
|
v = 1 - bl; |
|
1058
|
|
|
f = 6 * h - i; |
|
1059
|
|
|
if ((i & 0x01) != 0) { |
|
1060
|
|
|
f = 1 - f; |
|
1061
|
|
|
} |
|
1062
|
|
|
n = wh + f * (v - wh); // linear interpolation |
|
1063
|
|
|
|
|
1064
|
|
|
switch (i) { |
|
1065
|
|
|
default: |
|
|
|
|
|
|
1066
|
|
|
case 6: |
|
1067
|
|
|
case 0: r = v; g = n; b = wh; break; |
|
|
|
|
|
|
1068
|
|
|
case 1: r = n; g = v; b = wh; break; |
|
1069
|
|
|
case 2: r = wh; g = v; b = n; break; |
|
1070
|
|
|
case 3: r = wh; g = n; b = v; break; |
|
1071
|
|
|
case 4: r = n; g = wh; b = v; break; |
|
1072
|
|
|
case 5: r = v; g = wh; b = n; break; |
|
1073
|
|
|
} |
|
1074
|
|
|
|
|
1075
|
|
|
return [r * 255, g * 255, b * 255]; |
|
1076
|
|
|
} |
|
1077
|
|
|
|
|
1078
|
|
|
function hwb2hsl(args) { |
|
1079
|
|
|
return rgb2hsl(hwb2rgb(args)); |
|
1080
|
|
|
} |
|
1081
|
|
|
|
|
1082
|
|
|
function hwb2hsv(args) { |
|
1083
|
|
|
return rgb2hsv(hwb2rgb(args)); |
|
1084
|
|
|
} |
|
1085
|
|
|
|
|
1086
|
|
|
function hwb2cmyk(args) { |
|
1087
|
|
|
return rgb2cmyk(hwb2rgb(args)); |
|
1088
|
|
|
} |
|
1089
|
|
|
|
|
1090
|
|
|
function hwb2keyword(args) { |
|
1091
|
|
|
return rgb2keyword(hwb2rgb(args)); |
|
1092
|
|
|
} |
|
1093
|
|
|
|
|
1094
|
|
|
function cmyk2rgb(cmyk) { |
|
1095
|
|
|
var c = cmyk[0] / 100, |
|
1096
|
|
|
m = cmyk[1] / 100, |
|
1097
|
|
|
y = cmyk[2] / 100, |
|
1098
|
|
|
k = cmyk[3] / 100, |
|
1099
|
|
|
r, g, b; |
|
1100
|
|
|
|
|
1101
|
|
|
r = 1 - Math.min(1, c * (1 - k) + k); |
|
1102
|
|
|
g = 1 - Math.min(1, m * (1 - k) + k); |
|
1103
|
|
|
b = 1 - Math.min(1, y * (1 - k) + k); |
|
1104
|
|
|
return [r * 255, g * 255, b * 255]; |
|
1105
|
|
|
} |
|
1106
|
|
|
|
|
1107
|
|
|
function cmyk2hsl(args) { |
|
1108
|
|
|
return rgb2hsl(cmyk2rgb(args)); |
|
1109
|
|
|
} |
|
1110
|
|
|
|
|
1111
|
|
|
function cmyk2hsv(args) { |
|
1112
|
|
|
return rgb2hsv(cmyk2rgb(args)); |
|
1113
|
|
|
} |
|
1114
|
|
|
|
|
1115
|
|
|
function cmyk2hwb(args) { |
|
1116
|
|
|
return rgb2hwb(cmyk2rgb(args)); |
|
1117
|
|
|
} |
|
1118
|
|
|
|
|
1119
|
|
|
function cmyk2keyword(args) { |
|
1120
|
|
|
return rgb2keyword(cmyk2rgb(args)); |
|
1121
|
|
|
} |
|
1122
|
|
|
|
|
1123
|
|
|
|
|
1124
|
|
|
function xyz2rgb(xyz) { |
|
1125
|
|
|
var x = xyz[0] / 100, |
|
1126
|
|
|
y = xyz[1] / 100, |
|
1127
|
|
|
z = xyz[2] / 100, |
|
1128
|
|
|
r, g, b; |
|
1129
|
|
|
|
|
1130
|
|
|
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); |
|
1131
|
|
|
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); |
|
1132
|
|
|
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); |
|
1133
|
|
|
|
|
1134
|
|
|
// assume sRGB |
|
1135
|
|
|
r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) |
|
1136
|
|
|
: r = (r * 12.92); |
|
|
|
|
|
|
1137
|
|
|
|
|
1138
|
|
|
g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) |
|
1139
|
|
|
: g = (g * 12.92); |
|
|
|
|
|
|
1140
|
|
|
|
|
1141
|
|
|
b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) |
|
1142
|
|
|
: b = (b * 12.92); |
|
|
|
|
|
|
1143
|
|
|
|
|
1144
|
|
|
r = Math.min(Math.max(0, r), 1); |
|
1145
|
|
|
g = Math.min(Math.max(0, g), 1); |
|
1146
|
|
|
b = Math.min(Math.max(0, b), 1); |
|
1147
|
|
|
|
|
1148
|
|
|
return [r * 255, g * 255, b * 255]; |
|
1149
|
|
|
} |
|
1150
|
|
|
|
|
1151
|
|
|
function xyz2lab(xyz) { |
|
1152
|
|
|
var x = xyz[0], |
|
1153
|
|
|
y = xyz[1], |
|
1154
|
|
|
z = xyz[2], |
|
1155
|
|
|
l, a, b; |
|
1156
|
|
|
|
|
1157
|
|
|
x /= 95.047; |
|
1158
|
|
|
y /= 100; |
|
1159
|
|
|
z /= 108.883; |
|
1160
|
|
|
|
|
1161
|
|
|
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); |
|
1162
|
|
|
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); |
|
1163
|
|
|
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); |
|
1164
|
|
|
|
|
1165
|
|
|
l = (116 * y) - 16; |
|
1166
|
|
|
a = 500 * (x - y); |
|
1167
|
|
|
b = 200 * (y - z); |
|
1168
|
|
|
|
|
1169
|
|
|
return [l, a, b]; |
|
1170
|
|
|
} |
|
1171
|
|
|
|
|
1172
|
|
|
function xyz2lch(args) { |
|
1173
|
|
|
return lab2lch(xyz2lab(args)); |
|
1174
|
|
|
} |
|
1175
|
|
|
|
|
1176
|
|
|
function lab2xyz(lab) { |
|
1177
|
|
|
var l = lab[0], |
|
1178
|
|
|
a = lab[1], |
|
1179
|
|
|
b = lab[2], |
|
1180
|
|
|
x, y, z, y2; |
|
1181
|
|
|
|
|
1182
|
|
|
if (l <= 8) { |
|
1183
|
|
|
y = (l * 100) / 903.3; |
|
1184
|
|
|
y2 = (7.787 * (y / 100)) + (16 / 116); |
|
1185
|
|
|
} else { |
|
1186
|
|
|
y = 100 * Math.pow((l + 16) / 116, 3); |
|
1187
|
|
|
y2 = Math.pow(y / 100, 1/3); |
|
1188
|
|
|
} |
|
1189
|
|
|
|
|
1190
|
|
|
x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); |
|
|
|
|
|
|
1191
|
|
|
|
|
1192
|
|
|
z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); |
|
|
|
|
|
|
1193
|
|
|
|
|
1194
|
|
|
return [x, y, z]; |
|
1195
|
|
|
} |
|
1196
|
|
|
|
|
1197
|
|
|
function lab2lch(lab) { |
|
1198
|
|
|
var l = lab[0], |
|
1199
|
|
|
a = lab[1], |
|
1200
|
|
|
b = lab[2], |
|
1201
|
|
|
hr, h, c; |
|
1202
|
|
|
|
|
1203
|
|
|
hr = Math.atan2(b, a); |
|
1204
|
|
|
h = hr * 360 / 2 / Math.PI; |
|
1205
|
|
|
if (h < 0) { |
|
1206
|
|
|
h += 360; |
|
1207
|
|
|
} |
|
1208
|
|
|
c = Math.sqrt(a * a + b * b); |
|
1209
|
|
|
return [l, c, h]; |
|
1210
|
|
|
} |
|
1211
|
|
|
|
|
1212
|
|
|
function lab2rgb(args) { |
|
1213
|
|
|
return xyz2rgb(lab2xyz(args)); |
|
1214
|
|
|
} |
|
1215
|
|
|
|
|
1216
|
|
|
function lch2lab(lch) { |
|
1217
|
|
|
var l = lch[0], |
|
1218
|
|
|
c = lch[1], |
|
1219
|
|
|
h = lch[2], |
|
1220
|
|
|
a, b, hr; |
|
1221
|
|
|
|
|
1222
|
|
|
hr = h / 360 * 2 * Math.PI; |
|
1223
|
|
|
a = c * Math.cos(hr); |
|
1224
|
|
|
b = c * Math.sin(hr); |
|
1225
|
|
|
return [l, a, b]; |
|
1226
|
|
|
} |
|
1227
|
|
|
|
|
1228
|
|
|
function lch2xyz(args) { |
|
1229
|
|
|
return lab2xyz(lch2lab(args)); |
|
1230
|
|
|
} |
|
1231
|
|
|
|
|
1232
|
|
|
function lch2rgb(args) { |
|
1233
|
|
|
return lab2rgb(lch2lab(args)); |
|
1234
|
|
|
} |
|
1235
|
|
|
|
|
1236
|
|
|
function keyword2rgb(keyword) { |
|
1237
|
|
|
return cssKeywords[keyword]; |
|
1238
|
|
|
} |
|
1239
|
|
|
|
|
1240
|
|
|
function keyword2hsl(args) { |
|
1241
|
|
|
return rgb2hsl(keyword2rgb(args)); |
|
1242
|
|
|
} |
|
1243
|
|
|
|
|
1244
|
|
|
function keyword2hsv(args) { |
|
1245
|
|
|
return rgb2hsv(keyword2rgb(args)); |
|
1246
|
|
|
} |
|
1247
|
|
|
|
|
1248
|
|
|
function keyword2hwb(args) { |
|
1249
|
|
|
return rgb2hwb(keyword2rgb(args)); |
|
1250
|
|
|
} |
|
1251
|
|
|
|
|
1252
|
|
|
function keyword2cmyk(args) { |
|
1253
|
|
|
return rgb2cmyk(keyword2rgb(args)); |
|
1254
|
|
|
} |
|
1255
|
|
|
|
|
1256
|
|
|
function keyword2lab(args) { |
|
1257
|
|
|
return rgb2lab(keyword2rgb(args)); |
|
1258
|
|
|
} |
|
1259
|
|
|
|
|
1260
|
|
|
function keyword2xyz(args) { |
|
1261
|
|
|
return rgb2xyz(keyword2rgb(args)); |
|
1262
|
|
|
} |
|
1263
|
|
|
|
|
1264
|
|
|
var cssKeywords = { |
|
1265
|
|
|
aliceblue: [240,248,255], |
|
1266
|
|
|
antiquewhite: [250,235,215], |
|
1267
|
|
|
aqua: [0,255,255], |
|
1268
|
|
|
aquamarine: [127,255,212], |
|
1269
|
|
|
azure: [240,255,255], |
|
1270
|
|
|
beige: [245,245,220], |
|
1271
|
|
|
bisque: [255,228,196], |
|
1272
|
|
|
black: [0,0,0], |
|
1273
|
|
|
blanchedalmond: [255,235,205], |
|
1274
|
|
|
blue: [0,0,255], |
|
1275
|
|
|
blueviolet: [138,43,226], |
|
1276
|
|
|
brown: [165,42,42], |
|
1277
|
|
|
burlywood: [222,184,135], |
|
1278
|
|
|
cadetblue: [95,158,160], |
|
1279
|
|
|
chartreuse: [127,255,0], |
|
1280
|
|
|
chocolate: [210,105,30], |
|
1281
|
|
|
coral: [255,127,80], |
|
1282
|
|
|
cornflowerblue: [100,149,237], |
|
1283
|
|
|
cornsilk: [255,248,220], |
|
1284
|
|
|
crimson: [220,20,60], |
|
1285
|
|
|
cyan: [0,255,255], |
|
1286
|
|
|
darkblue: [0,0,139], |
|
1287
|
|
|
darkcyan: [0,139,139], |
|
1288
|
|
|
darkgoldenrod: [184,134,11], |
|
1289
|
|
|
darkgray: [169,169,169], |
|
1290
|
|
|
darkgreen: [0,100,0], |
|
1291
|
|
|
darkgrey: [169,169,169], |
|
1292
|
|
|
darkkhaki: [189,183,107], |
|
1293
|
|
|
darkmagenta: [139,0,139], |
|
1294
|
|
|
darkolivegreen: [85,107,47], |
|
1295
|
|
|
darkorange: [255,140,0], |
|
1296
|
|
|
darkorchid: [153,50,204], |
|
1297
|
|
|
darkred: [139,0,0], |
|
1298
|
|
|
darksalmon: [233,150,122], |
|
1299
|
|
|
darkseagreen: [143,188,143], |
|
1300
|
|
|
darkslateblue: [72,61,139], |
|
1301
|
|
|
darkslategray: [47,79,79], |
|
1302
|
|
|
darkslategrey: [47,79,79], |
|
1303
|
|
|
darkturquoise: [0,206,209], |
|
1304
|
|
|
darkviolet: [148,0,211], |
|
1305
|
|
|
deeppink: [255,20,147], |
|
1306
|
|
|
deepskyblue: [0,191,255], |
|
1307
|
|
|
dimgray: [105,105,105], |
|
1308
|
|
|
dimgrey: [105,105,105], |
|
1309
|
|
|
dodgerblue: [30,144,255], |
|
1310
|
|
|
firebrick: [178,34,34], |
|
1311
|
|
|
floralwhite: [255,250,240], |
|
1312
|
|
|
forestgreen: [34,139,34], |
|
1313
|
|
|
fuchsia: [255,0,255], |
|
1314
|
|
|
gainsboro: [220,220,220], |
|
1315
|
|
|
ghostwhite: [248,248,255], |
|
1316
|
|
|
gold: [255,215,0], |
|
1317
|
|
|
goldenrod: [218,165,32], |
|
1318
|
|
|
gray: [128,128,128], |
|
1319
|
|
|
green: [0,128,0], |
|
1320
|
|
|
greenyellow: [173,255,47], |
|
1321
|
|
|
grey: [128,128,128], |
|
1322
|
|
|
honeydew: [240,255,240], |
|
1323
|
|
|
hotpink: [255,105,180], |
|
1324
|
|
|
indianred: [205,92,92], |
|
1325
|
|
|
indigo: [75,0,130], |
|
1326
|
|
|
ivory: [255,255,240], |
|
1327
|
|
|
khaki: [240,230,140], |
|
1328
|
|
|
lavender: [230,230,250], |
|
1329
|
|
|
lavenderblush: [255,240,245], |
|
1330
|
|
|
lawngreen: [124,252,0], |
|
1331
|
|
|
lemonchiffon: [255,250,205], |
|
1332
|
|
|
lightblue: [173,216,230], |
|
1333
|
|
|
lightcoral: [240,128,128], |
|
1334
|
|
|
lightcyan: [224,255,255], |
|
1335
|
|
|
lightgoldenrodyellow: [250,250,210], |
|
1336
|
|
|
lightgray: [211,211,211], |
|
1337
|
|
|
lightgreen: [144,238,144], |
|
1338
|
|
|
lightgrey: [211,211,211], |
|
1339
|
|
|
lightpink: [255,182,193], |
|
1340
|
|
|
lightsalmon: [255,160,122], |
|
1341
|
|
|
lightseagreen: [32,178,170], |
|
1342
|
|
|
lightskyblue: [135,206,250], |
|
1343
|
|
|
lightslategray: [119,136,153], |
|
1344
|
|
|
lightslategrey: [119,136,153], |
|
1345
|
|
|
lightsteelblue: [176,196,222], |
|
1346
|
|
|
lightyellow: [255,255,224], |
|
1347
|
|
|
lime: [0,255,0], |
|
1348
|
|
|
limegreen: [50,205,50], |
|
1349
|
|
|
linen: [250,240,230], |
|
1350
|
|
|
magenta: [255,0,255], |
|
1351
|
|
|
maroon: [128,0,0], |
|
1352
|
|
|
mediumaquamarine: [102,205,170], |
|
1353
|
|
|
mediumblue: [0,0,205], |
|
1354
|
|
|
mediumorchid: [186,85,211], |
|
1355
|
|
|
mediumpurple: [147,112,219], |
|
1356
|
|
|
mediumseagreen: [60,179,113], |
|
1357
|
|
|
mediumslateblue: [123,104,238], |
|
1358
|
|
|
mediumspringgreen: [0,250,154], |
|
1359
|
|
|
mediumturquoise: [72,209,204], |
|
1360
|
|
|
mediumvioletred: [199,21,133], |
|
1361
|
|
|
midnightblue: [25,25,112], |
|
1362
|
|
|
mintcream: [245,255,250], |
|
1363
|
|
|
mistyrose: [255,228,225], |
|
1364
|
|
|
moccasin: [255,228,181], |
|
1365
|
|
|
navajowhite: [255,222,173], |
|
1366
|
|
|
navy: [0,0,128], |
|
1367
|
|
|
oldlace: [253,245,230], |
|
1368
|
|
|
olive: [128,128,0], |
|
1369
|
|
|
olivedrab: [107,142,35], |
|
1370
|
|
|
orange: [255,165,0], |
|
1371
|
|
|
orangered: [255,69,0], |
|
1372
|
|
|
orchid: [218,112,214], |
|
1373
|
|
|
palegoldenrod: [238,232,170], |
|
1374
|
|
|
palegreen: [152,251,152], |
|
1375
|
|
|
paleturquoise: [175,238,238], |
|
1376
|
|
|
palevioletred: [219,112,147], |
|
1377
|
|
|
papayawhip: [255,239,213], |
|
1378
|
|
|
peachpuff: [255,218,185], |
|
1379
|
|
|
peru: [205,133,63], |
|
1380
|
|
|
pink: [255,192,203], |
|
1381
|
|
|
plum: [221,160,221], |
|
1382
|
|
|
powderblue: [176,224,230], |
|
1383
|
|
|
purple: [128,0,128], |
|
1384
|
|
|
rebeccapurple: [102, 51, 153], |
|
1385
|
|
|
red: [255,0,0], |
|
1386
|
|
|
rosybrown: [188,143,143], |
|
1387
|
|
|
royalblue: [65,105,225], |
|
1388
|
|
|
saddlebrown: [139,69,19], |
|
1389
|
|
|
salmon: [250,128,114], |
|
1390
|
|
|
sandybrown: [244,164,96], |
|
1391
|
|
|
seagreen: [46,139,87], |
|
1392
|
|
|
seashell: [255,245,238], |
|
1393
|
|
|
sienna: [160,82,45], |
|
1394
|
|
|
silver: [192,192,192], |
|
1395
|
|
|
skyblue: [135,206,235], |
|
1396
|
|
|
slateblue: [106,90,205], |
|
1397
|
|
|
slategray: [112,128,144], |
|
1398
|
|
|
slategrey: [112,128,144], |
|
1399
|
|
|
snow: [255,250,250], |
|
1400
|
|
|
springgreen: [0,255,127], |
|
1401
|
|
|
steelblue: [70,130,180], |
|
1402
|
|
|
tan: [210,180,140], |
|
1403
|
|
|
teal: [0,128,128], |
|
1404
|
|
|
thistle: [216,191,216], |
|
1405
|
|
|
tomato: [255,99,71], |
|
1406
|
|
|
turquoise: [64,224,208], |
|
1407
|
|
|
violet: [238,130,238], |
|
1408
|
|
|
wheat: [245,222,179], |
|
1409
|
|
|
white: [255,255,255], |
|
1410
|
|
|
whitesmoke: [245,245,245], |
|
1411
|
|
|
yellow: [255,255,0], |
|
1412
|
|
|
yellowgreen: [154,205,50] |
|
1413
|
|
|
}; |
|
1414
|
|
|
|
|
1415
|
|
|
var reverseKeywords = {}; |
|
1416
|
|
|
for (var key in cssKeywords) { |
|
|
|
|
|
|
1417
|
|
|
reverseKeywords[JSON.stringify(cssKeywords[key])] = key; |
|
1418
|
|
|
} |
|
1419
|
|
|
|
|
1420
|
|
|
},{}],5:[function(require,module,exports){ |
|
1421
|
|
|
var conversions = require(4); |
|
1422
|
|
|
|
|
1423
|
|
|
var convert = function() { |
|
1424
|
|
|
return new Converter(); |
|
1425
|
|
|
} |
|
1426
|
|
|
|
|
1427
|
|
|
for (var func in conversions) { |
|
|
|
|
|
|
1428
|
|
|
// export Raw versions |
|
1429
|
|
|
convert[func + "Raw"] = (function(func) { |
|
1430
|
|
|
// accept array or plain args |
|
1431
|
|
|
return function(arg) { |
|
1432
|
|
|
if (typeof arg == "number") |
|
1433
|
|
|
arg = Array.prototype.slice.call(arguments); |
|
|
|
|
|
|
1434
|
|
|
return conversions[func](arg); |
|
1435
|
|
|
} |
|
1436
|
|
|
})(func); |
|
1437
|
|
|
|
|
1438
|
|
|
var pair = /(\w+)2(\w+)/.exec(func), |
|
1439
|
|
|
from = pair[1], |
|
1440
|
|
|
to = pair[2]; |
|
1441
|
|
|
|
|
1442
|
|
|
// export rgb2hsl and ["rgb"]["hsl"] |
|
1443
|
|
|
convert[from] = convert[from] || {}; |
|
1444
|
|
|
|
|
1445
|
|
|
convert[from][to] = convert[func] = (function(func) { |
|
1446
|
|
|
return function(arg) { |
|
1447
|
|
|
if (typeof arg == "number") |
|
1448
|
|
|
arg = Array.prototype.slice.call(arguments); |
|
|
|
|
|
|
1449
|
|
|
|
|
1450
|
|
|
var val = conversions[func](arg); |
|
1451
|
|
|
if (typeof val == "string" || val === undefined) |
|
1452
|
|
|
return val; // keyword |
|
|
|
|
|
|
1453
|
|
|
|
|
1454
|
|
|
for (var i = 0; i < val.length; i++) |
|
1455
|
|
|
val[i] = Math.round(val[i]); |
|
|
|
|
|
|
1456
|
|
|
return val; |
|
1457
|
|
|
} |
|
1458
|
|
|
})(func); |
|
1459
|
|
|
} |
|
1460
|
|
|
|
|
1461
|
|
|
|
|
1462
|
|
|
/* Converter does lazy conversion and caching */ |
|
1463
|
|
|
var Converter = function() { |
|
1464
|
|
|
this.convs = {}; |
|
1465
|
|
|
}; |
|
1466
|
|
|
|
|
1467
|
|
|
/* Either get the values for a space or |
|
1468
|
|
|
set the values for a space, depending on args */ |
|
1469
|
|
|
Converter.prototype.routeSpace = function(space, args) { |
|
1470
|
|
|
var values = args[0]; |
|
1471
|
|
|
if (values === undefined) { |
|
1472
|
|
|
// color.rgb() |
|
1473
|
|
|
return this.getValues(space); |
|
1474
|
|
|
} |
|
1475
|
|
|
// color.rgb(10, 10, 10) |
|
1476
|
|
|
if (typeof values == "number") { |
|
1477
|
|
|
values = Array.prototype.slice.call(args); |
|
1478
|
|
|
} |
|
1479
|
|
|
|
|
1480
|
|
|
return this.setValues(space, values); |
|
1481
|
|
|
}; |
|
1482
|
|
|
|
|
1483
|
|
|
/* Set the values for a space, invalidating cache */ |
|
1484
|
|
|
Converter.prototype.setValues = function(space, values) { |
|
1485
|
|
|
this.space = space; |
|
1486
|
|
|
this.convs = {}; |
|
1487
|
|
|
this.convs[space] = values; |
|
1488
|
|
|
return this; |
|
1489
|
|
|
}; |
|
1490
|
|
|
|
|
1491
|
|
|
/* Get the values for a space. If there's already |
|
1492
|
|
|
a conversion for the space, fetch it, otherwise |
|
1493
|
|
|
compute it */ |
|
1494
|
|
|
Converter.prototype.getValues = function(space) { |
|
1495
|
|
|
var vals = this.convs[space]; |
|
1496
|
|
|
if (!vals) { |
|
1497
|
|
|
var fspace = this.space, |
|
1498
|
|
|
from = this.convs[fspace]; |
|
1499
|
|
|
vals = convert[fspace][space](from); |
|
1500
|
|
|
|
|
1501
|
|
|
this.convs[space] = vals; |
|
1502
|
|
|
} |
|
1503
|
|
|
return vals; |
|
1504
|
|
|
}; |
|
1505
|
|
|
|
|
1506
|
|
|
["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { |
|
1507
|
|
|
Converter.prototype[space] = function(vals) { |
|
|
|
|
|
|
1508
|
|
|
return this.routeSpace(space, arguments); |
|
1509
|
|
|
} |
|
1510
|
|
|
}); |
|
1511
|
|
|
|
|
1512
|
|
|
module.exports = convert; |
|
1513
|
|
|
},{"4":4}],6:[function(require,module,exports){ |
|
1514
|
|
|
module.exports = {
|
|
1515
|
|
|
"aliceblue": [240, 248, 255],
|
|
1516
|
|
|
"antiquewhite": [250, 235, 215],
|
|
1517
|
|
|
"aqua": [0, 255, 255],
|
|
1518
|
|
|
"aquamarine": [127, 255, 212],
|
|
1519
|
|
|
"azure": [240, 255, 255],
|
|
1520
|
|
|
"beige": [245, 245, 220],
|
|
1521
|
|
|
"bisque": [255, 228, 196],
|
|
1522
|
|
|
"black": [0, 0, 0],
|
|
1523
|
|
|
"blanchedalmond": [255, 235, 205],
|
|
1524
|
|
|
"blue": [0, 0, 255],
|
|
1525
|
|
|
"blueviolet": [138, 43, 226],
|
|
1526
|
|
|
"brown": [165, 42, 42],
|
|
1527
|
|
|
"burlywood": [222, 184, 135],
|
|
1528
|
|
|
"cadetblue": [95, 158, 160],
|
|
1529
|
|
|
"chartreuse": [127, 255, 0],
|
|
1530
|
|
|
"chocolate": [210, 105, 30],
|
|
1531
|
|
|
"coral": [255, 127, 80],
|
|
1532
|
|
|
"cornflowerblue": [100, 149, 237],
|
|
1533
|
|
|
"cornsilk": [255, 248, 220],
|
|
1534
|
|
|
"crimson": [220, 20, 60],
|
|
1535
|
|
|
"cyan": [0, 255, 255],
|
|
1536
|
|
|
"darkblue": [0, 0, 139],
|
|
1537
|
|
|
"darkcyan": [0, 139, 139],
|
|
1538
|
|
|
"darkgoldenrod": [184, 134, 11],
|
|
1539
|
|
|
"darkgray": [169, 169, 169],
|
|
1540
|
|
|
"darkgreen": [0, 100, 0],
|
|
1541
|
|
|
"darkgrey": [169, 169, 169],
|
|
1542
|
|
|
"darkkhaki": [189, 183, 107],
|
|
1543
|
|
|
"darkmagenta": [139, 0, 139],
|
|
1544
|
|
|
"darkolivegreen": [85, 107, 47],
|
|
1545
|
|
|
"darkorange": [255, 140, 0],
|
|
1546
|
|
|
"darkorchid": [153, 50, 204],
|
|
1547
|
|
|
"darkred": [139, 0, 0],
|
|
1548
|
|
|
"darksalmon": [233, 150, 122],
|
|
1549
|
|
|
"darkseagreen": [143, 188, 143],
|
|
1550
|
|
|
"darkslateblue": [72, 61, 139],
|
|
1551
|
|
|
"darkslategray": [47, 79, 79],
|
|
1552
|
|
|
"darkslategrey": [47, 79, 79],
|
|
1553
|
|
|
"darkturquoise": [0, 206, 209],
|
|
1554
|
|
|
"darkviolet": [148, 0, 211],
|
|
1555
|
|
|
"deeppink": [255, 20, 147],
|
|
1556
|
|
|
"deepskyblue": [0, 191, 255],
|
|
1557
|
|
|
"dimgray": [105, 105, 105],
|
|
1558
|
|
|
"dimgrey": [105, 105, 105],
|
|
1559
|
|
|
"dodgerblue": [30, 144, 255],
|
|
1560
|
|
|
"firebrick": [178, 34, 34],
|
|
1561
|
|
|
"floralwhite": [255, 250, 240],
|
|
1562
|
|
|
"forestgreen": [34, 139, 34],
|
|
1563
|
|
|
"fuchsia": [255, 0, 255],
|
|
1564
|
|
|
"gainsboro": [220, 220, 220],
|
|
1565
|
|
|
"ghostwhite": [248, 248, 255],
|
|
1566
|
|
|
"gold": [255, 215, 0],
|
|
1567
|
|
|
"goldenrod": [218, 165, 32],
|
|
1568
|
|
|
"gray": [128, 128, 128],
|
|
1569
|
|
|
"green": [0, 128, 0],
|
|
1570
|
|
|
"greenyellow": [173, 255, 47],
|
|
1571
|
|
|
"grey": [128, 128, 128],
|
|
1572
|
|
|
"honeydew": [240, 255, 240],
|
|
1573
|
|
|
"hotpink": [255, 105, 180],
|
|
1574
|
|
|
"indianred": [205, 92, 92],
|
|
1575
|
|
|
"indigo": [75, 0, 130],
|
|
1576
|
|
|
"ivory": [255, 255, 240],
|
|
1577
|
|
|
"khaki": [240, 230, 140],
|
|
1578
|
|
|
"lavender": [230, 230, 250],
|
|
1579
|
|
|
"lavenderblush": [255, 240, 245],
|
|
1580
|
|
|
"lawngreen": [124, 252, 0],
|
|
1581
|
|
|
"lemonchiffon": [255, 250, 205],
|
|
1582
|
|
|
"lightblue": [173, 216, 230],
|
|
1583
|
|
|
"lightcoral": [240, 128, 128],
|
|
1584
|
|
|
"lightcyan": [224, 255, 255],
|
|
1585
|
|
|
"lightgoldenrodyellow": [250, 250, 210],
|
|
1586
|
|
|
"lightgray": [211, 211, 211],
|
|
1587
|
|
|
"lightgreen": [144, 238, 144],
|
|
1588
|
|
|
"lightgrey": [211, 211, 211],
|
|
1589
|
|
|
"lightpink": [255, 182, 193],
|
|
1590
|
|
|
"lightsalmon": [255, 160, 122],
|
|
1591
|
|
|
"lightseagreen": [32, 178, 170],
|
|
1592
|
|
|
"lightskyblue": [135, 206, 250],
|
|
1593
|
|
|
"lightslategray": [119, 136, 153],
|
|
1594
|
|
|
"lightslategrey": [119, 136, 153],
|
|
1595
|
|
|
"lightsteelblue": [176, 196, 222],
|
|
1596
|
|
|
"lightyellow": [255, 255, 224],
|
|
1597
|
|
|
"lime": [0, 255, 0],
|
|
1598
|
|
|
"limegreen": [50, 205, 50],
|
|
1599
|
|
|
"linen": [250, 240, 230],
|
|
1600
|
|
|
"magenta": [255, 0, 255],
|
|
1601
|
|
|
"maroon": [128, 0, 0],
|
|
1602
|
|
|
"mediumaquamarine": [102, 205, 170],
|
|
1603
|
|
|
"mediumblue": [0, 0, 205],
|
|
1604
|
|
|
"mediumorchid": [186, 85, 211],
|
|
1605
|
|
|
"mediumpurple": [147, 112, 219],
|
|
1606
|
|
|
"mediumseagreen": [60, 179, 113],
|
|
1607
|
|
|
"mediumslateblue": [123, 104, 238],
|
|
1608
|
|
|
"mediumspringgreen": [0, 250, 154],
|
|
1609
|
|
|
"mediumturquoise": [72, 209, 204],
|
|
1610
|
|
|
"mediumvioletred": [199, 21, 133],
|
|
1611
|
|
|
"midnightblue": [25, 25, 112],
|
|
1612
|
|
|
"mintcream": [245, 255, 250],
|
|
1613
|
|
|
"mistyrose": [255, 228, 225],
|
|
1614
|
|
|
"moccasin": [255, 228, 181],
|
|
1615
|
|
|
"navajowhite": [255, 222, 173],
|
|
1616
|
|
|
"navy": [0, 0, 128],
|
|
1617
|
|
|
"oldlace": [253, 245, 230],
|
|
1618
|
|
|
"olive": [128, 128, 0],
|
|
1619
|
|
|
"olivedrab": [107, 142, 35],
|
|
1620
|
|
|
"orange": [255, 165, 0],
|
|
1621
|
|
|
"orangered": [255, 69, 0],
|
|
1622
|
|
|
"orchid": [218, 112, 214],
|
|
1623
|
|
|
"palegoldenrod": [238, 232, 170],
|
|
1624
|
|
|
"palegreen": [152, 251, 152],
|
|
1625
|
|
|
"paleturquoise": [175, 238, 238],
|
|
1626
|
|
|
"palevioletred": [219, 112, 147],
|
|
1627
|
|
|
"papayawhip": [255, 239, 213],
|
|
1628
|
|
|
"peachpuff": [255, 218, 185],
|
|
1629
|
|
|
"peru": [205, 133, 63],
|
|
1630
|
|
|
"pink": [255, 192, 203],
|
|
1631
|
|
|
"plum": [221, 160, 221],
|
|
1632
|
|
|
"powderblue": [176, 224, 230],
|
|
1633
|
|
|
"purple": [128, 0, 128],
|
|
1634
|
|
|
"rebeccapurple": [102, 51, 153],
|
|
1635
|
|
|
"red": [255, 0, 0],
|
|
1636
|
|
|
"rosybrown": [188, 143, 143],
|
|
1637
|
|
|
"royalblue": [65, 105, 225],
|
|
1638
|
|
|
"saddlebrown": [139, 69, 19],
|
|
1639
|
|
|
"salmon": [250, 128, 114],
|
|
1640
|
|
|
"sandybrown": [244, 164, 96],
|
|
1641
|
|
|
"seagreen": [46, 139, 87],
|
|
1642
|
|
|
"seashell": [255, 245, 238],
|
|
1643
|
|
|
"sienna": [160, 82, 45],
|
|
1644
|
|
|
"silver": [192, 192, 192],
|
|
1645
|
|
|
"skyblue": [135, 206, 235],
|
|
1646
|
|
|
"slateblue": [106, 90, 205],
|
|
1647
|
|
|
"slategray": [112, 128, 144],
|
|
1648
|
|
|
"slategrey": [112, 128, 144],
|
|
1649
|
|
|
"snow": [255, 250, 250],
|
|
1650
|
|
|
"springgreen": [0, 255, 127],
|
|
1651
|
|
|
"steelblue": [70, 130, 180],
|
|
1652
|
|
|
"tan": [210, 180, 140],
|
|
1653
|
|
|
"teal": [0, 128, 128],
|
|
1654
|
|
|
"thistle": [216, 191, 216],
|
|
1655
|
|
|
"tomato": [255, 99, 71],
|
|
1656
|
|
|
"turquoise": [64, 224, 208],
|
|
1657
|
|
|
"violet": [238, 130, 238],
|
|
1658
|
|
|
"wheat": [245, 222, 179],
|
|
1659
|
|
|
"white": [255, 255, 255],
|
|
1660
|
|
|
"whitesmoke": [245, 245, 245],
|
|
1661
|
|
|
"yellow": [255, 255, 0],
|
|
1662
|
|
|
"yellowgreen": [154, 205, 50]
|
|
1663
|
|
|
}; |
|
1664
|
|
|
},{}],7:[function(require,module,exports){ |
|
1665
|
|
|
/** |
|
1666
|
|
|
* @namespace Chart |
|
1667
|
|
|
*/ |
|
1668
|
|
|
var Chart = require(27)(); |
|
1669
|
|
|
|
|
1670
|
|
|
require(26)(Chart); |
|
1671
|
|
|
require(22)(Chart); |
|
1672
|
|
|
require(25)(Chart); |
|
1673
|
|
|
require(21)(Chart); |
|
1674
|
|
|
require(23)(Chart); |
|
1675
|
|
|
require(24)(Chart); |
|
1676
|
|
|
require(28)(Chart); |
|
1677
|
|
|
require(32)(Chart); |
|
1678
|
|
|
require(30)(Chart); |
|
1679
|
|
|
require(31)(Chart); |
|
1680
|
|
|
require(33)(Chart); |
|
1681
|
|
|
require(29)(Chart); |
|
1682
|
|
|
require(34)(Chart); |
|
1683
|
|
|
|
|
1684
|
|
|
require(35)(Chart); |
|
1685
|
|
|
require(36)(Chart); |
|
1686
|
|
|
require(37)(Chart); |
|
1687
|
|
|
require(38)(Chart); |
|
1688
|
|
|
|
|
1689
|
|
|
require(41)(Chart); |
|
1690
|
|
|
require(39)(Chart); |
|
1691
|
|
|
require(40)(Chart); |
|
1692
|
|
|
require(42)(Chart); |
|
1693
|
|
|
require(43)(Chart); |
|
1694
|
|
|
require(44)(Chart); |
|
1695
|
|
|
|
|
1696
|
|
|
// Controllers must be loaded after elements |
|
1697
|
|
|
// See Chart.core.datasetController.dataElementType |
|
1698
|
|
|
require(15)(Chart); |
|
1699
|
|
|
require(16)(Chart); |
|
1700
|
|
|
require(17)(Chart); |
|
1701
|
|
|
require(18)(Chart); |
|
1702
|
|
|
require(19)(Chart); |
|
1703
|
|
|
require(20)(Chart); |
|
1704
|
|
|
|
|
1705
|
|
|
require(8)(Chart); |
|
1706
|
|
|
require(9)(Chart); |
|
1707
|
|
|
require(10)(Chart); |
|
1708
|
|
|
require(11)(Chart); |
|
1709
|
|
|
require(12)(Chart); |
|
1710
|
|
|
require(13)(Chart); |
|
1711
|
|
|
require(14)(Chart); |
|
1712
|
|
|
|
|
1713
|
|
|
window.Chart = module.exports = Chart; |
|
1714
|
|
|
|
|
1715
|
|
|
},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"8":8,"9":9}],8:[function(require,module,exports){ |
|
1716
|
|
|
"use strict"; |
|
1717
|
|
|
|
|
1718
|
|
|
module.exports = function(Chart) { |
|
1719
|
|
|
|
|
1720
|
|
|
Chart.Bar = function(context, config) { |
|
1721
|
|
|
config.type = 'bar'; |
|
1722
|
|
|
|
|
1723
|
|
|
return new Chart(context, config); |
|
1724
|
|
|
}; |
|
1725
|
|
|
|
|
1726
|
|
|
}; |
|
1727
|
|
|
},{}],9:[function(require,module,exports){ |
|
1728
|
|
|
"use strict"; |
|
1729
|
|
|
|
|
1730
|
|
|
module.exports = function(Chart) { |
|
1731
|
|
|
|
|
1732
|
|
|
Chart.Bubble = function(context, config) { |
|
1733
|
|
|
config.type = 'bubble'; |
|
1734
|
|
|
return new Chart(context, config); |
|
1735
|
|
|
}; |
|
1736
|
|
|
|
|
1737
|
|
|
}; |
|
1738
|
|
|
},{}],10:[function(require,module,exports){ |
|
1739
|
|
|
"use strict"; |
|
1740
|
|
|
|
|
1741
|
|
|
module.exports = function(Chart) { |
|
1742
|
|
|
|
|
1743
|
|
|
Chart.Doughnut = function(context, config) { |
|
1744
|
|
|
config.type = 'doughnut'; |
|
1745
|
|
|
|
|
1746
|
|
|
return new Chart(context, config); |
|
1747
|
|
|
}; |
|
1748
|
|
|
|
|
1749
|
|
|
}; |
|
1750
|
|
|
},{}],11:[function(require,module,exports){ |
|
1751
|
|
|
"use strict"; |
|
1752
|
|
|
|
|
1753
|
|
|
module.exports = function(Chart) { |
|
1754
|
|
|
|
|
1755
|
|
|
Chart.Line = function(context, config) { |
|
1756
|
|
|
config.type = 'line'; |
|
1757
|
|
|
|
|
1758
|
|
|
return new Chart(context, config); |
|
1759
|
|
|
}; |
|
1760
|
|
|
|
|
1761
|
|
|
}; |
|
1762
|
|
|
},{}],12:[function(require,module,exports){ |
|
1763
|
|
|
"use strict"; |
|
1764
|
|
|
|
|
1765
|
|
|
module.exports = function(Chart) { |
|
1766
|
|
|
|
|
1767
|
|
|
Chart.PolarArea = function(context, config) { |
|
1768
|
|
|
config.type = 'polarArea'; |
|
1769
|
|
|
|
|
1770
|
|
|
return new Chart(context, config); |
|
1771
|
|
|
}; |
|
1772
|
|
|
|
|
1773
|
|
|
}; |
|
1774
|
|
|
},{}],13:[function(require,module,exports){ |
|
1775
|
|
|
"use strict"; |
|
1776
|
|
|
|
|
1777
|
|
|
module.exports = function(Chart) { |
|
1778
|
|
|
|
|
1779
|
|
|
Chart.Radar = function(context, config) { |
|
1780
|
|
|
config.options = Chart.helpers.configMerge({ aspectRatio: 1 }, config.options); |
|
1781
|
|
|
config.type = 'radar'; |
|
1782
|
|
|
|
|
1783
|
|
|
return new Chart(context, config); |
|
1784
|
|
|
}; |
|
1785
|
|
|
|
|
1786
|
|
|
}; |
|
1787
|
|
|
|
|
1788
|
|
|
},{}],14:[function(require,module,exports){ |
|
1789
|
|
|
"use strict"; |
|
1790
|
|
|
|
|
1791
|
|
|
module.exports = function(Chart) { |
|
1792
|
|
|
|
|
1793
|
|
|
var defaultConfig = { |
|
1794
|
|
|
hover: { |
|
1795
|
|
|
mode: 'single' |
|
1796
|
|
|
}, |
|
1797
|
|
|
|
|
1798
|
|
|
scales: { |
|
1799
|
|
|
xAxes: [{ |
|
1800
|
|
|
type: "linear", // scatter should not use a category axis |
|
1801
|
|
|
position: "bottom", |
|
1802
|
|
|
id: "x-axis-1" // need an ID so datasets can reference the scale |
|
1803
|
|
|
}], |
|
1804
|
|
|
yAxes: [{ |
|
1805
|
|
|
type: "linear", |
|
1806
|
|
|
position: "left", |
|
1807
|
|
|
id: "y-axis-1" |
|
1808
|
|
|
}] |
|
1809
|
|
|
}, |
|
1810
|
|
|
|
|
1811
|
|
|
tooltips: { |
|
1812
|
|
|
callbacks: { |
|
1813
|
|
|
title: function() { |
|
1814
|
|
|
// Title doesn't make sense for scatter since we format the data as a point |
|
1815
|
|
|
return ''; |
|
1816
|
|
|
}, |
|
1817
|
|
|
label: function(tooltipItem) { |
|
1818
|
|
|
return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; |
|
1819
|
|
|
} |
|
1820
|
|
|
} |
|
1821
|
|
|
} |
|
1822
|
|
|
}; |
|
1823
|
|
|
|
|
1824
|
|
|
// Register the default config for this type |
|
1825
|
|
|
Chart.defaults.scatter = defaultConfig; |
|
1826
|
|
|
|
|
1827
|
|
|
// Scatter charts use line controllers |
|
1828
|
|
|
Chart.controllers.scatter = Chart.controllers.line; |
|
1829
|
|
|
|
|
1830
|
|
|
Chart.Scatter = function(context, config) { |
|
1831
|
|
|
config.type = 'scatter'; |
|
1832
|
|
|
return new Chart(context, config); |
|
1833
|
|
|
}; |
|
1834
|
|
|
|
|
1835
|
|
|
}; |
|
1836
|
|
|
},{}],15:[function(require,module,exports){ |
|
1837
|
|
|
"use strict"; |
|
1838
|
|
|
|
|
1839
|
|
|
module.exports = function(Chart) { |
|
1840
|
|
|
|
|
1841
|
|
|
var helpers = Chart.helpers; |
|
1842
|
|
|
|
|
1843
|
|
|
Chart.defaults.bar = { |
|
1844
|
|
|
hover: { |
|
1845
|
|
|
mode: "label" |
|
1846
|
|
|
}, |
|
1847
|
|
|
|
|
1848
|
|
|
scales: { |
|
1849
|
|
|
xAxes: [{ |
|
1850
|
|
|
type: "category", |
|
1851
|
|
|
|
|
1852
|
|
|
// Specific to Bar Controller |
|
1853
|
|
|
categoryPercentage: 0.8, |
|
1854
|
|
|
barPercentage: 0.9, |
|
1855
|
|
|
|
|
1856
|
|
|
// grid line settings |
|
1857
|
|
|
gridLines: { |
|
1858
|
|
|
offsetGridLines: true |
|
1859
|
|
|
} |
|
1860
|
|
|
}], |
|
1861
|
|
|
yAxes: [{ |
|
1862
|
|
|
type: "linear" |
|
1863
|
|
|
}] |
|
1864
|
|
|
} |
|
1865
|
|
|
}; |
|
1866
|
|
|
|
|
1867
|
|
|
Chart.controllers.bar = Chart.DatasetController.extend({ |
|
1868
|
|
|
|
|
1869
|
|
|
dataElementType: Chart.elements.Rectangle, |
|
1870
|
|
|
|
|
1871
|
|
|
initialize: function(chart, datasetIndex) { |
|
1872
|
|
|
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex); |
|
1873
|
|
|
|
|
1874
|
|
|
// Use this to indicate that this is a bar dataset. |
|
1875
|
|
|
this.getMeta().bar = true; |
|
1876
|
|
|
}, |
|
1877
|
|
|
|
|
1878
|
|
|
// Get the number of datasets that display bars. We use this to correctly calculate the bar width |
|
1879
|
|
|
getBarCount: function() { |
|
1880
|
|
|
var me = this; |
|
1881
|
|
|
var barCount = 0; |
|
1882
|
|
|
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { |
|
1883
|
|
|
var meta = me.chart.getDatasetMeta(datasetIndex); |
|
1884
|
|
|
if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) { |
|
1885
|
|
|
++barCount; |
|
1886
|
|
|
} |
|
1887
|
|
|
}, me); |
|
1888
|
|
|
return barCount; |
|
1889
|
|
|
}, |
|
1890
|
|
|
|
|
1891
|
|
|
update: function(reset) { |
|
1892
|
|
|
var me = this; |
|
1893
|
|
|
helpers.each(me.getMeta().data, function(rectangle, index) { |
|
1894
|
|
|
me.updateElement(rectangle, index, reset); |
|
1895
|
|
|
}, me); |
|
1896
|
|
|
}, |
|
1897
|
|
|
|
|
1898
|
|
|
updateElement: function(rectangle, index, reset) { |
|
1899
|
|
|
var me = this; |
|
1900
|
|
|
var meta = me.getMeta(); |
|
1901
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
1902
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
1903
|
|
|
var scaleBase = yScale.getBasePixel(); |
|
1904
|
|
|
var rectangleElementOptions = me.chart.options.elements.rectangle; |
|
1905
|
|
|
var custom = rectangle.custom || {}; |
|
1906
|
|
|
var dataset = me.getDataset(); |
|
1907
|
|
|
|
|
1908
|
|
|
helpers.extend(rectangle, { |
|
1909
|
|
|
// Utility |
|
1910
|
|
|
_xScale: xScale, |
|
1911
|
|
|
_yScale: yScale, |
|
1912
|
|
|
_datasetIndex: me.index, |
|
1913
|
|
|
_index: index, |
|
1914
|
|
|
|
|
1915
|
|
|
// Desired view properties |
|
1916
|
|
|
_model: { |
|
1917
|
|
|
x: me.calculateBarX(index, me.index), |
|
1918
|
|
|
y: reset ? scaleBase : me.calculateBarY(index, me.index), |
|
1919
|
|
|
|
|
1920
|
|
|
// Tooltip |
|
1921
|
|
|
label: me.chart.data.labels[index], |
|
1922
|
|
|
datasetLabel: dataset.label, |
|
1923
|
|
|
|
|
1924
|
|
|
// Appearance |
|
1925
|
|
|
base: reset ? scaleBase : me.calculateBarBase(me.index, index), |
|
1926
|
|
|
width: me.calculateBarWidth(index), |
|
1927
|
|
|
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), |
|
1928
|
|
|
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, |
|
1929
|
|
|
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), |
|
1930
|
|
|
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) |
|
1931
|
|
|
} |
|
1932
|
|
|
}); |
|
1933
|
|
|
rectangle.pivot(); |
|
1934
|
|
|
}, |
|
1935
|
|
|
|
|
1936
|
|
|
calculateBarBase: function(datasetIndex, index) { |
|
1937
|
|
|
var me = this; |
|
1938
|
|
|
var meta = me.getMeta(); |
|
1939
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
1940
|
|
|
var base = 0; |
|
1941
|
|
|
|
|
1942
|
|
|
if (yScale.options.stacked) { |
|
1943
|
|
|
var chart = me.chart; |
|
1944
|
|
|
var datasets = chart.data.datasets; |
|
1945
|
|
|
var value = Number(datasets[datasetIndex].data[index]); |
|
1946
|
|
|
|
|
1947
|
|
|
for (var i = 0; i < datasetIndex; i++) { |
|
1948
|
|
|
var currentDs = datasets[i]; |
|
1949
|
|
|
var currentDsMeta = chart.getDatasetMeta(i); |
|
1950
|
|
|
if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { |
|
1951
|
|
|
var currentVal = Number(currentDs.data[index]); |
|
1952
|
|
|
base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); |
|
1953
|
|
|
} |
|
1954
|
|
|
} |
|
1955
|
|
|
|
|
1956
|
|
|
return yScale.getPixelForValue(base); |
|
1957
|
|
|
} |
|
1958
|
|
|
|
|
1959
|
|
|
return yScale.getBasePixel(); |
|
1960
|
|
|
}, |
|
1961
|
|
|
|
|
1962
|
|
|
getRuler: function(index) { |
|
|
|
|
|
|
1963
|
|
|
var me = this; |
|
1964
|
|
|
var meta = me.getMeta(); |
|
1965
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
1966
|
|
|
var datasetCount = me.getBarCount(); |
|
1967
|
|
|
|
|
1968
|
|
|
var tickWidth; |
|
1969
|
|
|
|
|
1970
|
|
|
if (xScale.options.type === 'category') { |
|
1971
|
|
|
tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index); |
|
1972
|
|
|
} else { |
|
1973
|
|
|
// Average width |
|
1974
|
|
|
tickWidth = xScale.width / xScale.ticks.length; |
|
1975
|
|
|
} |
|
1976
|
|
|
var categoryWidth = tickWidth * xScale.options.categoryPercentage; |
|
1977
|
|
|
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; |
|
1978
|
|
|
var fullBarWidth = categoryWidth / datasetCount; |
|
1979
|
|
|
|
|
1980
|
|
|
if (xScale.ticks.length !== me.chart.data.labels.length) { |
|
1981
|
|
|
var perc = xScale.ticks.length / me.chart.data.labels.length; |
|
1982
|
|
|
fullBarWidth = fullBarWidth * perc; |
|
1983
|
|
|
} |
|
1984
|
|
|
|
|
1985
|
|
|
var barWidth = fullBarWidth * xScale.options.barPercentage; |
|
1986
|
|
|
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage); |
|
1987
|
|
|
|
|
1988
|
|
|
return { |
|
1989
|
|
|
datasetCount: datasetCount, |
|
1990
|
|
|
tickWidth: tickWidth, |
|
1991
|
|
|
categoryWidth: categoryWidth, |
|
1992
|
|
|
categorySpacing: categorySpacing, |
|
1993
|
|
|
fullBarWidth: fullBarWidth, |
|
1994
|
|
|
barWidth: barWidth, |
|
1995
|
|
|
barSpacing: barSpacing |
|
1996
|
|
|
}; |
|
1997
|
|
|
}, |
|
1998
|
|
|
|
|
1999
|
|
|
calculateBarWidth: function(index) { |
|
2000
|
|
|
var xScale = this.getScaleForId(this.getMeta().xAxisID); |
|
2001
|
|
|
if (xScale.options.barThickness) { |
|
2002
|
|
|
return xScale.options.barThickness; |
|
2003
|
|
|
} |
|
2004
|
|
|
var ruler = this.getRuler(index); |
|
2005
|
|
|
return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth; |
|
2006
|
|
|
}, |
|
2007
|
|
|
|
|
2008
|
|
|
// Get bar index from the given dataset index accounting for the fact that not all bars are visible |
|
2009
|
|
|
getBarIndex: function(datasetIndex) { |
|
2010
|
|
|
var barIndex = 0; |
|
2011
|
|
|
var meta, j; |
|
2012
|
|
|
|
|
2013
|
|
|
for (j = 0; j < datasetIndex; ++j) { |
|
2014
|
|
|
meta = this.chart.getDatasetMeta(j); |
|
2015
|
|
|
if (meta.bar && this.chart.isDatasetVisible(j)) { |
|
2016
|
|
|
++barIndex; |
|
2017
|
|
|
} |
|
2018
|
|
|
} |
|
2019
|
|
|
|
|
2020
|
|
|
return barIndex; |
|
2021
|
|
|
}, |
|
2022
|
|
|
|
|
2023
|
|
|
calculateBarX: function(index, datasetIndex) { |
|
2024
|
|
|
var me = this; |
|
2025
|
|
|
var meta = me.getMeta(); |
|
2026
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
2027
|
|
|
var barIndex = me.getBarIndex(datasetIndex); |
|
2028
|
|
|
|
|
2029
|
|
|
var ruler = me.getRuler(index); |
|
2030
|
|
|
var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); |
|
2031
|
|
|
leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0; |
|
2032
|
|
|
|
|
2033
|
|
|
if (xScale.options.stacked) { |
|
2034
|
|
|
return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing; |
|
2035
|
|
|
} |
|
2036
|
|
|
|
|
2037
|
|
|
return leftTick + |
|
2038
|
|
|
(ruler.barWidth / 2) + |
|
2039
|
|
|
ruler.categorySpacing + |
|
2040
|
|
|
(ruler.barWidth * barIndex) + |
|
2041
|
|
|
(ruler.barSpacing / 2) + |
|
2042
|
|
|
(ruler.barSpacing * barIndex); |
|
2043
|
|
|
}, |
|
2044
|
|
|
|
|
2045
|
|
|
calculateBarY: function(index, datasetIndex) { |
|
|
|
|
|
|
2046
|
|
|
var me = this; |
|
2047
|
|
|
var meta = me.getMeta(); |
|
2048
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
2049
|
|
|
var value = Number(me.getDataset().data[index]); |
|
2050
|
|
|
|
|
2051
|
|
|
if (yScale.options.stacked) { |
|
2052
|
|
|
|
|
2053
|
|
|
var sumPos = 0, |
|
2054
|
|
|
sumNeg = 0; |
|
2055
|
|
|
|
|
2056
|
|
|
for (var i = 0; i < datasetIndex; i++) { |
|
2057
|
|
|
var ds = me.chart.data.datasets[i]; |
|
2058
|
|
|
var dsMeta = me.chart.getDatasetMeta(i); |
|
2059
|
|
|
if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) { |
|
2060
|
|
|
var stackedVal = Number(ds.data[index]); |
|
2061
|
|
|
if (stackedVal < 0) { |
|
2062
|
|
|
sumNeg += stackedVal || 0; |
|
2063
|
|
|
} else { |
|
2064
|
|
|
sumPos += stackedVal || 0; |
|
2065
|
|
|
} |
|
2066
|
|
|
} |
|
2067
|
|
|
} |
|
2068
|
|
|
|
|
2069
|
|
|
if (value < 0) { |
|
2070
|
|
|
return yScale.getPixelForValue(sumNeg + value); |
|
2071
|
|
|
} else { |
|
2072
|
|
|
return yScale.getPixelForValue(sumPos + value); |
|
2073
|
|
|
} |
|
2074
|
|
|
} |
|
2075
|
|
|
|
|
2076
|
|
|
return yScale.getPixelForValue(value); |
|
2077
|
|
|
}, |
|
2078
|
|
|
|
|
2079
|
|
|
draw: function(ease) { |
|
2080
|
|
|
var me = this; |
|
2081
|
|
|
var easingDecimal = ease || 1; |
|
2082
|
|
|
helpers.each(me.getMeta().data, function(rectangle, index) { |
|
2083
|
|
|
var d = me.getDataset().data[index]; |
|
2084
|
|
|
if (d !== null && d !== undefined && !isNaN(d)) { |
|
2085
|
|
|
rectangle.transition(easingDecimal).draw(); |
|
2086
|
|
|
} |
|
2087
|
|
|
}, me); |
|
2088
|
|
|
}, |
|
2089
|
|
|
|
|
2090
|
|
|
setHoverStyle: function(rectangle) { |
|
2091
|
|
|
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; |
|
2092
|
|
|
var index = rectangle._index; |
|
2093
|
|
|
|
|
2094
|
|
|
var custom = rectangle.custom || {}; |
|
2095
|
|
|
var model = rectangle._model; |
|
2096
|
|
|
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
|
2097
|
|
|
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
|
2098
|
|
|
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); |
|
2099
|
|
|
}, |
|
2100
|
|
|
|
|
2101
|
|
|
removeHoverStyle: function(rectangle) { |
|
2102
|
|
|
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; |
|
2103
|
|
|
var index = rectangle._index; |
|
2104
|
|
|
var custom = rectangle.custom || {}; |
|
2105
|
|
|
var model = rectangle._model; |
|
2106
|
|
|
var rectangleElementOptions = this.chart.options.elements.rectangle; |
|
2107
|
|
|
|
|
2108
|
|
|
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); |
|
2109
|
|
|
model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); |
|
2110
|
|
|
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); |
|
2111
|
|
|
} |
|
2112
|
|
|
|
|
2113
|
|
|
}); |
|
2114
|
|
|
|
|
2115
|
|
|
|
|
2116
|
|
|
// including horizontalBar in the bar file, instead of a file of its own |
|
2117
|
|
|
// it extends bar (like pie extends doughnut) |
|
2118
|
|
|
Chart.defaults.horizontalBar = { |
|
2119
|
|
|
hover: { |
|
2120
|
|
|
mode: "label" |
|
2121
|
|
|
}, |
|
2122
|
|
|
|
|
2123
|
|
|
scales: { |
|
2124
|
|
|
xAxes: [{ |
|
2125
|
|
|
type: "linear", |
|
2126
|
|
|
position: "bottom" |
|
2127
|
|
|
}], |
|
2128
|
|
|
yAxes: [{ |
|
2129
|
|
|
position: "left", |
|
2130
|
|
|
type: "category", |
|
2131
|
|
|
|
|
2132
|
|
|
// Specific to Horizontal Bar Controller |
|
2133
|
|
|
categoryPercentage: 0.8, |
|
2134
|
|
|
barPercentage: 0.9, |
|
2135
|
|
|
|
|
2136
|
|
|
// grid line settings |
|
2137
|
|
|
gridLines: { |
|
2138
|
|
|
offsetGridLines: true |
|
2139
|
|
|
} |
|
2140
|
|
|
}] |
|
2141
|
|
|
}, |
|
2142
|
|
|
elements: { |
|
2143
|
|
|
rectangle: { |
|
2144
|
|
|
borderSkipped: 'left' |
|
2145
|
|
|
} |
|
2146
|
|
|
}, |
|
2147
|
|
|
tooltips: { |
|
2148
|
|
|
callbacks: { |
|
2149
|
|
|
title: function(tooltipItems, data) { |
|
2150
|
|
|
// Pick first xLabel for now |
|
2151
|
|
|
var title = ''; |
|
2152
|
|
|
|
|
2153
|
|
|
if (tooltipItems.length > 0) { |
|
2154
|
|
|
if (tooltipItems[0].yLabel) { |
|
2155
|
|
|
title = tooltipItems[0].yLabel; |
|
2156
|
|
|
} else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { |
|
2157
|
|
|
title = data.labels[tooltipItems[0].index]; |
|
2158
|
|
|
} |
|
2159
|
|
|
} |
|
2160
|
|
|
|
|
2161
|
|
|
return title; |
|
2162
|
|
|
}, |
|
2163
|
|
|
label: function(tooltipItem, data) { |
|
2164
|
|
|
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; |
|
2165
|
|
|
return datasetLabel + ': ' + tooltipItem.xLabel; |
|
2166
|
|
|
} |
|
2167
|
|
|
} |
|
2168
|
|
|
} |
|
2169
|
|
|
}; |
|
2170
|
|
|
|
|
2171
|
|
|
Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ |
|
2172
|
|
|
updateElement: function(rectangle, index, reset) { |
|
2173
|
|
|
var me = this; |
|
2174
|
|
|
var meta = me.getMeta(); |
|
2175
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
2176
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
2177
|
|
|
var scaleBase = xScale.getBasePixel(); |
|
2178
|
|
|
var custom = rectangle.custom || {}; |
|
2179
|
|
|
var dataset = me.getDataset(); |
|
2180
|
|
|
var rectangleElementOptions = me.chart.options.elements.rectangle; |
|
2181
|
|
|
|
|
2182
|
|
|
helpers.extend(rectangle, { |
|
2183
|
|
|
// Utility |
|
2184
|
|
|
_xScale: xScale, |
|
2185
|
|
|
_yScale: yScale, |
|
2186
|
|
|
_datasetIndex: me.index, |
|
2187
|
|
|
_index: index, |
|
2188
|
|
|
|
|
2189
|
|
|
// Desired view properties |
|
2190
|
|
|
_model: { |
|
2191
|
|
|
x: reset ? scaleBase : me.calculateBarX(index, me.index), |
|
2192
|
|
|
y: me.calculateBarY(index, me.index), |
|
2193
|
|
|
|
|
2194
|
|
|
// Tooltip |
|
2195
|
|
|
label: me.chart.data.labels[index], |
|
2196
|
|
|
datasetLabel: dataset.label, |
|
2197
|
|
|
|
|
2198
|
|
|
// Appearance |
|
2199
|
|
|
base: reset ? scaleBase : me.calculateBarBase(me.index, index), |
|
2200
|
|
|
height: me.calculateBarHeight(index), |
|
2201
|
|
|
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), |
|
2202
|
|
|
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, |
|
2203
|
|
|
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), |
|
2204
|
|
|
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) |
|
2205
|
|
|
}, |
|
2206
|
|
|
|
|
2207
|
|
|
draw: function () { |
|
|
|
|
|
|
2208
|
|
|
var ctx = this._chart.ctx; |
|
2209
|
|
|
var vm = this._view; |
|
2210
|
|
|
|
|
2211
|
|
|
var halfHeight = vm.height / 2, |
|
2212
|
|
|
topY = vm.y - halfHeight, |
|
2213
|
|
|
bottomY = vm.y + halfHeight, |
|
2214
|
|
|
right = vm.base - (vm.base - vm.x), |
|
2215
|
|
|
halfStroke = vm.borderWidth / 2; |
|
2216
|
|
|
|
|
2217
|
|
|
// Canvas doesn't allow us to stroke inside the width so we can |
|
2218
|
|
|
// adjust the sizes to fit if we're setting a stroke on the line |
|
2219
|
|
|
if (vm.borderWidth) { |
|
2220
|
|
|
topY += halfStroke; |
|
2221
|
|
|
bottomY -= halfStroke; |
|
2222
|
|
|
right += halfStroke; |
|
2223
|
|
|
} |
|
2224
|
|
|
|
|
2225
|
|
|
ctx.beginPath(); |
|
2226
|
|
|
|
|
2227
|
|
|
ctx.fillStyle = vm.backgroundColor; |
|
2228
|
|
|
ctx.strokeStyle = vm.borderColor; |
|
2229
|
|
|
ctx.lineWidth = vm.borderWidth; |
|
2230
|
|
|
|
|
2231
|
|
|
// Corner points, from bottom-left to bottom-right clockwise |
|
2232
|
|
|
// | 1 2 | |
|
2233
|
|
|
// | 0 3 | |
|
2234
|
|
|
var corners = [ |
|
2235
|
|
|
[vm.base, bottomY], |
|
2236
|
|
|
[vm.base, topY], |
|
2237
|
|
|
[right, topY], |
|
2238
|
|
|
[right, bottomY] |
|
2239
|
|
|
]; |
|
2240
|
|
|
|
|
2241
|
|
|
// Find first (starting) corner with fallback to 'bottom' |
|
2242
|
|
|
var borders = ['bottom', 'left', 'top', 'right']; |
|
2243
|
|
|
var startCorner = borders.indexOf(vm.borderSkipped, 0); |
|
2244
|
|
|
if (startCorner === -1) |
|
2245
|
|
|
startCorner = 0; |
|
|
|
|
|
|
2246
|
|
|
|
|
2247
|
|
|
function cornerAt(index) { |
|
2248
|
|
|
return corners[(startCorner + index) % 4]; |
|
2249
|
|
|
} |
|
2250
|
|
|
|
|
2251
|
|
|
// Draw rectangle from 'startCorner' |
|
2252
|
|
|
ctx.moveTo.apply(ctx, cornerAt(0)); |
|
2253
|
|
|
for (var i = 1; i < 4; i++) |
|
2254
|
|
|
ctx.lineTo.apply(ctx, cornerAt(i)); |
|
|
|
|
|
|
2255
|
|
|
|
|
2256
|
|
|
ctx.fill(); |
|
2257
|
|
|
if (vm.borderWidth) { |
|
2258
|
|
|
ctx.stroke(); |
|
2259
|
|
|
} |
|
2260
|
|
|
}, |
|
2261
|
|
|
|
|
2262
|
|
|
inRange: function (mouseX, mouseY) { |
|
|
|
|
|
|
2263
|
|
|
var vm = this._view; |
|
2264
|
|
|
var inRange = false; |
|
2265
|
|
|
|
|
2266
|
|
|
if (vm) { |
|
2267
|
|
|
if (vm.x < vm.base) { |
|
2268
|
|
|
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base); |
|
2269
|
|
|
} else { |
|
2270
|
|
|
inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x); |
|
2271
|
|
|
} |
|
2272
|
|
|
} |
|
2273
|
|
|
|
|
2274
|
|
|
return inRange; |
|
2275
|
|
|
} |
|
2276
|
|
|
}); |
|
2277
|
|
|
|
|
2278
|
|
|
rectangle.pivot(); |
|
2279
|
|
|
}, |
|
2280
|
|
|
|
|
2281
|
|
|
calculateBarBase: function (datasetIndex, index) { |
|
2282
|
|
|
var me = this; |
|
2283
|
|
|
var meta = me.getMeta(); |
|
2284
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
2285
|
|
|
var base = 0; |
|
2286
|
|
|
|
|
2287
|
|
|
if (xScale.options.stacked) { |
|
2288
|
|
|
var chart = me.chart; |
|
2289
|
|
|
var datasets = chart.data.datasets; |
|
2290
|
|
|
var value = Number(datasets[datasetIndex].data[index]); |
|
2291
|
|
|
|
|
2292
|
|
|
for (var i = 0; i < datasetIndex; i++) { |
|
2293
|
|
|
var currentDs = datasets[i]; |
|
2294
|
|
|
var currentDsMeta = chart.getDatasetMeta(i); |
|
2295
|
|
|
if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) { |
|
2296
|
|
|
var currentVal = Number(currentDs.data[index]); |
|
2297
|
|
|
base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); |
|
2298
|
|
|
} |
|
2299
|
|
|
} |
|
2300
|
|
|
|
|
2301
|
|
|
return xScale.getPixelForValue(base); |
|
2302
|
|
|
} |
|
2303
|
|
|
|
|
2304
|
|
|
return xScale.getBasePixel(); |
|
2305
|
|
|
}, |
|
2306
|
|
|
|
|
2307
|
|
|
getRuler: function (index) { |
|
|
|
|
|
|
2308
|
|
|
var me = this; |
|
2309
|
|
|
var meta = me.getMeta(); |
|
2310
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
2311
|
|
|
var datasetCount = me.getBarCount(); |
|
2312
|
|
|
|
|
2313
|
|
|
var tickHeight; |
|
2314
|
|
|
if (yScale.options.type === 'category') { |
|
2315
|
|
|
tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index); |
|
2316
|
|
|
} else { |
|
2317
|
|
|
// Average width |
|
2318
|
|
|
tickHeight = yScale.width / yScale.ticks.length; |
|
2319
|
|
|
} |
|
2320
|
|
|
var categoryHeight = tickHeight * yScale.options.categoryPercentage; |
|
2321
|
|
|
var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2; |
|
2322
|
|
|
var fullBarHeight = categoryHeight / datasetCount; |
|
2323
|
|
|
|
|
2324
|
|
|
if (yScale.ticks.length !== me.chart.data.labels.length) { |
|
2325
|
|
|
var perc = yScale.ticks.length / me.chart.data.labels.length; |
|
2326
|
|
|
fullBarHeight = fullBarHeight * perc; |
|
2327
|
|
|
} |
|
2328
|
|
|
|
|
2329
|
|
|
var barHeight = fullBarHeight * yScale.options.barPercentage; |
|
2330
|
|
|
var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage); |
|
2331
|
|
|
|
|
2332
|
|
|
return { |
|
2333
|
|
|
datasetCount: datasetCount, |
|
2334
|
|
|
tickHeight: tickHeight, |
|
2335
|
|
|
categoryHeight: categoryHeight, |
|
2336
|
|
|
categorySpacing: categorySpacing, |
|
2337
|
|
|
fullBarHeight: fullBarHeight, |
|
2338
|
|
|
barHeight: barHeight, |
|
2339
|
|
|
barSpacing: barSpacing, |
|
2340
|
|
|
}; |
|
2341
|
|
|
}, |
|
2342
|
|
|
|
|
2343
|
|
|
calculateBarHeight: function (index) { |
|
2344
|
|
|
var me = this; |
|
2345
|
|
|
var yScale = me.getScaleForId(me.getMeta().yAxisID); |
|
2346
|
|
|
if (yScale.options.barThickness) { |
|
2347
|
|
|
return yScale.options.barThickness; |
|
2348
|
|
|
} |
|
2349
|
|
|
var ruler = me.getRuler(index); |
|
2350
|
|
|
return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight; |
|
2351
|
|
|
}, |
|
2352
|
|
|
|
|
2353
|
|
|
calculateBarX: function (index, datasetIndex) { |
|
|
|
|
|
|
2354
|
|
|
var me = this; |
|
2355
|
|
|
var meta = me.getMeta(); |
|
2356
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
2357
|
|
|
var value = Number(me.getDataset().data[index]); |
|
2358
|
|
|
|
|
2359
|
|
|
if (xScale.options.stacked) { |
|
2360
|
|
|
|
|
2361
|
|
|
var sumPos = 0, |
|
2362
|
|
|
sumNeg = 0; |
|
2363
|
|
|
|
|
2364
|
|
|
for (var i = 0; i < datasetIndex; i++) { |
|
2365
|
|
|
var ds = me.chart.data.datasets[i]; |
|
2366
|
|
|
var dsMeta = me.chart.getDatasetMeta(i); |
|
2367
|
|
|
if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) { |
|
2368
|
|
|
var stackedVal = Number(ds.data[index]); |
|
2369
|
|
|
if (stackedVal < 0) { |
|
2370
|
|
|
sumNeg += stackedVal || 0; |
|
2371
|
|
|
} else { |
|
2372
|
|
|
sumPos += stackedVal || 0; |
|
2373
|
|
|
} |
|
2374
|
|
|
} |
|
2375
|
|
|
} |
|
2376
|
|
|
|
|
2377
|
|
|
if (value < 0) { |
|
2378
|
|
|
return xScale.getPixelForValue(sumNeg + value); |
|
2379
|
|
|
} else { |
|
2380
|
|
|
return xScale.getPixelForValue(sumPos + value); |
|
2381
|
|
|
} |
|
2382
|
|
|
} |
|
2383
|
|
|
|
|
2384
|
|
|
return xScale.getPixelForValue(value); |
|
2385
|
|
|
}, |
|
2386
|
|
|
|
|
2387
|
|
|
calculateBarY: function (index, datasetIndex) { |
|
2388
|
|
|
var me = this; |
|
2389
|
|
|
var meta = me.getMeta(); |
|
2390
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
2391
|
|
|
var barIndex = me.getBarIndex(datasetIndex); |
|
2392
|
|
|
|
|
2393
|
|
|
var ruler = me.getRuler(index); |
|
2394
|
|
|
var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); |
|
2395
|
|
|
topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0; |
|
2396
|
|
|
|
|
2397
|
|
|
if (yScale.options.stacked) { |
|
2398
|
|
|
return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing; |
|
2399
|
|
|
} |
|
2400
|
|
|
|
|
2401
|
|
|
return topTick + |
|
2402
|
|
|
(ruler.barHeight / 2) + |
|
2403
|
|
|
ruler.categorySpacing + |
|
2404
|
|
|
(ruler.barHeight * barIndex) + |
|
2405
|
|
|
(ruler.barSpacing / 2) + |
|
2406
|
|
|
(ruler.barSpacing * barIndex); |
|
2407
|
|
|
} |
|
2408
|
|
|
}); |
|
2409
|
|
|
}; |
|
2410
|
|
|
|
|
2411
|
|
|
},{}],16:[function(require,module,exports){ |
|
2412
|
|
|
"use strict"; |
|
2413
|
|
|
|
|
2414
|
|
|
module.exports = function(Chart) { |
|
2415
|
|
|
|
|
2416
|
|
|
var helpers = Chart.helpers; |
|
2417
|
|
|
|
|
2418
|
|
|
Chart.defaults.bubble = { |
|
2419
|
|
|
hover: { |
|
2420
|
|
|
mode: "single" |
|
2421
|
|
|
}, |
|
2422
|
|
|
|
|
2423
|
|
|
scales: { |
|
2424
|
|
|
xAxes: [{ |
|
2425
|
|
|
type: "linear", // bubble should probably use a linear scale by default |
|
2426
|
|
|
position: "bottom", |
|
2427
|
|
|
id: "x-axis-0" // need an ID so datasets can reference the scale |
|
2428
|
|
|
}], |
|
2429
|
|
|
yAxes: [{ |
|
2430
|
|
|
type: "linear", |
|
2431
|
|
|
position: "left", |
|
2432
|
|
|
id: "y-axis-0" |
|
2433
|
|
|
}] |
|
2434
|
|
|
}, |
|
2435
|
|
|
|
|
2436
|
|
|
tooltips: { |
|
2437
|
|
|
callbacks: { |
|
2438
|
|
|
title: function() { |
|
2439
|
|
|
// Title doesn't make sense for scatter since we format the data as a point |
|
2440
|
|
|
return ''; |
|
2441
|
|
|
}, |
|
2442
|
|
|
label: function(tooltipItem, data) { |
|
2443
|
|
|
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; |
|
2444
|
|
|
var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; |
|
2445
|
|
|
return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')'; |
|
2446
|
|
|
} |
|
2447
|
|
|
} |
|
2448
|
|
|
} |
|
2449
|
|
|
}; |
|
2450
|
|
|
|
|
2451
|
|
|
Chart.controllers.bubble = Chart.DatasetController.extend({ |
|
2452
|
|
|
|
|
2453
|
|
|
dataElementType: Chart.elements.Point, |
|
2454
|
|
|
|
|
2455
|
|
|
update: function(reset) { |
|
2456
|
|
|
var me = this; |
|
2457
|
|
|
var meta = me.getMeta(); |
|
2458
|
|
|
var points = meta.data; |
|
2459
|
|
|
|
|
2460
|
|
|
// Update Points |
|
2461
|
|
|
helpers.each(points, function(point, index) { |
|
2462
|
|
|
me.updateElement(point, index, reset); |
|
2463
|
|
|
}); |
|
2464
|
|
|
}, |
|
2465
|
|
|
|
|
2466
|
|
|
updateElement: function(point, index, reset) { |
|
2467
|
|
|
var me = this; |
|
2468
|
|
|
var meta = me.getMeta(); |
|
2469
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
2470
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
2471
|
|
|
|
|
2472
|
|
|
var custom = point.custom || {}; |
|
2473
|
|
|
var dataset = me.getDataset(); |
|
2474
|
|
|
var data = dataset.data[index]; |
|
2475
|
|
|
var pointElementOptions = me.chart.options.elements.point; |
|
2476
|
|
|
var dsIndex = me.index; |
|
2477
|
|
|
|
|
2478
|
|
|
helpers.extend(point, { |
|
2479
|
|
|
// Utility |
|
2480
|
|
|
_xScale: xScale, |
|
2481
|
|
|
_yScale: yScale, |
|
2482
|
|
|
_datasetIndex: dsIndex, |
|
2483
|
|
|
_index: index, |
|
2484
|
|
|
|
|
2485
|
|
|
// Desired view properties |
|
2486
|
|
|
_model: { |
|
2487
|
|
|
x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), |
|
2488
|
|
|
y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), |
|
2489
|
|
|
// Appearance |
|
2490
|
|
|
radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), |
|
2491
|
|
|
|
|
2492
|
|
|
// Tooltip |
|
2493
|
|
|
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) |
|
2494
|
|
|
} |
|
2495
|
|
|
}); |
|
2496
|
|
|
|
|
2497
|
|
|
// Trick to reset the styles of the point |
|
2498
|
|
|
Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); |
|
2499
|
|
|
|
|
2500
|
|
|
var model = point._model; |
|
2501
|
|
|
model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); |
|
2502
|
|
|
|
|
2503
|
|
|
point.pivot(); |
|
2504
|
|
|
}, |
|
2505
|
|
|
|
|
2506
|
|
|
getRadius: function(value) { |
|
2507
|
|
|
return value.r || this.chart.options.elements.point.radius; |
|
2508
|
|
|
}, |
|
2509
|
|
|
|
|
2510
|
|
|
setHoverStyle: function(point) { |
|
2511
|
|
|
var me = this; |
|
2512
|
|
|
Chart.DatasetController.prototype.setHoverStyle.call(me, point); |
|
2513
|
|
|
|
|
2514
|
|
|
// Radius |
|
2515
|
|
|
var dataset = me.chart.data.datasets[point._datasetIndex]; |
|
2516
|
|
|
var index = point._index; |
|
2517
|
|
|
var custom = point.custom || {}; |
|
2518
|
|
|
var model = point._model; |
|
2519
|
|
|
model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); |
|
2520
|
|
|
}, |
|
2521
|
|
|
|
|
2522
|
|
|
removeHoverStyle: function(point) { |
|
2523
|
|
|
var me = this; |
|
2524
|
|
|
Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); |
|
2525
|
|
|
|
|
2526
|
|
|
var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; |
|
2527
|
|
|
var custom = point.custom || {}; |
|
2528
|
|
|
var model = point._model; |
|
2529
|
|
|
|
|
2530
|
|
|
model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); |
|
2531
|
|
|
} |
|
2532
|
|
|
}); |
|
2533
|
|
|
}; |
|
2534
|
|
|
|
|
2535
|
|
|
},{}],17:[function(require,module,exports){ |
|
2536
|
|
|
"use strict"; |
|
2537
|
|
|
|
|
2538
|
|
|
module.exports = function(Chart) { |
|
2539
|
|
|
|
|
2540
|
|
|
var helpers = Chart.helpers, |
|
2541
|
|
|
defaults = Chart.defaults; |
|
2542
|
|
|
|
|
2543
|
|
|
defaults.doughnut = { |
|
2544
|
|
|
animation: { |
|
2545
|
|
|
//Boolean - Whether we animate the rotation of the Doughnut |
|
2546
|
|
|
animateRotate: true, |
|
2547
|
|
|
//Boolean - Whether we animate scaling the Doughnut from the centre |
|
2548
|
|
|
animateScale: false |
|
2549
|
|
|
}, |
|
2550
|
|
|
aspectRatio: 1, |
|
2551
|
|
|
hover: { |
|
2552
|
|
|
mode: 'single' |
|
2553
|
|
|
}, |
|
2554
|
|
|
legendCallback: function(chart) { |
|
2555
|
|
|
var text = []; |
|
2556
|
|
|
text.push('<ul class="' + chart.id + '-legend">'); |
|
2557
|
|
|
|
|
2558
|
|
|
var data = chart.data; |
|
2559
|
|
|
var datasets = data.datasets; |
|
2560
|
|
|
var labels = data.labels; |
|
2561
|
|
|
|
|
2562
|
|
|
if (datasets.length) { |
|
2563
|
|
|
for (var i = 0; i < datasets[0].data.length; ++i) { |
|
2564
|
|
|
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); |
|
2565
|
|
|
if (labels[i]) { |
|
2566
|
|
|
text.push(labels[i]); |
|
2567
|
|
|
} |
|
2568
|
|
|
text.push('</li>'); |
|
2569
|
|
|
} |
|
2570
|
|
|
} |
|
2571
|
|
|
|
|
2572
|
|
|
text.push('</ul>'); |
|
2573
|
|
|
return text.join(""); |
|
2574
|
|
|
}, |
|
2575
|
|
|
legend: { |
|
2576
|
|
|
labels: { |
|
2577
|
|
|
generateLabels: function(chart) { |
|
|
|
|
|
|
2578
|
|
|
var data = chart.data; |
|
2579
|
|
|
if (data.labels.length && data.datasets.length) { |
|
2580
|
|
|
return data.labels.map(function(label, i) { |
|
2581
|
|
|
var meta = chart.getDatasetMeta(0); |
|
2582
|
|
|
var ds = data.datasets[0]; |
|
2583
|
|
|
var arc = meta.data[i]; |
|
2584
|
|
|
var custom = arc && arc.custom || {}; |
|
2585
|
|
|
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
|
2586
|
|
|
var arcOpts = chart.options.elements.arc; |
|
2587
|
|
|
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); |
|
2588
|
|
|
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); |
|
2589
|
|
|
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); |
|
2590
|
|
|
|
|
2591
|
|
|
return { |
|
2592
|
|
|
text: label, |
|
2593
|
|
|
fillStyle: fill, |
|
2594
|
|
|
strokeStyle: stroke, |
|
2595
|
|
|
lineWidth: bw, |
|
2596
|
|
|
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, |
|
2597
|
|
|
|
|
2598
|
|
|
// Extra data used for toggling the correct item |
|
2599
|
|
|
index: i |
|
2600
|
|
|
}; |
|
2601
|
|
|
}); |
|
2602
|
|
|
} else { |
|
2603
|
|
|
return []; |
|
2604
|
|
|
} |
|
2605
|
|
|
} |
|
2606
|
|
|
}, |
|
2607
|
|
|
|
|
2608
|
|
|
onClick: function(e, legendItem) { |
|
2609
|
|
|
var index = legendItem.index; |
|
2610
|
|
|
var chart = this.chart; |
|
2611
|
|
|
var i, ilen, meta; |
|
2612
|
|
|
|
|
2613
|
|
|
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
|
2614
|
|
|
meta = chart.getDatasetMeta(i); |
|
2615
|
|
|
meta.data[index].hidden = !meta.data[index].hidden; |
|
2616
|
|
|
} |
|
2617
|
|
|
|
|
2618
|
|
|
chart.update(); |
|
2619
|
|
|
} |
|
2620
|
|
|
}, |
|
2621
|
|
|
|
|
2622
|
|
|
//The percentage of the chart that we cut out of the middle. |
|
2623
|
|
|
cutoutPercentage: 50, |
|
2624
|
|
|
|
|
2625
|
|
|
//The rotation of the chart, where the first data arc begins. |
|
2626
|
|
|
rotation: Math.PI * -0.5, |
|
2627
|
|
|
|
|
2628
|
|
|
//The total circumference of the chart. |
|
2629
|
|
|
circumference: Math.PI * 2.0, |
|
2630
|
|
|
|
|
2631
|
|
|
// Need to override these to give a nice default |
|
2632
|
|
|
tooltips: { |
|
2633
|
|
|
callbacks: { |
|
2634
|
|
|
title: function() { |
|
2635
|
|
|
return ''; |
|
2636
|
|
|
}, |
|
2637
|
|
|
label: function(tooltipItem, data) { |
|
2638
|
|
|
return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; |
|
2639
|
|
|
} |
|
2640
|
|
|
} |
|
2641
|
|
|
} |
|
2642
|
|
|
}; |
|
2643
|
|
|
|
|
2644
|
|
|
defaults.pie = helpers.clone(defaults.doughnut); |
|
2645
|
|
|
helpers.extend(defaults.pie, { |
|
2646
|
|
|
cutoutPercentage: 0 |
|
2647
|
|
|
}); |
|
2648
|
|
|
|
|
2649
|
|
|
|
|
2650
|
|
|
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ |
|
2651
|
|
|
|
|
2652
|
|
|
dataElementType: Chart.elements.Arc, |
|
2653
|
|
|
|
|
2654
|
|
|
linkScales: helpers.noop, |
|
2655
|
|
|
|
|
2656
|
|
|
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly |
|
2657
|
|
|
getRingIndex: function(datasetIndex) { |
|
2658
|
|
|
var ringIndex = 0; |
|
2659
|
|
|
|
|
2660
|
|
|
for (var j = 0; j < datasetIndex; ++j) { |
|
2661
|
|
|
if (this.chart.isDatasetVisible(j)) { |
|
2662
|
|
|
++ringIndex; |
|
2663
|
|
|
} |
|
2664
|
|
|
} |
|
2665
|
|
|
|
|
2666
|
|
|
return ringIndex; |
|
2667
|
|
|
}, |
|
2668
|
|
|
|
|
2669
|
|
|
update: function(reset) { |
|
2670
|
|
|
var me = this; |
|
2671
|
|
|
var chart = me.chart, |
|
2672
|
|
|
chartArea = chart.chartArea, |
|
2673
|
|
|
opts = chart.options, |
|
2674
|
|
|
arcOpts = opts.elements.arc, |
|
2675
|
|
|
availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, |
|
2676
|
|
|
availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, |
|
2677
|
|
|
minSize = Math.min(availableWidth, availableHeight), |
|
2678
|
|
|
offset = { |
|
2679
|
|
|
x: 0, |
|
2680
|
|
|
y: 0 |
|
2681
|
|
|
}, |
|
2682
|
|
|
meta = me.getMeta(), |
|
2683
|
|
|
cutoutPercentage = opts.cutoutPercentage, |
|
2684
|
|
|
circumference = opts.circumference; |
|
2685
|
|
|
|
|
2686
|
|
|
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc |
|
2687
|
|
|
if (circumference < Math.PI * 2.0) { |
|
2688
|
|
|
var startAngle = opts.rotation % (Math.PI * 2.0); |
|
2689
|
|
|
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); |
|
2690
|
|
|
var endAngle = startAngle + circumference; |
|
2691
|
|
|
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; |
|
2692
|
|
|
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; |
|
2693
|
|
|
var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); |
|
2694
|
|
|
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); |
|
2695
|
|
|
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); |
|
2696
|
|
|
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); |
|
2697
|
|
|
var cutout = cutoutPercentage / 100.0; |
|
2698
|
|
|
var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; |
|
2699
|
|
|
var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; |
|
2700
|
|
|
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; |
|
2701
|
|
|
minSize = Math.min(availableWidth / size.width, availableHeight / size.height); |
|
2702
|
|
|
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; |
|
2703
|
|
|
} |
|
2704
|
|
|
chart.borderWidth = me.getMaxBorderWidth(meta.data); |
|
2705
|
|
|
|
|
2706
|
|
|
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); |
|
2707
|
|
|
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0); |
|
2708
|
|
|
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); |
|
2709
|
|
|
chart.offsetX = offset.x * chart.outerRadius; |
|
2710
|
|
|
chart.offsetY = offset.y * chart.outerRadius; |
|
2711
|
|
|
|
|
2712
|
|
|
meta.total = me.calculateTotal(); |
|
2713
|
|
|
|
|
2714
|
|
|
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); |
|
2715
|
|
|
me.innerRadius = me.outerRadius - chart.radiusLength; |
|
2716
|
|
|
|
|
2717
|
|
|
helpers.each(meta.data, function(arc, index) { |
|
2718
|
|
|
me.updateElement(arc, index, reset); |
|
2719
|
|
|
}); |
|
2720
|
|
|
}, |
|
2721
|
|
|
|
|
2722
|
|
|
updateElement: function(arc, index, reset) { |
|
2723
|
|
|
var me = this; |
|
2724
|
|
|
var chart = me.chart, |
|
2725
|
|
|
chartArea = chart.chartArea, |
|
2726
|
|
|
opts = chart.options, |
|
2727
|
|
|
animationOpts = opts.animation, |
|
2728
|
|
|
centerX = (chartArea.left + chartArea.right) / 2, |
|
2729
|
|
|
centerY = (chartArea.top + chartArea.bottom) / 2, |
|
2730
|
|
|
startAngle = opts.rotation, // non reset case handled later |
|
2731
|
|
|
endAngle = opts.rotation, // non reset case handled later |
|
2732
|
|
|
dataset = me.getDataset(), |
|
2733
|
|
|
circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), |
|
2734
|
|
|
innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, |
|
2735
|
|
|
outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, |
|
2736
|
|
|
valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
|
2737
|
|
|
|
|
2738
|
|
|
helpers.extend(arc, { |
|
2739
|
|
|
// Utility |
|
2740
|
|
|
_datasetIndex: me.index, |
|
2741
|
|
|
_index: index, |
|
2742
|
|
|
|
|
2743
|
|
|
// Desired view properties |
|
2744
|
|
|
_model: { |
|
2745
|
|
|
x: centerX + chart.offsetX, |
|
2746
|
|
|
y: centerY + chart.offsetY, |
|
2747
|
|
|
startAngle: startAngle, |
|
2748
|
|
|
endAngle: endAngle, |
|
2749
|
|
|
circumference: circumference, |
|
2750
|
|
|
outerRadius: outerRadius, |
|
2751
|
|
|
innerRadius: innerRadius, |
|
2752
|
|
|
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) |
|
2753
|
|
|
} |
|
2754
|
|
|
}); |
|
2755
|
|
|
|
|
2756
|
|
|
var model = arc._model; |
|
2757
|
|
|
// Resets the visual styles |
|
2758
|
|
|
this.removeHoverStyle(arc); |
|
2759
|
|
|
|
|
2760
|
|
|
// Set correct angles if not resetting |
|
2761
|
|
|
if (!reset || !animationOpts.animateRotate) { |
|
2762
|
|
|
if (index === 0) { |
|
2763
|
|
|
model.startAngle = opts.rotation; |
|
2764
|
|
|
} else { |
|
2765
|
|
|
model.startAngle = me.getMeta().data[index - 1]._model.endAngle; |
|
2766
|
|
|
} |
|
2767
|
|
|
|
|
2768
|
|
|
model.endAngle = model.startAngle + model.circumference; |
|
2769
|
|
|
} |
|
2770
|
|
|
|
|
2771
|
|
|
arc.pivot(); |
|
2772
|
|
|
}, |
|
2773
|
|
|
|
|
2774
|
|
|
removeHoverStyle: function(arc) { |
|
2775
|
|
|
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); |
|
2776
|
|
|
}, |
|
2777
|
|
|
|
|
2778
|
|
|
calculateTotal: function() { |
|
2779
|
|
|
var dataset = this.getDataset(); |
|
2780
|
|
|
var meta = this.getMeta(); |
|
2781
|
|
|
var total = 0; |
|
2782
|
|
|
var value; |
|
2783
|
|
|
|
|
2784
|
|
|
helpers.each(meta.data, function(element, index) { |
|
2785
|
|
|
value = dataset.data[index]; |
|
2786
|
|
|
if (!isNaN(value) && !element.hidden) { |
|
2787
|
|
|
total += Math.abs(value); |
|
2788
|
|
|
} |
|
2789
|
|
|
}); |
|
2790
|
|
|
|
|
2791
|
|
|
/*if (total === 0) { |
|
2792
|
|
|
total = NaN; |
|
2793
|
|
|
}*/ |
|
2794
|
|
|
|
|
2795
|
|
|
return total; |
|
2796
|
|
|
}, |
|
2797
|
|
|
|
|
2798
|
|
|
calculateCircumference: function(value) { |
|
2799
|
|
|
var total = this.getMeta().total; |
|
2800
|
|
|
if (total > 0 && !isNaN(value)) { |
|
2801
|
|
|
return (Math.PI * 2.0) * (value / total); |
|
2802
|
|
|
} else { |
|
2803
|
|
|
return 0; |
|
2804
|
|
|
} |
|
2805
|
|
|
}, |
|
2806
|
|
|
|
|
2807
|
|
|
//gets the max border or hover width to properly scale pie charts |
|
2808
|
|
|
getMaxBorderWidth: function (elements) { |
|
2809
|
|
|
var max = 0, |
|
2810
|
|
|
index = this.index, |
|
2811
|
|
|
length = elements.length, |
|
2812
|
|
|
borderWidth, |
|
2813
|
|
|
hoverWidth; |
|
2814
|
|
|
|
|
2815
|
|
|
for (var i = 0; i < length; i++) { |
|
2816
|
|
|
borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; |
|
2817
|
|
|
hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; |
|
2818
|
|
|
|
|
2819
|
|
|
max = borderWidth > max ? borderWidth : max; |
|
2820
|
|
|
max = hoverWidth > max ? hoverWidth : max; |
|
2821
|
|
|
} |
|
2822
|
|
|
return max; |
|
2823
|
|
|
} |
|
2824
|
|
|
}); |
|
2825
|
|
|
}; |
|
2826
|
|
|
|
|
2827
|
|
|
},{}],18:[function(require,module,exports){ |
|
2828
|
|
|
"use strict"; |
|
2829
|
|
|
|
|
2830
|
|
|
module.exports = function(Chart) { |
|
2831
|
|
|
|
|
2832
|
|
|
var helpers = Chart.helpers; |
|
2833
|
|
|
|
|
2834
|
|
|
Chart.defaults.line = { |
|
2835
|
|
|
showLines: true, |
|
2836
|
|
|
spanGaps: false, |
|
2837
|
|
|
|
|
2838
|
|
|
hover: { |
|
2839
|
|
|
mode: "label" |
|
2840
|
|
|
}, |
|
2841
|
|
|
|
|
2842
|
|
|
scales: { |
|
2843
|
|
|
xAxes: [{ |
|
2844
|
|
|
type: "category", |
|
2845
|
|
|
id: 'x-axis-0' |
|
2846
|
|
|
}], |
|
2847
|
|
|
yAxes: [{ |
|
2848
|
|
|
type: "linear", |
|
2849
|
|
|
id: 'y-axis-0' |
|
2850
|
|
|
}] |
|
2851
|
|
|
} |
|
2852
|
|
|
}; |
|
2853
|
|
|
|
|
2854
|
|
|
function lineEnabled(dataset, options) { |
|
2855
|
|
|
return helpers.getValueOrDefault(dataset.showLine, options.showLines); |
|
2856
|
|
|
} |
|
2857
|
|
|
|
|
2858
|
|
|
Chart.controllers.line = Chart.DatasetController.extend({ |
|
2859
|
|
|
|
|
2860
|
|
|
datasetElementType: Chart.elements.Line, |
|
2861
|
|
|
|
|
2862
|
|
|
dataElementType: Chart.elements.Point, |
|
2863
|
|
|
|
|
2864
|
|
|
addElementAndReset: function(index) { |
|
2865
|
|
|
var me = this; |
|
2866
|
|
|
var options = me.chart.options; |
|
2867
|
|
|
var meta = me.getMeta(); |
|
2868
|
|
|
|
|
2869
|
|
|
Chart.DatasetController.prototype.addElementAndReset.call(me, index); |
|
2870
|
|
|
|
|
2871
|
|
|
// Make sure bezier control points are updated |
|
2872
|
|
|
if (lineEnabled(me.getDataset(), options) && meta.dataset._model.tension !== 0) { |
|
2873
|
|
|
me.updateBezierControlPoints(); |
|
2874
|
|
|
} |
|
2875
|
|
|
}, |
|
2876
|
|
|
|
|
2877
|
|
|
update: function(reset) { |
|
2878
|
|
|
var me = this; |
|
2879
|
|
|
var meta = me.getMeta(); |
|
2880
|
|
|
var line = meta.dataset; |
|
2881
|
|
|
var points = meta.data || []; |
|
2882
|
|
|
var options = me.chart.options; |
|
2883
|
|
|
var lineElementOptions = options.elements.line; |
|
2884
|
|
|
var scale = me.getScaleForId(meta.yAxisID); |
|
2885
|
|
|
var i, ilen, custom; |
|
2886
|
|
|
var dataset = me.getDataset(); |
|
2887
|
|
|
var showLine = lineEnabled(dataset, options); |
|
2888
|
|
|
|
|
2889
|
|
|
// Update Line |
|
2890
|
|
|
if (showLine) { |
|
2891
|
|
|
custom = line.custom || {}; |
|
2892
|
|
|
|
|
2893
|
|
|
// Compatibility: If the properties are defined with only the old name, use those values |
|
2894
|
|
|
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { |
|
2895
|
|
|
dataset.lineTension = dataset.tension; |
|
2896
|
|
|
} |
|
2897
|
|
|
|
|
2898
|
|
|
// Utility |
|
2899
|
|
|
line._scale = scale; |
|
2900
|
|
|
line._datasetIndex = me.index; |
|
2901
|
|
|
// Data |
|
2902
|
|
|
line._children = points; |
|
2903
|
|
|
// Model |
|
2904
|
|
|
line._model = { |
|
2905
|
|
|
// Appearance |
|
2906
|
|
|
// The default behavior of lines is to break at null values, according |
|
2907
|
|
|
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 |
|
2908
|
|
|
// This option gives linse the ability to span gaps |
|
2909
|
|
|
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, |
|
2910
|
|
|
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), |
|
2911
|
|
|
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), |
|
2912
|
|
|
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), |
|
2913
|
|
|
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), |
|
2914
|
|
|
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), |
|
2915
|
|
|
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), |
|
2916
|
|
|
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), |
|
2917
|
|
|
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), |
|
2918
|
|
|
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), |
|
2919
|
|
|
steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), |
|
2920
|
|
|
// Scale |
|
2921
|
|
|
scaleTop: scale.top, |
|
2922
|
|
|
scaleBottom: scale.bottom, |
|
2923
|
|
|
scaleZero: scale.getBasePixel() |
|
2924
|
|
|
}; |
|
2925
|
|
|
|
|
2926
|
|
|
line.pivot(); |
|
2927
|
|
|
} |
|
2928
|
|
|
|
|
2929
|
|
|
// Update Points |
|
2930
|
|
|
for (i=0, ilen=points.length; i<ilen; ++i) { |
|
2931
|
|
|
me.updateElement(points[i], i, reset); |
|
2932
|
|
|
} |
|
2933
|
|
|
|
|
2934
|
|
|
if (showLine && line._model.tension !== 0) { |
|
2935
|
|
|
me.updateBezierControlPoints(); |
|
2936
|
|
|
} |
|
2937
|
|
|
|
|
2938
|
|
|
// Now pivot the point for animation |
|
2939
|
|
|
for (i=0, ilen=points.length; i<ilen; ++i) { |
|
2940
|
|
|
points[i].pivot(); |
|
2941
|
|
|
} |
|
2942
|
|
|
}, |
|
2943
|
|
|
|
|
2944
|
|
|
getPointBackgroundColor: function(point, index) { |
|
2945
|
|
|
var backgroundColor = this.chart.options.elements.point.backgroundColor; |
|
2946
|
|
|
var dataset = this.getDataset(); |
|
2947
|
|
|
var custom = point.custom || {}; |
|
2948
|
|
|
|
|
2949
|
|
|
if (custom.backgroundColor) { |
|
2950
|
|
|
backgroundColor = custom.backgroundColor; |
|
2951
|
|
|
} else if (dataset.pointBackgroundColor) { |
|
2952
|
|
|
backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); |
|
2953
|
|
|
} else if (dataset.backgroundColor) { |
|
2954
|
|
|
backgroundColor = dataset.backgroundColor; |
|
2955
|
|
|
} |
|
2956
|
|
|
|
|
2957
|
|
|
return backgroundColor; |
|
2958
|
|
|
}, |
|
2959
|
|
|
|
|
2960
|
|
|
getPointBorderColor: function(point, index) { |
|
2961
|
|
|
var borderColor = this.chart.options.elements.point.borderColor; |
|
2962
|
|
|
var dataset = this.getDataset(); |
|
2963
|
|
|
var custom = point.custom || {}; |
|
2964
|
|
|
|
|
2965
|
|
|
if (custom.borderColor) { |
|
2966
|
|
|
borderColor = custom.borderColor; |
|
2967
|
|
|
} else if (dataset.pointBorderColor) { |
|
2968
|
|
|
borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); |
|
2969
|
|
|
} else if (dataset.borderColor) { |
|
2970
|
|
|
borderColor = dataset.borderColor; |
|
2971
|
|
|
} |
|
2972
|
|
|
|
|
2973
|
|
|
return borderColor; |
|
2974
|
|
|
}, |
|
2975
|
|
|
|
|
2976
|
|
|
getPointBorderWidth: function(point, index) { |
|
2977
|
|
|
var borderWidth = this.chart.options.elements.point.borderWidth; |
|
2978
|
|
|
var dataset = this.getDataset(); |
|
2979
|
|
|
var custom = point.custom || {}; |
|
2980
|
|
|
|
|
2981
|
|
|
if (custom.borderWidth) { |
|
2982
|
|
|
borderWidth = custom.borderWidth; |
|
2983
|
|
|
} else if (dataset.pointBorderWidth) { |
|
2984
|
|
|
borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); |
|
2985
|
|
|
} else if (dataset.borderWidth) { |
|
2986
|
|
|
borderWidth = dataset.borderWidth; |
|
2987
|
|
|
} |
|
2988
|
|
|
|
|
2989
|
|
|
return borderWidth; |
|
2990
|
|
|
}, |
|
2991
|
|
|
|
|
2992
|
|
|
updateElement: function(point, index, reset) { |
|
2993
|
|
|
var me = this; |
|
2994
|
|
|
var meta = me.getMeta(); |
|
2995
|
|
|
var custom = point.custom || {}; |
|
2996
|
|
|
var dataset = me.getDataset(); |
|
2997
|
|
|
var datasetIndex = me.index; |
|
2998
|
|
|
var value = dataset.data[index]; |
|
2999
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
3000
|
|
|
var xScale = me.getScaleForId(meta.xAxisID); |
|
3001
|
|
|
var pointOptions = me.chart.options.elements.point; |
|
3002
|
|
|
var x, y; |
|
3003
|
|
|
|
|
3004
|
|
|
// Compatibility: If the properties are defined with only the old name, use those values |
|
3005
|
|
|
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
|
3006
|
|
|
dataset.pointRadius = dataset.radius; |
|
3007
|
|
|
} |
|
3008
|
|
|
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { |
|
3009
|
|
|
dataset.pointHitRadius = dataset.hitRadius; |
|
3010
|
|
|
} |
|
3011
|
|
|
|
|
3012
|
|
|
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, me.chart.isCombo); |
|
3013
|
|
|
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); |
|
3014
|
|
|
|
|
3015
|
|
|
// Utility |
|
3016
|
|
|
point._xScale = xScale; |
|
3017
|
|
|
point._yScale = yScale; |
|
3018
|
|
|
point._datasetIndex = datasetIndex; |
|
3019
|
|
|
point._index = index; |
|
3020
|
|
|
|
|
3021
|
|
|
// Desired view properties |
|
3022
|
|
|
point._model = { |
|
3023
|
|
|
x: x, |
|
3024
|
|
|
y: y, |
|
3025
|
|
|
skip: custom.skip || isNaN(x) || isNaN(y), |
|
3026
|
|
|
// Appearance |
|
3027
|
|
|
radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), |
|
3028
|
|
|
pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), |
|
3029
|
|
|
backgroundColor: me.getPointBackgroundColor(point, index), |
|
3030
|
|
|
borderColor: me.getPointBorderColor(point, index), |
|
3031
|
|
|
borderWidth: me.getPointBorderWidth(point, index), |
|
3032
|
|
|
tension: meta.dataset._model ? meta.dataset._model.tension : 0, |
|
3033
|
|
|
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, |
|
3034
|
|
|
// Tooltip |
|
3035
|
|
|
hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) |
|
3036
|
|
|
}; |
|
3037
|
|
|
}, |
|
3038
|
|
|
|
|
3039
|
|
|
calculatePointY: function(value, index, datasetIndex) { |
|
|
|
|
|
|
3040
|
|
|
var me = this; |
|
3041
|
|
|
var chart = me.chart; |
|
3042
|
|
|
var meta = me.getMeta(); |
|
3043
|
|
|
var yScale = me.getScaleForId(meta.yAxisID); |
|
3044
|
|
|
var sumPos = 0; |
|
3045
|
|
|
var sumNeg = 0; |
|
3046
|
|
|
var i, ds, dsMeta; |
|
3047
|
|
|
|
|
3048
|
|
|
if (yScale.options.stacked) { |
|
3049
|
|
|
for (i = 0; i < datasetIndex; i++) { |
|
3050
|
|
|
ds = chart.data.datasets[i]; |
|
3051
|
|
|
dsMeta = chart.getDatasetMeta(i); |
|
3052
|
|
|
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { |
|
3053
|
|
|
var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); |
|
3054
|
|
|
if (stackedRightValue < 0) { |
|
3055
|
|
|
sumNeg += stackedRightValue || 0; |
|
3056
|
|
|
} else { |
|
3057
|
|
|
sumPos += stackedRightValue || 0; |
|
3058
|
|
|
} |
|
3059
|
|
|
} |
|
3060
|
|
|
} |
|
3061
|
|
|
|
|
3062
|
|
|
var rightValue = Number(yScale.getRightValue(value)); |
|
3063
|
|
|
if (rightValue < 0) { |
|
3064
|
|
|
return yScale.getPixelForValue(sumNeg + rightValue); |
|
3065
|
|
|
} else { |
|
3066
|
|
|
return yScale.getPixelForValue(sumPos + rightValue); |
|
3067
|
|
|
} |
|
3068
|
|
|
} |
|
3069
|
|
|
|
|
3070
|
|
|
return yScale.getPixelForValue(value); |
|
3071
|
|
|
}, |
|
3072
|
|
|
|
|
3073
|
|
|
updateBezierControlPoints: function() { |
|
3074
|
|
|
var me = this; |
|
3075
|
|
|
var meta = me.getMeta(); |
|
3076
|
|
|
var area = me.chart.chartArea; |
|
3077
|
|
|
|
|
3078
|
|
|
// only consider points that are drawn in case the spanGaps option is ued |
|
3079
|
|
|
var points = (meta.data || []).filter(function(pt) { return !pt._model.skip; }); |
|
3080
|
|
|
var i, ilen, point, model, controlPoints; |
|
3081
|
|
|
|
|
3082
|
|
|
var needToCap = me.chart.options.elements.line.capBezierPoints; |
|
3083
|
|
|
function capIfNecessary(pt, min, max) { |
|
3084
|
|
|
return needToCap ? Math.max(Math.min(pt, max), min) : pt; |
|
3085
|
|
|
} |
|
3086
|
|
|
|
|
3087
|
|
|
for (i=0, ilen=points.length; i<ilen; ++i) { |
|
3088
|
|
|
point = points[i]; |
|
3089
|
|
|
model = point._model; |
|
3090
|
|
|
controlPoints = helpers.splineCurve( |
|
3091
|
|
|
helpers.previousItem(points, i)._model, |
|
3092
|
|
|
model, |
|
3093
|
|
|
helpers.nextItem(points, i)._model, |
|
3094
|
|
|
meta.dataset._model.tension |
|
3095
|
|
|
); |
|
3096
|
|
|
|
|
3097
|
|
|
model.controlPointPreviousX = capIfNecessary(controlPoints.previous.x, area.left, area.right); |
|
3098
|
|
|
model.controlPointPreviousY = capIfNecessary(controlPoints.previous.y, area.top, area.bottom); |
|
3099
|
|
|
model.controlPointNextX = capIfNecessary(controlPoints.next.x, area.left, area.right); |
|
3100
|
|
|
model.controlPointNextY = capIfNecessary(controlPoints.next.y, area.top, area.bottom); |
|
3101
|
|
|
} |
|
3102
|
|
|
}, |
|
3103
|
|
|
|
|
3104
|
|
|
draw: function(ease) { |
|
3105
|
|
|
var me = this; |
|
3106
|
|
|
var meta = me.getMeta(); |
|
3107
|
|
|
var points = meta.data || []; |
|
3108
|
|
|
var easingDecimal = ease || 1; |
|
3109
|
|
|
var i, ilen; |
|
3110
|
|
|
|
|
3111
|
|
|
// Transition Point Locations |
|
3112
|
|
|
for (i=0, ilen=points.length; i<ilen; ++i) { |
|
3113
|
|
|
points[i].transition(easingDecimal); |
|
3114
|
|
|
} |
|
3115
|
|
|
|
|
3116
|
|
|
// Transition and Draw the line |
|
3117
|
|
|
if (lineEnabled(me.getDataset(), me.chart.options)) { |
|
3118
|
|
|
meta.dataset.transition(easingDecimal).draw(); |
|
3119
|
|
|
} |
|
3120
|
|
|
|
|
3121
|
|
|
// Draw the points |
|
3122
|
|
|
for (i=0, ilen=points.length; i<ilen; ++i) { |
|
3123
|
|
|
points[i].draw(); |
|
3124
|
|
|
} |
|
3125
|
|
|
}, |
|
3126
|
|
|
|
|
3127
|
|
|
setHoverStyle: function(point) { |
|
3128
|
|
|
// Point |
|
3129
|
|
|
var dataset = this.chart.data.datasets[point._datasetIndex]; |
|
3130
|
|
|
var index = point._index; |
|
3131
|
|
|
var custom = point.custom || {}; |
|
3132
|
|
|
var model = point._model; |
|
3133
|
|
|
|
|
3134
|
|
|
model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); |
|
3135
|
|
|
model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
|
3136
|
|
|
model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
|
3137
|
|
|
model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); |
|
3138
|
|
|
}, |
|
3139
|
|
|
|
|
3140
|
|
|
removeHoverStyle: function(point) { |
|
3141
|
|
|
var me = this; |
|
3142
|
|
|
var dataset = me.chart.data.datasets[point._datasetIndex]; |
|
3143
|
|
|
var index = point._index; |
|
3144
|
|
|
var custom = point.custom || {}; |
|
3145
|
|
|
var model = point._model; |
|
3146
|
|
|
|
|
3147
|
|
|
// Compatibility: If the properties are defined with only the old name, use those values |
|
3148
|
|
|
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
|
3149
|
|
|
dataset.pointRadius = dataset.radius; |
|
3150
|
|
|
} |
|
3151
|
|
|
|
|
3152
|
|
|
model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); |
|
3153
|
|
|
model.backgroundColor = me.getPointBackgroundColor(point, index); |
|
3154
|
|
|
model.borderColor = me.getPointBorderColor(point, index); |
|
3155
|
|
|
model.borderWidth = me.getPointBorderWidth(point, index); |
|
3156
|
|
|
} |
|
3157
|
|
|
}); |
|
3158
|
|
|
}; |
|
3159
|
|
|
|
|
3160
|
|
|
},{}],19:[function(require,module,exports){ |
|
3161
|
|
|
"use strict"; |
|
3162
|
|
|
|
|
3163
|
|
|
module.exports = function(Chart) { |
|
3164
|
|
|
|
|
3165
|
|
|
var helpers = Chart.helpers; |
|
3166
|
|
|
|
|
3167
|
|
|
Chart.defaults.polarArea = { |
|
3168
|
|
|
|
|
3169
|
|
|
scale: { |
|
3170
|
|
|
type: "radialLinear", |
|
3171
|
|
|
lineArc: true, // so that lines are circular |
|
3172
|
|
|
ticks: { |
|
3173
|
|
|
beginAtZero: true |
|
3174
|
|
|
} |
|
3175
|
|
|
}, |
|
3176
|
|
|
|
|
3177
|
|
|
//Boolean - Whether to animate the rotation of the chart |
|
3178
|
|
|
animation: { |
|
3179
|
|
|
animateRotate: true, |
|
3180
|
|
|
animateScale: true |
|
3181
|
|
|
}, |
|
3182
|
|
|
|
|
3183
|
|
|
startAngle: -0.5 * Math.PI, |
|
3184
|
|
|
aspectRatio: 1, |
|
3185
|
|
|
legendCallback: function(chart) { |
|
3186
|
|
|
var text = []; |
|
3187
|
|
|
text.push('<ul class="' + chart.id + '-legend">'); |
|
3188
|
|
|
|
|
3189
|
|
|
var data = chart.data; |
|
3190
|
|
|
var datasets = data.datasets; |
|
3191
|
|
|
var labels = data.labels; |
|
3192
|
|
|
|
|
3193
|
|
|
if (datasets.length) { |
|
3194
|
|
|
for (var i = 0; i < datasets[0].data.length; ++i) { |
|
3195
|
|
|
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '">'); |
|
3196
|
|
|
if (labels[i]) { |
|
3197
|
|
|
text.push(labels[i]); |
|
3198
|
|
|
} |
|
3199
|
|
|
text.push('</span></li>'); |
|
3200
|
|
|
} |
|
3201
|
|
|
} |
|
3202
|
|
|
|
|
3203
|
|
|
text.push('</ul>'); |
|
3204
|
|
|
return text.join(""); |
|
3205
|
|
|
}, |
|
3206
|
|
|
legend: { |
|
3207
|
|
|
labels: { |
|
3208
|
|
|
generateLabels: function(chart) { |
|
|
|
|
|
|
3209
|
|
|
var data = chart.data; |
|
3210
|
|
|
if (data.labels.length && data.datasets.length) { |
|
3211
|
|
|
return data.labels.map(function(label, i) { |
|
3212
|
|
|
var meta = chart.getDatasetMeta(0); |
|
3213
|
|
|
var ds = data.datasets[0]; |
|
3214
|
|
|
var arc = meta.data[i]; |
|
3215
|
|
|
var custom = arc.custom || {}; |
|
3216
|
|
|
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
|
3217
|
|
|
var arcOpts = chart.options.elements.arc; |
|
3218
|
|
|
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); |
|
3219
|
|
|
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); |
|
3220
|
|
|
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); |
|
3221
|
|
|
|
|
3222
|
|
|
return { |
|
3223
|
|
|
text: label, |
|
3224
|
|
|
fillStyle: fill, |
|
3225
|
|
|
strokeStyle: stroke, |
|
3226
|
|
|
lineWidth: bw, |
|
3227
|
|
|
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, |
|
3228
|
|
|
|
|
3229
|
|
|
// Extra data used for toggling the correct item |
|
3230
|
|
|
index: i |
|
3231
|
|
|
}; |
|
3232
|
|
|
}); |
|
3233
|
|
|
} else { |
|
3234
|
|
|
return []; |
|
3235
|
|
|
} |
|
3236
|
|
|
} |
|
3237
|
|
|
}, |
|
3238
|
|
|
|
|
3239
|
|
|
onClick: function(e, legendItem) { |
|
3240
|
|
|
var index = legendItem.index; |
|
3241
|
|
|
var chart = this.chart; |
|
3242
|
|
|
var i, ilen, meta; |
|
3243
|
|
|
|
|
3244
|
|
|
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
|
3245
|
|
|
meta = chart.getDatasetMeta(i); |
|
3246
|
|
|
meta.data[index].hidden = !meta.data[index].hidden; |
|
3247
|
|
|
} |
|
3248
|
|
|
|
|
3249
|
|
|
chart.update(); |
|
3250
|
|
|
} |
|
3251
|
|
|
}, |
|
3252
|
|
|
|
|
3253
|
|
|
// Need to override these to give a nice default |
|
3254
|
|
|
tooltips: { |
|
3255
|
|
|
callbacks: { |
|
3256
|
|
|
title: function() { |
|
3257
|
|
|
return ''; |
|
3258
|
|
|
}, |
|
3259
|
|
|
label: function(tooltipItem, data) { |
|
3260
|
|
|
return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; |
|
3261
|
|
|
} |
|
3262
|
|
|
} |
|
3263
|
|
|
} |
|
3264
|
|
|
}; |
|
3265
|
|
|
|
|
3266
|
|
|
Chart.controllers.polarArea = Chart.DatasetController.extend({ |
|
3267
|
|
|
|
|
3268
|
|
|
dataElementType: Chart.elements.Arc, |
|
3269
|
|
|
|
|
3270
|
|
|
linkScales: helpers.noop, |
|
3271
|
|
|
|
|
3272
|
|
|
update: function(reset) { |
|
3273
|
|
|
var me = this; |
|
3274
|
|
|
var chart = me.chart; |
|
3275
|
|
|
var chartArea = chart.chartArea; |
|
3276
|
|
|
var meta = me.getMeta(); |
|
3277
|
|
|
var opts = chart.options; |
|
3278
|
|
|
var arcOpts = opts.elements.arc; |
|
3279
|
|
|
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); |
|
3280
|
|
|
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); |
|
3281
|
|
|
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); |
|
3282
|
|
|
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); |
|
3283
|
|
|
|
|
3284
|
|
|
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); |
|
3285
|
|
|
me.innerRadius = me.outerRadius - chart.radiusLength; |
|
3286
|
|
|
|
|
3287
|
|
|
meta.count = me.countVisibleElements(); |
|
3288
|
|
|
|
|
3289
|
|
|
helpers.each(meta.data, function(arc, index) { |
|
3290
|
|
|
me.updateElement(arc, index, reset); |
|
3291
|
|
|
}); |
|
3292
|
|
|
}, |
|
3293
|
|
|
|
|
3294
|
|
|
updateElement: function(arc, index, reset) { |
|
3295
|
|
|
var me = this; |
|
3296
|
|
|
var chart = me.chart; |
|
3297
|
|
|
var dataset = me.getDataset(); |
|
3298
|
|
|
var opts = chart.options; |
|
3299
|
|
|
var animationOpts = opts.animation; |
|
3300
|
|
|
var scale = chart.scale; |
|
3301
|
|
|
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
|
3302
|
|
|
var labels = chart.data.labels; |
|
3303
|
|
|
|
|
3304
|
|
|
var circumference = me.calculateCircumference(dataset.data[index]); |
|
3305
|
|
|
var centerX = scale.xCenter; |
|
3306
|
|
|
var centerY = scale.yCenter; |
|
3307
|
|
|
|
|
3308
|
|
|
// If there is NaN data before us, we need to calculate the starting angle correctly. |
|
3309
|
|
|
// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data |
|
3310
|
|
|
var visibleCount = 0; |
|
3311
|
|
|
var meta = me.getMeta(); |
|
3312
|
|
|
for (var i = 0; i < index; ++i) { |
|
3313
|
|
|
if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { |
|
3314
|
|
|
++visibleCount; |
|
3315
|
|
|
} |
|
3316
|
|
|
} |
|
3317
|
|
|
|
|
3318
|
|
|
//var negHalfPI = -0.5 * Math.PI; |
|
3319
|
|
|
var datasetStartAngle = opts.startAngle; |
|
3320
|
|
|
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); |
|
3321
|
|
|
var startAngle = datasetStartAngle + (circumference * visibleCount); |
|
3322
|
|
|
var endAngle = startAngle + (arc.hidden ? 0 : circumference); |
|
3323
|
|
|
|
|
3324
|
|
|
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); |
|
3325
|
|
|
|
|
3326
|
|
|
helpers.extend(arc, { |
|
3327
|
|
|
// Utility |
|
3328
|
|
|
_datasetIndex: me.index, |
|
3329
|
|
|
_index: index, |
|
3330
|
|
|
_scale: scale, |
|
3331
|
|
|
|
|
3332
|
|
|
// Desired view properties |
|
3333
|
|
|
_model: { |
|
3334
|
|
|
x: centerX, |
|
3335
|
|
|
y: centerY, |
|
3336
|
|
|
innerRadius: 0, |
|
3337
|
|
|
outerRadius: reset ? resetRadius : distance, |
|
3338
|
|
|
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, |
|
3339
|
|
|
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, |
|
3340
|
|
|
label: getValueAtIndexOrDefault(labels, index, labels[index]) |
|
3341
|
|
|
} |
|
3342
|
|
|
}); |
|
3343
|
|
|
|
|
3344
|
|
|
// Apply border and fill style |
|
3345
|
|
|
me.removeHoverStyle(arc); |
|
3346
|
|
|
|
|
3347
|
|
|
arc.pivot(); |
|
3348
|
|
|
}, |
|
3349
|
|
|
|
|
3350
|
|
|
removeHoverStyle: function(arc) { |
|
3351
|
|
|
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); |
|
3352
|
|
|
}, |
|
3353
|
|
|
|
|
3354
|
|
|
countVisibleElements: function() { |
|
3355
|
|
|
var dataset = this.getDataset(); |
|
3356
|
|
|
var meta = this.getMeta(); |
|
3357
|
|
|
var count = 0; |
|
3358
|
|
|
|
|
3359
|
|
|
helpers.each(meta.data, function(element, index) { |
|
3360
|
|
|
if (!isNaN(dataset.data[index]) && !element.hidden) { |
|
3361
|
|
|
count++; |
|
3362
|
|
|
} |
|
3363
|
|
|
}); |
|
3364
|
|
|
|
|
3365
|
|
|
return count; |
|
3366
|
|
|
}, |
|
3367
|
|
|
|
|
3368
|
|
|
calculateCircumference: function(value) { |
|
3369
|
|
|
var count = this.getMeta().count; |
|
3370
|
|
|
if (count > 0 && !isNaN(value)) { |
|
3371
|
|
|
return (2 * Math.PI) / count; |
|
3372
|
|
|
} else { |
|
3373
|
|
|
return 0; |
|
3374
|
|
|
} |
|
3375
|
|
|
} |
|
3376
|
|
|
}); |
|
3377
|
|
|
}; |
|
3378
|
|
|
|
|
3379
|
|
|
},{}],20:[function(require,module,exports){ |
|
3380
|
|
|
"use strict"; |
|
3381
|
|
|
|
|
3382
|
|
|
module.exports = function(Chart) { |
|
3383
|
|
|
|
|
3384
|
|
|
var helpers = Chart.helpers; |
|
3385
|
|
|
|
|
3386
|
|
|
Chart.defaults.radar = { |
|
3387
|
|
|
scale: { |
|
3388
|
|
|
type: "radialLinear" |
|
3389
|
|
|
}, |
|
3390
|
|
|
elements: { |
|
3391
|
|
|
line: { |
|
3392
|
|
|
tension: 0 // no bezier in radar |
|
3393
|
|
|
} |
|
3394
|
|
|
} |
|
3395
|
|
|
}; |
|
3396
|
|
|
|
|
3397
|
|
|
Chart.controllers.radar = Chart.DatasetController.extend({ |
|
3398
|
|
|
|
|
3399
|
|
|
datasetElementType: Chart.elements.Line, |
|
3400
|
|
|
|
|
3401
|
|
|
dataElementType: Chart.elements.Point, |
|
3402
|
|
|
|
|
3403
|
|
|
linkScales: helpers.noop, |
|
3404
|
|
|
|
|
3405
|
|
|
addElementAndReset: function(index) { |
|
3406
|
|
|
Chart.DatasetController.prototype.addElementAndReset.call(this, index); |
|
3407
|
|
|
|
|
3408
|
|
|
// Make sure bezier control points are updated |
|
3409
|
|
|
this.updateBezierControlPoints(); |
|
3410
|
|
|
}, |
|
3411
|
|
|
|
|
3412
|
|
|
update: function(reset) { |
|
3413
|
|
|
var me = this; |
|
3414
|
|
|
var meta = me.getMeta(); |
|
3415
|
|
|
var line = meta.dataset; |
|
3416
|
|
|
var points = meta.data; |
|
3417
|
|
|
var custom = line.custom || {}; |
|
3418
|
|
|
var dataset = me.getDataset(); |
|
3419
|
|
|
var lineElementOptions = me.chart.options.elements.line; |
|
3420
|
|
|
var scale = me.chart.scale; |
|
3421
|
|
|
|
|
3422
|
|
|
// Compatibility: If the properties are defined with only the old name, use those values |
|
3423
|
|
|
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { |
|
3424
|
|
|
dataset.lineTension = dataset.tension; |
|
3425
|
|
|
} |
|
3426
|
|
|
|
|
3427
|
|
|
helpers.extend(meta.dataset, { |
|
3428
|
|
|
// Utility |
|
3429
|
|
|
_datasetIndex: me.index, |
|
3430
|
|
|
// Data |
|
3431
|
|
|
_children: points, |
|
3432
|
|
|
_loop: true, |
|
3433
|
|
|
// Model |
|
3434
|
|
|
_model: { |
|
3435
|
|
|
// Appearance |
|
3436
|
|
|
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), |
|
3437
|
|
|
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), |
|
3438
|
|
|
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), |
|
3439
|
|
|
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), |
|
3440
|
|
|
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), |
|
3441
|
|
|
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), |
|
3442
|
|
|
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), |
|
3443
|
|
|
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), |
|
3444
|
|
|
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), |
|
3445
|
|
|
|
|
3446
|
|
|
// Scale |
|
3447
|
|
|
scaleTop: scale.top, |
|
3448
|
|
|
scaleBottom: scale.bottom, |
|
3449
|
|
|
scaleZero: scale.getBasePosition() |
|
3450
|
|
|
} |
|
3451
|
|
|
}); |
|
3452
|
|
|
|
|
3453
|
|
|
meta.dataset.pivot(); |
|
3454
|
|
|
|
|
3455
|
|
|
// Update Points |
|
3456
|
|
|
helpers.each(points, function(point, index) { |
|
3457
|
|
|
me.updateElement(point, index, reset); |
|
3458
|
|
|
}, me); |
|
3459
|
|
|
|
|
3460
|
|
|
|
|
3461
|
|
|
// Update bezier control points |
|
3462
|
|
|
me.updateBezierControlPoints(); |
|
3463
|
|
|
}, |
|
3464
|
|
|
updateElement: function(point, index, reset) { |
|
3465
|
|
|
var me = this; |
|
3466
|
|
|
var custom = point.custom || {}; |
|
3467
|
|
|
var dataset = me.getDataset(); |
|
3468
|
|
|
var scale = me.chart.scale; |
|
3469
|
|
|
var pointElementOptions = me.chart.options.elements.point; |
|
3470
|
|
|
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); |
|
3471
|
|
|
|
|
3472
|
|
|
helpers.extend(point, { |
|
3473
|
|
|
// Utility |
|
3474
|
|
|
_datasetIndex: me.index, |
|
3475
|
|
|
_index: index, |
|
3476
|
|
|
_scale: scale, |
|
3477
|
|
|
|
|
3478
|
|
|
// Desired view properties |
|
3479
|
|
|
_model: { |
|
3480
|
|
|
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales |
|
3481
|
|
|
y: reset ? scale.yCenter : pointPosition.y, |
|
3482
|
|
|
|
|
3483
|
|
|
// Appearance |
|
3484
|
|
|
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension), |
|
3485
|
|
|
radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), |
|
3486
|
|
|
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), |
|
3487
|
|
|
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), |
|
3488
|
|
|
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), |
|
3489
|
|
|
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), |
|
3490
|
|
|
|
|
3491
|
|
|
// Tooltip |
|
3492
|
|
|
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) |
|
3493
|
|
|
} |
|
3494
|
|
|
}); |
|
3495
|
|
|
|
|
3496
|
|
|
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); |
|
3497
|
|
|
}, |
|
3498
|
|
|
updateBezierControlPoints: function() { |
|
3499
|
|
|
var chartArea = this.chart.chartArea; |
|
3500
|
|
|
var meta = this.getMeta(); |
|
3501
|
|
|
|
|
3502
|
|
|
helpers.each(meta.data, function(point, index) { |
|
3503
|
|
|
var model = point._model; |
|
3504
|
|
|
var controlPoints = helpers.splineCurve( |
|
3505
|
|
|
helpers.previousItem(meta.data, index, true)._model, |
|
3506
|
|
|
model, |
|
3507
|
|
|
helpers.nextItem(meta.data, index, true)._model, |
|
3508
|
|
|
model.tension |
|
3509
|
|
|
); |
|
3510
|
|
|
|
|
3511
|
|
|
// Prevent the bezier going outside of the bounds of the graph |
|
3512
|
|
|
model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); |
|
3513
|
|
|
model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); |
|
3514
|
|
|
|
|
3515
|
|
|
model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); |
|
3516
|
|
|
model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); |
|
3517
|
|
|
|
|
3518
|
|
|
// Now pivot the point for animation |
|
3519
|
|
|
point.pivot(); |
|
3520
|
|
|
}); |
|
3521
|
|
|
}, |
|
3522
|
|
|
|
|
3523
|
|
|
draw: function(ease) { |
|
3524
|
|
|
var meta = this.getMeta(); |
|
3525
|
|
|
var easingDecimal = ease || 1; |
|
3526
|
|
|
|
|
3527
|
|
|
// Transition Point Locations |
|
3528
|
|
|
helpers.each(meta.data, function(point) { |
|
3529
|
|
|
point.transition(easingDecimal); |
|
3530
|
|
|
}); |
|
3531
|
|
|
|
|
3532
|
|
|
// Transition and Draw the line |
|
3533
|
|
|
meta.dataset.transition(easingDecimal).draw(); |
|
3534
|
|
|
|
|
3535
|
|
|
// Draw the points |
|
3536
|
|
|
helpers.each(meta.data, function(point) { |
|
3537
|
|
|
point.draw(); |
|
3538
|
|
|
}); |
|
3539
|
|
|
}, |
|
3540
|
|
|
|
|
3541
|
|
|
setHoverStyle: function(point) { |
|
3542
|
|
|
// Point |
|
3543
|
|
|
var dataset = this.chart.data.datasets[point._datasetIndex]; |
|
3544
|
|
|
var custom = point.custom || {}; |
|
3545
|
|
|
var index = point._index; |
|
3546
|
|
|
var model = point._model; |
|
3547
|
|
|
|
|
3548
|
|
|
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); |
|
3549
|
|
|
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
|
3550
|
|
|
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
|
3551
|
|
|
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); |
|
3552
|
|
|
}, |
|
3553
|
|
|
|
|
3554
|
|
|
removeHoverStyle: function(point) { |
|
3555
|
|
|
var dataset = this.chart.data.datasets[point._datasetIndex]; |
|
3556
|
|
|
var custom = point.custom || {}; |
|
3557
|
|
|
var index = point._index; |
|
3558
|
|
|
var model = point._model; |
|
3559
|
|
|
var pointElementOptions = this.chart.options.elements.point; |
|
3560
|
|
|
|
|
3561
|
|
|
model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius); |
|
3562
|
|
|
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); |
|
3563
|
|
|
model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); |
|
3564
|
|
|
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); |
|
3565
|
|
|
} |
|
3566
|
|
|
}); |
|
3567
|
|
|
}; |
|
3568
|
|
|
|
|
3569
|
|
|
},{}],21:[function(require,module,exports){ |
|
3570
|
|
|
/*global window: false */ |
|
3571
|
|
|
"use strict"; |
|
3572
|
|
|
|
|
3573
|
|
|
module.exports = function(Chart) { |
|
3574
|
|
|
|
|
3575
|
|
|
var helpers = Chart.helpers; |
|
3576
|
|
|
|
|
3577
|
|
|
Chart.defaults.global.animation = { |
|
3578
|
|
|
duration: 1000, |
|
3579
|
|
|
easing: "easeOutQuart", |
|
3580
|
|
|
onProgress: helpers.noop, |
|
3581
|
|
|
onComplete: helpers.noop |
|
3582
|
|
|
}; |
|
3583
|
|
|
|
|
3584
|
|
|
Chart.Animation = Chart.Element.extend({ |
|
3585
|
|
|
currentStep: null, // the current animation step |
|
3586
|
|
|
numSteps: 60, // default number of steps |
|
3587
|
|
|
easing: "", // the easing to use for this animation |
|
3588
|
|
|
render: null, // render function used by the animation service |
|
3589
|
|
|
|
|
3590
|
|
|
onAnimationProgress: null, // user specified callback to fire on each step of the animation |
|
3591
|
|
|
onAnimationComplete: null // user specified callback to fire when the animation finishes |
|
3592
|
|
|
}); |
|
3593
|
|
|
|
|
3594
|
|
|
Chart.animationService = { |
|
3595
|
|
|
frameDuration: 17, |
|
3596
|
|
|
animations: [], |
|
3597
|
|
|
dropFrames: 0, |
|
3598
|
|
|
request: null, |
|
3599
|
|
|
addAnimation: function(chartInstance, animationObject, duration, lazy) { |
|
3600
|
|
|
var me = this; |
|
3601
|
|
|
|
|
3602
|
|
|
if (!lazy) { |
|
3603
|
|
|
chartInstance.animating = true; |
|
3604
|
|
|
} |
|
3605
|
|
|
|
|
3606
|
|
|
for (var index = 0; index < me.animations.length; ++index) { |
|
3607
|
|
|
if (me.animations[index].chartInstance === chartInstance) { |
|
3608
|
|
|
// replacing an in progress animation |
|
3609
|
|
|
me.animations[index].animationObject = animationObject; |
|
3610
|
|
|
return; |
|
3611
|
|
|
} |
|
3612
|
|
|
} |
|
3613
|
|
|
|
|
3614
|
|
|
me.animations.push({ |
|
3615
|
|
|
chartInstance: chartInstance, |
|
3616
|
|
|
animationObject: animationObject |
|
3617
|
|
|
}); |
|
3618
|
|
|
|
|
3619
|
|
|
// If there are no animations queued, manually kickstart a digest, for lack of a better word |
|
3620
|
|
|
if (me.animations.length === 1) { |
|
3621
|
|
|
me.requestAnimationFrame(); |
|
3622
|
|
|
} |
|
3623
|
|
|
}, |
|
3624
|
|
|
// Cancel the animation for a given chart instance |
|
3625
|
|
|
cancelAnimation: function(chartInstance) { |
|
3626
|
|
|
var index = helpers.findIndex(this.animations, function(animationWrapper) { |
|
3627
|
|
|
return animationWrapper.chartInstance === chartInstance; |
|
3628
|
|
|
}); |
|
3629
|
|
|
|
|
3630
|
|
|
if (index !== -1) { |
|
3631
|
|
|
this.animations.splice(index, 1); |
|
3632
|
|
|
chartInstance.animating = false; |
|
3633
|
|
|
} |
|
3634
|
|
|
}, |
|
3635
|
|
|
requestAnimationFrame: function() { |
|
3636
|
|
|
var me = this; |
|
3637
|
|
|
if (me.request === null) { |
|
3638
|
|
|
// Skip animation frame requests until the active one is executed. |
|
3639
|
|
|
// This can happen when processing mouse events, e.g. 'mousemove' |
|
3640
|
|
|
// and 'mouseout' events will trigger multiple renders. |
|
3641
|
|
|
me.request = helpers.requestAnimFrame.call(window, function() { |
|
3642
|
|
|
me.request = null; |
|
3643
|
|
|
me.startDigest(); |
|
3644
|
|
|
}); |
|
3645
|
|
|
} |
|
3646
|
|
|
}, |
|
3647
|
|
|
startDigest: function() { |
|
3648
|
|
|
var me = this; |
|
3649
|
|
|
|
|
3650
|
|
|
var startTime = Date.now(); |
|
3651
|
|
|
var framesToDrop = 0; |
|
3652
|
|
|
|
|
3653
|
|
|
if (me.dropFrames > 1) { |
|
3654
|
|
|
framesToDrop = Math.floor(me.dropFrames); |
|
3655
|
|
|
me.dropFrames = me.dropFrames % 1; |
|
3656
|
|
|
} |
|
3657
|
|
|
|
|
3658
|
|
|
var i = 0; |
|
3659
|
|
|
while (i < me.animations.length) { |
|
3660
|
|
|
if (me.animations[i].animationObject.currentStep === null) { |
|
3661
|
|
|
me.animations[i].animationObject.currentStep = 0; |
|
3662
|
|
|
} |
|
3663
|
|
|
|
|
3664
|
|
|
me.animations[i].animationObject.currentStep += 1 + framesToDrop; |
|
3665
|
|
|
|
|
3666
|
|
|
if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) { |
|
3667
|
|
|
me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps; |
|
3668
|
|
|
} |
|
3669
|
|
|
|
|
3670
|
|
|
me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject); |
|
3671
|
|
|
if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) { |
|
3672
|
|
|
me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]); |
|
3673
|
|
|
} |
|
3674
|
|
|
|
|
3675
|
|
|
if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) { |
|
3676
|
|
|
if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) { |
|
3677
|
|
|
me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]); |
|
3678
|
|
|
} |
|
3679
|
|
|
|
|
3680
|
|
|
// executed the last frame. Remove the animation. |
|
3681
|
|
|
me.animations[i].chartInstance.animating = false; |
|
3682
|
|
|
|
|
3683
|
|
|
me.animations.splice(i, 1); |
|
3684
|
|
|
} else { |
|
3685
|
|
|
++i; |
|
3686
|
|
|
} |
|
3687
|
|
|
} |
|
3688
|
|
|
|
|
3689
|
|
|
var endTime = Date.now(); |
|
3690
|
|
|
var dropFrames = (endTime - startTime) / me.frameDuration; |
|
3691
|
|
|
|
|
3692
|
|
|
me.dropFrames += dropFrames; |
|
3693
|
|
|
|
|
3694
|
|
|
// Do we have more stuff to animate? |
|
3695
|
|
|
if (me.animations.length > 0) { |
|
3696
|
|
|
me.requestAnimationFrame(); |
|
3697
|
|
|
} |
|
3698
|
|
|
} |
|
3699
|
|
|
}; |
|
3700
|
|
|
}; |
|
3701
|
|
|
},{}],22:[function(require,module,exports){ |
|
3702
|
|
|
"use strict"; |
|
3703
|
|
|
|
|
3704
|
|
|
module.exports = function(Chart) { |
|
3705
|
|
|
// Global Chart canvas helpers object for drawing items to canvas |
|
3706
|
|
|
var helpers = Chart.canvasHelpers = {}; |
|
3707
|
|
|
|
|
3708
|
|
|
helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { |
|
3709
|
|
|
var type, edgeLength, xOffset, yOffset, height, size; |
|
3710
|
|
|
|
|
3711
|
|
|
if (typeof pointStyle === 'object') { |
|
3712
|
|
|
type = pointStyle.toString(); |
|
3713
|
|
|
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { |
|
3714
|
|
|
ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); |
|
3715
|
|
|
return; |
|
3716
|
|
|
} |
|
3717
|
|
|
} |
|
3718
|
|
|
|
|
3719
|
|
|
if (isNaN(radius) || radius <= 0) { |
|
3720
|
|
|
return; |
|
3721
|
|
|
} |
|
3722
|
|
|
|
|
3723
|
|
|
switch (pointStyle) { |
|
3724
|
|
|
// Default includes circle |
|
3725
|
|
|
default: |
|
|
|
|
|
|
3726
|
|
|
ctx.beginPath(); |
|
3727
|
|
|
ctx.arc(x, y, radius, 0, Math.PI * 2); |
|
3728
|
|
|
ctx.closePath(); |
|
3729
|
|
|
ctx.fill(); |
|
3730
|
|
|
break; |
|
3731
|
|
|
case 'triangle': |
|
3732
|
|
|
ctx.beginPath(); |
|
3733
|
|
|
edgeLength = 3 * radius / Math.sqrt(3); |
|
3734
|
|
|
height = edgeLength * Math.sqrt(3) / 2; |
|
3735
|
|
|
ctx.moveTo(x - edgeLength / 2, y + height / 3); |
|
3736
|
|
|
ctx.lineTo(x + edgeLength / 2, y + height / 3); |
|
3737
|
|
|
ctx.lineTo(x, y - 2 * height / 3); |
|
3738
|
|
|
ctx.closePath(); |
|
3739
|
|
|
ctx.fill(); |
|
3740
|
|
|
break; |
|
3741
|
|
|
case 'rect': |
|
3742
|
|
|
size = 1 / Math.SQRT2 * radius; |
|
3743
|
|
|
ctx.beginPath(); |
|
3744
|
|
|
ctx.fillRect(x - size, y - size, 2 * size, 2 * size); |
|
3745
|
|
|
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); |
|
3746
|
|
|
break; |
|
3747
|
|
|
case 'rectRot': |
|
3748
|
|
|
size = 1 / Math.SQRT2 * radius; |
|
3749
|
|
|
ctx.beginPath(); |
|
3750
|
|
|
ctx.moveTo(x - size, y); |
|
3751
|
|
|
ctx.lineTo(x, y + size); |
|
3752
|
|
|
ctx.lineTo(x + size, y); |
|
3753
|
|
|
ctx.lineTo(x, y - size); |
|
3754
|
|
|
ctx.closePath(); |
|
3755
|
|
|
ctx.fill(); |
|
3756
|
|
|
break; |
|
3757
|
|
|
case 'cross': |
|
3758
|
|
|
ctx.beginPath(); |
|
3759
|
|
|
ctx.moveTo(x, y + radius); |
|
3760
|
|
|
ctx.lineTo(x, y - radius); |
|
3761
|
|
|
ctx.moveTo(x - radius, y); |
|
3762
|
|
|
ctx.lineTo(x + radius, y); |
|
3763
|
|
|
ctx.closePath(); |
|
3764
|
|
|
break; |
|
3765
|
|
|
case 'crossRot': |
|
3766
|
|
|
ctx.beginPath(); |
|
3767
|
|
|
xOffset = Math.cos(Math.PI / 4) * radius; |
|
3768
|
|
|
yOffset = Math.sin(Math.PI / 4) * radius; |
|
3769
|
|
|
ctx.moveTo(x - xOffset, y - yOffset); |
|
3770
|
|
|
ctx.lineTo(x + xOffset, y + yOffset); |
|
3771
|
|
|
ctx.moveTo(x - xOffset, y + yOffset); |
|
3772
|
|
|
ctx.lineTo(x + xOffset, y - yOffset); |
|
3773
|
|
|
ctx.closePath(); |
|
3774
|
|
|
break; |
|
3775
|
|
|
case 'star': |
|
3776
|
|
|
ctx.beginPath(); |
|
3777
|
|
|
ctx.moveTo(x, y + radius); |
|
3778
|
|
|
ctx.lineTo(x, y - radius); |
|
3779
|
|
|
ctx.moveTo(x - radius, y); |
|
3780
|
|
|
ctx.lineTo(x + radius, y); |
|
3781
|
|
|
xOffset = Math.cos(Math.PI / 4) * radius; |
|
3782
|
|
|
yOffset = Math.sin(Math.PI / 4) * radius; |
|
3783
|
|
|
ctx.moveTo(x - xOffset, y - yOffset); |
|
3784
|
|
|
ctx.lineTo(x + xOffset, y + yOffset); |
|
3785
|
|
|
ctx.moveTo(x - xOffset, y + yOffset); |
|
3786
|
|
|
ctx.lineTo(x + xOffset, y - yOffset); |
|
3787
|
|
|
ctx.closePath(); |
|
3788
|
|
|
break; |
|
3789
|
|
|
case 'line': |
|
3790
|
|
|
ctx.beginPath(); |
|
3791
|
|
|
ctx.moveTo(x - radius, y); |
|
3792
|
|
|
ctx.lineTo(x + radius, y); |
|
3793
|
|
|
ctx.closePath(); |
|
3794
|
|
|
break; |
|
3795
|
|
|
case 'dash': |
|
3796
|
|
|
ctx.beginPath(); |
|
3797
|
|
|
ctx.moveTo(x, y); |
|
3798
|
|
|
ctx.lineTo(x + radius, y); |
|
3799
|
|
|
ctx.closePath(); |
|
3800
|
|
|
break; |
|
3801
|
|
|
} |
|
3802
|
|
|
|
|
3803
|
|
|
ctx.stroke(); |
|
3804
|
|
|
}; |
|
3805
|
|
|
}; |
|
3806
|
|
|
},{}],23:[function(require,module,exports){ |
|
3807
|
|
|
"use strict"; |
|
3808
|
|
|
|
|
3809
|
|
|
module.exports = function(Chart) { |
|
3810
|
|
|
|
|
3811
|
|
|
var helpers = Chart.helpers; |
|
3812
|
|
|
//Create a dictionary of chart types, to allow for extension of existing types |
|
3813
|
|
|
Chart.types = {}; |
|
3814
|
|
|
|
|
3815
|
|
|
//Store a reference to each instance - allowing us to globally resize chart instances on window resize. |
|
3816
|
|
|
//Destroy method on the chart will remove the instance of the chart from this reference. |
|
3817
|
|
|
Chart.instances = {}; |
|
3818
|
|
|
|
|
3819
|
|
|
// Controllers available for dataset visualization eg. bar, line, slice, etc. |
|
3820
|
|
|
Chart.controllers = {}; |
|
3821
|
|
|
|
|
3822
|
|
|
/** |
|
3823
|
|
|
* @class Chart.Controller |
|
3824
|
|
|
* The main controller of a chart. |
|
3825
|
|
|
*/ |
|
3826
|
|
|
Chart.Controller = function(instance) { |
|
3827
|
|
|
|
|
3828
|
|
|
this.chart = instance; |
|
3829
|
|
|
this.config = instance.config; |
|
3830
|
|
|
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {}); |
|
3831
|
|
|
this.id = helpers.uid(); |
|
3832
|
|
|
|
|
3833
|
|
|
Object.defineProperty(this, 'data', { |
|
3834
|
|
|
get: function() { |
|
3835
|
|
|
return this.config.data; |
|
3836
|
|
|
} |
|
3837
|
|
|
}); |
|
3838
|
|
|
|
|
3839
|
|
|
//Add the chart instance to the global namespace |
|
3840
|
|
|
Chart.instances[this.id] = this; |
|
3841
|
|
|
|
|
3842
|
|
|
if (this.options.responsive) { |
|
3843
|
|
|
// Silent resize before chart draws |
|
3844
|
|
|
this.resize(true); |
|
3845
|
|
|
} |
|
3846
|
|
|
|
|
3847
|
|
|
this.initialize(); |
|
3848
|
|
|
|
|
3849
|
|
|
return this; |
|
3850
|
|
|
}; |
|
3851
|
|
|
|
|
3852
|
|
|
helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ { |
|
3853
|
|
|
|
|
3854
|
|
|
initialize: function() { |
|
3855
|
|
|
var me = this; |
|
3856
|
|
|
// Before init plugin notification |
|
3857
|
|
|
Chart.plugins.notify('beforeInit', [me]); |
|
3858
|
|
|
|
|
3859
|
|
|
me.bindEvents(); |
|
3860
|
|
|
|
|
3861
|
|
|
// Make sure controllers are built first so that each dataset is bound to an axis before the scales |
|
3862
|
|
|
// are built |
|
3863
|
|
|
me.ensureScalesHaveIDs(); |
|
3864
|
|
|
me.buildOrUpdateControllers(); |
|
3865
|
|
|
me.buildScales(); |
|
3866
|
|
|
me.updateLayout(); |
|
3867
|
|
|
me.resetElements(); |
|
3868
|
|
|
me.initToolTip(); |
|
3869
|
|
|
me.update(); |
|
3870
|
|
|
|
|
3871
|
|
|
// After init plugin notification |
|
3872
|
|
|
Chart.plugins.notify('afterInit', [me]); |
|
3873
|
|
|
|
|
3874
|
|
|
return me; |
|
3875
|
|
|
}, |
|
3876
|
|
|
|
|
3877
|
|
|
clear: function() { |
|
3878
|
|
|
helpers.clear(this.chart); |
|
3879
|
|
|
return this; |
|
3880
|
|
|
}, |
|
3881
|
|
|
|
|
3882
|
|
|
stop: function() { |
|
3883
|
|
|
// Stops any current animation loop occuring |
|
3884
|
|
|
Chart.animationService.cancelAnimation(this); |
|
3885
|
|
|
return this; |
|
3886
|
|
|
}, |
|
3887
|
|
|
|
|
3888
|
|
|
resize: function resize(silent) { |
|
3889
|
|
|
var me = this; |
|
3890
|
|
|
var chart = me.chart; |
|
3891
|
|
|
var canvas = chart.canvas; |
|
3892
|
|
|
var newWidth = helpers.getMaximumWidth(canvas); |
|
3893
|
|
|
var aspectRatio = chart.aspectRatio; |
|
3894
|
|
|
var newHeight = (me.options.maintainAspectRatio && isNaN(aspectRatio) === false && isFinite(aspectRatio) && aspectRatio !== 0) ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas); |
|
3895
|
|
|
|
|
3896
|
|
|
var sizeChanged = chart.width !== newWidth || chart.height !== newHeight; |
|
3897
|
|
|
|
|
3898
|
|
|
if (!sizeChanged) { |
|
3899
|
|
|
return me; |
|
3900
|
|
|
} |
|
3901
|
|
|
|
|
3902
|
|
|
canvas.width = chart.width = newWidth; |
|
3903
|
|
|
canvas.height = chart.height = newHeight; |
|
3904
|
|
|
|
|
3905
|
|
|
helpers.retinaScale(chart); |
|
3906
|
|
|
|
|
3907
|
|
|
// Notify any plugins about the resize |
|
3908
|
|
|
var newSize = { width: newWidth, height: newHeight }; |
|
3909
|
|
|
Chart.plugins.notify('resize', [me, newSize]); |
|
3910
|
|
|
|
|
3911
|
|
|
// Notify of resize |
|
3912
|
|
|
if (me.options.onResize) { |
|
3913
|
|
|
me.options.onResize(me, newSize); |
|
3914
|
|
|
} |
|
3915
|
|
|
|
|
3916
|
|
|
if (!silent) { |
|
3917
|
|
|
me.stop(); |
|
3918
|
|
|
me.update(me.options.responsiveAnimationDuration); |
|
3919
|
|
|
} |
|
3920
|
|
|
|
|
3921
|
|
|
return me; |
|
3922
|
|
|
}, |
|
3923
|
|
|
|
|
3924
|
|
|
ensureScalesHaveIDs: function() { |
|
3925
|
|
|
var options = this.options; |
|
3926
|
|
|
var scalesOptions = options.scales || {}; |
|
3927
|
|
|
var scaleOptions = options.scale; |
|
3928
|
|
|
|
|
3929
|
|
|
helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { |
|
3930
|
|
|
xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); |
|
3931
|
|
|
}); |
|
3932
|
|
|
|
|
3933
|
|
|
helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { |
|
3934
|
|
|
yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); |
|
3935
|
|
|
}); |
|
3936
|
|
|
|
|
3937
|
|
|
if (scaleOptions) { |
|
3938
|
|
|
scaleOptions.id = scaleOptions.id || 'scale'; |
|
3939
|
|
|
} |
|
3940
|
|
|
}, |
|
3941
|
|
|
|
|
3942
|
|
|
/** |
|
3943
|
|
|
* Builds a map of scale ID to scale object for future lookup. |
|
3944
|
|
|
*/ |
|
3945
|
|
|
buildScales: function() { |
|
3946
|
|
|
var me = this; |
|
3947
|
|
|
var options = me.options; |
|
3948
|
|
|
var scales = me.scales = {}; |
|
3949
|
|
|
var items = []; |
|
3950
|
|
|
|
|
3951
|
|
|
if (options.scales) { |
|
3952
|
|
|
items = items.concat( |
|
3953
|
|
|
(options.scales.xAxes || []).map(function(xAxisOptions) { |
|
3954
|
|
|
return { options: xAxisOptions, dtype: 'category' }; }), |
|
3955
|
|
|
(options.scales.yAxes || []).map(function(yAxisOptions) { |
|
3956
|
|
|
return { options: yAxisOptions, dtype: 'linear' }; })); |
|
3957
|
|
|
} |
|
3958
|
|
|
|
|
3959
|
|
|
if (options.scale) { |
|
3960
|
|
|
items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true }); |
|
3961
|
|
|
} |
|
3962
|
|
|
|
|
3963
|
|
|
helpers.each(items, function(item) { |
|
3964
|
|
|
var scaleOptions = item.options; |
|
3965
|
|
|
var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); |
|
3966
|
|
|
var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); |
|
3967
|
|
|
if (!scaleClass) { |
|
3968
|
|
|
return; |
|
3969
|
|
|
} |
|
3970
|
|
|
|
|
3971
|
|
|
var scale = new scaleClass({ |
|
|
|
|
|
|
3972
|
|
|
id: scaleOptions.id, |
|
3973
|
|
|
options: scaleOptions, |
|
3974
|
|
|
ctx: me.chart.ctx, |
|
3975
|
|
|
chart: me |
|
3976
|
|
|
}); |
|
3977
|
|
|
|
|
3978
|
|
|
scales[scale.id] = scale; |
|
3979
|
|
|
|
|
3980
|
|
|
// TODO(SB): I think we should be able to remove this custom case (options.scale) |
|
3981
|
|
|
// and consider it as a regular scale part of the "scales"" map only! This would |
|
3982
|
|
|
// make the logic easier and remove some useless? custom code. |
|
3983
|
|
|
if (item.isDefault) { |
|
3984
|
|
|
me.scale = scale; |
|
3985
|
|
|
} |
|
3986
|
|
|
}); |
|
3987
|
|
|
|
|
3988
|
|
|
Chart.scaleService.addScalesToLayout(this); |
|
3989
|
|
|
}, |
|
3990
|
|
|
|
|
3991
|
|
|
updateLayout: function() { |
|
3992
|
|
|
Chart.layoutService.update(this, this.chart.width, this.chart.height); |
|
3993
|
|
|
}, |
|
3994
|
|
|
|
|
3995
|
|
|
buildOrUpdateControllers: function() { |
|
3996
|
|
|
var me = this; |
|
3997
|
|
|
var types = []; |
|
3998
|
|
|
var newControllers = []; |
|
3999
|
|
|
|
|
4000
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4001
|
|
|
var meta = me.getDatasetMeta(datasetIndex); |
|
4002
|
|
|
if (!meta.type) { |
|
4003
|
|
|
meta.type = dataset.type || me.config.type; |
|
4004
|
|
|
} |
|
4005
|
|
|
|
|
4006
|
|
|
types.push(meta.type); |
|
4007
|
|
|
|
|
4008
|
|
|
if (meta.controller) { |
|
4009
|
|
|
meta.controller.updateIndex(datasetIndex); |
|
4010
|
|
|
} else { |
|
4011
|
|
|
meta.controller = new Chart.controllers[meta.type](me, datasetIndex); |
|
4012
|
|
|
newControllers.push(meta.controller); |
|
4013
|
|
|
} |
|
4014
|
|
|
}, me); |
|
4015
|
|
|
|
|
4016
|
|
|
if (types.length > 1) { |
|
4017
|
|
|
for (var i = 1; i < types.length; i++) { |
|
4018
|
|
|
if (types[i] !== types[i - 1]) { |
|
4019
|
|
|
me.isCombo = true; |
|
4020
|
|
|
break; |
|
4021
|
|
|
} |
|
4022
|
|
|
} |
|
4023
|
|
|
} |
|
4024
|
|
|
|
|
4025
|
|
|
return newControllers; |
|
4026
|
|
|
}, |
|
4027
|
|
|
|
|
4028
|
|
|
resetElements: function() { |
|
4029
|
|
|
var me = this; |
|
4030
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4031
|
|
|
me.getDatasetMeta(datasetIndex).controller.reset(); |
|
4032
|
|
|
}, me); |
|
4033
|
|
|
}, |
|
4034
|
|
|
|
|
4035
|
|
|
update: function update(animationDuration, lazy) { |
|
4036
|
|
|
var me = this; |
|
4037
|
|
|
Chart.plugins.notify('beforeUpdate', [me]); |
|
4038
|
|
|
|
|
4039
|
|
|
// In case the entire data object changed |
|
4040
|
|
|
me.tooltip._data = me.data; |
|
4041
|
|
|
|
|
4042
|
|
|
// Make sure dataset controllers are updated and new controllers are reset |
|
4043
|
|
|
var newControllers = me.buildOrUpdateControllers(); |
|
4044
|
|
|
|
|
4045
|
|
|
// Make sure all dataset controllers have correct meta data counts |
|
4046
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4047
|
|
|
me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); |
|
4048
|
|
|
}, me); |
|
4049
|
|
|
|
|
4050
|
|
|
Chart.layoutService.update(me, me.chart.width, me.chart.height); |
|
4051
|
|
|
|
|
4052
|
|
|
// Apply changes to the dataets that require the scales to have been calculated i.e BorderColor chages |
|
4053
|
|
|
Chart.plugins.notify('afterScaleUpdate', [me]); |
|
4054
|
|
|
|
|
4055
|
|
|
// Can only reset the new controllers after the scales have been updated |
|
4056
|
|
|
helpers.each(newControllers, function(controller) { |
|
4057
|
|
|
controller.reset(); |
|
4058
|
|
|
}); |
|
4059
|
|
|
|
|
4060
|
|
|
me.updateDatasets(); |
|
4061
|
|
|
|
|
4062
|
|
|
// Do this before render so that any plugins that need final scale updates can use it |
|
4063
|
|
|
Chart.plugins.notify('afterUpdate', [me]); |
|
4064
|
|
|
|
|
4065
|
|
|
me.render(animationDuration, lazy); |
|
4066
|
|
|
}, |
|
4067
|
|
|
|
|
4068
|
|
|
/** |
|
4069
|
|
|
* @method beforeDatasetsUpdate |
|
4070
|
|
|
* @description Called before all datasets are updated. If a plugin returns false, |
|
4071
|
|
|
* the datasets update will be cancelled until another chart update is triggered. |
|
4072
|
|
|
* @param {Object} instance the chart instance being updated. |
|
4073
|
|
|
* @returns {Boolean} false to cancel the datasets update. |
|
4074
|
|
|
* @memberof Chart.PluginBase |
|
4075
|
|
|
* @since version 2.1.5 |
|
4076
|
|
|
* @instance |
|
4077
|
|
|
*/ |
|
4078
|
|
|
|
|
4079
|
|
|
/** |
|
4080
|
|
|
* @method afterDatasetsUpdate |
|
4081
|
|
|
* @description Called after all datasets have been updated. Note that this |
|
4082
|
|
|
* extension will not be called if the datasets update has been cancelled. |
|
4083
|
|
|
* @param {Object} instance the chart instance being updated. |
|
4084
|
|
|
* @memberof Chart.PluginBase |
|
4085
|
|
|
* @since version 2.1.5 |
|
4086
|
|
|
* @instance |
|
4087
|
|
|
*/ |
|
4088
|
|
|
|
|
4089
|
|
|
/** |
|
4090
|
|
|
* Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate |
|
4091
|
|
|
* extension, in which case no datasets will be updated and the afterDatasetsUpdate |
|
4092
|
|
|
* notification will be skipped. |
|
4093
|
|
|
* @protected |
|
4094
|
|
|
* @instance |
|
4095
|
|
|
*/ |
|
4096
|
|
|
updateDatasets: function() { |
|
4097
|
|
|
var me = this; |
|
4098
|
|
|
var i, ilen; |
|
4099
|
|
|
|
|
4100
|
|
|
if (Chart.plugins.notify('beforeDatasetsUpdate', [ me ])) { |
|
4101
|
|
|
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { |
|
4102
|
|
|
me.getDatasetMeta(i).controller.update(); |
|
4103
|
|
|
} |
|
4104
|
|
|
|
|
4105
|
|
|
Chart.plugins.notify('afterDatasetsUpdate', [ me ]); |
|
4106
|
|
|
} |
|
4107
|
|
|
}, |
|
4108
|
|
|
|
|
4109
|
|
|
render: function render(duration, lazy) { |
|
4110
|
|
|
var me = this; |
|
4111
|
|
|
Chart.plugins.notify('beforeRender', [me]); |
|
4112
|
|
|
|
|
4113
|
|
|
var animationOptions = me.options.animation; |
|
4114
|
|
|
if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { |
|
4115
|
|
|
var animation = new Chart.Animation(); |
|
4116
|
|
|
animation.numSteps = (duration || animationOptions.duration) / 16.66; //60 fps |
|
4117
|
|
|
animation.easing = animationOptions.easing; |
|
4118
|
|
|
|
|
4119
|
|
|
// render function |
|
4120
|
|
|
animation.render = function(chartInstance, animationObject) { |
|
4121
|
|
|
var easingFunction = helpers.easingEffects[animationObject.easing]; |
|
4122
|
|
|
var stepDecimal = animationObject.currentStep / animationObject.numSteps; |
|
4123
|
|
|
var easeDecimal = easingFunction(stepDecimal); |
|
4124
|
|
|
|
|
4125
|
|
|
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); |
|
4126
|
|
|
}; |
|
4127
|
|
|
|
|
4128
|
|
|
// user events |
|
4129
|
|
|
animation.onAnimationProgress = animationOptions.onProgress; |
|
4130
|
|
|
animation.onAnimationComplete = animationOptions.onComplete; |
|
4131
|
|
|
|
|
4132
|
|
|
Chart.animationService.addAnimation(me, animation, duration, lazy); |
|
4133
|
|
|
} else { |
|
4134
|
|
|
me.draw(); |
|
4135
|
|
|
if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) { |
|
4136
|
|
|
animationOptions.onComplete.call(me); |
|
4137
|
|
|
} |
|
4138
|
|
|
} |
|
4139
|
|
|
return me; |
|
4140
|
|
|
}, |
|
4141
|
|
|
|
|
4142
|
|
|
draw: function(ease) { |
|
4143
|
|
|
var me = this; |
|
4144
|
|
|
var easingDecimal = ease || 1; |
|
4145
|
|
|
me.clear(); |
|
4146
|
|
|
|
|
4147
|
|
|
Chart.plugins.notify('beforeDraw', [me, easingDecimal]); |
|
4148
|
|
|
|
|
4149
|
|
|
// Draw all the scales |
|
4150
|
|
|
helpers.each(me.boxes, function(box) { |
|
4151
|
|
|
box.draw(me.chartArea); |
|
4152
|
|
|
}, me); |
|
4153
|
|
|
if (me.scale) { |
|
4154
|
|
|
me.scale.draw(); |
|
4155
|
|
|
} |
|
4156
|
|
|
|
|
4157
|
|
|
Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]); |
|
4158
|
|
|
|
|
4159
|
|
|
// Draw each dataset via its respective controller (reversed to support proper line stacking) |
|
4160
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4161
|
|
|
if (me.isDatasetVisible(datasetIndex)) { |
|
4162
|
|
|
me.getDatasetMeta(datasetIndex).controller.draw(ease); |
|
4163
|
|
|
} |
|
4164
|
|
|
}, me, true); |
|
4165
|
|
|
|
|
4166
|
|
|
Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]); |
|
4167
|
|
|
|
|
4168
|
|
|
// Finally draw the tooltip |
|
4169
|
|
|
me.tooltip.transition(easingDecimal).draw(); |
|
4170
|
|
|
|
|
4171
|
|
|
Chart.plugins.notify('afterDraw', [me, easingDecimal]); |
|
4172
|
|
|
}, |
|
4173
|
|
|
|
|
4174
|
|
|
// Get the single element that was clicked on |
|
4175
|
|
|
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw |
|
4176
|
|
|
getElementAtEvent: function(e) { |
|
4177
|
|
|
var me = this; |
|
4178
|
|
|
var eventPosition = helpers.getRelativePosition(e, me.chart); |
|
4179
|
|
|
var elementsArray = []; |
|
4180
|
|
|
|
|
4181
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4182
|
|
|
if (me.isDatasetVisible(datasetIndex)) { |
|
4183
|
|
|
var meta = me.getDatasetMeta(datasetIndex); |
|
4184
|
|
|
helpers.each(meta.data, function(element) { |
|
4185
|
|
|
if (element.inRange(eventPosition.x, eventPosition.y)) { |
|
|
|
|
|
|
4186
|
|
|
elementsArray.push(element); |
|
4187
|
|
|
return elementsArray; |
|
4188
|
|
|
} |
|
4189
|
|
|
}); |
|
4190
|
|
|
} |
|
4191
|
|
|
}); |
|
4192
|
|
|
|
|
4193
|
|
|
return elementsArray.slice(0, 1); |
|
4194
|
|
|
}, |
|
4195
|
|
|
|
|
4196
|
|
|
getElementsAtEvent: function(e) { |
|
4197
|
|
|
var me = this; |
|
4198
|
|
|
var eventPosition = helpers.getRelativePosition(e, me.chart); |
|
4199
|
|
|
var elementsArray = []; |
|
4200
|
|
|
|
|
4201
|
|
|
var found = (function() { |
|
4202
|
|
|
if (me.data.datasets) { |
|
|
|
|
|
|
4203
|
|
|
for (var i = 0; i < me.data.datasets.length; i++) { |
|
4204
|
|
|
var meta = me.getDatasetMeta(i); |
|
4205
|
|
|
if (me.isDatasetVisible(i)) { |
|
4206
|
|
|
for (var j = 0; j < meta.data.length; j++) { |
|
4207
|
|
|
if (meta.data[j].inRange(eventPosition.x, eventPosition.y)) { |
|
4208
|
|
|
return meta.data[j]; |
|
4209
|
|
|
} |
|
4210
|
|
|
} |
|
4211
|
|
|
} |
|
4212
|
|
|
} |
|
|
|
|
|
|
4213
|
|
|
} |
|
4214
|
|
|
}).call(me); |
|
4215
|
|
|
|
|
4216
|
|
|
if (!found) { |
|
4217
|
|
|
return elementsArray; |
|
4218
|
|
|
} |
|
4219
|
|
|
|
|
4220
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4221
|
|
|
if (me.isDatasetVisible(datasetIndex)) { |
|
4222
|
|
|
var meta = me.getDatasetMeta(datasetIndex), |
|
4223
|
|
|
element = meta.data[found._index]; |
|
4224
|
|
|
if(element && !element._view.skip){ |
|
4225
|
|
|
elementsArray.push(element); |
|
4226
|
|
|
} |
|
4227
|
|
|
} |
|
4228
|
|
|
}, me); |
|
4229
|
|
|
|
|
4230
|
|
|
return elementsArray; |
|
4231
|
|
|
}, |
|
4232
|
|
|
|
|
4233
|
|
|
getElementsAtXAxis: function(e) { |
|
4234
|
|
|
var me = this; |
|
4235
|
|
|
var eventPosition = helpers.getRelativePosition(e, me.chart); |
|
4236
|
|
|
var elementsArray = []; |
|
4237
|
|
|
|
|
4238
|
|
|
var found = (function() { |
|
4239
|
|
|
if (me.data.datasets) { |
|
|
|
|
|
|
4240
|
|
|
for (var i = 0; i < me.data.datasets.length; i++) { |
|
4241
|
|
|
var meta = me.getDatasetMeta(i); |
|
4242
|
|
|
if (me.isDatasetVisible(i)) { |
|
4243
|
|
|
for (var j = 0; j < meta.data.length; j++) { |
|
4244
|
|
|
if (meta.data[j].inLabelRange(eventPosition.x, eventPosition.y)) { |
|
4245
|
|
|
return meta.data[j]; |
|
4246
|
|
|
} |
|
4247
|
|
|
} |
|
4248
|
|
|
} |
|
4249
|
|
|
} |
|
|
|
|
|
|
4250
|
|
|
} |
|
4251
|
|
|
}).call(me); |
|
4252
|
|
|
|
|
4253
|
|
|
if (!found) { |
|
4254
|
|
|
return elementsArray; |
|
4255
|
|
|
} |
|
4256
|
|
|
|
|
4257
|
|
|
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
|
4258
|
|
|
if (me.isDatasetVisible(datasetIndex)) { |
|
4259
|
|
|
var meta = me.getDatasetMeta(datasetIndex); |
|
4260
|
|
|
var index = helpers.findIndex(meta.data, function (it) { |
|
4261
|
|
|
return found._model.x === it._model.x; |
|
4262
|
|
|
}); |
|
4263
|
|
|
if(index !== -1 && !meta.data[index]._view.skip) { |
|
4264
|
|
|
elementsArray.push(meta.data[index]); |
|
4265
|
|
|
} |
|
4266
|
|
|
} |
|
4267
|
|
|
}, me); |
|
4268
|
|
|
|
|
4269
|
|
|
return elementsArray; |
|
4270
|
|
|
}, |
|
4271
|
|
|
|
|
4272
|
|
|
getElementsAtEventForMode: function(e, mode) { |
|
4273
|
|
|
var me = this; |
|
4274
|
|
|
switch (mode) { |
|
4275
|
|
|
case 'single': |
|
4276
|
|
|
return me.getElementAtEvent(e); |
|
4277
|
|
|
case 'label': |
|
4278
|
|
|
return me.getElementsAtEvent(e); |
|
4279
|
|
|
case 'dataset': |
|
4280
|
|
|
return me.getDatasetAtEvent(e); |
|
4281
|
|
|
case 'x-axis': |
|
4282
|
|
|
return me.getElementsAtXAxis(e); |
|
4283
|
|
|
default: |
|
4284
|
|
|
return e; |
|
4285
|
|
|
} |
|
4286
|
|
|
}, |
|
4287
|
|
|
|
|
4288
|
|
|
getDatasetAtEvent: function(e) { |
|
4289
|
|
|
var elementsArray = this.getElementAtEvent(e); |
|
4290
|
|
|
|
|
4291
|
|
|
if (elementsArray.length > 0) { |
|
4292
|
|
|
elementsArray = this.getDatasetMeta(elementsArray[0]._datasetIndex).data; |
|
4293
|
|
|
} |
|
4294
|
|
|
|
|
4295
|
|
|
return elementsArray; |
|
4296
|
|
|
}, |
|
4297
|
|
|
|
|
4298
|
|
|
getDatasetMeta: function(datasetIndex) { |
|
4299
|
|
|
var me = this; |
|
4300
|
|
|
var dataset = me.data.datasets[datasetIndex]; |
|
4301
|
|
|
if (!dataset._meta) { |
|
4302
|
|
|
dataset._meta = {}; |
|
4303
|
|
|
} |
|
4304
|
|
|
|
|
4305
|
|
|
var meta = dataset._meta[me.id]; |
|
4306
|
|
|
if (!meta) { |
|
4307
|
|
|
meta = dataset._meta[me.id] = { |
|
4308
|
|
|
type: null, |
|
4309
|
|
|
data: [], |
|
4310
|
|
|
dataset: null, |
|
4311
|
|
|
controller: null, |
|
4312
|
|
|
hidden: null, // See isDatasetVisible() comment |
|
4313
|
|
|
xAxisID: null, |
|
4314
|
|
|
yAxisID: null |
|
4315
|
|
|
}; |
|
4316
|
|
|
} |
|
4317
|
|
|
|
|
4318
|
|
|
return meta; |
|
4319
|
|
|
}, |
|
4320
|
|
|
|
|
4321
|
|
|
getVisibleDatasetCount: function() { |
|
4322
|
|
|
var count = 0; |
|
4323
|
|
|
for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) { |
|
4324
|
|
|
if (this.isDatasetVisible(i)) { |
|
4325
|
|
|
count++; |
|
4326
|
|
|
} |
|
4327
|
|
|
} |
|
4328
|
|
|
return count; |
|
4329
|
|
|
}, |
|
4330
|
|
|
|
|
4331
|
|
|
isDatasetVisible: function(datasetIndex) { |
|
4332
|
|
|
var meta = this.getDatasetMeta(datasetIndex); |
|
4333
|
|
|
|
|
4334
|
|
|
// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, |
|
4335
|
|
|
// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. |
|
4336
|
|
|
return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden; |
|
4337
|
|
|
}, |
|
4338
|
|
|
|
|
4339
|
|
|
generateLegend: function() { |
|
4340
|
|
|
return this.options.legendCallback(this); |
|
4341
|
|
|
}, |
|
4342
|
|
|
|
|
4343
|
|
|
destroy: function() { |
|
4344
|
|
|
var me = this; |
|
4345
|
|
|
me.stop(); |
|
4346
|
|
|
me.clear(); |
|
4347
|
|
|
helpers.unbindEvents(me, me.events); |
|
4348
|
|
|
helpers.removeResizeListener(me.chart.canvas.parentNode); |
|
4349
|
|
|
|
|
4350
|
|
|
// Reset canvas height/width attributes |
|
4351
|
|
|
var canvas = me.chart.canvas; |
|
4352
|
|
|
canvas.width = me.chart.width; |
|
4353
|
|
|
canvas.height = me.chart.height; |
|
4354
|
|
|
|
|
4355
|
|
|
// if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here |
|
4356
|
|
|
if (me.chart.originalDevicePixelRatio !== undefined) { |
|
4357
|
|
|
me.chart.ctx.scale(1 / me.chart.originalDevicePixelRatio, 1 / me.chart.originalDevicePixelRatio); |
|
4358
|
|
|
} |
|
4359
|
|
|
|
|
4360
|
|
|
// Reset to the old style since it may have been changed by the device pixel ratio changes |
|
4361
|
|
|
canvas.style.width = me.chart.originalCanvasStyleWidth; |
|
4362
|
|
|
canvas.style.height = me.chart.originalCanvasStyleHeight; |
|
4363
|
|
|
|
|
4364
|
|
|
Chart.plugins.notify('destroy', [me]); |
|
4365
|
|
|
|
|
4366
|
|
|
delete Chart.instances[me.id]; |
|
4367
|
|
|
}, |
|
4368
|
|
|
|
|
4369
|
|
|
toBase64Image: function() { |
|
4370
|
|
|
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); |
|
4371
|
|
|
}, |
|
4372
|
|
|
|
|
4373
|
|
|
initToolTip: function() { |
|
4374
|
|
|
var me = this; |
|
4375
|
|
|
me.tooltip = new Chart.Tooltip({ |
|
4376
|
|
|
_chart: me.chart, |
|
4377
|
|
|
_chartInstance: me, |
|
4378
|
|
|
_data: me.data, |
|
4379
|
|
|
_options: me.options.tooltips |
|
4380
|
|
|
}, me); |
|
4381
|
|
|
}, |
|
4382
|
|
|
|
|
4383
|
|
|
bindEvents: function() { |
|
4384
|
|
|
var me = this; |
|
4385
|
|
|
helpers.bindEvents(me, me.options.events, function(evt) { |
|
4386
|
|
|
me.eventHandler(evt); |
|
4387
|
|
|
}); |
|
4388
|
|
|
}, |
|
4389
|
|
|
|
|
4390
|
|
|
updateHoverStyle: function(elements, mode, enabled) { |
|
4391
|
|
|
var method = enabled? 'setHoverStyle' : 'removeHoverStyle'; |
|
4392
|
|
|
var element, i, ilen; |
|
4393
|
|
|
|
|
4394
|
|
|
switch (mode) { |
|
4395
|
|
|
case 'single': |
|
4396
|
|
|
elements = [ elements[0] ]; |
|
4397
|
|
|
break; |
|
4398
|
|
|
case 'label': |
|
4399
|
|
|
case 'dataset': |
|
4400
|
|
|
case 'x-axis': |
|
4401
|
|
|
// elements = elements; |
|
4402
|
|
|
break; |
|
4403
|
|
|
default: |
|
4404
|
|
|
// unsupported mode |
|
4405
|
|
|
return; |
|
4406
|
|
|
} |
|
4407
|
|
|
|
|
4408
|
|
|
for (i=0, ilen=elements.length; i<ilen; ++i) { |
|
4409
|
|
|
element = elements[i]; |
|
4410
|
|
|
if (element) { |
|
4411
|
|
|
this.getDatasetMeta(element._datasetIndex).controller[method](element); |
|
4412
|
|
|
} |
|
4413
|
|
|
} |
|
4414
|
|
|
}, |
|
4415
|
|
|
|
|
4416
|
|
|
eventHandler: function eventHandler(e) { |
|
4417
|
|
|
var me = this; |
|
4418
|
|
|
var tooltip = me.tooltip; |
|
4419
|
|
|
var options = me.options || {}; |
|
4420
|
|
|
var hoverOptions = options.hover; |
|
4421
|
|
|
var tooltipsOptions = options.tooltips; |
|
4422
|
|
|
|
|
4423
|
|
|
me.lastActive = me.lastActive || []; |
|
4424
|
|
|
me.lastTooltipActive = me.lastTooltipActive || []; |
|
4425
|
|
|
|
|
4426
|
|
|
// Find Active Elements for hover and tooltips |
|
4427
|
|
|
if (e.type === 'mouseout') { |
|
4428
|
|
|
me.active = []; |
|
4429
|
|
|
me.tooltipActive = []; |
|
4430
|
|
|
} else { |
|
4431
|
|
|
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode); |
|
4432
|
|
|
me.tooltipActive = me.getElementsAtEventForMode(e, tooltipsOptions.mode); |
|
4433
|
|
|
} |
|
4434
|
|
|
|
|
4435
|
|
|
// On Hover hook |
|
4436
|
|
|
if (hoverOptions.onHover) { |
|
4437
|
|
|
hoverOptions.onHover.call(me, me.active); |
|
4438
|
|
|
} |
|
4439
|
|
|
|
|
4440
|
|
|
if (e.type === 'mouseup' || e.type === 'click') { |
|
4441
|
|
|
if (options.onClick) { |
|
4442
|
|
|
options.onClick.call(me, e, me.active); |
|
4443
|
|
|
} |
|
4444
|
|
|
if (me.legend && me.legend.handleEvent) { |
|
4445
|
|
|
me.legend.handleEvent(e); |
|
4446
|
|
|
} |
|
4447
|
|
|
} |
|
4448
|
|
|
|
|
4449
|
|
|
// Remove styling for last active (even if it may still be active) |
|
4450
|
|
|
if (me.lastActive.length) { |
|
4451
|
|
|
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); |
|
4452
|
|
|
} |
|
4453
|
|
|
|
|
4454
|
|
|
// Built in hover styling |
|
4455
|
|
|
if (me.active.length && hoverOptions.mode) { |
|
4456
|
|
|
me.updateHoverStyle(me.active, hoverOptions.mode, true); |
|
4457
|
|
|
} |
|
4458
|
|
|
|
|
4459
|
|
|
// Built in Tooltips |
|
4460
|
|
|
if (tooltipsOptions.enabled || tooltipsOptions.custom) { |
|
4461
|
|
|
tooltip.initialize(); |
|
4462
|
|
|
tooltip._active = me.tooltipActive; |
|
4463
|
|
|
tooltip.update(true); |
|
4464
|
|
|
} |
|
4465
|
|
|
|
|
4466
|
|
|
// Hover animations |
|
4467
|
|
|
tooltip.pivot(); |
|
4468
|
|
|
|
|
4469
|
|
|
if (!me.animating) { |
|
4470
|
|
|
// If entering, leaving, or changing elements, animate the change via pivot |
|
4471
|
|
|
if (!helpers.arrayEquals(me.active, me.lastActive) || |
|
4472
|
|
|
!helpers.arrayEquals(me.tooltipActive, me.lastTooltipActive)) { |
|
4473
|
|
|
|
|
4474
|
|
|
me.stop(); |
|
4475
|
|
|
|
|
4476
|
|
|
if (tooltipsOptions.enabled || tooltipsOptions.custom) { |
|
4477
|
|
|
tooltip.update(true); |
|
4478
|
|
|
} |
|
4479
|
|
|
|
|
4480
|
|
|
// We only need to render at this point. Updating will cause scales to be |
|
4481
|
|
|
// recomputed generating flicker & using more memory than necessary. |
|
4482
|
|
|
me.render(hoverOptions.animationDuration, true); |
|
4483
|
|
|
} |
|
4484
|
|
|
} |
|
4485
|
|
|
|
|
4486
|
|
|
// Remember Last Actives |
|
4487
|
|
|
me.lastActive = me.active; |
|
4488
|
|
|
me.lastTooltipActive = me.tooltipActive; |
|
4489
|
|
|
return me; |
|
4490
|
|
|
} |
|
4491
|
|
|
}); |
|
4492
|
|
|
}; |
|
4493
|
|
|
|
|
4494
|
|
|
},{}],24:[function(require,module,exports){ |
|
4495
|
|
|
"use strict"; |
|
4496
|
|
|
|
|
4497
|
|
|
module.exports = function(Chart) { |
|
4498
|
|
|
|
|
4499
|
|
|
var helpers = Chart.helpers; |
|
4500
|
|
|
var noop = helpers.noop; |
|
4501
|
|
|
|
|
4502
|
|
|
// Base class for all dataset controllers (line, bar, etc) |
|
4503
|
|
|
Chart.DatasetController = function(chart, datasetIndex) { |
|
4504
|
|
|
this.initialize.call(this, chart, datasetIndex); |
|
4505
|
|
|
}; |
|
4506
|
|
|
|
|
4507
|
|
|
helpers.extend(Chart.DatasetController.prototype, { |
|
4508
|
|
|
|
|
4509
|
|
|
/** |
|
4510
|
|
|
* Element type used to generate a meta dataset (e.g. Chart.element.Line). |
|
4511
|
|
|
* @type {Chart.core.element} |
|
4512
|
|
|
*/ |
|
4513
|
|
|
datasetElementType: null, |
|
4514
|
|
|
|
|
4515
|
|
|
/** |
|
4516
|
|
|
* Element type used to generate a meta data (e.g. Chart.element.Point). |
|
4517
|
|
|
* @type {Chart.core.element} |
|
4518
|
|
|
*/ |
|
4519
|
|
|
dataElementType: null, |
|
4520
|
|
|
|
|
4521
|
|
|
initialize: function(chart, datasetIndex) { |
|
4522
|
|
|
var me = this; |
|
4523
|
|
|
me.chart = chart; |
|
4524
|
|
|
me.index = datasetIndex; |
|
4525
|
|
|
me.linkScales(); |
|
4526
|
|
|
me.addElements(); |
|
4527
|
|
|
}, |
|
4528
|
|
|
|
|
4529
|
|
|
updateIndex: function(datasetIndex) { |
|
4530
|
|
|
this.index = datasetIndex; |
|
4531
|
|
|
}, |
|
4532
|
|
|
|
|
4533
|
|
|
linkScales: function() { |
|
4534
|
|
|
var me = this; |
|
4535
|
|
|
var meta = me.getMeta(); |
|
4536
|
|
|
var dataset = me.getDataset(); |
|
4537
|
|
|
|
|
4538
|
|
|
if (meta.xAxisID === null) { |
|
4539
|
|
|
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; |
|
4540
|
|
|
} |
|
4541
|
|
|
if (meta.yAxisID === null) { |
|
4542
|
|
|
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; |
|
4543
|
|
|
} |
|
4544
|
|
|
}, |
|
4545
|
|
|
|
|
4546
|
|
|
getDataset: function() { |
|
4547
|
|
|
return this.chart.data.datasets[this.index]; |
|
4548
|
|
|
}, |
|
4549
|
|
|
|
|
4550
|
|
|
getMeta: function() { |
|
4551
|
|
|
return this.chart.getDatasetMeta(this.index); |
|
4552
|
|
|
}, |
|
4553
|
|
|
|
|
4554
|
|
|
getScaleForId: function(scaleID) { |
|
4555
|
|
|
return this.chart.scales[scaleID]; |
|
4556
|
|
|
}, |
|
4557
|
|
|
|
|
4558
|
|
|
reset: function() { |
|
4559
|
|
|
this.update(true); |
|
4560
|
|
|
}, |
|
4561
|
|
|
|
|
4562
|
|
|
createMetaDataset: function() { |
|
4563
|
|
|
var me = this; |
|
4564
|
|
|
var type = me.datasetElementType; |
|
4565
|
|
|
return type && new type({ |
|
|
|
|
|
|
4566
|
|
|
_chart: me.chart.chart, |
|
4567
|
|
|
_datasetIndex: me.index |
|
4568
|
|
|
}); |
|
4569
|
|
|
}, |
|
4570
|
|
|
|
|
4571
|
|
|
createMetaData: function(index) { |
|
4572
|
|
|
var me = this; |
|
4573
|
|
|
var type = me.dataElementType; |
|
4574
|
|
|
return type && new type({ |
|
|
|
|
|
|
4575
|
|
|
_chart: me.chart.chart, |
|
4576
|
|
|
_datasetIndex: me.index, |
|
4577
|
|
|
_index: index |
|
4578
|
|
|
}); |
|
4579
|
|
|
}, |
|
4580
|
|
|
|
|
4581
|
|
|
addElements: function() { |
|
4582
|
|
|
var me = this; |
|
4583
|
|
|
var meta = me.getMeta(); |
|
4584
|
|
|
var data = me.getDataset().data || []; |
|
4585
|
|
|
var metaData = meta.data; |
|
4586
|
|
|
var i, ilen; |
|
4587
|
|
|
|
|
4588
|
|
|
for (i=0, ilen=data.length; i<ilen; ++i) { |
|
4589
|
|
|
metaData[i] = metaData[i] || me.createMetaData(meta, i); |
|
4590
|
|
|
} |
|
4591
|
|
|
|
|
4592
|
|
|
meta.dataset = meta.dataset || me.createMetaDataset(); |
|
4593
|
|
|
}, |
|
4594
|
|
|
|
|
4595
|
|
|
addElementAndReset: function(index) { |
|
4596
|
|
|
var me = this; |
|
4597
|
|
|
var element = me.createMetaData(index); |
|
4598
|
|
|
me.getMeta().data.splice(index, 0, element); |
|
4599
|
|
|
me.updateElement(element, index, true); |
|
4600
|
|
|
}, |
|
4601
|
|
|
|
|
4602
|
|
|
buildOrUpdateElements: function() { |
|
4603
|
|
|
// Handle the number of data points changing |
|
4604
|
|
|
var meta = this.getMeta(), |
|
4605
|
|
|
md = meta.data, |
|
4606
|
|
|
numData = this.getDataset().data.length, |
|
4607
|
|
|
numMetaData = md.length; |
|
4608
|
|
|
|
|
4609
|
|
|
// Make sure that we handle number of datapoints changing |
|
4610
|
|
|
if (numData < numMetaData) { |
|
4611
|
|
|
// Remove excess bars for data points that have been removed |
|
4612
|
|
|
md.splice(numData, numMetaData - numData); |
|
4613
|
|
|
} else if (numData > numMetaData) { |
|
4614
|
|
|
// Add new elements |
|
4615
|
|
|
for (var index = numMetaData; index < numData; ++index) { |
|
4616
|
|
|
this.addElementAndReset(index); |
|
4617
|
|
|
} |
|
4618
|
|
|
} |
|
4619
|
|
|
}, |
|
4620
|
|
|
|
|
4621
|
|
|
update: noop, |
|
4622
|
|
|
|
|
4623
|
|
|
draw: function(ease) { |
|
4624
|
|
|
var easingDecimal = ease || 1; |
|
4625
|
|
|
helpers.each(this.getMeta().data, function(element) { |
|
4626
|
|
|
element.transition(easingDecimal).draw(); |
|
4627
|
|
|
}); |
|
4628
|
|
|
}, |
|
4629
|
|
|
|
|
4630
|
|
|
removeHoverStyle: function(element, elementOpts) { |
|
4631
|
|
|
var dataset = this.chart.data.datasets[element._datasetIndex], |
|
4632
|
|
|
index = element._index, |
|
4633
|
|
|
custom = element.custom || {}, |
|
4634
|
|
|
valueOrDefault = helpers.getValueAtIndexOrDefault, |
|
4635
|
|
|
model = element._model; |
|
4636
|
|
|
|
|
4637
|
|
|
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); |
|
4638
|
|
|
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); |
|
4639
|
|
|
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); |
|
4640
|
|
|
}, |
|
4641
|
|
|
|
|
4642
|
|
|
setHoverStyle: function(element) { |
|
4643
|
|
|
var dataset = this.chart.data.datasets[element._datasetIndex], |
|
4644
|
|
|
index = element._index, |
|
4645
|
|
|
custom = element.custom || {}, |
|
4646
|
|
|
valueOrDefault = helpers.getValueAtIndexOrDefault, |
|
4647
|
|
|
getHoverColor = helpers.getHoverColor, |
|
4648
|
|
|
model = element._model; |
|
4649
|
|
|
|
|
4650
|
|
|
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); |
|
4651
|
|
|
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); |
|
4652
|
|
|
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); |
|
4653
|
|
|
} |
|
4654
|
|
|
|
|
4655
|
|
|
}); |
|
4656
|
|
|
|
|
4657
|
|
|
|
|
4658
|
|
|
Chart.DatasetController.extend = helpers.inherits; |
|
4659
|
|
|
}; |
|
4660
|
|
|
},{}],25:[function(require,module,exports){ |
|
4661
|
|
|
"use strict"; |
|
4662
|
|
|
|
|
4663
|
|
|
module.exports = function(Chart) { |
|
4664
|
|
|
|
|
4665
|
|
|
var helpers = Chart.helpers; |
|
4666
|
|
|
|
|
4667
|
|
|
Chart.elements = {}; |
|
4668
|
|
|
|
|
4669
|
|
|
Chart.Element = function(configuration) { |
|
4670
|
|
|
helpers.extend(this, configuration); |
|
4671
|
|
|
this.initialize.apply(this, arguments); |
|
4672
|
|
|
}; |
|
4673
|
|
|
|
|
4674
|
|
|
helpers.extend(Chart.Element.prototype, { |
|
4675
|
|
|
|
|
4676
|
|
|
initialize: function() { |
|
4677
|
|
|
this.hidden = false; |
|
4678
|
|
|
}, |
|
4679
|
|
|
|
|
4680
|
|
|
pivot: function() { |
|
4681
|
|
|
var me = this; |
|
4682
|
|
|
if (!me._view) { |
|
4683
|
|
|
me._view = helpers.clone(me._model); |
|
4684
|
|
|
} |
|
4685
|
|
|
me._start = helpers.clone(me._view); |
|
4686
|
|
|
return me; |
|
4687
|
|
|
}, |
|
4688
|
|
|
|
|
4689
|
|
|
transition: function(ease) { |
|
4690
|
|
|
var me = this; |
|
4691
|
|
|
|
|
4692
|
|
|
if (!me._view) { |
|
4693
|
|
|
me._view = helpers.clone(me._model); |
|
4694
|
|
|
} |
|
4695
|
|
|
|
|
4696
|
|
|
// No animation -> No Transition |
|
4697
|
|
|
if (ease === 1) { |
|
4698
|
|
|
me._view = me._model; |
|
4699
|
|
|
me._start = null; |
|
4700
|
|
|
return me; |
|
4701
|
|
|
} |
|
4702
|
|
|
|
|
4703
|
|
|
if (!me._start) { |
|
4704
|
|
|
me.pivot(); |
|
4705
|
|
|
} |
|
4706
|
|
|
|
|
4707
|
|
|
helpers.each(me._model, function(value, key) { |
|
4708
|
|
|
|
|
4709
|
|
|
if (key[0] === '_') { |
|
4710
|
|
|
// Only non-underscored properties |
|
4711
|
|
|
} |
|
4712
|
|
|
|
|
4713
|
|
|
// Init if doesn't exist |
|
4714
|
|
|
else if (!me._view.hasOwnProperty(key)) { |
|
4715
|
|
|
if (typeof value === 'number' && !isNaN(me._view[key])) { |
|
4716
|
|
|
me._view[key] = value * ease; |
|
4717
|
|
|
} else { |
|
4718
|
|
|
me._view[key] = value; |
|
4719
|
|
|
} |
|
4720
|
|
|
} |
|
4721
|
|
|
|
|
4722
|
|
|
// No unnecessary computations |
|
4723
|
|
|
else if (value === me._view[key]) { |
|
4724
|
|
|
// It's the same! Woohoo! |
|
4725
|
|
|
} |
|
4726
|
|
|
|
|
4727
|
|
|
// Color transitions if possible |
|
4728
|
|
|
else if (typeof value === 'string') { |
|
4729
|
|
|
try { |
|
4730
|
|
|
var color = helpers.color(me._model[key]).mix(helpers.color(me._start[key]), ease); |
|
4731
|
|
|
me._view[key] = color.rgbString(); |
|
4732
|
|
|
} catch (err) { |
|
4733
|
|
|
me._view[key] = value; |
|
4734
|
|
|
} |
|
4735
|
|
|
} |
|
4736
|
|
|
// Number transitions |
|
4737
|
|
|
else if (typeof value === 'number') { |
|
4738
|
|
|
var startVal = me._start[key] !== undefined && isNaN(me._start[key]) === false ? me._start[key] : 0; |
|
4739
|
|
|
me._view[key] = ((me._model[key] - startVal) * ease) + startVal; |
|
4740
|
|
|
} |
|
4741
|
|
|
// Everything else |
|
4742
|
|
|
else { |
|
4743
|
|
|
me._view[key] = value; |
|
4744
|
|
|
} |
|
4745
|
|
|
}, me); |
|
4746
|
|
|
|
|
4747
|
|
|
return me; |
|
4748
|
|
|
}, |
|
4749
|
|
|
|
|
4750
|
|
|
tooltipPosition: function() { |
|
4751
|
|
|
return { |
|
4752
|
|
|
x: this._model.x, |
|
4753
|
|
|
y: this._model.y |
|
4754
|
|
|
}; |
|
4755
|
|
|
}, |
|
4756
|
|
|
|
|
4757
|
|
|
hasValue: function() { |
|
4758
|
|
|
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); |
|
4759
|
|
|
} |
|
4760
|
|
|
}); |
|
4761
|
|
|
|
|
4762
|
|
|
Chart.Element.extend = helpers.inherits; |
|
4763
|
|
|
|
|
4764
|
|
|
}; |
|
4765
|
|
|
|
|
4766
|
|
|
},{}],26:[function(require,module,exports){ |
|
4767
|
|
|
/*global window: false */ |
|
4768
|
|
|
/*global document: false */ |
|
4769
|
|
|
"use strict"; |
|
4770
|
|
|
|
|
4771
|
|
|
var color = require(3); |
|
4772
|
|
|
|
|
4773
|
|
|
module.exports = function(Chart) { |
|
4774
|
|
|
//Global Chart helpers object for utility methods and classes |
|
4775
|
|
|
var helpers = Chart.helpers = {}; |
|
4776
|
|
|
|
|
4777
|
|
|
//-- Basic js utility methods |
|
4778
|
|
|
helpers.each = function(loopable, callback, self, reverse) { |
|
4779
|
|
|
// Check to see if null or undefined firstly. |
|
4780
|
|
|
var i, len; |
|
4781
|
|
|
if (helpers.isArray(loopable)) { |
|
4782
|
|
|
len = loopable.length; |
|
4783
|
|
|
if (reverse) { |
|
4784
|
|
|
for (i = len - 1; i >= 0; i--) { |
|
4785
|
|
|
callback.call(self, loopable[i], i); |
|
4786
|
|
|
} |
|
4787
|
|
|
} else { |
|
4788
|
|
|
for (i = 0; i < len; i++) { |
|
4789
|
|
|
callback.call(self, loopable[i], i); |
|
4790
|
|
|
} |
|
4791
|
|
|
} |
|
4792
|
|
|
} else if (typeof loopable === 'object') { |
|
4793
|
|
|
var keys = Object.keys(loopable); |
|
4794
|
|
|
len = keys.length; |
|
4795
|
|
|
for (i = 0; i < len; i++) { |
|
4796
|
|
|
callback.call(self, loopable[keys[i]], keys[i]); |
|
4797
|
|
|
} |
|
4798
|
|
|
} |
|
4799
|
|
|
}; |
|
4800
|
|
|
helpers.clone = function(obj) { |
|
4801
|
|
|
var objClone = {}; |
|
4802
|
|
|
helpers.each(obj, function(value, key) { |
|
4803
|
|
|
if (helpers.isArray(value)) { |
|
4804
|
|
|
objClone[key] = value.slice(0); |
|
4805
|
|
|
} else if (typeof value === 'object' && value !== null) { |
|
4806
|
|
|
objClone[key] = helpers.clone(value); |
|
4807
|
|
|
} else { |
|
4808
|
|
|
objClone[key] = value; |
|
4809
|
|
|
} |
|
4810
|
|
|
}); |
|
4811
|
|
|
return objClone; |
|
4812
|
|
|
}; |
|
4813
|
|
|
helpers.extend = function(base) { |
|
4814
|
|
|
var setFn = function(value, key) { base[key] = value; }; |
|
4815
|
|
|
for (var i = 1, ilen = arguments.length; i < ilen; i++) { |
|
4816
|
|
|
helpers.each(arguments[i], setFn); |
|
4817
|
|
|
} |
|
4818
|
|
|
return base; |
|
4819
|
|
|
}; |
|
4820
|
|
|
// Need a special merge function to chart configs since they are now grouped |
|
4821
|
|
|
helpers.configMerge = function(_base) { |
|
4822
|
|
|
var base = helpers.clone(_base); |
|
4823
|
|
|
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) { |
|
4824
|
|
|
helpers.each(extension, function(value, key) { |
|
4825
|
|
|
if (key === 'scales') { |
|
4826
|
|
|
// Scale config merging is complex. Add out own function here for that |
|
4827
|
|
|
base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value); |
|
4828
|
|
|
|
|
4829
|
|
|
} else if (key === 'scale') { |
|
4830
|
|
|
// Used in polar area & radar charts since there is only one scale |
|
4831
|
|
|
base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value); |
|
4832
|
|
|
} else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) { |
|
4833
|
|
|
// In this case we have an array of objects replacing another array. Rather than doing a strict replace, |
|
4834
|
|
|
// merge. This allows easy scale option merging |
|
4835
|
|
|
var baseArray = base[key]; |
|
4836
|
|
|
|
|
4837
|
|
|
helpers.each(value, function(valueObj, index) { |
|
4838
|
|
|
|
|
4839
|
|
|
if (index < baseArray.length) { |
|
4840
|
|
|
if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) { |
|
4841
|
|
|
// Two objects are coming together. Do a merge of them. |
|
4842
|
|
|
baseArray[index] = helpers.configMerge(baseArray[index], valueObj); |
|
4843
|
|
|
} else { |
|
4844
|
|
|
// Just overwrite in this case since there is nothing to merge |
|
4845
|
|
|
baseArray[index] = valueObj; |
|
4846
|
|
|
} |
|
4847
|
|
|
} else { |
|
4848
|
|
|
baseArray.push(valueObj); // nothing to merge |
|
4849
|
|
|
} |
|
4850
|
|
|
}); |
|
4851
|
|
|
|
|
4852
|
|
|
} else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { |
|
4853
|
|
|
// If we are overwriting an object with an object, do a merge of the properties. |
|
4854
|
|
|
base[key] = helpers.configMerge(base[key], value); |
|
4855
|
|
|
|
|
4856
|
|
|
} else { |
|
4857
|
|
|
// can just overwrite the value in this case |
|
4858
|
|
|
base[key] = value; |
|
4859
|
|
|
} |
|
4860
|
|
|
}); |
|
4861
|
|
|
}); |
|
4862
|
|
|
|
|
4863
|
|
|
return base; |
|
4864
|
|
|
}; |
|
4865
|
|
|
helpers.scaleMerge = function(_base, extension) { |
|
4866
|
|
|
var base = helpers.clone(_base); |
|
4867
|
|
|
|
|
4868
|
|
|
helpers.each(extension, function(value, key) { |
|
4869
|
|
|
if (key === 'xAxes' || key === 'yAxes') { |
|
4870
|
|
|
// These properties are arrays of items |
|
4871
|
|
|
if (base.hasOwnProperty(key)) { |
|
4872
|
|
|
helpers.each(value, function(valueObj, index) { |
|
4873
|
|
|
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); |
|
4874
|
|
|
var axisDefaults = Chart.scaleService.getScaleDefaults(axisType); |
|
4875
|
|
|
if (index >= base[key].length || !base[key][index].type) { |
|
4876
|
|
|
base[key].push(helpers.configMerge(axisDefaults, valueObj)); |
|
4877
|
|
|
} else if (valueObj.type && valueObj.type !== base[key][index].type) { |
|
4878
|
|
|
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults |
|
4879
|
|
|
base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj); |
|
4880
|
|
|
} else { |
|
4881
|
|
|
// Type is the same |
|
4882
|
|
|
base[key][index] = helpers.configMerge(base[key][index], valueObj); |
|
4883
|
|
|
} |
|
4884
|
|
|
}); |
|
4885
|
|
|
} else { |
|
4886
|
|
|
base[key] = []; |
|
4887
|
|
|
helpers.each(value, function(valueObj) { |
|
4888
|
|
|
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); |
|
4889
|
|
|
base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj)); |
|
4890
|
|
|
}); |
|
4891
|
|
|
} |
|
4892
|
|
|
} else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { |
|
4893
|
|
|
// If we are overwriting an object with an object, do a merge of the properties. |
|
4894
|
|
|
base[key] = helpers.configMerge(base[key], value); |
|
4895
|
|
|
|
|
4896
|
|
|
} else { |
|
4897
|
|
|
// can just overwrite the value in this case |
|
4898
|
|
|
base[key] = value; |
|
4899
|
|
|
} |
|
4900
|
|
|
}); |
|
4901
|
|
|
|
|
4902
|
|
|
return base; |
|
4903
|
|
|
}; |
|
4904
|
|
|
helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) { |
|
4905
|
|
|
if (value === undefined || value === null) { |
|
4906
|
|
|
return defaultValue; |
|
4907
|
|
|
} |
|
4908
|
|
|
|
|
4909
|
|
|
if (helpers.isArray(value)) { |
|
4910
|
|
|
return index < value.length ? value[index] : defaultValue; |
|
4911
|
|
|
} |
|
4912
|
|
|
|
|
4913
|
|
|
return value; |
|
4914
|
|
|
}; |
|
4915
|
|
|
helpers.getValueOrDefault = function(value, defaultValue) { |
|
4916
|
|
|
return value === undefined ? defaultValue : value; |
|
4917
|
|
|
}; |
|
4918
|
|
|
helpers.indexOf = Array.prototype.indexOf? |
|
4919
|
|
|
function(array, item) { return array.indexOf(item); } : |
|
4920
|
|
|
function(array, item) { |
|
4921
|
|
|
for (var i = 0, ilen = array.length; i < ilen; ++i) { |
|
4922
|
|
|
if (array[i] === item) { |
|
4923
|
|
|
return i; |
|
4924
|
|
|
} |
|
4925
|
|
|
} |
|
4926
|
|
|
return -1; |
|
4927
|
|
|
}; |
|
4928
|
|
|
helpers.where = function(collection, filterCallback) { |
|
4929
|
|
|
if (helpers.isArray(collection) && Array.prototype.filter) { |
|
4930
|
|
|
return collection.filter(filterCallback); |
|
4931
|
|
|
} else { |
|
4932
|
|
|
var filtered = []; |
|
4933
|
|
|
|
|
4934
|
|
|
helpers.each(collection, function(item) { |
|
4935
|
|
|
if (filterCallback(item)) { |
|
4936
|
|
|
filtered.push(item); |
|
4937
|
|
|
} |
|
4938
|
|
|
}); |
|
4939
|
|
|
|
|
4940
|
|
|
return filtered; |
|
4941
|
|
|
} |
|
4942
|
|
|
}; |
|
4943
|
|
|
helpers.findIndex = Array.prototype.findIndex? |
|
4944
|
|
|
function(array, callback, scope) { return array.findIndex(callback, scope); } : |
|
4945
|
|
|
function(array, callback, scope) { |
|
4946
|
|
|
scope = scope === undefined? array : scope; |
|
4947
|
|
|
for (var i = 0, ilen = array.length; i < ilen; ++i) { |
|
4948
|
|
|
if (callback.call(scope, array[i], i, array)) { |
|
4949
|
|
|
return i; |
|
4950
|
|
|
} |
|
4951
|
|
|
} |
|
4952
|
|
|
return -1; |
|
4953
|
|
|
}; |
|
4954
|
|
|
helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { |
|
4955
|
|
|
// Default to start of the array |
|
4956
|
|
|
if (startIndex === undefined || startIndex === null) { |
|
4957
|
|
|
startIndex = -1; |
|
4958
|
|
|
} |
|
4959
|
|
|
for (var i = startIndex + 1; i < arrayToSearch.length; i++) { |
|
4960
|
|
|
var currentItem = arrayToSearch[i]; |
|
4961
|
|
|
if (filterCallback(currentItem)) { |
|
4962
|
|
|
return currentItem; |
|
4963
|
|
|
} |
|
4964
|
|
|
} |
|
|
|
|
|
|
4965
|
|
|
}; |
|
4966
|
|
|
helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { |
|
4967
|
|
|
// Default to end of the array |
|
4968
|
|
|
if (startIndex === undefined || startIndex === null) { |
|
4969
|
|
|
startIndex = arrayToSearch.length; |
|
4970
|
|
|
} |
|
4971
|
|
|
for (var i = startIndex - 1; i >= 0; i--) { |
|
4972
|
|
|
var currentItem = arrayToSearch[i]; |
|
4973
|
|
|
if (filterCallback(currentItem)) { |
|
4974
|
|
|
return currentItem; |
|
4975
|
|
|
} |
|
4976
|
|
|
} |
|
|
|
|
|
|
4977
|
|
|
}; |
|
4978
|
|
|
helpers.inherits = function(extensions) { |
|
4979
|
|
|
//Basic javascript inheritance based on the model created in Backbone.js |
|
4980
|
|
|
var parent = this; |
|
4981
|
|
|
var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() { |
|
4982
|
|
|
return parent.apply(this, arguments); |
|
4983
|
|
|
}; |
|
4984
|
|
|
|
|
4985
|
|
|
var Surrogate = function() { |
|
4986
|
|
|
this.constructor = ChartElement; |
|
4987
|
|
|
}; |
|
4988
|
|
|
Surrogate.prototype = parent.prototype; |
|
4989
|
|
|
ChartElement.prototype = new Surrogate(); |
|
4990
|
|
|
|
|
4991
|
|
|
ChartElement.extend = helpers.inherits; |
|
4992
|
|
|
|
|
4993
|
|
|
if (extensions) { |
|
4994
|
|
|
helpers.extend(ChartElement.prototype, extensions); |
|
4995
|
|
|
} |
|
4996
|
|
|
|
|
4997
|
|
|
ChartElement.__super__ = parent.prototype; |
|
4998
|
|
|
|
|
4999
|
|
|
return ChartElement; |
|
5000
|
|
|
}; |
|
5001
|
|
|
helpers.noop = function() {}; |
|
5002
|
|
|
helpers.uid = (function() { |
|
5003
|
|
|
var id = 0; |
|
5004
|
|
|
return function() { |
|
5005
|
|
|
return id++; |
|
5006
|
|
|
}; |
|
5007
|
|
|
})(); |
|
5008
|
|
|
//-- Math methods |
|
5009
|
|
|
helpers.isNumber = function(n) { |
|
5010
|
|
|
return !isNaN(parseFloat(n)) && isFinite(n); |
|
5011
|
|
|
}; |
|
5012
|
|
|
helpers.almostEquals = function(x, y, epsilon) { |
|
5013
|
|
|
return Math.abs(x - y) < epsilon; |
|
5014
|
|
|
}; |
|
5015
|
|
|
helpers.max = function(array) { |
|
5016
|
|
|
return array.reduce(function(max, value) { |
|
5017
|
|
|
if (!isNaN(value)) { |
|
5018
|
|
|
return Math.max(max, value); |
|
5019
|
|
|
} else { |
|
5020
|
|
|
return max; |
|
5021
|
|
|
} |
|
5022
|
|
|
}, Number.NEGATIVE_INFINITY); |
|
5023
|
|
|
}; |
|
5024
|
|
|
helpers.min = function(array) { |
|
5025
|
|
|
return array.reduce(function(min, value) { |
|
5026
|
|
|
if (!isNaN(value)) { |
|
5027
|
|
|
return Math.min(min, value); |
|
5028
|
|
|
} else { |
|
5029
|
|
|
return min; |
|
5030
|
|
|
} |
|
5031
|
|
|
}, Number.POSITIVE_INFINITY); |
|
5032
|
|
|
}; |
|
5033
|
|
|
helpers.sign = Math.sign? |
|
5034
|
|
|
function(x) { return Math.sign(x); } : |
|
5035
|
|
|
function(x) { |
|
5036
|
|
|
x = +x; // convert to a number |
|
5037
|
|
|
if (x === 0 || isNaN(x)) { |
|
5038
|
|
|
return x; |
|
5039
|
|
|
} |
|
5040
|
|
|
return x > 0 ? 1 : -1; |
|
5041
|
|
|
}; |
|
5042
|
|
|
helpers.log10 = Math.log10? |
|
5043
|
|
|
function(x) { return Math.log10(x); } : |
|
5044
|
|
|
function(x) { |
|
5045
|
|
|
return Math.log(x) / Math.LN10; |
|
5046
|
|
|
}; |
|
5047
|
|
|
helpers.toRadians = function(degrees) { |
|
5048
|
|
|
return degrees * (Math.PI / 180); |
|
5049
|
|
|
}; |
|
5050
|
|
|
helpers.toDegrees = function(radians) { |
|
5051
|
|
|
return radians * (180 / Math.PI); |
|
5052
|
|
|
}; |
|
5053
|
|
|
// Gets the angle from vertical upright to the point about a centre. |
|
5054
|
|
|
helpers.getAngleFromPoint = function(centrePoint, anglePoint) { |
|
5055
|
|
|
var distanceFromXCenter = anglePoint.x - centrePoint.x, |
|
5056
|
|
|
distanceFromYCenter = anglePoint.y - centrePoint.y, |
|
5057
|
|
|
radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); |
|
5058
|
|
|
|
|
5059
|
|
|
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); |
|
5060
|
|
|
|
|
5061
|
|
|
if (angle < (-0.5 * Math.PI)) { |
|
5062
|
|
|
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] |
|
5063
|
|
|
} |
|
5064
|
|
|
|
|
5065
|
|
|
return { |
|
5066
|
|
|
angle: angle, |
|
5067
|
|
|
distance: radialDistanceFromCenter |
|
5068
|
|
|
}; |
|
5069
|
|
|
}; |
|
5070
|
|
|
helpers.aliasPixel = function(pixelWidth) { |
|
5071
|
|
|
return (pixelWidth % 2 === 0) ? 0 : 0.5; |
|
5072
|
|
|
}; |
|
5073
|
|
|
helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { |
|
5074
|
|
|
//Props to Rob Spencer at scaled innovation for his post on splining between points |
|
5075
|
|
|
//http://scaledinnovation.com/analytics/splines/aboutSplines.html |
|
5076
|
|
|
|
|
5077
|
|
|
// This function must also respect "skipped" points |
|
5078
|
|
|
|
|
5079
|
|
|
var previous = firstPoint.skip ? middlePoint : firstPoint, |
|
5080
|
|
|
current = middlePoint, |
|
5081
|
|
|
next = afterPoint.skip ? middlePoint : afterPoint; |
|
5082
|
|
|
|
|
5083
|
|
|
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); |
|
5084
|
|
|
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); |
|
5085
|
|
|
|
|
5086
|
|
|
var s01 = d01 / (d01 + d12); |
|
5087
|
|
|
var s12 = d12 / (d01 + d12); |
|
5088
|
|
|
|
|
5089
|
|
|
// If all points are the same, s01 & s02 will be inf |
|
5090
|
|
|
s01 = isNaN(s01) ? 0 : s01; |
|
5091
|
|
|
s12 = isNaN(s12) ? 0 : s12; |
|
5092
|
|
|
|
|
5093
|
|
|
var fa = t * s01; // scaling factor for triangle Ta |
|
5094
|
|
|
var fb = t * s12; |
|
5095
|
|
|
|
|
5096
|
|
|
return { |
|
5097
|
|
|
previous: { |
|
5098
|
|
|
x: current.x - fa * (next.x - previous.x), |
|
5099
|
|
|
y: current.y - fa * (next.y - previous.y) |
|
5100
|
|
|
}, |
|
5101
|
|
|
next: { |
|
5102
|
|
|
x: current.x + fb * (next.x - previous.x), |
|
5103
|
|
|
y: current.y + fb * (next.y - previous.y) |
|
5104
|
|
|
} |
|
5105
|
|
|
}; |
|
5106
|
|
|
}; |
|
5107
|
|
|
helpers.nextItem = function(collection, index, loop) { |
|
5108
|
|
|
if (loop) { |
|
5109
|
|
|
return index >= collection.length - 1 ? collection[0] : collection[index + 1]; |
|
5110
|
|
|
} |
|
5111
|
|
|
|
|
5112
|
|
|
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; |
|
5113
|
|
|
}; |
|
5114
|
|
|
helpers.previousItem = function(collection, index, loop) { |
|
5115
|
|
|
if (loop) { |
|
5116
|
|
|
return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; |
|
5117
|
|
|
} |
|
5118
|
|
|
return index <= 0 ? collection[0] : collection[index - 1]; |
|
5119
|
|
|
}; |
|
5120
|
|
|
// Implementation of the nice number algorithm used in determining where axis labels will go |
|
5121
|
|
|
helpers.niceNum = function(range, round) { |
|
5122
|
|
|
var exponent = Math.floor(helpers.log10(range)); |
|
5123
|
|
|
var fraction = range / Math.pow(10, exponent); |
|
5124
|
|
|
var niceFraction; |
|
5125
|
|
|
|
|
5126
|
|
|
if (round) { |
|
5127
|
|
|
if (fraction < 1.5) { |
|
5128
|
|
|
niceFraction = 1; |
|
5129
|
|
|
} else if (fraction < 3) { |
|
5130
|
|
|
niceFraction = 2; |
|
5131
|
|
|
} else if (fraction < 7) { |
|
5132
|
|
|
niceFraction = 5; |
|
5133
|
|
|
} else { |
|
5134
|
|
|
niceFraction = 10; |
|
5135
|
|
|
} |
|
5136
|
|
|
} else { |
|
5137
|
|
|
if (fraction <= 1.0) { |
|
5138
|
|
|
niceFraction = 1; |
|
5139
|
|
|
} else if (fraction <= 2) { |
|
5140
|
|
|
niceFraction = 2; |
|
5141
|
|
|
} else if (fraction <= 5) { |
|
5142
|
|
|
niceFraction = 5; |
|
5143
|
|
|
} else { |
|
5144
|
|
|
niceFraction = 10; |
|
5145
|
|
|
} |
|
5146
|
|
|
} |
|
5147
|
|
|
|
|
5148
|
|
|
return niceFraction * Math.pow(10, exponent); |
|
5149
|
|
|
}; |
|
5150
|
|
|
//Easing functions adapted from Robert Penner's easing equations |
|
5151
|
|
|
//http://www.robertpenner.com/easing/ |
|
5152
|
|
|
var easingEffects = helpers.easingEffects = { |
|
5153
|
|
|
linear: function(t) { |
|
5154
|
|
|
return t; |
|
5155
|
|
|
}, |
|
5156
|
|
|
easeInQuad: function(t) { |
|
5157
|
|
|
return t * t; |
|
5158
|
|
|
}, |
|
5159
|
|
|
easeOutQuad: function(t) { |
|
5160
|
|
|
return -1 * t * (t - 2); |
|
5161
|
|
|
}, |
|
5162
|
|
|
easeInOutQuad: function(t) { |
|
5163
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5164
|
|
|
return 1 / 2 * t * t; |
|
5165
|
|
|
} |
|
5166
|
|
|
return -1 / 2 * ((--t) * (t - 2) - 1); |
|
5167
|
|
|
}, |
|
5168
|
|
|
easeInCubic: function(t) { |
|
5169
|
|
|
return t * t * t; |
|
5170
|
|
|
}, |
|
5171
|
|
|
easeOutCubic: function(t) { |
|
5172
|
|
|
return 1 * ((t = t / 1 - 1) * t * t + 1); |
|
5173
|
|
|
}, |
|
5174
|
|
|
easeInOutCubic: function(t) { |
|
5175
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5176
|
|
|
return 1 / 2 * t * t * t; |
|
5177
|
|
|
} |
|
5178
|
|
|
return 1 / 2 * ((t -= 2) * t * t + 2); |
|
5179
|
|
|
}, |
|
5180
|
|
|
easeInQuart: function(t) { |
|
5181
|
|
|
return t * t * t * t; |
|
5182
|
|
|
}, |
|
5183
|
|
|
easeOutQuart: function(t) { |
|
5184
|
|
|
return -1 * ((t = t / 1 - 1) * t * t * t - 1); |
|
5185
|
|
|
}, |
|
5186
|
|
|
easeInOutQuart: function(t) { |
|
5187
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5188
|
|
|
return 1 / 2 * t * t * t * t; |
|
5189
|
|
|
} |
|
5190
|
|
|
return -1 / 2 * ((t -= 2) * t * t * t - 2); |
|
5191
|
|
|
}, |
|
5192
|
|
|
easeInQuint: function(t) { |
|
5193
|
|
|
return 1 * (t /= 1) * t * t * t * t; |
|
5194
|
|
|
}, |
|
5195
|
|
|
easeOutQuint: function(t) { |
|
5196
|
|
|
return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); |
|
5197
|
|
|
}, |
|
5198
|
|
|
easeInOutQuint: function(t) { |
|
5199
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5200
|
|
|
return 1 / 2 * t * t * t * t * t; |
|
5201
|
|
|
} |
|
5202
|
|
|
return 1 / 2 * ((t -= 2) * t * t * t * t + 2); |
|
5203
|
|
|
}, |
|
5204
|
|
|
easeInSine: function(t) { |
|
5205
|
|
|
return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; |
|
5206
|
|
|
}, |
|
5207
|
|
|
easeOutSine: function(t) { |
|
5208
|
|
|
return 1 * Math.sin(t / 1 * (Math.PI / 2)); |
|
5209
|
|
|
}, |
|
5210
|
|
|
easeInOutSine: function(t) { |
|
5211
|
|
|
return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); |
|
5212
|
|
|
}, |
|
5213
|
|
|
easeInExpo: function(t) { |
|
5214
|
|
|
return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); |
|
5215
|
|
|
}, |
|
5216
|
|
|
easeOutExpo: function(t) { |
|
5217
|
|
|
return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); |
|
5218
|
|
|
}, |
|
5219
|
|
|
easeInOutExpo: function(t) { |
|
5220
|
|
|
if (t === 0) { |
|
5221
|
|
|
return 0; |
|
5222
|
|
|
} |
|
5223
|
|
|
if (t === 1) { |
|
5224
|
|
|
return 1; |
|
5225
|
|
|
} |
|
5226
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5227
|
|
|
return 1 / 2 * Math.pow(2, 10 * (t - 1)); |
|
5228
|
|
|
} |
|
5229
|
|
|
return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); |
|
5230
|
|
|
}, |
|
5231
|
|
|
easeInCirc: function(t) { |
|
5232
|
|
|
if (t >= 1) { |
|
5233
|
|
|
return t; |
|
5234
|
|
|
} |
|
5235
|
|
|
return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); |
|
5236
|
|
|
}, |
|
5237
|
|
|
easeOutCirc: function(t) { |
|
5238
|
|
|
return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); |
|
5239
|
|
|
}, |
|
5240
|
|
|
easeInOutCirc: function(t) { |
|
5241
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5242
|
|
|
return -1 / 2 * (Math.sqrt(1 - t * t) - 1); |
|
5243
|
|
|
} |
|
5244
|
|
|
return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); |
|
5245
|
|
|
}, |
|
5246
|
|
|
easeInElastic: function(t) { |
|
|
|
|
|
|
5247
|
|
|
var s = 1.70158; |
|
|
|
|
|
|
5248
|
|
|
var p = 0; |
|
5249
|
|
|
var a = 1; |
|
5250
|
|
|
if (t === 0) { |
|
5251
|
|
|
return 0; |
|
5252
|
|
|
} |
|
5253
|
|
|
if ((t /= 1) === 1) { |
|
5254
|
|
|
return 1; |
|
5255
|
|
|
} |
|
5256
|
|
|
if (!p) { |
|
5257
|
|
|
p = 1 * 0.3; |
|
5258
|
|
|
} |
|
5259
|
|
|
if (a < Math.abs(1)) { |
|
5260
|
|
|
a = 1; |
|
5261
|
|
|
s = p / 4; |
|
5262
|
|
|
} else { |
|
5263
|
|
|
s = p / (2 * Math.PI) * Math.asin(1 / a); |
|
5264
|
|
|
} |
|
5265
|
|
|
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); |
|
5266
|
|
|
}, |
|
5267
|
|
|
easeOutElastic: function(t) { |
|
|
|
|
|
|
5268
|
|
|
var s = 1.70158; |
|
|
|
|
|
|
5269
|
|
|
var p = 0; |
|
5270
|
|
|
var a = 1; |
|
5271
|
|
|
if (t === 0) { |
|
5272
|
|
|
return 0; |
|
5273
|
|
|
} |
|
5274
|
|
|
if ((t /= 1) === 1) { |
|
5275
|
|
|
return 1; |
|
5276
|
|
|
} |
|
5277
|
|
|
if (!p) { |
|
5278
|
|
|
p = 1 * 0.3; |
|
5279
|
|
|
} |
|
5280
|
|
|
if (a < Math.abs(1)) { |
|
5281
|
|
|
a = 1; |
|
5282
|
|
|
s = p / 4; |
|
5283
|
|
|
} else { |
|
5284
|
|
|
s = p / (2 * Math.PI) * Math.asin(1 / a); |
|
5285
|
|
|
} |
|
5286
|
|
|
return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; |
|
5287
|
|
|
}, |
|
5288
|
|
|
easeInOutElastic: function(t) { |
|
5289
|
|
|
var s = 1.70158; |
|
|
|
|
|
|
5290
|
|
|
var p = 0; |
|
5291
|
|
|
var a = 1; |
|
5292
|
|
|
if (t === 0) { |
|
5293
|
|
|
return 0; |
|
5294
|
|
|
} |
|
5295
|
|
|
if ((t /= 1 / 2) === 2) { |
|
5296
|
|
|
return 1; |
|
5297
|
|
|
} |
|
5298
|
|
|
if (!p) { |
|
5299
|
|
|
p = 1 * (0.3 * 1.5); |
|
5300
|
|
|
} |
|
5301
|
|
|
if (a < Math.abs(1)) { |
|
5302
|
|
|
a = 1; |
|
5303
|
|
|
s = p / 4; |
|
5304
|
|
|
} else { |
|
5305
|
|
|
s = p / (2 * Math.PI) * Math.asin(1 / a); |
|
5306
|
|
|
} |
|
5307
|
|
|
if (t < 1) { |
|
5308
|
|
|
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); |
|
5309
|
|
|
} |
|
5310
|
|
|
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; |
|
5311
|
|
|
}, |
|
5312
|
|
|
easeInBack: function(t) { |
|
5313
|
|
|
var s = 1.70158; |
|
5314
|
|
|
return 1 * (t /= 1) * t * ((s + 1) * t - s); |
|
5315
|
|
|
}, |
|
5316
|
|
|
easeOutBack: function(t) { |
|
5317
|
|
|
var s = 1.70158; |
|
5318
|
|
|
return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); |
|
5319
|
|
|
}, |
|
5320
|
|
|
easeInOutBack: function(t) { |
|
5321
|
|
|
var s = 1.70158; |
|
5322
|
|
|
if ((t /= 1 / 2) < 1) { |
|
5323
|
|
|
return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); |
|
5324
|
|
|
} |
|
5325
|
|
|
return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); |
|
5326
|
|
|
}, |
|
5327
|
|
|
easeInBounce: function(t) { |
|
5328
|
|
|
return 1 - easingEffects.easeOutBounce(1 - t); |
|
5329
|
|
|
}, |
|
5330
|
|
|
easeOutBounce: function(t) { |
|
5331
|
|
|
if ((t /= 1) < (1 / 2.75)) { |
|
5332
|
|
|
return 1 * (7.5625 * t * t); |
|
5333
|
|
|
} else if (t < (2 / 2.75)) { |
|
5334
|
|
|
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); |
|
5335
|
|
|
} else if (t < (2.5 / 2.75)) { |
|
5336
|
|
|
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); |
|
5337
|
|
|
} else { |
|
5338
|
|
|
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); |
|
5339
|
|
|
} |
|
5340
|
|
|
}, |
|
5341
|
|
|
easeInOutBounce: function(t) { |
|
5342
|
|
|
if (t < 1 / 2) { |
|
5343
|
|
|
return easingEffects.easeInBounce(t * 2) * 0.5; |
|
5344
|
|
|
} |
|
5345
|
|
|
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; |
|
5346
|
|
|
} |
|
5347
|
|
|
}; |
|
5348
|
|
|
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ |
|
5349
|
|
|
helpers.requestAnimFrame = (function() { |
|
5350
|
|
|
return window.requestAnimationFrame || |
|
5351
|
|
|
window.webkitRequestAnimationFrame || |
|
5352
|
|
|
window.mozRequestAnimationFrame || |
|
5353
|
|
|
window.oRequestAnimationFrame || |
|
5354
|
|
|
window.msRequestAnimationFrame || |
|
5355
|
|
|
function(callback) { |
|
5356
|
|
|
return window.setTimeout(callback, 1000 / 60); |
|
5357
|
|
|
}; |
|
5358
|
|
|
})(); |
|
5359
|
|
|
helpers.cancelAnimFrame = (function() { |
|
5360
|
|
|
return window.cancelAnimationFrame || |
|
5361
|
|
|
window.webkitCancelAnimationFrame || |
|
5362
|
|
|
window.mozCancelAnimationFrame || |
|
5363
|
|
|
window.oCancelAnimationFrame || |
|
5364
|
|
|
window.msCancelAnimationFrame || |
|
5365
|
|
|
function(callback) { |
|
5366
|
|
|
return window.clearTimeout(callback, 1000 / 60); |
|
5367
|
|
|
}; |
|
5368
|
|
|
})(); |
|
5369
|
|
|
//-- DOM methods |
|
5370
|
|
|
helpers.getRelativePosition = function(evt, chart) { |
|
5371
|
|
|
var mouseX, mouseY; |
|
5372
|
|
|
var e = evt.originalEvent || evt, |
|
5373
|
|
|
canvas = evt.currentTarget || evt.srcElement, |
|
5374
|
|
|
boundingRect = canvas.getBoundingClientRect(); |
|
5375
|
|
|
|
|
5376
|
|
|
var touches = e.touches; |
|
5377
|
|
|
if (touches && touches.length > 0) { |
|
5378
|
|
|
mouseX = touches[0].clientX; |
|
5379
|
|
|
mouseY = touches[0].clientY; |
|
5380
|
|
|
|
|
5381
|
|
|
} else { |
|
5382
|
|
|
mouseX = e.clientX; |
|
5383
|
|
|
mouseY = e.clientY; |
|
5384
|
|
|
} |
|
5385
|
|
|
|
|
5386
|
|
|
// Scale mouse coordinates into canvas coordinates |
|
5387
|
|
|
// by following the pattern laid out by 'jerryj' in the comments of |
|
5388
|
|
|
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ |
|
5389
|
|
|
var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); |
|
5390
|
|
|
var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); |
|
5391
|
|
|
var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); |
|
5392
|
|
|
var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); |
|
5393
|
|
|
var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; |
|
5394
|
|
|
var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; |
|
5395
|
|
|
|
|
5396
|
|
|
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However |
|
5397
|
|
|
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here |
|
5398
|
|
|
mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); |
|
5399
|
|
|
mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); |
|
5400
|
|
|
|
|
5401
|
|
|
return { |
|
5402
|
|
|
x: mouseX, |
|
5403
|
|
|
y: mouseY |
|
5404
|
|
|
}; |
|
5405
|
|
|
|
|
5406
|
|
|
}; |
|
5407
|
|
|
helpers.addEvent = function(node, eventType, method) { |
|
5408
|
|
|
if (node.addEventListener) { |
|
5409
|
|
|
node.addEventListener(eventType, method); |
|
5410
|
|
|
} else if (node.attachEvent) { |
|
5411
|
|
|
node.attachEvent("on" + eventType, method); |
|
5412
|
|
|
} else { |
|
5413
|
|
|
node["on" + eventType] = method; |
|
5414
|
|
|
} |
|
5415
|
|
|
}; |
|
5416
|
|
|
helpers.removeEvent = function(node, eventType, handler) { |
|
5417
|
|
|
if (node.removeEventListener) { |
|
5418
|
|
|
node.removeEventListener(eventType, handler, false); |
|
5419
|
|
|
} else if (node.detachEvent) { |
|
5420
|
|
|
node.detachEvent("on" + eventType, handler); |
|
5421
|
|
|
} else { |
|
5422
|
|
|
node["on" + eventType] = helpers.noop; |
|
5423
|
|
|
} |
|
5424
|
|
|
}; |
|
5425
|
|
|
helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) { |
|
5426
|
|
|
// Create the events object if it's not already present |
|
5427
|
|
|
var events = chartInstance.events = chartInstance.events || {}; |
|
5428
|
|
|
|
|
5429
|
|
|
helpers.each(arrayOfEvents, function(eventName) { |
|
5430
|
|
|
events[eventName] = function() { |
|
5431
|
|
|
handler.apply(chartInstance, arguments); |
|
5432
|
|
|
}; |
|
5433
|
|
|
helpers.addEvent(chartInstance.chart.canvas, eventName, events[eventName]); |
|
5434
|
|
|
}); |
|
5435
|
|
|
}; |
|
5436
|
|
|
helpers.unbindEvents = function(chartInstance, arrayOfEvents) { |
|
5437
|
|
|
var canvas = chartInstance.chart.canvas; |
|
5438
|
|
|
helpers.each(arrayOfEvents, function(handler, eventName) { |
|
5439
|
|
|
helpers.removeEvent(canvas, eventName, handler); |
|
5440
|
|
|
}); |
|
5441
|
|
|
}; |
|
5442
|
|
|
|
|
5443
|
|
|
// Private helper function to convert max-width/max-height values that may be percentages into a number |
|
5444
|
|
|
function parseMaxStyle(styleValue, node, parentProperty) { |
|
5445
|
|
|
var valueInPixels; |
|
5446
|
|
|
if (typeof(styleValue) === 'string') { |
|
5447
|
|
|
valueInPixels = parseInt(styleValue, 10); |
|
5448
|
|
|
|
|
5449
|
|
|
if (styleValue.indexOf('%') != -1) { |
|
5450
|
|
|
// percentage * size in dimension |
|
5451
|
|
|
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; |
|
5452
|
|
|
} |
|
5453
|
|
|
} else { |
|
5454
|
|
|
valueInPixels = styleValue; |
|
5455
|
|
|
} |
|
5456
|
|
|
|
|
5457
|
|
|
return valueInPixels; |
|
5458
|
|
|
} |
|
5459
|
|
|
|
|
5460
|
|
|
/** |
|
5461
|
|
|
* Returns if the given value contains an effective constraint. |
|
5462
|
|
|
* @private |
|
5463
|
|
|
*/ |
|
5464
|
|
|
function isConstrainedValue(value) { |
|
5465
|
|
|
return value !== undefined && value !== null && value !== 'none'; |
|
5466
|
|
|
} |
|
5467
|
|
|
|
|
5468
|
|
|
// Private helper to get a constraint dimension |
|
5469
|
|
|
// @param domNode : the node to check the constraint on |
|
5470
|
|
|
// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) |
|
5471
|
|
|
// @param percentageProperty : property of parent to use when calculating width as a percentage |
|
5472
|
|
|
// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser |
|
5473
|
|
|
function getConstraintDimension(domNode, maxStyle, percentageProperty) { |
|
5474
|
|
|
var view = document.defaultView; |
|
5475
|
|
|
var parentNode = domNode.parentNode; |
|
5476
|
|
|
var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; |
|
5477
|
|
|
var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; |
|
5478
|
|
|
var hasCNode = isConstrainedValue(constrainedNode); |
|
5479
|
|
|
var hasCContainer = isConstrainedValue(constrainedContainer); |
|
5480
|
|
|
var infinity = Number.POSITIVE_INFINITY; |
|
5481
|
|
|
|
|
5482
|
|
|
if (hasCNode || hasCContainer) { |
|
5483
|
|
|
return Math.min( |
|
5484
|
|
|
hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, |
|
5485
|
|
|
hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); |
|
5486
|
|
|
} |
|
5487
|
|
|
|
|
5488
|
|
|
return 'none'; |
|
5489
|
|
|
} |
|
5490
|
|
|
// returns Number or undefined if no constraint |
|
5491
|
|
|
helpers.getConstraintWidth = function(domNode) { |
|
5492
|
|
|
return getConstraintDimension(domNode, 'max-width', 'clientWidth'); |
|
5493
|
|
|
}; |
|
5494
|
|
|
// returns Number or undefined if no constraint |
|
5495
|
|
|
helpers.getConstraintHeight = function(domNode) { |
|
5496
|
|
|
return getConstraintDimension(domNode, 'max-height', 'clientHeight'); |
|
5497
|
|
|
}; |
|
5498
|
|
|
helpers.getMaximumWidth = function(domNode) { |
|
5499
|
|
|
var container = domNode.parentNode; |
|
5500
|
|
|
var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right')); |
|
5501
|
|
|
var w = container.clientWidth - padding; |
|
5502
|
|
|
var cw = helpers.getConstraintWidth(domNode); |
|
5503
|
|
|
return isNaN(cw)? w : Math.min(w, cw); |
|
5504
|
|
|
}; |
|
5505
|
|
|
helpers.getMaximumHeight = function(domNode) { |
|
5506
|
|
|
var container = domNode.parentNode; |
|
5507
|
|
|
var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom')); |
|
5508
|
|
|
var h = container.clientHeight - padding; |
|
5509
|
|
|
var ch = helpers.getConstraintHeight(domNode); |
|
5510
|
|
|
return isNaN(ch)? h : Math.min(h, ch); |
|
5511
|
|
|
}; |
|
5512
|
|
|
helpers.getStyle = function(el, property) { |
|
5513
|
|
|
return el.currentStyle ? |
|
5514
|
|
|
el.currentStyle[property] : |
|
5515
|
|
|
document.defaultView.getComputedStyle(el, null).getPropertyValue(property); |
|
5516
|
|
|
}; |
|
5517
|
|
|
helpers.retinaScale = function(chart) { |
|
5518
|
|
|
var ctx = chart.ctx; |
|
5519
|
|
|
var canvas = chart.canvas; |
|
5520
|
|
|
var width = canvas.width; |
|
5521
|
|
|
var height = canvas.height; |
|
5522
|
|
|
var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1; |
|
5523
|
|
|
|
|
5524
|
|
|
if (pixelRatio !== 1) { |
|
5525
|
|
|
canvas.height = height * pixelRatio; |
|
5526
|
|
|
canvas.width = width * pixelRatio; |
|
5527
|
|
|
ctx.scale(pixelRatio, pixelRatio); |
|
5528
|
|
|
|
|
5529
|
|
|
// Store the device pixel ratio so that we can go backwards in `destroy`. |
|
5530
|
|
|
// The devicePixelRatio changes with zoom, so there are no guarantees that it is the same |
|
5531
|
|
|
// when destroy is called |
|
5532
|
|
|
chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio; |
|
5533
|
|
|
} |
|
5534
|
|
|
|
|
5535
|
|
|
canvas.style.width = width + 'px'; |
|
5536
|
|
|
canvas.style.height = height + 'px'; |
|
5537
|
|
|
}; |
|
5538
|
|
|
//-- Canvas methods |
|
5539
|
|
|
helpers.clear = function(chart) { |
|
5540
|
|
|
chart.ctx.clearRect(0, 0, chart.width, chart.height); |
|
5541
|
|
|
}; |
|
5542
|
|
|
helpers.fontString = function(pixelSize, fontStyle, fontFamily) { |
|
5543
|
|
|
return fontStyle + " " + pixelSize + "px " + fontFamily; |
|
5544
|
|
|
}; |
|
5545
|
|
|
helpers.longestText = function(ctx, font, arrayOfThings, cache) { |
|
5546
|
|
|
cache = cache || {}; |
|
5547
|
|
|
var data = cache.data = cache.data || {}; |
|
5548
|
|
|
var gc = cache.garbageCollect = cache.garbageCollect || []; |
|
5549
|
|
|
|
|
5550
|
|
|
if (cache.font !== font) { |
|
5551
|
|
|
data = cache.data = {}; |
|
5552
|
|
|
gc = cache.garbageCollect = []; |
|
5553
|
|
|
cache.font = font; |
|
5554
|
|
|
} |
|
5555
|
|
|
|
|
5556
|
|
|
ctx.font = font; |
|
5557
|
|
|
var longest = 0; |
|
5558
|
|
|
helpers.each(arrayOfThings, function(thing) { |
|
5559
|
|
|
// Undefined strings and arrays should not be measured |
|
5560
|
|
|
if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { |
|
5561
|
|
|
longest = helpers.measureText(ctx, data, gc, longest, thing); |
|
5562
|
|
|
} else if (helpers.isArray(thing)) { |
|
5563
|
|
|
// if it is an array lets measure each element |
|
5564
|
|
|
// to do maybe simplify this function a bit so we can do this more recursively? |
|
5565
|
|
|
helpers.each(thing, function(nestedThing) { |
|
5566
|
|
|
// Undefined strings and arrays should not be measured |
|
5567
|
|
|
if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { |
|
5568
|
|
|
longest = helpers.measureText(ctx, data, gc, longest, nestedThing); |
|
5569
|
|
|
} |
|
5570
|
|
|
}); |
|
5571
|
|
|
} |
|
5572
|
|
|
}); |
|
5573
|
|
|
|
|
5574
|
|
|
var gcLen = gc.length / 2; |
|
5575
|
|
|
if (gcLen > arrayOfThings.length) { |
|
5576
|
|
|
for (var i = 0; i < gcLen; i++) { |
|
5577
|
|
|
delete data[gc[i]]; |
|
5578
|
|
|
} |
|
5579
|
|
|
gc.splice(0, gcLen); |
|
5580
|
|
|
} |
|
5581
|
|
|
return longest; |
|
5582
|
|
|
}; |
|
5583
|
|
|
helpers.measureText = function (ctx, data, gc, longest, string) { |
|
5584
|
|
|
var textWidth = data[string]; |
|
5585
|
|
|
if (!textWidth) { |
|
5586
|
|
|
textWidth = data[string] = ctx.measureText(string).width; |
|
5587
|
|
|
gc.push(string); |
|
5588
|
|
|
} |
|
5589
|
|
|
if (textWidth > longest) { |
|
5590
|
|
|
longest = textWidth; |
|
5591
|
|
|
} |
|
5592
|
|
|
return longest; |
|
5593
|
|
|
}; |
|
5594
|
|
|
helpers.numberOfLabelLines = function(arrayOfThings) { |
|
5595
|
|
|
var numberOfLines = 1; |
|
5596
|
|
|
helpers.each(arrayOfThings, function(thing) { |
|
5597
|
|
|
if (helpers.isArray(thing)) { |
|
5598
|
|
|
if (thing.length > numberOfLines) { |
|
5599
|
|
|
numberOfLines = thing.length; |
|
5600
|
|
|
} |
|
5601
|
|
|
} |
|
5602
|
|
|
}); |
|
5603
|
|
|
return numberOfLines; |
|
5604
|
|
|
}; |
|
5605
|
|
|
helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) { |
|
5606
|
|
|
ctx.beginPath(); |
|
5607
|
|
|
ctx.moveTo(x + radius, y); |
|
5608
|
|
|
ctx.lineTo(x + width - radius, y); |
|
5609
|
|
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius); |
|
5610
|
|
|
ctx.lineTo(x + width, y + height - radius); |
|
5611
|
|
|
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); |
|
5612
|
|
|
ctx.lineTo(x + radius, y + height); |
|
5613
|
|
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius); |
|
5614
|
|
|
ctx.lineTo(x, y + radius); |
|
5615
|
|
|
ctx.quadraticCurveTo(x, y, x + radius, y); |
|
5616
|
|
|
ctx.closePath(); |
|
5617
|
|
|
}; |
|
5618
|
|
|
helpers.color = function(c) { |
|
5619
|
|
|
if (!color) { |
|
5620
|
|
|
console.log('Color.js not found!'); |
|
|
|
|
|
|
5621
|
|
|
return c; |
|
5622
|
|
|
} |
|
5623
|
|
|
|
|
5624
|
|
|
/* global CanvasGradient */ |
|
5625
|
|
|
if (c instanceof CanvasGradient) { |
|
5626
|
|
|
return color(Chart.defaults.global.defaultColor); |
|
5627
|
|
|
} |
|
5628
|
|
|
|
|
5629
|
|
|
return color(c); |
|
5630
|
|
|
}; |
|
5631
|
|
|
helpers.addResizeListener = function(node, callback) { |
|
5632
|
|
|
// Hide an iframe before the node |
|
5633
|
|
|
var hiddenIframe = document.createElement('iframe'); |
|
5634
|
|
|
var hiddenIframeClass = 'chartjs-hidden-iframe'; |
|
5635
|
|
|
|
|
5636
|
|
|
if (hiddenIframe.classlist) { |
|
5637
|
|
|
// can use classlist |
|
5638
|
|
|
hiddenIframe.classlist.add(hiddenIframeClass); |
|
5639
|
|
|
} else { |
|
5640
|
|
|
hiddenIframe.setAttribute('class', hiddenIframeClass); |
|
5641
|
|
|
} |
|
5642
|
|
|
|
|
5643
|
|
|
// Set the style |
|
5644
|
|
|
var style = hiddenIframe.style; |
|
5645
|
|
|
style.width = '100%'; |
|
5646
|
|
|
style.display = 'block'; |
|
5647
|
|
|
style.border = 0; |
|
5648
|
|
|
style.height = 0; |
|
5649
|
|
|
style.margin = 0; |
|
5650
|
|
|
style.position = 'absolute'; |
|
5651
|
|
|
style.left = 0; |
|
5652
|
|
|
style.right = 0; |
|
5653
|
|
|
style.top = 0; |
|
5654
|
|
|
style.bottom = 0; |
|
5655
|
|
|
|
|
5656
|
|
|
// Insert the iframe so that contentWindow is available |
|
5657
|
|
|
node.insertBefore(hiddenIframe, node.firstChild); |
|
5658
|
|
|
|
|
5659
|
|
|
(hiddenIframe.contentWindow || hiddenIframe).onresize = function() { |
|
5660
|
|
|
if (callback) { |
|
5661
|
|
|
callback(); |
|
5662
|
|
|
} |
|
5663
|
|
|
}; |
|
5664
|
|
|
}; |
|
5665
|
|
|
helpers.removeResizeListener = function(node) { |
|
5666
|
|
|
var hiddenIframe = node.querySelector('.chartjs-hidden-iframe'); |
|
5667
|
|
|
|
|
5668
|
|
|
// Remove the resize detect iframe |
|
5669
|
|
|
if (hiddenIframe) { |
|
5670
|
|
|
hiddenIframe.parentNode.removeChild(hiddenIframe); |
|
5671
|
|
|
} |
|
5672
|
|
|
}; |
|
5673
|
|
|
helpers.isArray = Array.isArray? |
|
5674
|
|
|
function(obj) { return Array.isArray(obj); } : |
|
5675
|
|
|
function(obj) { |
|
5676
|
|
|
return Object.prototype.toString.call(obj) === '[object Array]'; |
|
5677
|
|
|
}; |
|
5678
|
|
|
//! @see http://stackoverflow.com/a/14853974 |
|
5679
|
|
|
helpers.arrayEquals = function(a0, a1) { |
|
5680
|
|
|
var i, ilen, v0, v1; |
|
5681
|
|
|
|
|
5682
|
|
|
if (!a0 || !a1 || a0.length != a1.length) { |
|
5683
|
|
|
return false; |
|
5684
|
|
|
} |
|
5685
|
|
|
|
|
5686
|
|
|
for (i = 0, ilen=a0.length; i < ilen; ++i) { |
|
5687
|
|
|
v0 = a0[i]; |
|
5688
|
|
|
v1 = a1[i]; |
|
5689
|
|
|
|
|
5690
|
|
|
if (v0 instanceof Array && v1 instanceof Array) { |
|
5691
|
|
|
if (!helpers.arrayEquals(v0, v1)) { |
|
5692
|
|
|
return false; |
|
5693
|
|
|
} |
|
5694
|
|
|
} else if (v0 != v1) { |
|
5695
|
|
|
// NOTE: two different object instances will never be equal: {x:20} != {x:20} |
|
5696
|
|
|
return false; |
|
5697
|
|
|
} |
|
5698
|
|
|
} |
|
5699
|
|
|
|
|
5700
|
|
|
return true; |
|
5701
|
|
|
}; |
|
5702
|
|
|
helpers.callCallback = function(fn, args, _tArg) { |
|
5703
|
|
|
if (fn && typeof fn.call === 'function') { |
|
5704
|
|
|
fn.apply(_tArg, args); |
|
5705
|
|
|
} |
|
5706
|
|
|
}; |
|
5707
|
|
|
helpers.getHoverColor = function(color) { |
|
5708
|
|
|
/* global CanvasPattern */ |
|
5709
|
|
|
return (color instanceof CanvasPattern) ? |
|
5710
|
|
|
color : |
|
5711
|
|
|
helpers.color(color).saturate(0.5).darken(0.1).rgbString(); |
|
5712
|
|
|
}; |
|
5713
|
|
|
}; |
|
5714
|
|
|
|
|
5715
|
|
|
},{"3":3}],27:[function(require,module,exports){ |
|
5716
|
|
|
"use strict"; |
|
5717
|
|
|
|
|
5718
|
|
|
module.exports = function() { |
|
5719
|
|
|
|
|
5720
|
|
|
//Occupy the global variable of Chart, and create a simple base class |
|
5721
|
|
|
var Chart = function(context, config) { |
|
5722
|
|
|
var me = this; |
|
5723
|
|
|
var helpers = Chart.helpers; |
|
5724
|
|
|
me.config = config || { |
|
5725
|
|
|
data: { |
|
5726
|
|
|
datasets: [] |
|
5727
|
|
|
} |
|
5728
|
|
|
}; |
|
5729
|
|
|
|
|
5730
|
|
|
// Support a jQuery'd canvas element |
|
5731
|
|
|
if (context.length && context[0].getContext) { |
|
5732
|
|
|
context = context[0]; |
|
5733
|
|
|
} |
|
5734
|
|
|
|
|
5735
|
|
|
// Support a canvas domnode |
|
5736
|
|
|
if (context.getContext) { |
|
5737
|
|
|
context = context.getContext("2d"); |
|
5738
|
|
|
} |
|
5739
|
|
|
|
|
5740
|
|
|
me.ctx = context; |
|
5741
|
|
|
me.canvas = context.canvas; |
|
5742
|
|
|
|
|
5743
|
|
|
context.canvas.style.display = context.canvas.style.display || 'block'; |
|
5744
|
|
|
|
|
5745
|
|
|
// Figure out what the size of the chart will be. |
|
5746
|
|
|
// If the canvas has a specified width and height, we use those else |
|
5747
|
|
|
// we look to see if the canvas node has a CSS width and height. |
|
5748
|
|
|
// If there is still no height, fill the parent container |
|
5749
|
|
|
me.width = context.canvas.width || parseInt(helpers.getStyle(context.canvas, 'width'), 10) || helpers.getMaximumWidth(context.canvas); |
|
5750
|
|
|
me.height = context.canvas.height || parseInt(helpers.getStyle(context.canvas, 'height'), 10) || helpers.getMaximumHeight(context.canvas); |
|
5751
|
|
|
|
|
5752
|
|
|
me.aspectRatio = me.width / me.height; |
|
5753
|
|
|
|
|
5754
|
|
|
if (isNaN(me.aspectRatio) || isFinite(me.aspectRatio) === false) { |
|
5755
|
|
|
// If the canvas has no size, try and figure out what the aspect ratio will be. |
|
5756
|
|
|
// Some charts prefer square canvases (pie, radar, etc). If that is specified, use that |
|
5757
|
|
|
// else use the canvas default ratio of 2 |
|
5758
|
|
|
me.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2; |
|
5759
|
|
|
} |
|
5760
|
|
|
|
|
5761
|
|
|
// Store the original style of the element so we can set it back |
|
5762
|
|
|
me.originalCanvasStyleWidth = context.canvas.style.width; |
|
5763
|
|
|
me.originalCanvasStyleHeight = context.canvas.style.height; |
|
5764
|
|
|
|
|
5765
|
|
|
// High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. |
|
5766
|
|
|
helpers.retinaScale(me); |
|
5767
|
|
|
me.controller = new Chart.Controller(me); |
|
5768
|
|
|
|
|
5769
|
|
|
// Always bind this so that if the responsive state changes we still work |
|
5770
|
|
|
helpers.addResizeListener(context.canvas.parentNode, function() { |
|
5771
|
|
|
if (me.controller && me.controller.config.options.responsive) { |
|
5772
|
|
|
me.controller.resize(); |
|
5773
|
|
|
} |
|
5774
|
|
|
}); |
|
5775
|
|
|
|
|
5776
|
|
|
return me.controller ? me.controller : me; |
|
5777
|
|
|
|
|
5778
|
|
|
}; |
|
5779
|
|
|
|
|
5780
|
|
|
//Globally expose the defaults to allow for user updating/changing |
|
5781
|
|
|
Chart.defaults = { |
|
5782
|
|
|
global: { |
|
5783
|
|
|
responsive: true, |
|
5784
|
|
|
responsiveAnimationDuration: 0, |
|
5785
|
|
|
maintainAspectRatio: true, |
|
5786
|
|
|
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"], |
|
5787
|
|
|
hover: { |
|
5788
|
|
|
onHover: null, |
|
5789
|
|
|
mode: 'single', |
|
5790
|
|
|
animationDuration: 400 |
|
5791
|
|
|
}, |
|
5792
|
|
|
onClick: null, |
|
5793
|
|
|
defaultColor: 'rgba(0,0,0,0.1)', |
|
5794
|
|
|
defaultFontColor: '#666', |
|
5795
|
|
|
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", |
|
5796
|
|
|
defaultFontSize: 12, |
|
5797
|
|
|
defaultFontStyle: 'normal', |
|
5798
|
|
|
showLines: true, |
|
5799
|
|
|
|
|
5800
|
|
|
// Element defaults defined in element extensions |
|
5801
|
|
|
elements: {}, |
|
5802
|
|
|
|
|
5803
|
|
|
// Legend callback string |
|
5804
|
|
|
legendCallback: function(chart) { |
|
5805
|
|
|
var text = []; |
|
5806
|
|
|
text.push('<ul class="' + chart.id + '-legend">'); |
|
5807
|
|
|
for (var i = 0; i < chart.data.datasets.length; i++) { |
|
5808
|
|
|
text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); |
|
5809
|
|
|
if (chart.data.datasets[i].label) { |
|
5810
|
|
|
text.push(chart.data.datasets[i].label); |
|
5811
|
|
|
} |
|
5812
|
|
|
text.push('</li>'); |
|
5813
|
|
|
} |
|
5814
|
|
|
text.push('</ul>'); |
|
5815
|
|
|
|
|
5816
|
|
|
return text.join(""); |
|
5817
|
|
|
} |
|
5818
|
|
|
} |
|
5819
|
|
|
}; |
|
5820
|
|
|
|
|
5821
|
|
|
Chart.Chart = Chart; |
|
5822
|
|
|
|
|
5823
|
|
|
return Chart; |
|
5824
|
|
|
|
|
5825
|
|
|
}; |
|
5826
|
|
|
|
|
5827
|
|
|
},{}],28:[function(require,module,exports){ |
|
5828
|
|
|
"use strict"; |
|
5829
|
|
|
|
|
5830
|
|
|
module.exports = function(Chart) { |
|
5831
|
|
|
|
|
5832
|
|
|
var helpers = Chart.helpers; |
|
5833
|
|
|
|
|
5834
|
|
|
// The layout service is very self explanatory. It's responsible for the layout within a chart. |
|
5835
|
|
|
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need |
|
5836
|
|
|
// It is this service's responsibility of carrying out that layout. |
|
5837
|
|
|
Chart.layoutService = { |
|
5838
|
|
|
defaults: {}, |
|
5839
|
|
|
|
|
5840
|
|
|
// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins. |
|
5841
|
|
|
addBox: function(chartInstance, box) { |
|
5842
|
|
|
if (!chartInstance.boxes) { |
|
5843
|
|
|
chartInstance.boxes = []; |
|
5844
|
|
|
} |
|
5845
|
|
|
chartInstance.boxes.push(box); |
|
5846
|
|
|
}, |
|
5847
|
|
|
|
|
5848
|
|
|
removeBox: function(chartInstance, box) { |
|
5849
|
|
|
if (!chartInstance.boxes) { |
|
5850
|
|
|
return; |
|
5851
|
|
|
} |
|
5852
|
|
|
chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1); |
|
5853
|
|
|
}, |
|
5854
|
|
|
|
|
5855
|
|
|
// The most important function |
|
5856
|
|
|
update: function(chartInstance, width, height) { |
|
5857
|
|
|
|
|
5858
|
|
|
if (!chartInstance) { |
|
5859
|
|
|
return; |
|
5860
|
|
|
} |
|
5861
|
|
|
|
|
5862
|
|
|
var xPadding = 0; |
|
5863
|
|
|
var yPadding = 0; |
|
5864
|
|
|
|
|
5865
|
|
|
var leftBoxes = helpers.where(chartInstance.boxes, function(box) { |
|
5866
|
|
|
return box.options.position === "left"; |
|
5867
|
|
|
}); |
|
5868
|
|
|
var rightBoxes = helpers.where(chartInstance.boxes, function(box) { |
|
5869
|
|
|
return box.options.position === "right"; |
|
5870
|
|
|
}); |
|
5871
|
|
|
var topBoxes = helpers.where(chartInstance.boxes, function(box) { |
|
5872
|
|
|
return box.options.position === "top"; |
|
5873
|
|
|
}); |
|
5874
|
|
|
var bottomBoxes = helpers.where(chartInstance.boxes, function(box) { |
|
5875
|
|
|
return box.options.position === "bottom"; |
|
5876
|
|
|
}); |
|
5877
|
|
|
|
|
5878
|
|
|
// Boxes that overlay the chartarea such as the radialLinear scale |
|
5879
|
|
|
var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) { |
|
5880
|
|
|
return box.options.position === "chartArea"; |
|
5881
|
|
|
}); |
|
5882
|
|
|
|
|
5883
|
|
|
// Ensure that full width boxes are at the very top / bottom |
|
5884
|
|
|
topBoxes.sort(function(a, b) { |
|
5885
|
|
|
return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0); |
|
5886
|
|
|
}); |
|
5887
|
|
|
bottomBoxes.sort(function(a, b) { |
|
5888
|
|
|
return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0); |
|
5889
|
|
|
}); |
|
5890
|
|
|
|
|
5891
|
|
|
// Essentially we now have any number of boxes on each of the 4 sides. |
|
5892
|
|
|
// Our canvas looks like the following. |
|
5893
|
|
|
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and |
|
5894
|
|
|
// B1 is the bottom axis |
|
5895
|
|
|
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays |
|
5896
|
|
|
// These locations are single-box locations only, when trying to register a chartArea location that is already taken, |
|
5897
|
|
|
// an error will be thrown. |
|
5898
|
|
|
// |
|
5899
|
|
|
// |----------------------------------------------------| |
|
5900
|
|
|
// | T1 (Full Width) | |
|
5901
|
|
|
// |----------------------------------------------------| |
|
5902
|
|
|
// | | | T2 | | |
|
5903
|
|
|
// | |----|-------------------------------------|----| |
|
5904
|
|
|
// | | | C1 | | C2 | | |
|
5905
|
|
|
// | | |----| |----| | |
|
5906
|
|
|
// | | | | | |
|
5907
|
|
|
// | L1 | L2 | ChartArea (C0) | R1 | |
|
5908
|
|
|
// | | | | | |
|
5909
|
|
|
// | | |----| |----| | |
|
5910
|
|
|
// | | | C3 | | C4 | | |
|
5911
|
|
|
// | |----|-------------------------------------|----| |
|
5912
|
|
|
// | | | B1 | | |
|
5913
|
|
|
// |----------------------------------------------------| |
|
5914
|
|
|
// | B2 (Full Width) | |
|
5915
|
|
|
// |----------------------------------------------------| |
|
5916
|
|
|
// |
|
5917
|
|
|
// What we do to find the best sizing, we do the following |
|
5918
|
|
|
// 1. Determine the minimum size of the chart area. |
|
5919
|
|
|
// 2. Split the remaining width equally between each vertical axis |
|
5920
|
|
|
// 3. Split the remaining height equally between each horizontal axis |
|
5921
|
|
|
// 4. Give each layout the maximum size it can be. The layout will return it's minimum size |
|
5922
|
|
|
// 5. Adjust the sizes of each axis based on it's minimum reported size. |
|
5923
|
|
|
// 6. Refit each axis |
|
5924
|
|
|
// 7. Position each axis in the final location |
|
5925
|
|
|
// 8. Tell the chart the final location of the chart area |
|
5926
|
|
|
// 9. Tell any axes that overlay the chart area the positions of the chart area |
|
5927
|
|
|
|
|
5928
|
|
|
// Step 1 |
|
5929
|
|
|
var chartWidth = width - (2 * xPadding); |
|
5930
|
|
|
var chartHeight = height - (2 * yPadding); |
|
5931
|
|
|
var chartAreaWidth = chartWidth / 2; // min 50% |
|
5932
|
|
|
var chartAreaHeight = chartHeight / 2; // min 50% |
|
5933
|
|
|
|
|
5934
|
|
|
// Step 2 |
|
5935
|
|
|
var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); |
|
5936
|
|
|
|
|
5937
|
|
|
// Step 3 |
|
5938
|
|
|
var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); |
|
5939
|
|
|
|
|
5940
|
|
|
// Step 4 |
|
5941
|
|
|
var maxChartAreaWidth = chartWidth; |
|
5942
|
|
|
var maxChartAreaHeight = chartHeight; |
|
5943
|
|
|
var minBoxSizes = []; |
|
5944
|
|
|
|
|
5945
|
|
|
helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); |
|
5946
|
|
|
|
|
5947
|
|
|
function getMinimumBoxSize(box) { |
|
5948
|
|
|
var minSize; |
|
5949
|
|
|
var isHorizontal = box.isHorizontal(); |
|
5950
|
|
|
|
|
5951
|
|
|
if (isHorizontal) { |
|
5952
|
|
|
minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); |
|
5953
|
|
|
maxChartAreaHeight -= minSize.height; |
|
5954
|
|
|
} else { |
|
5955
|
|
|
minSize = box.update(verticalBoxWidth, chartAreaHeight); |
|
5956
|
|
|
maxChartAreaWidth -= minSize.width; |
|
5957
|
|
|
} |
|
5958
|
|
|
|
|
5959
|
|
|
minBoxSizes.push({ |
|
5960
|
|
|
horizontal: isHorizontal, |
|
5961
|
|
|
minSize: minSize, |
|
5962
|
|
|
box: box |
|
5963
|
|
|
}); |
|
5964
|
|
|
} |
|
5965
|
|
|
|
|
5966
|
|
|
// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could |
|
5967
|
|
|
// be if the axes are drawn at their minimum sizes. |
|
5968
|
|
|
|
|
5969
|
|
|
// Steps 5 & 6 |
|
5970
|
|
|
var totalLeftBoxesWidth = xPadding; |
|
5971
|
|
|
var totalRightBoxesWidth = xPadding; |
|
5972
|
|
|
var totalTopBoxesHeight = yPadding; |
|
5973
|
|
|
var totalBottomBoxesHeight = yPadding; |
|
5974
|
|
|
|
|
5975
|
|
|
// Update, and calculate the left and right margins for the horizontal boxes |
|
5976
|
|
|
helpers.each(leftBoxes.concat(rightBoxes), fitBox); |
|
5977
|
|
|
|
|
5978
|
|
|
helpers.each(leftBoxes, function(box) { |
|
5979
|
|
|
totalLeftBoxesWidth += box.width; |
|
5980
|
|
|
}); |
|
5981
|
|
|
|
|
5982
|
|
|
helpers.each(rightBoxes, function(box) { |
|
5983
|
|
|
totalRightBoxesWidth += box.width; |
|
5984
|
|
|
}); |
|
5985
|
|
|
|
|
5986
|
|
|
// Set the Left and Right margins for the horizontal boxes |
|
5987
|
|
|
helpers.each(topBoxes.concat(bottomBoxes), fitBox); |
|
5988
|
|
|
|
|
5989
|
|
|
// Function to fit a box |
|
5990
|
|
|
function fitBox(box) { |
|
5991
|
|
|
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) { |
|
5992
|
|
|
return minBoxSize.box === box; |
|
5993
|
|
|
}); |
|
5994
|
|
|
|
|
5995
|
|
|
if (minBoxSize) { |
|
5996
|
|
|
if (box.isHorizontal()) { |
|
5997
|
|
|
var scaleMargin = { |
|
5998
|
|
|
left: totalLeftBoxesWidth, |
|
5999
|
|
|
right: totalRightBoxesWidth, |
|
6000
|
|
|
top: 0, |
|
6001
|
|
|
bottom: 0 |
|
6002
|
|
|
}; |
|
6003
|
|
|
|
|
6004
|
|
|
// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends |
|
6005
|
|
|
// on the margin. Sometimes they need to increase in size slightly |
|
6006
|
|
|
box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); |
|
6007
|
|
|
} else { |
|
6008
|
|
|
box.update(minBoxSize.minSize.width, maxChartAreaHeight); |
|
6009
|
|
|
} |
|
6010
|
|
|
} |
|
6011
|
|
|
} |
|
6012
|
|
|
|
|
6013
|
|
|
// Figure out how much margin is on the top and bottom of the vertical boxes |
|
6014
|
|
|
helpers.each(topBoxes, function(box) { |
|
6015
|
|
|
totalTopBoxesHeight += box.height; |
|
6016
|
|
|
}); |
|
6017
|
|
|
|
|
6018
|
|
|
helpers.each(bottomBoxes, function(box) { |
|
6019
|
|
|
totalBottomBoxesHeight += box.height; |
|
6020
|
|
|
}); |
|
6021
|
|
|
|
|
6022
|
|
|
// Let the left layout know the final margin |
|
6023
|
|
|
helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); |
|
6024
|
|
|
|
|
6025
|
|
|
function finalFitVerticalBox(box) { |
|
6026
|
|
|
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) { |
|
6027
|
|
|
return minBoxSize.box === box; |
|
6028
|
|
|
}); |
|
6029
|
|
|
|
|
6030
|
|
|
var scaleMargin = { |
|
6031
|
|
|
left: 0, |
|
6032
|
|
|
right: 0, |
|
6033
|
|
|
top: totalTopBoxesHeight, |
|
6034
|
|
|
bottom: totalBottomBoxesHeight |
|
6035
|
|
|
}; |
|
6036
|
|
|
|
|
6037
|
|
|
if (minBoxSize) { |
|
6038
|
|
|
box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); |
|
6039
|
|
|
} |
|
6040
|
|
|
} |
|
6041
|
|
|
|
|
6042
|
|
|
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) |
|
6043
|
|
|
totalLeftBoxesWidth = xPadding; |
|
6044
|
|
|
totalRightBoxesWidth = xPadding; |
|
6045
|
|
|
totalTopBoxesHeight = yPadding; |
|
6046
|
|
|
totalBottomBoxesHeight = yPadding; |
|
6047
|
|
|
|
|
6048
|
|
|
helpers.each(leftBoxes, function(box) { |
|
6049
|
|
|
totalLeftBoxesWidth += box.width; |
|
6050
|
|
|
}); |
|
6051
|
|
|
|
|
6052
|
|
|
helpers.each(rightBoxes, function(box) { |
|
6053
|
|
|
totalRightBoxesWidth += box.width; |
|
6054
|
|
|
}); |
|
6055
|
|
|
|
|
6056
|
|
|
helpers.each(topBoxes, function(box) { |
|
6057
|
|
|
totalTopBoxesHeight += box.height; |
|
6058
|
|
|
}); |
|
6059
|
|
|
helpers.each(bottomBoxes, function(box) { |
|
6060
|
|
|
totalBottomBoxesHeight += box.height; |
|
6061
|
|
|
}); |
|
6062
|
|
|
|
|
6063
|
|
|
// Figure out if our chart area changed. This would occur if the dataset layout label rotation |
|
6064
|
|
|
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do |
|
6065
|
|
|
// without calling `fit` again |
|
6066
|
|
|
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; |
|
6067
|
|
|
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; |
|
6068
|
|
|
|
|
6069
|
|
|
if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { |
|
6070
|
|
|
helpers.each(leftBoxes, function(box) { |
|
6071
|
|
|
box.height = newMaxChartAreaHeight; |
|
6072
|
|
|
}); |
|
6073
|
|
|
|
|
6074
|
|
|
helpers.each(rightBoxes, function(box) { |
|
6075
|
|
|
box.height = newMaxChartAreaHeight; |
|
6076
|
|
|
}); |
|
6077
|
|
|
|
|
6078
|
|
|
helpers.each(topBoxes, function(box) { |
|
6079
|
|
|
if (!box.options.fullWidth) { |
|
6080
|
|
|
box.width = newMaxChartAreaWidth; |
|
6081
|
|
|
} |
|
6082
|
|
|
}); |
|
6083
|
|
|
|
|
6084
|
|
|
helpers.each(bottomBoxes, function(box) { |
|
6085
|
|
|
if (!box.options.fullWidth) { |
|
6086
|
|
|
box.width = newMaxChartAreaWidth; |
|
6087
|
|
|
} |
|
6088
|
|
|
}); |
|
6089
|
|
|
|
|
6090
|
|
|
maxChartAreaHeight = newMaxChartAreaHeight; |
|
6091
|
|
|
maxChartAreaWidth = newMaxChartAreaWidth; |
|
6092
|
|
|
} |
|
6093
|
|
|
|
|
6094
|
|
|
// Step 7 - Position the boxes |
|
6095
|
|
|
var left = xPadding; |
|
6096
|
|
|
var top = yPadding; |
|
6097
|
|
|
|
|
6098
|
|
|
helpers.each(leftBoxes.concat(topBoxes), placeBox); |
|
6099
|
|
|
|
|
6100
|
|
|
// Account for chart width and height |
|
6101
|
|
|
left += maxChartAreaWidth; |
|
6102
|
|
|
top += maxChartAreaHeight; |
|
6103
|
|
|
|
|
6104
|
|
|
helpers.each(rightBoxes, placeBox); |
|
6105
|
|
|
helpers.each(bottomBoxes, placeBox); |
|
6106
|
|
|
|
|
6107
|
|
|
function placeBox(box) { |
|
6108
|
|
|
if (box.isHorizontal()) { |
|
6109
|
|
|
box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth; |
|
6110
|
|
|
box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth; |
|
6111
|
|
|
box.top = top; |
|
6112
|
|
|
box.bottom = top + box.height; |
|
6113
|
|
|
|
|
6114
|
|
|
// Move to next point |
|
6115
|
|
|
top = box.bottom; |
|
6116
|
|
|
|
|
6117
|
|
|
} else { |
|
6118
|
|
|
|
|
6119
|
|
|
box.left = left; |
|
6120
|
|
|
box.right = left + box.width; |
|
6121
|
|
|
box.top = totalTopBoxesHeight; |
|
6122
|
|
|
box.bottom = totalTopBoxesHeight + maxChartAreaHeight; |
|
6123
|
|
|
|
|
6124
|
|
|
// Move to next point |
|
6125
|
|
|
left = box.right; |
|
6126
|
|
|
} |
|
6127
|
|
|
} |
|
6128
|
|
|
|
|
6129
|
|
|
// Step 8 |
|
6130
|
|
|
chartInstance.chartArea = { |
|
6131
|
|
|
left: totalLeftBoxesWidth, |
|
6132
|
|
|
top: totalTopBoxesHeight, |
|
6133
|
|
|
right: totalLeftBoxesWidth + maxChartAreaWidth, |
|
6134
|
|
|
bottom: totalTopBoxesHeight + maxChartAreaHeight |
|
6135
|
|
|
}; |
|
6136
|
|
|
|
|
6137
|
|
|
// Step 9 |
|
6138
|
|
|
helpers.each(chartAreaBoxes, function(box) { |
|
6139
|
|
|
box.left = chartInstance.chartArea.left; |
|
6140
|
|
|
box.top = chartInstance.chartArea.top; |
|
6141
|
|
|
box.right = chartInstance.chartArea.right; |
|
6142
|
|
|
box.bottom = chartInstance.chartArea.bottom; |
|
6143
|
|
|
|
|
6144
|
|
|
box.update(maxChartAreaWidth, maxChartAreaHeight); |
|
6145
|
|
|
}); |
|
6146
|
|
|
} |
|
6147
|
|
|
}; |
|
6148
|
|
|
}; |
|
6149
|
|
|
|
|
6150
|
|
|
},{}],29:[function(require,module,exports){ |
|
6151
|
|
|
"use strict"; |
|
6152
|
|
|
|
|
6153
|
|
|
module.exports = function(Chart) { |
|
6154
|
|
|
|
|
6155
|
|
|
var helpers = Chart.helpers; |
|
6156
|
|
|
var noop = helpers.noop; |
|
6157
|
|
|
|
|
6158
|
|
|
Chart.defaults.global.legend = { |
|
6159
|
|
|
|
|
6160
|
|
|
display: true, |
|
6161
|
|
|
position: 'top', |
|
6162
|
|
|
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes) |
|
6163
|
|
|
reverse: false, |
|
6164
|
|
|
|
|
6165
|
|
|
// a callback that will handle |
|
6166
|
|
|
onClick: function(e, legendItem) { |
|
6167
|
|
|
var index = legendItem.datasetIndex; |
|
6168
|
|
|
var ci = this.chart; |
|
6169
|
|
|
var meta = ci.getDatasetMeta(index); |
|
6170
|
|
|
|
|
6171
|
|
|
// See controller.isDatasetVisible comment |
|
6172
|
|
|
meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null; |
|
6173
|
|
|
|
|
6174
|
|
|
// We hid a dataset ... rerender the chart |
|
6175
|
|
|
ci.update(); |
|
6176
|
|
|
}, |
|
6177
|
|
|
|
|
6178
|
|
|
labels: { |
|
6179
|
|
|
boxWidth: 40, |
|
6180
|
|
|
padding: 10, |
|
6181
|
|
|
// Generates labels shown in the legend |
|
6182
|
|
|
// Valid properties to return: |
|
6183
|
|
|
// text : text to display |
|
6184
|
|
|
// fillStyle : fill of coloured box |
|
6185
|
|
|
// strokeStyle: stroke of coloured box |
|
6186
|
|
|
// hidden : if this legend item refers to a hidden item |
|
6187
|
|
|
// lineCap : cap style for line |
|
6188
|
|
|
// lineDash |
|
6189
|
|
|
// lineDashOffset : |
|
6190
|
|
|
// lineJoin : |
|
6191
|
|
|
// lineWidth : |
|
6192
|
|
|
generateLabels: function(chart) { |
|
6193
|
|
|
var data = chart.data; |
|
6194
|
|
|
return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { |
|
6195
|
|
|
return { |
|
6196
|
|
|
text: dataset.label, |
|
6197
|
|
|
fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), |
|
6198
|
|
|
hidden: !chart.isDatasetVisible(i), |
|
6199
|
|
|
lineCap: dataset.borderCapStyle, |
|
6200
|
|
|
lineDash: dataset.borderDash, |
|
6201
|
|
|
lineDashOffset: dataset.borderDashOffset, |
|
6202
|
|
|
lineJoin: dataset.borderJoinStyle, |
|
6203
|
|
|
lineWidth: dataset.borderWidth, |
|
6204
|
|
|
strokeStyle: dataset.borderColor, |
|
6205
|
|
|
pointStyle: dataset.pointStyle, |
|
6206
|
|
|
|
|
6207
|
|
|
// Below is extra data used for toggling the datasets |
|
6208
|
|
|
datasetIndex: i |
|
6209
|
|
|
}; |
|
6210
|
|
|
}, this) : []; |
|
6211
|
|
|
} |
|
6212
|
|
|
} |
|
6213
|
|
|
}; |
|
6214
|
|
|
|
|
6215
|
|
|
Chart.Legend = Chart.Element.extend({ |
|
6216
|
|
|
|
|
6217
|
|
|
initialize: function(config) { |
|
6218
|
|
|
helpers.extend(this, config); |
|
6219
|
|
|
|
|
6220
|
|
|
// Contains hit boxes for each dataset (in dataset order) |
|
6221
|
|
|
this.legendHitBoxes = []; |
|
6222
|
|
|
|
|
6223
|
|
|
// Are we in doughnut mode which has a different data type |
|
6224
|
|
|
this.doughnutMode = false; |
|
6225
|
|
|
}, |
|
6226
|
|
|
|
|
6227
|
|
|
// These methods are ordered by lifecyle. Utilities then follow. |
|
6228
|
|
|
// Any function defined here is inherited by all legend types. |
|
6229
|
|
|
// Any function can be extended by the legend type |
|
6230
|
|
|
|
|
6231
|
|
|
beforeUpdate: noop, |
|
6232
|
|
|
update: function(maxWidth, maxHeight, margins) { |
|
6233
|
|
|
var me = this; |
|
6234
|
|
|
|
|
6235
|
|
|
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
|
6236
|
|
|
me.beforeUpdate(); |
|
6237
|
|
|
|
|
6238
|
|
|
// Absorb the master measurements |
|
6239
|
|
|
me.maxWidth = maxWidth; |
|
6240
|
|
|
me.maxHeight = maxHeight; |
|
6241
|
|
|
me.margins = margins; |
|
6242
|
|
|
|
|
6243
|
|
|
// Dimensions |
|
6244
|
|
|
me.beforeSetDimensions(); |
|
6245
|
|
|
me.setDimensions(); |
|
6246
|
|
|
me.afterSetDimensions(); |
|
6247
|
|
|
// Labels |
|
6248
|
|
|
me.beforeBuildLabels(); |
|
6249
|
|
|
me.buildLabels(); |
|
6250
|
|
|
me.afterBuildLabels(); |
|
6251
|
|
|
|
|
6252
|
|
|
// Fit |
|
6253
|
|
|
me.beforeFit(); |
|
6254
|
|
|
me.fit(); |
|
6255
|
|
|
me.afterFit(); |
|
6256
|
|
|
// |
|
6257
|
|
|
me.afterUpdate(); |
|
6258
|
|
|
|
|
6259
|
|
|
return me.minSize; |
|
6260
|
|
|
}, |
|
6261
|
|
|
afterUpdate: noop, |
|
6262
|
|
|
|
|
6263
|
|
|
// |
|
6264
|
|
|
|
|
6265
|
|
|
beforeSetDimensions: noop, |
|
6266
|
|
|
setDimensions: function() { |
|
6267
|
|
|
var me = this; |
|
6268
|
|
|
// Set the unconstrained dimension before label rotation |
|
6269
|
|
|
if (me.isHorizontal()) { |
|
6270
|
|
|
// Reset position before calculating rotation |
|
6271
|
|
|
me.width = me.maxWidth; |
|
6272
|
|
|
me.left = 0; |
|
6273
|
|
|
me.right = me.width; |
|
6274
|
|
|
} else { |
|
6275
|
|
|
me.height = me.maxHeight; |
|
6276
|
|
|
|
|
6277
|
|
|
// Reset position before calculating rotation |
|
6278
|
|
|
me.top = 0; |
|
6279
|
|
|
me.bottom = me.height; |
|
6280
|
|
|
} |
|
6281
|
|
|
|
|
6282
|
|
|
// Reset padding |
|
6283
|
|
|
me.paddingLeft = 0; |
|
6284
|
|
|
me.paddingTop = 0; |
|
6285
|
|
|
me.paddingRight = 0; |
|
6286
|
|
|
me.paddingBottom = 0; |
|
6287
|
|
|
|
|
6288
|
|
|
// Reset minSize |
|
6289
|
|
|
me.minSize = { |
|
6290
|
|
|
width: 0, |
|
6291
|
|
|
height: 0 |
|
6292
|
|
|
}; |
|
6293
|
|
|
}, |
|
6294
|
|
|
afterSetDimensions: noop, |
|
6295
|
|
|
|
|
6296
|
|
|
// |
|
6297
|
|
|
|
|
6298
|
|
|
beforeBuildLabels: noop, |
|
6299
|
|
|
buildLabels: function() { |
|
6300
|
|
|
var me = this; |
|
6301
|
|
|
me.legendItems = me.options.labels.generateLabels.call(me, me.chart); |
|
6302
|
|
|
if(me.options.reverse){ |
|
6303
|
|
|
me.legendItems.reverse(); |
|
6304
|
|
|
} |
|
6305
|
|
|
}, |
|
6306
|
|
|
afterBuildLabels: noop, |
|
6307
|
|
|
|
|
6308
|
|
|
// |
|
6309
|
|
|
|
|
6310
|
|
|
beforeFit: noop, |
|
6311
|
|
|
fit: function() { |
|
6312
|
|
|
var me = this; |
|
6313
|
|
|
var opts = me.options; |
|
6314
|
|
|
var labelOpts = opts.labels; |
|
6315
|
|
|
var display = opts.display; |
|
6316
|
|
|
|
|
6317
|
|
|
var ctx = me.ctx; |
|
6318
|
|
|
|
|
6319
|
|
|
var globalDefault = Chart.defaults.global, |
|
6320
|
|
|
itemOrDefault = helpers.getValueOrDefault, |
|
6321
|
|
|
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize), |
|
6322
|
|
|
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle), |
|
6323
|
|
|
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily), |
|
6324
|
|
|
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
|
6325
|
|
|
|
|
6326
|
|
|
// Reset hit boxes |
|
6327
|
|
|
var hitboxes = me.legendHitBoxes = []; |
|
6328
|
|
|
|
|
6329
|
|
|
var minSize = me.minSize; |
|
6330
|
|
|
var isHorizontal = me.isHorizontal(); |
|
6331
|
|
|
|
|
6332
|
|
|
if (isHorizontal) { |
|
6333
|
|
|
minSize.width = me.maxWidth; // fill all the width |
|
6334
|
|
|
minSize.height = display ? 10 : 0; |
|
6335
|
|
|
} else { |
|
6336
|
|
|
minSize.width = display ? 10 : 0; |
|
6337
|
|
|
minSize.height = me.maxHeight; // fill all the height |
|
6338
|
|
|
} |
|
6339
|
|
|
|
|
6340
|
|
|
// Increase sizes here |
|
6341
|
|
|
if (display) { |
|
6342
|
|
|
ctx.font = labelFont; |
|
6343
|
|
|
|
|
6344
|
|
|
if (isHorizontal) { |
|
6345
|
|
|
// Labels |
|
6346
|
|
|
|
|
6347
|
|
|
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one |
|
6348
|
|
|
var lineWidths = me.lineWidths = [0]; |
|
6349
|
|
|
var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; |
|
6350
|
|
|
|
|
6351
|
|
|
ctx.textAlign = "left"; |
|
6352
|
|
|
ctx.textBaseline = 'top'; |
|
6353
|
|
|
|
|
6354
|
|
|
helpers.each(me.legendItems, function(legendItem, i) { |
|
6355
|
|
|
var boxWidth = labelOpts.usePointStyle ? |
|
6356
|
|
|
fontSize * Math.sqrt(2) : |
|
6357
|
|
|
labelOpts.boxWidth; |
|
6358
|
|
|
|
|
6359
|
|
|
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; |
|
6360
|
|
|
if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { |
|
6361
|
|
|
totalHeight += fontSize + (labelOpts.padding); |
|
6362
|
|
|
lineWidths[lineWidths.length] = me.left; |
|
6363
|
|
|
} |
|
6364
|
|
|
|
|
6365
|
|
|
// Store the hitbox width and height here. Final position will be updated in `draw` |
|
6366
|
|
|
hitboxes[i] = { |
|
6367
|
|
|
left: 0, |
|
6368
|
|
|
top: 0, |
|
6369
|
|
|
width: width, |
|
6370
|
|
|
height: fontSize |
|
6371
|
|
|
}; |
|
6372
|
|
|
|
|
6373
|
|
|
lineWidths[lineWidths.length - 1] += width + labelOpts.padding; |
|
6374
|
|
|
}); |
|
6375
|
|
|
|
|
6376
|
|
|
minSize.height += totalHeight; |
|
6377
|
|
|
|
|
6378
|
|
|
} else { |
|
6379
|
|
|
var vPadding = labelOpts.padding; |
|
6380
|
|
|
var columnWidths = me.columnWidths = []; |
|
6381
|
|
|
var totalWidth = labelOpts.padding; |
|
6382
|
|
|
var currentColWidth = 0; |
|
6383
|
|
|
var currentColHeight = 0; |
|
6384
|
|
|
var itemHeight = fontSize + vPadding; |
|
6385
|
|
|
|
|
6386
|
|
|
helpers.each(me.legendItems, function(legendItem, i) { |
|
6387
|
|
|
// If usePointStyle is set, multiple boxWidth by 2 since it represents |
|
6388
|
|
|
// the radius and not truly the width |
|
6389
|
|
|
var boxWidth = labelOpts.usePointStyle ? 2 * labelOpts.boxWidth : labelOpts.boxWidth; |
|
6390
|
|
|
|
|
6391
|
|
|
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; |
|
6392
|
|
|
|
|
6393
|
|
|
// If too tall, go to new column |
|
6394
|
|
|
if (currentColHeight + itemHeight > minSize.height) { |
|
6395
|
|
|
totalWidth += currentColWidth + labelOpts.padding; |
|
6396
|
|
|
columnWidths.push(currentColWidth); // previous column width |
|
6397
|
|
|
|
|
6398
|
|
|
currentColWidth = 0; |
|
6399
|
|
|
currentColHeight = 0; |
|
6400
|
|
|
} |
|
6401
|
|
|
|
|
6402
|
|
|
// Get max width |
|
6403
|
|
|
currentColWidth = Math.max(currentColWidth, itemWidth); |
|
6404
|
|
|
currentColHeight += itemHeight; |
|
6405
|
|
|
|
|
6406
|
|
|
// Store the hitbox width and height here. Final position will be updated in `draw` |
|
6407
|
|
|
hitboxes[i] = { |
|
6408
|
|
|
left: 0, |
|
6409
|
|
|
top: 0, |
|
6410
|
|
|
width: itemWidth, |
|
6411
|
|
|
height: fontSize |
|
6412
|
|
|
}; |
|
6413
|
|
|
}); |
|
6414
|
|
|
|
|
6415
|
|
|
totalWidth += currentColWidth; |
|
6416
|
|
|
columnWidths.push(currentColWidth); |
|
6417
|
|
|
minSize.width += totalWidth; |
|
6418
|
|
|
} |
|
6419
|
|
|
} |
|
6420
|
|
|
|
|
6421
|
|
|
me.width = minSize.width; |
|
6422
|
|
|
me.height = minSize.height; |
|
6423
|
|
|
}, |
|
6424
|
|
|
afterFit: noop, |
|
6425
|
|
|
|
|
6426
|
|
|
// Shared Methods |
|
6427
|
|
|
isHorizontal: function() { |
|
6428
|
|
|
return this.options.position === "top" || this.options.position === "bottom"; |
|
6429
|
|
|
}, |
|
6430
|
|
|
|
|
6431
|
|
|
// Actualy draw the legend on the canvas |
|
6432
|
|
|
draw: function() { |
|
6433
|
|
|
var me = this; |
|
6434
|
|
|
var opts = me.options; |
|
6435
|
|
|
var labelOpts = opts.labels; |
|
6436
|
|
|
var globalDefault = Chart.defaults.global, |
|
6437
|
|
|
lineDefault = globalDefault.elements.line, |
|
6438
|
|
|
legendWidth = me.width, |
|
6439
|
|
|
lineWidths = me.lineWidths; |
|
6440
|
|
|
|
|
6441
|
|
|
if (opts.display) { |
|
6442
|
|
|
var ctx = me.ctx, |
|
6443
|
|
|
cursor, |
|
6444
|
|
|
itemOrDefault = helpers.getValueOrDefault, |
|
6445
|
|
|
fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor), |
|
6446
|
|
|
fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize), |
|
6447
|
|
|
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle), |
|
6448
|
|
|
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily), |
|
6449
|
|
|
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
|
6450
|
|
|
|
|
6451
|
|
|
// Canvas setup |
|
6452
|
|
|
ctx.textAlign = "left"; |
|
6453
|
|
|
ctx.textBaseline = 'top'; |
|
6454
|
|
|
ctx.lineWidth = 0.5; |
|
6455
|
|
|
ctx.strokeStyle = fontColor; // for strikethrough effect |
|
6456
|
|
|
ctx.fillStyle = fontColor; // render in correct colour |
|
6457
|
|
|
ctx.font = labelFont; |
|
6458
|
|
|
|
|
6459
|
|
|
var boxWidth = labelOpts.boxWidth, |
|
6460
|
|
|
hitboxes = me.legendHitBoxes; |
|
6461
|
|
|
|
|
6462
|
|
|
// current position |
|
6463
|
|
|
var drawLegendBox = function(x, y, legendItem) { |
|
6464
|
|
|
if (isNaN(boxWidth) || boxWidth <= 0) { |
|
6465
|
|
|
return; |
|
6466
|
|
|
} |
|
6467
|
|
|
|
|
6468
|
|
|
// Set the ctx for the box |
|
6469
|
|
|
ctx.save(); |
|
6470
|
|
|
|
|
6471
|
|
|
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor); |
|
6472
|
|
|
ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); |
|
6473
|
|
|
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); |
|
6474
|
|
|
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); |
|
6475
|
|
|
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth); |
|
6476
|
|
|
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); |
|
6477
|
|
|
|
|
6478
|
|
|
if (ctx.setLineDash) { |
|
6479
|
|
|
// IE 9 and 10 do not support line dash |
|
6480
|
|
|
ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash)); |
|
6481
|
|
|
} |
|
6482
|
|
|
|
|
6483
|
|
|
if (opts.labels && opts.labels.usePointStyle) { |
|
6484
|
|
|
// Recalulate x and y for drawPoint() because its expecting |
|
6485
|
|
|
// x and y to be center of figure (instead of top left) |
|
6486
|
|
|
var radius = fontSize * Math.SQRT2 / 2; |
|
6487
|
|
|
var offSet = radius / Math.SQRT2; |
|
6488
|
|
|
var centerX = x + offSet; |
|
6489
|
|
|
var centerY = y + offSet; |
|
6490
|
|
|
|
|
6491
|
|
|
// Draw pointStyle as legend symbol |
|
6492
|
|
|
Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); |
|
6493
|
|
|
} |
|
6494
|
|
|
else { |
|
6495
|
|
|
// Draw box as legend symbol |
|
6496
|
|
|
ctx.strokeRect(x, y, boxWidth, fontSize); |
|
6497
|
|
|
ctx.fillRect(x, y, boxWidth, fontSize); |
|
6498
|
|
|
} |
|
6499
|
|
|
|
|
6500
|
|
|
ctx.restore(); |
|
6501
|
|
|
}; |
|
6502
|
|
|
var fillText = function(x, y, legendItem, textWidth) { |
|
6503
|
|
|
ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y); |
|
6504
|
|
|
|
|
6505
|
|
|
if (legendItem.hidden) { |
|
6506
|
|
|
// Strikethrough the text if hidden |
|
6507
|
|
|
ctx.beginPath(); |
|
6508
|
|
|
ctx.lineWidth = 2; |
|
6509
|
|
|
ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2)); |
|
6510
|
|
|
ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2)); |
|
6511
|
|
|
ctx.stroke(); |
|
6512
|
|
|
} |
|
6513
|
|
|
}; |
|
6514
|
|
|
|
|
6515
|
|
|
// Horizontal |
|
6516
|
|
|
var isHorizontal = me.isHorizontal(); |
|
6517
|
|
|
if (isHorizontal) { |
|
6518
|
|
|
cursor = { |
|
6519
|
|
|
x: me.left + ((legendWidth - lineWidths[0]) / 2), |
|
6520
|
|
|
y: me.top + labelOpts.padding, |
|
6521
|
|
|
line: 0 |
|
6522
|
|
|
}; |
|
6523
|
|
|
} else { |
|
6524
|
|
|
cursor = { |
|
6525
|
|
|
x: me.left + labelOpts.padding, |
|
6526
|
|
|
y: me.top + labelOpts.padding, |
|
6527
|
|
|
line: 0 |
|
6528
|
|
|
}; |
|
6529
|
|
|
} |
|
6530
|
|
|
|
|
6531
|
|
|
var itemHeight = fontSize + labelOpts.padding; |
|
6532
|
|
|
helpers.each(me.legendItems, function(legendItem, i) { |
|
6533
|
|
|
var textWidth = ctx.measureText(legendItem.text).width, |
|
6534
|
|
|
width = labelOpts.usePointStyle ? |
|
6535
|
|
|
fontSize + (fontSize / 2) + textWidth : |
|
6536
|
|
|
boxWidth + (fontSize / 2) + textWidth, |
|
6537
|
|
|
x = cursor.x, |
|
6538
|
|
|
y = cursor.y; |
|
6539
|
|
|
|
|
6540
|
|
|
if (isHorizontal) { |
|
6541
|
|
|
if (x + width >= legendWidth) { |
|
6542
|
|
|
y = cursor.y += itemHeight; |
|
6543
|
|
|
cursor.line++; |
|
6544
|
|
|
x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); |
|
6545
|
|
|
} |
|
6546
|
|
|
} else { |
|
6547
|
|
|
if (y + itemHeight > me.bottom) { |
|
6548
|
|
|
x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; |
|
6549
|
|
|
y = cursor.y = me.top; |
|
6550
|
|
|
cursor.line++; |
|
6551
|
|
|
} |
|
6552
|
|
|
} |
|
6553
|
|
|
|
|
6554
|
|
|
drawLegendBox(x, y, legendItem); |
|
6555
|
|
|
|
|
6556
|
|
|
hitboxes[i].left = x; |
|
6557
|
|
|
hitboxes[i].top = y; |
|
6558
|
|
|
|
|
6559
|
|
|
// Fill the actual label |
|
6560
|
|
|
fillText(x, y, legendItem, textWidth); |
|
6561
|
|
|
|
|
6562
|
|
|
if (isHorizontal) { |
|
6563
|
|
|
cursor.x += width + (labelOpts.padding); |
|
6564
|
|
|
} else { |
|
6565
|
|
|
cursor.y += itemHeight; |
|
6566
|
|
|
} |
|
6567
|
|
|
|
|
6568
|
|
|
}); |
|
6569
|
|
|
} |
|
6570
|
|
|
}, |
|
6571
|
|
|
|
|
6572
|
|
|
// Handle an event |
|
6573
|
|
|
handleEvent: function(e) { |
|
6574
|
|
|
var me = this; |
|
6575
|
|
|
var position = helpers.getRelativePosition(e, me.chart.chart), |
|
6576
|
|
|
x = position.x, |
|
6577
|
|
|
y = position.y, |
|
6578
|
|
|
opts = me.options; |
|
6579
|
|
|
|
|
6580
|
|
|
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { |
|
6581
|
|
|
// See if we are touching one of the dataset boxes |
|
6582
|
|
|
var lh = me.legendHitBoxes; |
|
6583
|
|
|
for (var i = 0; i < lh.length; ++i) { |
|
6584
|
|
|
var hitBox = lh[i]; |
|
6585
|
|
|
|
|
6586
|
|
|
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { |
|
6587
|
|
|
// Touching an element |
|
6588
|
|
|
if (opts.onClick) { |
|
6589
|
|
|
opts.onClick.call(me, e, me.legendItems[i]); |
|
6590
|
|
|
} |
|
6591
|
|
|
break; |
|
6592
|
|
|
} |
|
6593
|
|
|
} |
|
6594
|
|
|
} |
|
6595
|
|
|
} |
|
6596
|
|
|
}); |
|
6597
|
|
|
|
|
6598
|
|
|
// Register the legend plugin |
|
6599
|
|
|
Chart.plugins.register({ |
|
6600
|
|
|
beforeInit: function(chartInstance) { |
|
6601
|
|
|
var opts = chartInstance.options; |
|
6602
|
|
|
var legendOpts = opts.legend; |
|
6603
|
|
|
|
|
6604
|
|
|
if (legendOpts) { |
|
6605
|
|
|
chartInstance.legend = new Chart.Legend({ |
|
6606
|
|
|
ctx: chartInstance.chart.ctx, |
|
6607
|
|
|
options: legendOpts, |
|
6608
|
|
|
chart: chartInstance |
|
6609
|
|
|
}); |
|
6610
|
|
|
|
|
6611
|
|
|
Chart.layoutService.addBox(chartInstance, chartInstance.legend); |
|
6612
|
|
|
} |
|
6613
|
|
|
} |
|
6614
|
|
|
}); |
|
6615
|
|
|
}; |
|
6616
|
|
|
|
|
6617
|
|
|
},{}],30:[function(require,module,exports){ |
|
6618
|
|
|
"use strict"; |
|
6619
|
|
|
|
|
6620
|
|
|
module.exports = function(Chart) { |
|
6621
|
|
|
|
|
6622
|
|
|
var noop = Chart.helpers.noop; |
|
6623
|
|
|
|
|
6624
|
|
|
/** |
|
6625
|
|
|
* The plugin service singleton |
|
6626
|
|
|
* @namespace Chart.plugins |
|
6627
|
|
|
* @since 2.1.0 |
|
6628
|
|
|
*/ |
|
6629
|
|
|
Chart.plugins = { |
|
6630
|
|
|
_plugins: [], |
|
6631
|
|
|
|
|
6632
|
|
|
/** |
|
6633
|
|
|
* Registers the given plugin(s) if not already registered. |
|
6634
|
|
|
* @param {Array|Object} plugins plugin instance(s). |
|
6635
|
|
|
*/ |
|
6636
|
|
|
register: function(plugins) { |
|
6637
|
|
|
var p = this._plugins; |
|
6638
|
|
|
([]).concat(plugins).forEach(function(plugin) { |
|
6639
|
|
|
if (p.indexOf(plugin) === -1) { |
|
6640
|
|
|
p.push(plugin); |
|
6641
|
|
|
} |
|
6642
|
|
|
}); |
|
6643
|
|
|
}, |
|
6644
|
|
|
|
|
6645
|
|
|
/** |
|
6646
|
|
|
* Unregisters the given plugin(s) only if registered. |
|
6647
|
|
|
* @param {Array|Object} plugins plugin instance(s). |
|
6648
|
|
|
*/ |
|
6649
|
|
|
unregister: function(plugins) { |
|
6650
|
|
|
var p = this._plugins; |
|
6651
|
|
|
([]).concat(plugins).forEach(function(plugin) { |
|
6652
|
|
|
var idx = p.indexOf(plugin); |
|
6653
|
|
|
if (idx !== -1) { |
|
6654
|
|
|
p.splice(idx, 1); |
|
6655
|
|
|
} |
|
6656
|
|
|
}); |
|
6657
|
|
|
}, |
|
6658
|
|
|
|
|
6659
|
|
|
/** |
|
6660
|
|
|
* Remove all registered p^lugins. |
|
6661
|
|
|
* @since 2.1.5 |
|
6662
|
|
|
*/ |
|
6663
|
|
|
clear: function() { |
|
6664
|
|
|
this._plugins = []; |
|
6665
|
|
|
}, |
|
6666
|
|
|
|
|
6667
|
|
|
/** |
|
6668
|
|
|
* Returns the number of registered plugins? |
|
6669
|
|
|
* @returns {Number} |
|
6670
|
|
|
* @since 2.1.5 |
|
6671
|
|
|
*/ |
|
6672
|
|
|
count: function() { |
|
6673
|
|
|
return this._plugins.length; |
|
6674
|
|
|
}, |
|
6675
|
|
|
|
|
6676
|
|
|
/** |
|
6677
|
|
|
* Returns all registered plugin intances. |
|
6678
|
|
|
* @returns {Array} array of plugin objects. |
|
6679
|
|
|
* @since 2.1.5 |
|
6680
|
|
|
*/ |
|
6681
|
|
|
getAll: function() { |
|
6682
|
|
|
return this._plugins; |
|
6683
|
|
|
}, |
|
6684
|
|
|
|
|
6685
|
|
|
/** |
|
6686
|
|
|
* Calls registered plugins on the specified extension, with the given args. This |
|
6687
|
|
|
* method immediately returns as soon as a plugin explicitly returns false. The |
|
6688
|
|
|
* returned value can be used, for instance, to interrupt the current action. |
|
6689
|
|
|
* @param {String} extension the name of the plugin method to call (e.g. 'beforeUpdate'). |
|
6690
|
|
|
* @param {Array} [args] extra arguments to apply to the extension call. |
|
|
|
|
|
|
6691
|
|
|
* @returns {Boolean} false if any of the plugins return false, else returns true. |
|
6692
|
|
|
*/ |
|
6693
|
|
|
notify: function(extension, args) { |
|
6694
|
|
|
var plugins = this._plugins; |
|
6695
|
|
|
var ilen = plugins.length; |
|
6696
|
|
|
var i, plugin; |
|
6697
|
|
|
|
|
6698
|
|
|
for (i=0; i<ilen; ++i) { |
|
6699
|
|
|
plugin = plugins[i]; |
|
6700
|
|
|
if (typeof plugin[extension] === 'function') { |
|
6701
|
|
|
if (plugin[extension].apply(plugin, args || []) === false) { |
|
6702
|
|
|
return false; |
|
6703
|
|
|
} |
|
6704
|
|
|
} |
|
6705
|
|
|
} |
|
6706
|
|
|
|
|
6707
|
|
|
return true; |
|
6708
|
|
|
} |
|
6709
|
|
|
}; |
|
6710
|
|
|
|
|
6711
|
|
|
/** |
|
6712
|
|
|
* Plugin extension methods. |
|
6713
|
|
|
* @interface Chart.PluginBase |
|
6714
|
|
|
* @since 2.1.0 |
|
6715
|
|
|
*/ |
|
6716
|
|
|
Chart.PluginBase = Chart.Element.extend({ |
|
6717
|
|
|
// Called at start of chart init |
|
6718
|
|
|
beforeInit: noop, |
|
6719
|
|
|
|
|
6720
|
|
|
// Called at end of chart init |
|
6721
|
|
|
afterInit: noop, |
|
6722
|
|
|
|
|
6723
|
|
|
// Called at start of update |
|
6724
|
|
|
beforeUpdate: noop, |
|
6725
|
|
|
|
|
6726
|
|
|
// Called at end of update |
|
6727
|
|
|
afterUpdate: noop, |
|
6728
|
|
|
|
|
6729
|
|
|
// Called at start of draw |
|
6730
|
|
|
beforeDraw: noop, |
|
6731
|
|
|
|
|
6732
|
|
|
// Called at end of draw |
|
6733
|
|
|
afterDraw: noop, |
|
6734
|
|
|
|
|
6735
|
|
|
// Called during destroy |
|
6736
|
|
|
destroy: noop |
|
6737
|
|
|
}); |
|
6738
|
|
|
|
|
6739
|
|
|
/** |
|
6740
|
|
|
* Provided for backward compatibility, use Chart.plugins instead |
|
6741
|
|
|
* @namespace Chart.pluginService |
|
6742
|
|
|
* @deprecated since version 2.1.5 |
|
6743
|
|
|
* @todo remove me at version 3 |
|
6744
|
|
|
*/ |
|
6745
|
|
|
Chart.pluginService = Chart.plugins; |
|
6746
|
|
|
}; |
|
6747
|
|
|
|
|
6748
|
|
|
},{}],31:[function(require,module,exports){ |
|
6749
|
|
|
"use strict"; |
|
6750
|
|
|
|
|
6751
|
|
|
module.exports = function(Chart) { |
|
6752
|
|
|
|
|
6753
|
|
|
var helpers = Chart.helpers; |
|
6754
|
|
|
|
|
6755
|
|
|
Chart.defaults.scale = { |
|
6756
|
|
|
display: true, |
|
6757
|
|
|
position: "left", |
|
6758
|
|
|
|
|
6759
|
|
|
// grid line settings |
|
6760
|
|
|
gridLines: { |
|
6761
|
|
|
display: true, |
|
6762
|
|
|
color: "rgba(0, 0, 0, 0.1)", |
|
6763
|
|
|
lineWidth: 1, |
|
6764
|
|
|
drawBorder: true, |
|
6765
|
|
|
drawOnChartArea: true, |
|
6766
|
|
|
drawTicks: true, |
|
6767
|
|
|
tickMarkLength: 10, |
|
6768
|
|
|
zeroLineWidth: 1, |
|
6769
|
|
|
zeroLineColor: "rgba(0,0,0,0.25)", |
|
6770
|
|
|
offsetGridLines: false |
|
6771
|
|
|
}, |
|
6772
|
|
|
|
|
6773
|
|
|
// scale label |
|
6774
|
|
|
scaleLabel: { |
|
6775
|
|
|
// actual label |
|
6776
|
|
|
labelString: '', |
|
6777
|
|
|
|
|
6778
|
|
|
// display property |
|
6779
|
|
|
display: false |
|
6780
|
|
|
}, |
|
6781
|
|
|
|
|
6782
|
|
|
// label settings |
|
6783
|
|
|
ticks: { |
|
6784
|
|
|
beginAtZero: false, |
|
6785
|
|
|
minRotation: 0, |
|
6786
|
|
|
maxRotation: 50, |
|
6787
|
|
|
mirror: false, |
|
6788
|
|
|
padding: 10, |
|
6789
|
|
|
reverse: false, |
|
6790
|
|
|
display: true, |
|
6791
|
|
|
autoSkip: true, |
|
6792
|
|
|
autoSkipPadding: 0, |
|
6793
|
|
|
labelOffset: 0, |
|
6794
|
|
|
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here. |
|
6795
|
|
|
callback: function(value) { |
|
6796
|
|
|
return helpers.isArray(value) ? value : '' + value; |
|
6797
|
|
|
} |
|
6798
|
|
|
} |
|
6799
|
|
|
}; |
|
6800
|
|
|
|
|
6801
|
|
|
Chart.Scale = Chart.Element.extend({ |
|
6802
|
|
|
|
|
6803
|
|
|
// These methods are ordered by lifecyle. Utilities then follow. |
|
6804
|
|
|
// Any function defined here is inherited by all scale types. |
|
6805
|
|
|
// Any function can be extended by the scale type |
|
6806
|
|
|
|
|
6807
|
|
|
beforeUpdate: function() { |
|
6808
|
|
|
helpers.callCallback(this.options.beforeUpdate, [this]); |
|
6809
|
|
|
}, |
|
6810
|
|
|
update: function(maxWidth, maxHeight, margins) { |
|
6811
|
|
|
var me = this; |
|
6812
|
|
|
|
|
6813
|
|
|
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
|
6814
|
|
|
me.beforeUpdate(); |
|
6815
|
|
|
|
|
6816
|
|
|
// Absorb the master measurements |
|
6817
|
|
|
me.maxWidth = maxWidth; |
|
6818
|
|
|
me.maxHeight = maxHeight; |
|
6819
|
|
|
me.margins = helpers.extend({ |
|
6820
|
|
|
left: 0, |
|
6821
|
|
|
right: 0, |
|
6822
|
|
|
top: 0, |
|
6823
|
|
|
bottom: 0 |
|
6824
|
|
|
}, margins); |
|
6825
|
|
|
|
|
6826
|
|
|
// Dimensions |
|
6827
|
|
|
me.beforeSetDimensions(); |
|
6828
|
|
|
me.setDimensions(); |
|
6829
|
|
|
me.afterSetDimensions(); |
|
6830
|
|
|
|
|
6831
|
|
|
// Data min/max |
|
6832
|
|
|
me.beforeDataLimits(); |
|
6833
|
|
|
me.determineDataLimits(); |
|
6834
|
|
|
me.afterDataLimits(); |
|
6835
|
|
|
|
|
6836
|
|
|
// Ticks |
|
6837
|
|
|
me.beforeBuildTicks(); |
|
6838
|
|
|
me.buildTicks(); |
|
6839
|
|
|
me.afterBuildTicks(); |
|
6840
|
|
|
|
|
6841
|
|
|
me.beforeTickToLabelConversion(); |
|
6842
|
|
|
me.convertTicksToLabels(); |
|
6843
|
|
|
me.afterTickToLabelConversion(); |
|
6844
|
|
|
|
|
6845
|
|
|
// Tick Rotation |
|
6846
|
|
|
me.beforeCalculateTickRotation(); |
|
6847
|
|
|
me.calculateTickRotation(); |
|
6848
|
|
|
me.afterCalculateTickRotation(); |
|
6849
|
|
|
// Fit |
|
6850
|
|
|
me.beforeFit(); |
|
6851
|
|
|
me.fit(); |
|
6852
|
|
|
me.afterFit(); |
|
6853
|
|
|
// |
|
6854
|
|
|
me.afterUpdate(); |
|
6855
|
|
|
|
|
6856
|
|
|
return me.minSize; |
|
6857
|
|
|
|
|
6858
|
|
|
}, |
|
6859
|
|
|
afterUpdate: function() { |
|
6860
|
|
|
helpers.callCallback(this.options.afterUpdate, [this]); |
|
6861
|
|
|
}, |
|
6862
|
|
|
|
|
6863
|
|
|
// |
|
6864
|
|
|
|
|
6865
|
|
|
beforeSetDimensions: function() { |
|
6866
|
|
|
helpers.callCallback(this.options.beforeSetDimensions, [this]); |
|
6867
|
|
|
}, |
|
6868
|
|
|
setDimensions: function() { |
|
6869
|
|
|
var me = this; |
|
6870
|
|
|
// Set the unconstrained dimension before label rotation |
|
6871
|
|
|
if (me.isHorizontal()) { |
|
6872
|
|
|
// Reset position before calculating rotation |
|
6873
|
|
|
me.width = me.maxWidth; |
|
6874
|
|
|
me.left = 0; |
|
6875
|
|
|
me.right = me.width; |
|
6876
|
|
|
} else { |
|
6877
|
|
|
me.height = me.maxHeight; |
|
6878
|
|
|
|
|
6879
|
|
|
// Reset position before calculating rotation |
|
6880
|
|
|
me.top = 0; |
|
6881
|
|
|
me.bottom = me.height; |
|
6882
|
|
|
} |
|
6883
|
|
|
|
|
6884
|
|
|
// Reset padding |
|
6885
|
|
|
me.paddingLeft = 0; |
|
6886
|
|
|
me.paddingTop = 0; |
|
6887
|
|
|
me.paddingRight = 0; |
|
6888
|
|
|
me.paddingBottom = 0; |
|
6889
|
|
|
}, |
|
6890
|
|
|
afterSetDimensions: function() { |
|
6891
|
|
|
helpers.callCallback(this.options.afterSetDimensions, [this]); |
|
6892
|
|
|
}, |
|
6893
|
|
|
|
|
6894
|
|
|
// Data limits |
|
6895
|
|
|
beforeDataLimits: function() { |
|
6896
|
|
|
helpers.callCallback(this.options.beforeDataLimits, [this]); |
|
6897
|
|
|
}, |
|
6898
|
|
|
determineDataLimits: helpers.noop, |
|
6899
|
|
|
afterDataLimits: function() { |
|
6900
|
|
|
helpers.callCallback(this.options.afterDataLimits, [this]); |
|
6901
|
|
|
}, |
|
6902
|
|
|
|
|
6903
|
|
|
// |
|
6904
|
|
|
beforeBuildTicks: function() { |
|
6905
|
|
|
helpers.callCallback(this.options.beforeBuildTicks, [this]); |
|
6906
|
|
|
}, |
|
6907
|
|
|
buildTicks: helpers.noop, |
|
6908
|
|
|
afterBuildTicks: function() { |
|
6909
|
|
|
helpers.callCallback(this.options.afterBuildTicks, [this]); |
|
6910
|
|
|
}, |
|
6911
|
|
|
|
|
6912
|
|
|
beforeTickToLabelConversion: function() { |
|
6913
|
|
|
helpers.callCallback(this.options.beforeTickToLabelConversion, [this]); |
|
6914
|
|
|
}, |
|
6915
|
|
|
convertTicksToLabels: function() { |
|
6916
|
|
|
var me = this; |
|
6917
|
|
|
// Convert ticks to strings |
|
6918
|
|
|
me.ticks = me.ticks.map(function(numericalTick, index, ticks) { |
|
6919
|
|
|
if (me.options.ticks.userCallback) { |
|
6920
|
|
|
return me.options.ticks.userCallback(numericalTick, index, ticks); |
|
6921
|
|
|
} |
|
6922
|
|
|
return me.options.ticks.callback(numericalTick, index, ticks); |
|
6923
|
|
|
}, |
|
6924
|
|
|
me); |
|
6925
|
|
|
}, |
|
6926
|
|
|
afterTickToLabelConversion: function() { |
|
6927
|
|
|
helpers.callCallback(this.options.afterTickToLabelConversion, [this]); |
|
6928
|
|
|
}, |
|
6929
|
|
|
|
|
6930
|
|
|
// |
|
6931
|
|
|
|
|
6932
|
|
|
beforeCalculateTickRotation: function() { |
|
6933
|
|
|
helpers.callCallback(this.options.beforeCalculateTickRotation, [this]); |
|
6934
|
|
|
}, |
|
6935
|
|
|
calculateTickRotation: function() { |
|
6936
|
|
|
var me = this; |
|
6937
|
|
|
var context = me.ctx; |
|
6938
|
|
|
var globalDefaults = Chart.defaults.global; |
|
6939
|
|
|
var optionTicks = me.options.ticks; |
|
6940
|
|
|
|
|
6941
|
|
|
//Get the width of each grid by calculating the difference |
|
6942
|
|
|
//between x offsets between 0 and 1. |
|
6943
|
|
|
var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize); |
|
6944
|
|
|
var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle); |
|
6945
|
|
|
var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily); |
|
6946
|
|
|
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
|
6947
|
|
|
context.font = tickLabelFont; |
|
6948
|
|
|
|
|
6949
|
|
|
var firstWidth = context.measureText(me.ticks[0]).width; |
|
6950
|
|
|
var lastWidth = context.measureText(me.ticks[me.ticks.length - 1]).width; |
|
6951
|
|
|
var firstRotated; |
|
6952
|
|
|
|
|
6953
|
|
|
me.labelRotation = optionTicks.minRotation || 0; |
|
6954
|
|
|
me.paddingRight = 0; |
|
6955
|
|
|
me.paddingLeft = 0; |
|
6956
|
|
|
|
|
6957
|
|
|
if (me.options.display) { |
|
6958
|
|
|
if (me.isHorizontal()) { |
|
6959
|
|
|
me.paddingRight = lastWidth / 2 + 3; |
|
6960
|
|
|
me.paddingLeft = firstWidth / 2 + 3; |
|
6961
|
|
|
|
|
6962
|
|
|
if (!me.longestTextCache) { |
|
6963
|
|
|
me.longestTextCache = {}; |
|
6964
|
|
|
} |
|
6965
|
|
|
var originalLabelWidth = helpers.longestText(context, tickLabelFont, me.ticks, me.longestTextCache); |
|
6966
|
|
|
var labelWidth = originalLabelWidth; |
|
6967
|
|
|
var cosRotation; |
|
6968
|
|
|
var sinRotation; |
|
6969
|
|
|
|
|
6970
|
|
|
// Allow 3 pixels x2 padding either side for label readability |
|
6971
|
|
|
// only the index matters for a dataset scale, but we want a consistent interface between scales |
|
6972
|
|
|
var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; |
|
6973
|
|
|
|
|
6974
|
|
|
//Max label rotation can be set or default to 90 - also act as a loop counter |
|
6975
|
|
|
while (labelWidth > tickWidth && me.labelRotation < optionTicks.maxRotation) { |
|
6976
|
|
|
cosRotation = Math.cos(helpers.toRadians(me.labelRotation)); |
|
6977
|
|
|
sinRotation = Math.sin(helpers.toRadians(me.labelRotation)); |
|
6978
|
|
|
|
|
6979
|
|
|
firstRotated = cosRotation * firstWidth; |
|
6980
|
|
|
|
|
6981
|
|
|
// We're right aligning the text now. |
|
6982
|
|
|
if (firstRotated + tickFontSize / 2 > me.yLabelWidth) { |
|
6983
|
|
|
me.paddingLeft = firstRotated + tickFontSize / 2; |
|
6984
|
|
|
} |
|
6985
|
|
|
|
|
6986
|
|
|
me.paddingRight = tickFontSize / 2; |
|
6987
|
|
|
|
|
6988
|
|
|
if (sinRotation * originalLabelWidth > me.maxHeight) { |
|
6989
|
|
|
// go back one step |
|
6990
|
|
|
me.labelRotation--; |
|
6991
|
|
|
break; |
|
6992
|
|
|
} |
|
6993
|
|
|
|
|
6994
|
|
|
me.labelRotation++; |
|
6995
|
|
|
labelWidth = cosRotation * originalLabelWidth; |
|
6996
|
|
|
} |
|
6997
|
|
|
} |
|
6998
|
|
|
} |
|
6999
|
|
|
|
|
7000
|
|
|
if (me.margins) { |
|
7001
|
|
|
me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); |
|
7002
|
|
|
me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); |
|
7003
|
|
|
} |
|
7004
|
|
|
}, |
|
7005
|
|
|
afterCalculateTickRotation: function() { |
|
7006
|
|
|
helpers.callCallback(this.options.afterCalculateTickRotation, [this]); |
|
7007
|
|
|
}, |
|
7008
|
|
|
|
|
7009
|
|
|
// |
|
7010
|
|
|
|
|
7011
|
|
|
beforeFit: function() { |
|
7012
|
|
|
helpers.callCallback(this.options.beforeFit, [this]); |
|
7013
|
|
|
}, |
|
7014
|
|
|
fit: function() { |
|
7015
|
|
|
var me = this; |
|
7016
|
|
|
// Reset |
|
7017
|
|
|
var minSize = me.minSize = { |
|
7018
|
|
|
width: 0, |
|
7019
|
|
|
height: 0 |
|
7020
|
|
|
}; |
|
7021
|
|
|
|
|
7022
|
|
|
var opts = me.options; |
|
7023
|
|
|
var globalDefaults = Chart.defaults.global; |
|
7024
|
|
|
var tickOpts = opts.ticks; |
|
7025
|
|
|
var scaleLabelOpts = opts.scaleLabel; |
|
7026
|
|
|
var display = opts.display; |
|
7027
|
|
|
var isHorizontal = me.isHorizontal(); |
|
7028
|
|
|
|
|
7029
|
|
|
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
|
7030
|
|
|
var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); |
|
7031
|
|
|
var tickFontFamily = helpers.getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); |
|
7032
|
|
|
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
|
7033
|
|
|
|
|
7034
|
|
|
var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabelOpts.fontSize, globalDefaults.defaultFontSize); |
|
7035
|
|
|
|
|
7036
|
|
|
var tickMarkLength = opts.gridLines.tickMarkLength; |
|
7037
|
|
|
|
|
7038
|
|
|
// Width |
|
7039
|
|
|
if (isHorizontal) { |
|
7040
|
|
|
// subtract the margins to line up with the chartArea if we are a full width scale |
|
7041
|
|
|
minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; |
|
7042
|
|
|
} else { |
|
7043
|
|
|
minSize.width = display ? tickMarkLength : 0; |
|
7044
|
|
|
} |
|
7045
|
|
|
|
|
7046
|
|
|
// height |
|
7047
|
|
|
if (isHorizontal) { |
|
7048
|
|
|
minSize.height = display ? tickMarkLength : 0; |
|
7049
|
|
|
} else { |
|
7050
|
|
|
minSize.height = me.maxHeight; // fill all the height |
|
7051
|
|
|
} |
|
7052
|
|
|
|
|
7053
|
|
|
// Are we showing a title for the scale? |
|
7054
|
|
|
if (scaleLabelOpts.display && display) { |
|
7055
|
|
|
if (isHorizontal) { |
|
7056
|
|
|
minSize.height += (scaleLabelFontSize * 1.5); |
|
7057
|
|
|
} else { |
|
7058
|
|
|
minSize.width += (scaleLabelFontSize * 1.5); |
|
7059
|
|
|
} |
|
7060
|
|
|
} |
|
7061
|
|
|
|
|
7062
|
|
|
if (tickOpts.display && display) { |
|
7063
|
|
|
// Don't bother fitting the ticks if we are not showing them |
|
7064
|
|
|
if (!me.longestTextCache) { |
|
7065
|
|
|
me.longestTextCache = {}; |
|
7066
|
|
|
} |
|
7067
|
|
|
|
|
7068
|
|
|
var largestTextWidth = helpers.longestText(me.ctx, tickLabelFont, me.ticks, me.longestTextCache); |
|
7069
|
|
|
var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks); |
|
7070
|
|
|
var lineSpace = tickFontSize * 0.5; |
|
7071
|
|
|
|
|
7072
|
|
|
if (isHorizontal) { |
|
7073
|
|
|
// A horizontal axis is more constrained by the height. |
|
7074
|
|
|
me.longestLabelWidth = largestTextWidth; |
|
7075
|
|
|
|
|
7076
|
|
|
// TODO - improve this calculation |
|
7077
|
|
|
var labelHeight = (Math.sin(helpers.toRadians(me.labelRotation)) * me.longestLabelWidth) + (tickFontSize * tallestLabelHeightInLines) + (lineSpace * tallestLabelHeightInLines); |
|
7078
|
|
|
|
|
7079
|
|
|
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight); |
|
7080
|
|
|
me.ctx.font = tickLabelFont; |
|
7081
|
|
|
|
|
7082
|
|
|
var firstLabelWidth = me.ctx.measureText(me.ticks[0]).width; |
|
7083
|
|
|
var lastLabelWidth = me.ctx.measureText(me.ticks[me.ticks.length - 1]).width; |
|
7084
|
|
|
|
|
7085
|
|
|
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated |
|
7086
|
|
|
// by the font height |
|
7087
|
|
|
var cosRotation = Math.cos(helpers.toRadians(me.labelRotation)); |
|
7088
|
|
|
var sinRotation = Math.sin(helpers.toRadians(me.labelRotation)); |
|
7089
|
|
|
me.paddingLeft = me.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges |
|
7090
|
|
|
me.paddingRight = me.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated |
|
7091
|
|
|
} else { |
|
7092
|
|
|
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first |
|
7093
|
|
|
var maxLabelWidth = me.maxWidth - minSize.width; |
|
7094
|
|
|
|
|
7095
|
|
|
// Account for padding |
|
7096
|
|
|
var mirror = tickOpts.mirror; |
|
7097
|
|
|
if (!mirror) { |
|
7098
|
|
|
largestTextWidth += me.options.ticks.padding; |
|
7099
|
|
|
} else { |
|
7100
|
|
|
// If mirrored text is on the inside so don't expand |
|
7101
|
|
|
largestTextWidth = 0; |
|
7102
|
|
|
} |
|
7103
|
|
|
|
|
7104
|
|
|
if (largestTextWidth < maxLabelWidth) { |
|
7105
|
|
|
// We don't need all the room |
|
7106
|
|
|
minSize.width += largestTextWidth; |
|
7107
|
|
|
} else { |
|
7108
|
|
|
// Expand to max size |
|
7109
|
|
|
minSize.width = me.maxWidth; |
|
7110
|
|
|
} |
|
7111
|
|
|
|
|
7112
|
|
|
me.paddingTop = tickFontSize / 2; |
|
7113
|
|
|
me.paddingBottom = tickFontSize / 2; |
|
7114
|
|
|
} |
|
7115
|
|
|
} |
|
7116
|
|
|
|
|
7117
|
|
|
if (me.margins) { |
|
7118
|
|
|
me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); |
|
7119
|
|
|
me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); |
|
7120
|
|
|
me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); |
|
7121
|
|
|
me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); |
|
7122
|
|
|
} |
|
7123
|
|
|
|
|
7124
|
|
|
me.width = minSize.width; |
|
7125
|
|
|
me.height = minSize.height; |
|
7126
|
|
|
|
|
7127
|
|
|
}, |
|
7128
|
|
|
afterFit: function() { |
|
7129
|
|
|
helpers.callCallback(this.options.afterFit, [this]); |
|
7130
|
|
|
}, |
|
7131
|
|
|
|
|
7132
|
|
|
// Shared Methods |
|
7133
|
|
|
isHorizontal: function() { |
|
7134
|
|
|
return this.options.position === "top" || this.options.position === "bottom"; |
|
7135
|
|
|
}, |
|
7136
|
|
|
isFullWidth: function() { |
|
7137
|
|
|
return (this.options.fullWidth); |
|
7138
|
|
|
}, |
|
7139
|
|
|
|
|
7140
|
|
|
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not |
|
7141
|
|
|
getRightValue: function(rawValue) { |
|
7142
|
|
|
// Null and undefined values first |
|
7143
|
|
|
if (rawValue === null || typeof(rawValue) === 'undefined') { |
|
7144
|
|
|
return NaN; |
|
7145
|
|
|
} |
|
7146
|
|
|
// isNaN(object) returns true, so make sure NaN is checking for a number |
|
7147
|
|
|
if (typeof(rawValue) === 'number' && isNaN(rawValue)) { |
|
7148
|
|
|
return NaN; |
|
7149
|
|
|
} |
|
7150
|
|
|
// If it is in fact an object, dive in one more level |
|
7151
|
|
|
if (typeof(rawValue) === "object") { |
|
7152
|
|
|
if ((rawValue instanceof Date) || (rawValue.isValid)) { |
|
7153
|
|
|
return rawValue; |
|
7154
|
|
|
} else { |
|
7155
|
|
|
return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y); |
|
7156
|
|
|
} |
|
7157
|
|
|
} |
|
7158
|
|
|
|
|
7159
|
|
|
// Value is good, return it |
|
7160
|
|
|
return rawValue; |
|
7161
|
|
|
}, |
|
7162
|
|
|
|
|
7163
|
|
|
// Used to get the value to display in the tooltip for the data at the given index |
|
7164
|
|
|
// function getLabelForIndex(index, datasetIndex) |
|
7165
|
|
|
getLabelForIndex: helpers.noop, |
|
7166
|
|
|
|
|
7167
|
|
|
// Used to get data value locations. Value can either be an index or a numerical value |
|
7168
|
|
|
getPixelForValue: helpers.noop, |
|
7169
|
|
|
|
|
7170
|
|
|
// Used to get the data value from a given pixel. This is the inverse of getPixelForValue |
|
7171
|
|
|
getValueForPixel: helpers.noop, |
|
7172
|
|
|
|
|
7173
|
|
|
// Used for tick location, should |
|
7174
|
|
|
getPixelForTick: function(index, includeOffset) { |
|
|
|
|
|
|
7175
|
|
|
var me = this; |
|
7176
|
|
|
if (me.isHorizontal()) { |
|
7177
|
|
|
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
|
7178
|
|
|
var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); |
|
7179
|
|
|
var pixel = (tickWidth * index) + me.paddingLeft; |
|
7180
|
|
|
|
|
7181
|
|
|
if (includeOffset) { |
|
7182
|
|
|
pixel += tickWidth / 2; |
|
7183
|
|
|
} |
|
7184
|
|
|
|
|
7185
|
|
|
var finalVal = me.left + Math.round(pixel); |
|
7186
|
|
|
finalVal += me.isFullWidth() ? me.margins.left : 0; |
|
7187
|
|
|
return finalVal; |
|
7188
|
|
|
} else { |
|
7189
|
|
|
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); |
|
7190
|
|
|
return me.top + (index * (innerHeight / (me.ticks.length - 1))); |
|
7191
|
|
|
} |
|
7192
|
|
|
}, |
|
7193
|
|
|
|
|
7194
|
|
|
// Utility for getting the pixel location of a percentage of scale |
|
7195
|
|
|
getPixelForDecimal: function(decimal /*, includeOffset*/ ) { |
|
7196
|
|
|
var me = this; |
|
7197
|
|
|
if (me.isHorizontal()) { |
|
7198
|
|
|
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
|
7199
|
|
|
var valueOffset = (innerWidth * decimal) + me.paddingLeft; |
|
7200
|
|
|
|
|
7201
|
|
|
var finalVal = me.left + Math.round(valueOffset); |
|
7202
|
|
|
finalVal += me.isFullWidth() ? me.margins.left : 0; |
|
7203
|
|
|
return finalVal; |
|
7204
|
|
|
} else { |
|
7205
|
|
|
return me.top + (decimal * me.height); |
|
7206
|
|
|
} |
|
7207
|
|
|
}, |
|
7208
|
|
|
|
|
7209
|
|
|
getBasePixel: function() { |
|
7210
|
|
|
var me = this; |
|
7211
|
|
|
var min = me.min; |
|
7212
|
|
|
var max = me.max; |
|
7213
|
|
|
|
|
7214
|
|
|
return me.getPixelForValue( |
|
7215
|
|
|
me.beginAtZero? 0: |
|
7216
|
|
|
min < 0 && max < 0? max : |
|
7217
|
|
|
min > 0 && max > 0? min : |
|
7218
|
|
|
0); |
|
7219
|
|
|
}, |
|
7220
|
|
|
|
|
7221
|
|
|
// Actualy draw the scale on the canvas |
|
7222
|
|
|
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on |
|
7223
|
|
|
draw: function(chartArea) { |
|
7224
|
|
|
var me = this; |
|
7225
|
|
|
var options = me.options; |
|
7226
|
|
|
if (!options.display) { |
|
7227
|
|
|
return; |
|
7228
|
|
|
} |
|
7229
|
|
|
|
|
7230
|
|
|
var context = me.ctx; |
|
7231
|
|
|
var globalDefaults = Chart.defaults.global; |
|
7232
|
|
|
var optionTicks = options.ticks; |
|
7233
|
|
|
var gridLines = options.gridLines; |
|
7234
|
|
|
var scaleLabel = options.scaleLabel; |
|
7235
|
|
|
|
|
7236
|
|
|
var isRotated = me.labelRotation !== 0; |
|
7237
|
|
|
var skipRatio; |
|
7238
|
|
|
var useAutoskipper = optionTicks.autoSkip; |
|
7239
|
|
|
var isHorizontal = me.isHorizontal(); |
|
7240
|
|
|
|
|
7241
|
|
|
// figure out the maximum number of gridlines to show |
|
7242
|
|
|
var maxTicks; |
|
7243
|
|
|
if (optionTicks.maxTicksLimit) { |
|
7244
|
|
|
maxTicks = optionTicks.maxTicksLimit; |
|
7245
|
|
|
} |
|
7246
|
|
|
|
|
7247
|
|
|
var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); |
|
7248
|
|
|
var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize); |
|
7249
|
|
|
var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle); |
|
7250
|
|
|
var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily); |
|
7251
|
|
|
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
|
7252
|
|
|
var tl = gridLines.tickMarkLength; |
|
7253
|
|
|
|
|
7254
|
|
|
var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); |
|
7255
|
|
|
var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize); |
|
7256
|
|
|
var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabel.fontStyle, globalDefaults.defaultFontStyle); |
|
7257
|
|
|
var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily); |
|
7258
|
|
|
var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily); |
|
7259
|
|
|
|
|
7260
|
|
|
var labelRotationRadians = helpers.toRadians(me.labelRotation); |
|
7261
|
|
|
var cosRotation = Math.cos(labelRotationRadians); |
|
7262
|
|
|
var longestRotatedLabel = me.longestLabelWidth * cosRotation; |
|
7263
|
|
|
|
|
7264
|
|
|
// Make sure we draw text in the correct color and font |
|
7265
|
|
|
context.fillStyle = tickFontColor; |
|
7266
|
|
|
|
|
7267
|
|
|
var itemsToDraw = []; |
|
7268
|
|
|
|
|
7269
|
|
|
if (isHorizontal) { |
|
7270
|
|
|
skipRatio = false; |
|
7271
|
|
|
|
|
7272
|
|
|
// Only calculate the skip ratio with the half width of longestRotateLabel if we got an actual rotation |
|
7273
|
|
|
// See #2584 |
|
7274
|
|
|
if (isRotated) { |
|
7275
|
|
|
longestRotatedLabel /= 2; |
|
7276
|
|
|
} |
|
7277
|
|
|
|
|
7278
|
|
|
if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) { |
|
7279
|
|
|
skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight))); |
|
7280
|
|
|
} |
|
7281
|
|
|
|
|
7282
|
|
|
// if they defined a max number of optionTicks, |
|
7283
|
|
|
// increase skipRatio until that number is met |
|
7284
|
|
|
if (maxTicks && me.ticks.length > maxTicks) { |
|
7285
|
|
|
while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) { |
|
7286
|
|
|
if (!skipRatio) { |
|
7287
|
|
|
skipRatio = 1; |
|
7288
|
|
|
} |
|
7289
|
|
|
skipRatio += 1; |
|
7290
|
|
|
} |
|
7291
|
|
|
} |
|
7292
|
|
|
|
|
7293
|
|
|
if (!useAutoskipper) { |
|
7294
|
|
|
skipRatio = false; |
|
7295
|
|
|
} |
|
7296
|
|
|
} |
|
7297
|
|
|
|
|
7298
|
|
|
|
|
7299
|
|
|
var xTickStart = options.position === "right" ? me.left : me.right - tl; |
|
7300
|
|
|
var xTickEnd = options.position === "right" ? me.left + tl : me.right; |
|
7301
|
|
|
var yTickStart = options.position === "bottom" ? me.top : me.bottom - tl; |
|
7302
|
|
|
var yTickEnd = options.position === "bottom" ? me.top + tl : me.bottom; |
|
7303
|
|
|
|
|
7304
|
|
|
helpers.each(me.ticks, function(label, index) { |
|
7305
|
|
|
// If the callback returned a null or undefined value, do not draw this line |
|
7306
|
|
|
if (label === undefined || label === null) { |
|
7307
|
|
|
return; |
|
7308
|
|
|
} |
|
7309
|
|
|
|
|
7310
|
|
|
var isLastTick = me.ticks.length === index + 1; |
|
7311
|
|
|
|
|
7312
|
|
|
// Since we always show the last tick,we need may need to hide the last shown one before |
|
7313
|
|
|
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length); |
|
|
|
|
|
|
7314
|
|
|
if (shouldSkip && !isLastTick || (label === undefined || label === null)) { |
|
7315
|
|
|
return; |
|
7316
|
|
|
} |
|
7317
|
|
|
|
|
7318
|
|
|
var lineWidth, lineColor; |
|
7319
|
|
|
if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) { |
|
7320
|
|
|
// Draw the first index specially |
|
7321
|
|
|
lineWidth = gridLines.zeroLineWidth; |
|
7322
|
|
|
lineColor = gridLines.zeroLineColor; |
|
7323
|
|
|
} else { |
|
7324
|
|
|
lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index); |
|
7325
|
|
|
lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index); |
|
7326
|
|
|
} |
|
7327
|
|
|
|
|
7328
|
|
|
// Common properties |
|
7329
|
|
|
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; |
|
7330
|
|
|
var textAlign, textBaseline = 'middle'; |
|
7331
|
|
|
|
|
7332
|
|
|
if (isHorizontal) { |
|
7333
|
|
|
if (!isRotated) { |
|
7334
|
|
|
textBaseline = options.position === 'top' ? 'bottom' : 'top'; |
|
7335
|
|
|
} |
|
7336
|
|
|
|
|
7337
|
|
|
textAlign = isRotated ? 'right' : 'center'; |
|
7338
|
|
|
|
|
7339
|
|
|
var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines |
|
7340
|
|
|
labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) |
|
7341
|
|
|
labelY = (isRotated) ? me.top + 12 : options.position === 'top' ? me.bottom - tl : me.top + tl; |
|
7342
|
|
|
|
|
7343
|
|
|
tx1 = tx2 = x1 = x2 = xLineValue; |
|
7344
|
|
|
ty1 = yTickStart; |
|
7345
|
|
|
ty2 = yTickEnd; |
|
7346
|
|
|
y1 = chartArea.top; |
|
7347
|
|
|
y2 = chartArea.bottom; |
|
7348
|
|
|
} else { |
|
7349
|
|
|
if (options.position === 'left') { |
|
7350
|
|
|
if (optionTicks.mirror) { |
|
7351
|
|
|
labelX = me.right + optionTicks.padding; |
|
7352
|
|
|
textAlign = 'left'; |
|
7353
|
|
|
} else { |
|
7354
|
|
|
labelX = me.right - optionTicks.padding; |
|
7355
|
|
|
textAlign = 'right'; |
|
7356
|
|
|
} |
|
7357
|
|
|
} else { |
|
7358
|
|
|
// right side |
|
7359
|
|
|
if (optionTicks.mirror) { |
|
7360
|
|
|
labelX = me.left - optionTicks.padding; |
|
7361
|
|
|
textAlign = 'right'; |
|
7362
|
|
|
} else { |
|
7363
|
|
|
labelX = me.left + optionTicks.padding; |
|
7364
|
|
|
textAlign = 'left'; |
|
7365
|
|
|
} |
|
7366
|
|
|
} |
|
7367
|
|
|
|
|
7368
|
|
|
var yLineValue = me.getPixelForTick(index); // xvalues for grid lines |
|
7369
|
|
|
yLineValue += helpers.aliasPixel(lineWidth); |
|
7370
|
|
|
labelY = me.getPixelForTick(index, gridLines.offsetGridLines); |
|
7371
|
|
|
|
|
7372
|
|
|
tx1 = xTickStart; |
|
7373
|
|
|
tx2 = xTickEnd; |
|
7374
|
|
|
x1 = chartArea.left; |
|
7375
|
|
|
x2 = chartArea.right; |
|
7376
|
|
|
ty1 = ty2 = y1 = y2 = yLineValue; |
|
7377
|
|
|
} |
|
7378
|
|
|
|
|
7379
|
|
|
itemsToDraw.push({ |
|
7380
|
|
|
tx1: tx1, |
|
7381
|
|
|
ty1: ty1, |
|
7382
|
|
|
tx2: tx2, |
|
7383
|
|
|
ty2: ty2, |
|
7384
|
|
|
x1: x1, |
|
7385
|
|
|
y1: y1, |
|
7386
|
|
|
x2: x2, |
|
7387
|
|
|
y2: y2, |
|
7388
|
|
|
labelX: labelX, |
|
7389
|
|
|
labelY: labelY, |
|
7390
|
|
|
glWidth: lineWidth, |
|
7391
|
|
|
glColor: lineColor, |
|
7392
|
|
|
rotation: -1 * labelRotationRadians, |
|
7393
|
|
|
label: label, |
|
7394
|
|
|
textBaseline: textBaseline, |
|
7395
|
|
|
textAlign: textAlign |
|
7396
|
|
|
}); |
|
7397
|
|
|
}); |
|
7398
|
|
|
|
|
7399
|
|
|
// Draw all of the tick labels, tick marks, and grid lines at the correct places |
|
7400
|
|
|
helpers.each(itemsToDraw, function(itemToDraw) { |
|
7401
|
|
|
if (gridLines.display) { |
|
7402
|
|
|
context.lineWidth = itemToDraw.glWidth; |
|
7403
|
|
|
context.strokeStyle = itemToDraw.glColor; |
|
7404
|
|
|
|
|
7405
|
|
|
context.beginPath(); |
|
7406
|
|
|
|
|
7407
|
|
|
if (gridLines.drawTicks) { |
|
7408
|
|
|
context.moveTo(itemToDraw.tx1, itemToDraw.ty1); |
|
7409
|
|
|
context.lineTo(itemToDraw.tx2, itemToDraw.ty2); |
|
7410
|
|
|
} |
|
7411
|
|
|
|
|
7412
|
|
|
if (gridLines.drawOnChartArea) { |
|
7413
|
|
|
context.moveTo(itemToDraw.x1, itemToDraw.y1); |
|
7414
|
|
|
context.lineTo(itemToDraw.x2, itemToDraw.y2); |
|
7415
|
|
|
} |
|
7416
|
|
|
|
|
7417
|
|
|
context.stroke(); |
|
7418
|
|
|
} |
|
7419
|
|
|
|
|
7420
|
|
|
if (optionTicks.display) { |
|
7421
|
|
|
context.save(); |
|
7422
|
|
|
context.translate(itemToDraw.labelX, itemToDraw.labelY); |
|
7423
|
|
|
context.rotate(itemToDraw.rotation); |
|
7424
|
|
|
context.font = tickLabelFont; |
|
7425
|
|
|
context.textBaseline = itemToDraw.textBaseline; |
|
7426
|
|
|
context.textAlign = itemToDraw.textAlign; |
|
7427
|
|
|
|
|
7428
|
|
|
var label = itemToDraw.label; |
|
7429
|
|
|
if (helpers.isArray(label)) { |
|
7430
|
|
|
for (var i = 0, y = 0; i < label.length; ++i) { |
|
|
|
|
|
|
7431
|
|
|
// We just make sure the multiline element is a string here.. |
|
7432
|
|
|
context.fillText('' + label[i], 0, y); |
|
7433
|
|
|
// apply same lineSpacing as calculated @ L#320 |
|
7434
|
|
|
y += (tickFontSize * 1.5); |
|
7435
|
|
|
} |
|
7436
|
|
|
} else { |
|
7437
|
|
|
context.fillText(label, 0, 0); |
|
7438
|
|
|
} |
|
7439
|
|
|
context.restore(); |
|
7440
|
|
|
} |
|
7441
|
|
|
}); |
|
7442
|
|
|
|
|
7443
|
|
|
if (scaleLabel.display) { |
|
7444
|
|
|
// Draw the scale label |
|
7445
|
|
|
var scaleLabelX; |
|
7446
|
|
|
var scaleLabelY; |
|
7447
|
|
|
var rotation = 0; |
|
7448
|
|
|
|
|
7449
|
|
|
if (isHorizontal) { |
|
7450
|
|
|
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width |
|
7451
|
|
|
scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFontSize / 2) : me.top + (scaleLabelFontSize / 2); |
|
7452
|
|
|
} else { |
|
7453
|
|
|
var isLeft = options.position === 'left'; |
|
7454
|
|
|
scaleLabelX = isLeft ? me.left + (scaleLabelFontSize / 2) : me.right - (scaleLabelFontSize / 2); |
|
7455
|
|
|
scaleLabelY = me.top + ((me.bottom - me.top) / 2); |
|
7456
|
|
|
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; |
|
7457
|
|
|
} |
|
7458
|
|
|
|
|
7459
|
|
|
context.save(); |
|
7460
|
|
|
context.translate(scaleLabelX, scaleLabelY); |
|
7461
|
|
|
context.rotate(rotation); |
|
7462
|
|
|
context.textAlign = 'center'; |
|
7463
|
|
|
context.textBaseline = 'middle'; |
|
7464
|
|
|
context.fillStyle = scaleLabelFontColor; // render in correct colour |
|
7465
|
|
|
context.font = scaleLabelFont; |
|
7466
|
|
|
context.fillText(scaleLabel.labelString, 0, 0); |
|
7467
|
|
|
context.restore(); |
|
7468
|
|
|
} |
|
7469
|
|
|
|
|
7470
|
|
|
if (gridLines.drawBorder) { |
|
7471
|
|
|
// Draw the line at the edge of the axis |
|
7472
|
|
|
context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0); |
|
7473
|
|
|
context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0); |
|
7474
|
|
|
var x1 = me.left, |
|
7475
|
|
|
x2 = me.right, |
|
7476
|
|
|
y1 = me.top, |
|
7477
|
|
|
y2 = me.bottom; |
|
7478
|
|
|
|
|
7479
|
|
|
var aliasPixel = helpers.aliasPixel(context.lineWidth); |
|
7480
|
|
|
if (isHorizontal) { |
|
7481
|
|
|
y1 = y2 = options.position === 'top' ? me.bottom : me.top; |
|
7482
|
|
|
y1 += aliasPixel; |
|
7483
|
|
|
y2 += aliasPixel; |
|
7484
|
|
|
} else { |
|
7485
|
|
|
x1 = x2 = options.position === 'left' ? me.right : me.left; |
|
7486
|
|
|
x1 += aliasPixel; |
|
7487
|
|
|
x2 += aliasPixel; |
|
7488
|
|
|
} |
|
7489
|
|
|
|
|
7490
|
|
|
context.beginPath(); |
|
7491
|
|
|
context.moveTo(x1, y1); |
|
7492
|
|
|
context.lineTo(x2, y2); |
|
7493
|
|
|
context.stroke(); |
|
7494
|
|
|
} |
|
7495
|
|
|
} |
|
7496
|
|
|
}); |
|
7497
|
|
|
}; |
|
7498
|
|
|
|
|
7499
|
|
|
},{}],32:[function(require,module,exports){ |
|
7500
|
|
|
"use strict"; |
|
7501
|
|
|
|
|
7502
|
|
|
module.exports = function(Chart) { |
|
7503
|
|
|
|
|
7504
|
|
|
var helpers = Chart.helpers; |
|
7505
|
|
|
|
|
7506
|
|
|
Chart.scaleService = { |
|
7507
|
|
|
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then |
|
7508
|
|
|
// use the new chart options to grab the correct scale |
|
7509
|
|
|
constructors: {}, |
|
7510
|
|
|
// Use a registration function so that we can move to an ES6 map when we no longer need to support |
|
7511
|
|
|
// old browsers |
|
7512
|
|
|
|
|
7513
|
|
|
// Scale config defaults |
|
7514
|
|
|
defaults: {}, |
|
7515
|
|
|
registerScaleType: function(type, scaleConstructor, defaults) { |
|
7516
|
|
|
this.constructors[type] = scaleConstructor; |
|
7517
|
|
|
this.defaults[type] = helpers.clone(defaults); |
|
7518
|
|
|
}, |
|
7519
|
|
|
getScaleConstructor: function(type) { |
|
7520
|
|
|
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; |
|
7521
|
|
|
}, |
|
7522
|
|
|
getScaleDefaults: function(type) { |
|
7523
|
|
|
// Return the scale defaults merged with the global settings so that we always use the latest ones |
|
7524
|
|
|
return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {}; |
|
7525
|
|
|
}, |
|
7526
|
|
|
updateScaleDefaults: function(type, additions) { |
|
7527
|
|
|
var defaults = this.defaults; |
|
7528
|
|
|
if (defaults.hasOwnProperty(type)) { |
|
7529
|
|
|
defaults[type] = helpers.extend(defaults[type], additions); |
|
7530
|
|
|
} |
|
7531
|
|
|
}, |
|
7532
|
|
|
addScalesToLayout: function(chartInstance) { |
|
7533
|
|
|
// Adds each scale to the chart.boxes array to be sized accordingly |
|
7534
|
|
|
helpers.each(chartInstance.scales, function(scale) { |
|
7535
|
|
|
Chart.layoutService.addBox(chartInstance, scale); |
|
7536
|
|
|
}); |
|
7537
|
|
|
} |
|
7538
|
|
|
}; |
|
7539
|
|
|
}; |
|
7540
|
|
|
},{}],33:[function(require,module,exports){ |
|
7541
|
|
|
"use strict"; |
|
7542
|
|
|
|
|
7543
|
|
|
module.exports = function(Chart) { |
|
7544
|
|
|
|
|
7545
|
|
|
var helpers = Chart.helpers; |
|
7546
|
|
|
|
|
7547
|
|
|
Chart.defaults.global.title = { |
|
7548
|
|
|
display: false, |
|
7549
|
|
|
position: 'top', |
|
7550
|
|
|
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes) |
|
7551
|
|
|
|
|
7552
|
|
|
fontStyle: 'bold', |
|
7553
|
|
|
padding: 10, |
|
7554
|
|
|
|
|
7555
|
|
|
// actual title |
|
7556
|
|
|
text: '' |
|
7557
|
|
|
}; |
|
7558
|
|
|
|
|
7559
|
|
|
var noop = helpers.noop; |
|
7560
|
|
|
Chart.Title = Chart.Element.extend({ |
|
7561
|
|
|
|
|
7562
|
|
|
initialize: function(config) { |
|
7563
|
|
|
var me = this; |
|
7564
|
|
|
helpers.extend(me, config); |
|
7565
|
|
|
me.options = helpers.configMerge(Chart.defaults.global.title, config.options); |
|
7566
|
|
|
|
|
7567
|
|
|
// Contains hit boxes for each dataset (in dataset order) |
|
7568
|
|
|
me.legendHitBoxes = []; |
|
7569
|
|
|
}, |
|
7570
|
|
|
|
|
7571
|
|
|
// These methods are ordered by lifecyle. Utilities then follow. |
|
7572
|
|
|
|
|
7573
|
|
|
beforeUpdate: function () { |
|
7574
|
|
|
var chartOpts = this.chart.options; |
|
7575
|
|
|
if (chartOpts && chartOpts.title) { |
|
7576
|
|
|
this.options = helpers.configMerge(Chart.defaults.global.title, chartOpts.title); |
|
7577
|
|
|
} |
|
7578
|
|
|
}, |
|
7579
|
|
|
update: function(maxWidth, maxHeight, margins) { |
|
7580
|
|
|
var me = this; |
|
7581
|
|
|
|
|
7582
|
|
|
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
|
7583
|
|
|
me.beforeUpdate(); |
|
7584
|
|
|
|
|
7585
|
|
|
// Absorb the master measurements |
|
7586
|
|
|
me.maxWidth = maxWidth; |
|
7587
|
|
|
me.maxHeight = maxHeight; |
|
7588
|
|
|
me.margins = margins; |
|
7589
|
|
|
|
|
7590
|
|
|
// Dimensions |
|
7591
|
|
|
me.beforeSetDimensions(); |
|
7592
|
|
|
me.setDimensions(); |
|
7593
|
|
|
me.afterSetDimensions(); |
|
7594
|
|
|
// Labels |
|
7595
|
|
|
me.beforeBuildLabels(); |
|
7596
|
|
|
me.buildLabels(); |
|
7597
|
|
|
me.afterBuildLabels(); |
|
7598
|
|
|
|
|
7599
|
|
|
// Fit |
|
7600
|
|
|
me.beforeFit(); |
|
7601
|
|
|
me.fit(); |
|
7602
|
|
|
me.afterFit(); |
|
7603
|
|
|
// |
|
7604
|
|
|
me.afterUpdate(); |
|
7605
|
|
|
|
|
7606
|
|
|
return me.minSize; |
|
7607
|
|
|
|
|
7608
|
|
|
}, |
|
7609
|
|
|
afterUpdate: noop, |
|
7610
|
|
|
|
|
7611
|
|
|
// |
|
7612
|
|
|
|
|
7613
|
|
|
beforeSetDimensions: noop, |
|
7614
|
|
|
setDimensions: function() { |
|
7615
|
|
|
var me = this; |
|
7616
|
|
|
// Set the unconstrained dimension before label rotation |
|
7617
|
|
|
if (me.isHorizontal()) { |
|
7618
|
|
|
// Reset position before calculating rotation |
|
7619
|
|
|
me.width = me.maxWidth; |
|
7620
|
|
|
me.left = 0; |
|
7621
|
|
|
me.right = me.width; |
|
7622
|
|
|
} else { |
|
7623
|
|
|
me.height = me.maxHeight; |
|
7624
|
|
|
|
|
7625
|
|
|
// Reset position before calculating rotation |
|
7626
|
|
|
me.top = 0; |
|
7627
|
|
|
me.bottom = me.height; |
|
7628
|
|
|
} |
|
7629
|
|
|
|
|
7630
|
|
|
// Reset padding |
|
7631
|
|
|
me.paddingLeft = 0; |
|
7632
|
|
|
me.paddingTop = 0; |
|
7633
|
|
|
me.paddingRight = 0; |
|
7634
|
|
|
me.paddingBottom = 0; |
|
7635
|
|
|
|
|
7636
|
|
|
// Reset minSize |
|
7637
|
|
|
me.minSize = { |
|
7638
|
|
|
width: 0, |
|
7639
|
|
|
height: 0 |
|
7640
|
|
|
}; |
|
7641
|
|
|
}, |
|
7642
|
|
|
afterSetDimensions: noop, |
|
7643
|
|
|
|
|
7644
|
|
|
// |
|
7645
|
|
|
|
|
7646
|
|
|
beforeBuildLabels: noop, |
|
7647
|
|
|
buildLabels: noop, |
|
7648
|
|
|
afterBuildLabels: noop, |
|
7649
|
|
|
|
|
7650
|
|
|
// |
|
7651
|
|
|
|
|
7652
|
|
|
beforeFit: noop, |
|
7653
|
|
|
fit: function() { |
|
7654
|
|
|
var me = this, |
|
7655
|
|
|
valueOrDefault = helpers.getValueOrDefault, |
|
7656
|
|
|
opts = me.options, |
|
7657
|
|
|
globalDefaults = Chart.defaults.global, |
|
7658
|
|
|
display = opts.display, |
|
7659
|
|
|
fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), |
|
7660
|
|
|
minSize = me.minSize; |
|
7661
|
|
|
|
|
7662
|
|
|
if (me.isHorizontal()) { |
|
7663
|
|
|
minSize.width = me.maxWidth; // fill all the width |
|
7664
|
|
|
minSize.height = display ? fontSize + (opts.padding * 2) : 0; |
|
7665
|
|
|
} else { |
|
7666
|
|
|
minSize.width = display ? fontSize + (opts.padding * 2) : 0; |
|
7667
|
|
|
minSize.height = me.maxHeight; // fill all the height |
|
7668
|
|
|
} |
|
7669
|
|
|
|
|
7670
|
|
|
me.width = minSize.width; |
|
7671
|
|
|
me.height = minSize.height; |
|
7672
|
|
|
|
|
7673
|
|
|
}, |
|
7674
|
|
|
afterFit: noop, |
|
7675
|
|
|
|
|
7676
|
|
|
// Shared Methods |
|
7677
|
|
|
isHorizontal: function() { |
|
7678
|
|
|
var pos = this.options.position; |
|
7679
|
|
|
return pos === "top" || pos === "bottom"; |
|
7680
|
|
|
}, |
|
7681
|
|
|
|
|
7682
|
|
|
// Actualy draw the title block on the canvas |
|
7683
|
|
|
draw: function() { |
|
7684
|
|
|
var me = this, |
|
7685
|
|
|
ctx = me.ctx, |
|
7686
|
|
|
valueOrDefault = helpers.getValueOrDefault, |
|
7687
|
|
|
opts = me.options, |
|
7688
|
|
|
globalDefaults = Chart.defaults.global; |
|
7689
|
|
|
|
|
7690
|
|
|
if (opts.display) { |
|
7691
|
|
|
var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), |
|
7692
|
|
|
fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle), |
|
7693
|
|
|
fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily), |
|
7694
|
|
|
titleFont = helpers.fontString(fontSize, fontStyle, fontFamily), |
|
7695
|
|
|
rotation = 0, |
|
7696
|
|
|
titleX, |
|
7697
|
|
|
titleY, |
|
7698
|
|
|
top = me.top, |
|
7699
|
|
|
left = me.left, |
|
7700
|
|
|
bottom = me.bottom, |
|
7701
|
|
|
right = me.right; |
|
7702
|
|
|
|
|
7703
|
|
|
ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour |
|
7704
|
|
|
ctx.font = titleFont; |
|
7705
|
|
|
|
|
7706
|
|
|
// Horizontal |
|
7707
|
|
|
if (me.isHorizontal()) { |
|
7708
|
|
|
titleX = left + ((right - left) / 2); // midpoint of the width |
|
7709
|
|
|
titleY = top + ((bottom - top) / 2); // midpoint of the height |
|
7710
|
|
|
} else { |
|
7711
|
|
|
titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2); |
|
7712
|
|
|
titleY = top + ((bottom - top) / 2); |
|
7713
|
|
|
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); |
|
7714
|
|
|
} |
|
7715
|
|
|
|
|
7716
|
|
|
ctx.save(); |
|
7717
|
|
|
ctx.translate(titleX, titleY); |
|
7718
|
|
|
ctx.rotate(rotation); |
|
7719
|
|
|
ctx.textAlign = 'center'; |
|
7720
|
|
|
ctx.textBaseline = 'middle'; |
|
7721
|
|
|
ctx.fillText(opts.text, 0, 0); |
|
7722
|
|
|
ctx.restore(); |
|
7723
|
|
|
} |
|
7724
|
|
|
} |
|
7725
|
|
|
}); |
|
7726
|
|
|
|
|
7727
|
|
|
// Register the title plugin |
|
7728
|
|
|
Chart.plugins.register({ |
|
7729
|
|
|
beforeInit: function(chartInstance) { |
|
7730
|
|
|
var opts = chartInstance.options; |
|
7731
|
|
|
var titleOpts = opts.title; |
|
7732
|
|
|
|
|
7733
|
|
|
if (titleOpts) { |
|
7734
|
|
|
chartInstance.titleBlock = new Chart.Title({ |
|
7735
|
|
|
ctx: chartInstance.chart.ctx, |
|
7736
|
|
|
options: titleOpts, |
|
7737
|
|
|
chart: chartInstance |
|
7738
|
|
|
}); |
|
7739
|
|
|
|
|
7740
|
|
|
Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock); |
|
7741
|
|
|
} |
|
7742
|
|
|
} |
|
7743
|
|
|
}); |
|
7744
|
|
|
}; |
|
7745
|
|
|
|
|
7746
|
|
|
},{}],34:[function(require,module,exports){ |
|
7747
|
|
|
"use strict"; |
|
7748
|
|
|
|
|
7749
|
|
|
module.exports = function(Chart) { |
|
7750
|
|
|
|
|
7751
|
|
|
var helpers = Chart.helpers; |
|
7752
|
|
|
|
|
7753
|
|
|
Chart.defaults.global.tooltips = { |
|
7754
|
|
|
enabled: true, |
|
7755
|
|
|
custom: null, |
|
7756
|
|
|
mode: 'single', |
|
7757
|
|
|
backgroundColor: "rgba(0,0,0,0.8)", |
|
7758
|
|
|
titleFontStyle: "bold", |
|
7759
|
|
|
titleSpacing: 2, |
|
7760
|
|
|
titleMarginBottom: 6, |
|
7761
|
|
|
titleFontColor: "#fff", |
|
7762
|
|
|
titleAlign: "left", |
|
7763
|
|
|
bodySpacing: 2, |
|
7764
|
|
|
bodyFontColor: "#fff", |
|
7765
|
|
|
bodyAlign: "left", |
|
7766
|
|
|
footerFontStyle: "bold", |
|
7767
|
|
|
footerSpacing: 2, |
|
7768
|
|
|
footerMarginTop: 6, |
|
7769
|
|
|
footerFontColor: "#fff", |
|
7770
|
|
|
footerAlign: "left", |
|
7771
|
|
|
yPadding: 6, |
|
7772
|
|
|
xPadding: 6, |
|
7773
|
|
|
yAlign : 'center', |
|
7774
|
|
|
xAlign : 'center', |
|
7775
|
|
|
caretSize: 5, |
|
7776
|
|
|
cornerRadius: 6, |
|
7777
|
|
|
multiKeyBackground: '#fff', |
|
7778
|
|
|
callbacks: { |
|
7779
|
|
|
// Args are: (tooltipItems, data) |
|
7780
|
|
|
beforeTitle: helpers.noop, |
|
7781
|
|
|
title: function(tooltipItems, data) { |
|
7782
|
|
|
// Pick first xLabel for now |
|
7783
|
|
|
var title = ''; |
|
7784
|
|
|
var labels = data.labels; |
|
7785
|
|
|
var labelCount = labels ? labels.length : 0; |
|
7786
|
|
|
|
|
7787
|
|
|
if (tooltipItems.length > 0) { |
|
7788
|
|
|
var item = tooltipItems[0]; |
|
7789
|
|
|
|
|
7790
|
|
|
if (item.xLabel) { |
|
7791
|
|
|
title = item.xLabel; |
|
7792
|
|
|
} else if (labelCount > 0 && item.index < labelCount) { |
|
7793
|
|
|
title = labels[item.index]; |
|
7794
|
|
|
} |
|
7795
|
|
|
} |
|
7796
|
|
|
|
|
7797
|
|
|
return title; |
|
7798
|
|
|
}, |
|
7799
|
|
|
afterTitle: helpers.noop, |
|
7800
|
|
|
|
|
7801
|
|
|
// Args are: (tooltipItems, data) |
|
7802
|
|
|
beforeBody: helpers.noop, |
|
7803
|
|
|
|
|
7804
|
|
|
// Args are: (tooltipItem, data) |
|
7805
|
|
|
beforeLabel: helpers.noop, |
|
7806
|
|
|
label: function(tooltipItem, data) { |
|
7807
|
|
|
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; |
|
7808
|
|
|
return datasetLabel + ': ' + tooltipItem.yLabel; |
|
7809
|
|
|
}, |
|
7810
|
|
|
labelColor: function(tooltipItem, chartInstance) { |
|
7811
|
|
|
var meta = chartInstance.getDatasetMeta(tooltipItem.datasetIndex); |
|
7812
|
|
|
var activeElement = meta.data[tooltipItem.index]; |
|
7813
|
|
|
var view = activeElement._view; |
|
7814
|
|
|
return { |
|
7815
|
|
|
borderColor: view.borderColor, |
|
7816
|
|
|
backgroundColor: view.backgroundColor |
|
7817
|
|
|
}; |
|
7818
|
|
|
}, |
|
7819
|
|
|
afterLabel: helpers.noop, |
|
7820
|
|
|
|
|
7821
|
|
|
// Args are: (tooltipItems, data) |
|
7822
|
|
|
afterBody: helpers.noop, |
|
7823
|
|
|
|
|
7824
|
|
|
// Args are: (tooltipItems, data) |
|
7825
|
|
|
beforeFooter: helpers.noop, |
|
7826
|
|
|
footer: helpers.noop, |
|
7827
|
|
|
afterFooter: helpers.noop |
|
7828
|
|
|
} |
|
7829
|
|
|
}; |
|
7830
|
|
|
|
|
7831
|
|
|
// Helper to push or concat based on if the 2nd parameter is an array or not |
|
7832
|
|
|
function pushOrConcat(base, toPush) { |
|
7833
|
|
|
if (toPush) { |
|
7834
|
|
|
if (helpers.isArray(toPush)) { |
|
7835
|
|
|
//base = base.concat(toPush); |
|
7836
|
|
|
Array.prototype.push.apply(base, toPush); |
|
7837
|
|
|
} else { |
|
7838
|
|
|
base.push(toPush); |
|
7839
|
|
|
} |
|
7840
|
|
|
} |
|
7841
|
|
|
|
|
7842
|
|
|
return base; |
|
7843
|
|
|
} |
|
7844
|
|
|
|
|
7845
|
|
|
function getAveragePosition(elements) { |
|
7846
|
|
|
if (!elements.length) { |
|
7847
|
|
|
return false; |
|
7848
|
|
|
} |
|
7849
|
|
|
|
|
7850
|
|
|
var i, len; |
|
7851
|
|
|
var xPositions = []; |
|
7852
|
|
|
var yPositions = []; |
|
7853
|
|
|
|
|
7854
|
|
|
for (i = 0, len = elements.length; i < len; ++i) { |
|
7855
|
|
|
var el = elements[i]; |
|
7856
|
|
|
if (el && el.hasValue()){ |
|
7857
|
|
|
var pos = el.tooltipPosition(); |
|
7858
|
|
|
xPositions.push(pos.x); |
|
7859
|
|
|
yPositions.push(pos.y); |
|
7860
|
|
|
} |
|
7861
|
|
|
} |
|
7862
|
|
|
|
|
7863
|
|
|
var x = 0, |
|
7864
|
|
|
y = 0; |
|
7865
|
|
|
for (i = 0; i < xPositions.length; ++i) { |
|
7866
|
|
|
if (xPositions[ i ]) { |
|
7867
|
|
|
x += xPositions[i]; |
|
7868
|
|
|
y += yPositions[i]; |
|
7869
|
|
|
} |
|
7870
|
|
|
} |
|
7871
|
|
|
|
|
7872
|
|
|
return { |
|
7873
|
|
|
x: Math.round(x / xPositions.length), |
|
7874
|
|
|
y: Math.round(y / xPositions.length) |
|
7875
|
|
|
}; |
|
7876
|
|
|
} |
|
7877
|
|
|
|
|
7878
|
|
|
// Private helper to create a tooltip iteam model |
|
7879
|
|
|
// @param element : the chart element (point, arc, bar) to create the tooltip item for |
|
7880
|
|
|
// @return : new tooltip item |
|
7881
|
|
|
function createTooltipItem(element) { |
|
7882
|
|
|
var xScale = element._xScale; |
|
7883
|
|
|
var yScale = element._yScale || element._scale; // handle radar || polarArea charts |
|
7884
|
|
|
var index = element._index, |
|
7885
|
|
|
datasetIndex = element._datasetIndex; |
|
7886
|
|
|
|
|
7887
|
|
|
return { |
|
7888
|
|
|
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', |
|
7889
|
|
|
yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', |
|
7890
|
|
|
index: index, |
|
7891
|
|
|
datasetIndex: datasetIndex |
|
7892
|
|
|
}; |
|
7893
|
|
|
} |
|
7894
|
|
|
|
|
7895
|
|
|
Chart.Tooltip = Chart.Element.extend({ |
|
7896
|
|
|
initialize: function() { |
|
7897
|
|
|
var me = this; |
|
7898
|
|
|
var globalDefaults = Chart.defaults.global; |
|
7899
|
|
|
var tooltipOpts = me._options; |
|
7900
|
|
|
var getValueOrDefault = helpers.getValueOrDefault; |
|
7901
|
|
|
|
|
7902
|
|
|
helpers.extend(me, { |
|
7903
|
|
|
_model: { |
|
7904
|
|
|
// Positioning |
|
7905
|
|
|
xPadding: tooltipOpts.xPadding, |
|
7906
|
|
|
yPadding: tooltipOpts.yPadding, |
|
7907
|
|
|
xAlign : tooltipOpts.xAlign, |
|
7908
|
|
|
yAlign : tooltipOpts.yAlign, |
|
7909
|
|
|
|
|
7910
|
|
|
// Body |
|
7911
|
|
|
bodyFontColor: tooltipOpts.bodyFontColor, |
|
7912
|
|
|
_bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), |
|
7913
|
|
|
_bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), |
|
7914
|
|
|
_bodyAlign: tooltipOpts.bodyAlign, |
|
7915
|
|
|
bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), |
|
7916
|
|
|
bodySpacing: tooltipOpts.bodySpacing, |
|
7917
|
|
|
|
|
7918
|
|
|
// Title |
|
7919
|
|
|
titleFontColor: tooltipOpts.titleFontColor, |
|
7920
|
|
|
_titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), |
|
7921
|
|
|
_titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), |
|
7922
|
|
|
titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), |
|
7923
|
|
|
_titleAlign: tooltipOpts.titleAlign, |
|
7924
|
|
|
titleSpacing: tooltipOpts.titleSpacing, |
|
7925
|
|
|
titleMarginBottom: tooltipOpts.titleMarginBottom, |
|
7926
|
|
|
|
|
7927
|
|
|
// Footer |
|
7928
|
|
|
footerFontColor: tooltipOpts.footerFontColor, |
|
7929
|
|
|
_footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), |
|
7930
|
|
|
_footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), |
|
7931
|
|
|
footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), |
|
7932
|
|
|
_footerAlign: tooltipOpts.footerAlign, |
|
7933
|
|
|
footerSpacing: tooltipOpts.footerSpacing, |
|
7934
|
|
|
footerMarginTop: tooltipOpts.footerMarginTop, |
|
7935
|
|
|
|
|
7936
|
|
|
// Appearance |
|
7937
|
|
|
caretSize: tooltipOpts.caretSize, |
|
7938
|
|
|
cornerRadius: tooltipOpts.cornerRadius, |
|
7939
|
|
|
backgroundColor: tooltipOpts.backgroundColor, |
|
7940
|
|
|
opacity: 0, |
|
7941
|
|
|
legendColorBackground: tooltipOpts.multiKeyBackground |
|
7942
|
|
|
} |
|
7943
|
|
|
}); |
|
7944
|
|
|
}, |
|
7945
|
|
|
|
|
7946
|
|
|
// Get the title |
|
7947
|
|
|
// Args are: (tooltipItem, data) |
|
7948
|
|
|
getTitle: function() { |
|
7949
|
|
|
var me = this; |
|
7950
|
|
|
var opts = me._options; |
|
7951
|
|
|
var callbacks = opts.callbacks; |
|
7952
|
|
|
|
|
7953
|
|
|
var beforeTitle = callbacks.beforeTitle.apply(me, arguments), |
|
7954
|
|
|
title = callbacks.title.apply(me, arguments), |
|
7955
|
|
|
afterTitle = callbacks.afterTitle.apply(me, arguments); |
|
7956
|
|
|
|
|
7957
|
|
|
var lines = []; |
|
7958
|
|
|
lines = pushOrConcat(lines, beforeTitle); |
|
7959
|
|
|
lines = pushOrConcat(lines, title); |
|
7960
|
|
|
lines = pushOrConcat(lines, afterTitle); |
|
7961
|
|
|
|
|
7962
|
|
|
return lines; |
|
7963
|
|
|
}, |
|
7964
|
|
|
|
|
7965
|
|
|
// Args are: (tooltipItem, data) |
|
7966
|
|
|
getBeforeBody: function() { |
|
7967
|
|
|
var lines = this._options.callbacks.beforeBody.apply(this, arguments); |
|
7968
|
|
|
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; |
|
7969
|
|
|
}, |
|
7970
|
|
|
|
|
7971
|
|
|
// Args are: (tooltipItem, data) |
|
7972
|
|
|
getBody: function(tooltipItems, data) { |
|
7973
|
|
|
var me = this; |
|
7974
|
|
|
var callbacks = me._options.callbacks; |
|
7975
|
|
|
var bodyItems = []; |
|
7976
|
|
|
|
|
7977
|
|
|
helpers.each(tooltipItems, function(tooltipItem) { |
|
7978
|
|
|
var bodyItem = { |
|
7979
|
|
|
before: [], |
|
7980
|
|
|
lines: [], |
|
7981
|
|
|
after: [] |
|
7982
|
|
|
}; |
|
7983
|
|
|
pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); |
|
7984
|
|
|
pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); |
|
7985
|
|
|
pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); |
|
7986
|
|
|
|
|
7987
|
|
|
bodyItems.push(bodyItem); |
|
7988
|
|
|
}); |
|
7989
|
|
|
|
|
7990
|
|
|
return bodyItems; |
|
7991
|
|
|
}, |
|
7992
|
|
|
|
|
7993
|
|
|
// Args are: (tooltipItem, data) |
|
7994
|
|
|
getAfterBody: function() { |
|
7995
|
|
|
var lines = this._options.callbacks.afterBody.apply(this, arguments); |
|
7996
|
|
|
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; |
|
7997
|
|
|
}, |
|
7998
|
|
|
|
|
7999
|
|
|
// Get the footer and beforeFooter and afterFooter lines |
|
8000
|
|
|
// Args are: (tooltipItem, data) |
|
8001
|
|
|
getFooter: function() { |
|
8002
|
|
|
var me = this; |
|
8003
|
|
|
var callbacks = me._options.callbacks; |
|
8004
|
|
|
|
|
8005
|
|
|
var beforeFooter = callbacks.beforeFooter.apply(me, arguments); |
|
8006
|
|
|
var footer = callbacks.footer.apply(me, arguments); |
|
8007
|
|
|
var afterFooter = callbacks.afterFooter.apply(me, arguments); |
|
8008
|
|
|
|
|
8009
|
|
|
var lines = []; |
|
8010
|
|
|
lines = pushOrConcat(lines, beforeFooter); |
|
8011
|
|
|
lines = pushOrConcat(lines, footer); |
|
8012
|
|
|
lines = pushOrConcat(lines, afterFooter); |
|
8013
|
|
|
|
|
8014
|
|
|
return lines; |
|
8015
|
|
|
}, |
|
8016
|
|
|
|
|
8017
|
|
|
update: function(changed) { |
|
8018
|
|
|
var me = this; |
|
8019
|
|
|
var opts = me._options; |
|
8020
|
|
|
var model = me._model; |
|
8021
|
|
|
var active = me._active; |
|
8022
|
|
|
|
|
8023
|
|
|
var data = me._data; |
|
8024
|
|
|
var chartInstance = me._chartInstance; |
|
8025
|
|
|
|
|
8026
|
|
|
var i, len; |
|
8027
|
|
|
|
|
8028
|
|
|
if (active.length) { |
|
8029
|
|
|
model.opacity = 1; |
|
8030
|
|
|
|
|
8031
|
|
|
var labelColors = [], |
|
8032
|
|
|
tooltipPosition = getAveragePosition(active); |
|
8033
|
|
|
|
|
8034
|
|
|
var tooltipItems = []; |
|
8035
|
|
|
for (i = 0, len = active.length; i < len; ++i) { |
|
8036
|
|
|
tooltipItems.push(createTooltipItem(active[i])); |
|
8037
|
|
|
} |
|
8038
|
|
|
|
|
8039
|
|
|
// If the user provided a sorting function, use it to modify the tooltip items |
|
8040
|
|
|
if (opts.itemSort) { |
|
8041
|
|
|
tooltipItems = tooltipItems.sort(opts.itemSort); |
|
8042
|
|
|
} |
|
8043
|
|
|
|
|
8044
|
|
|
// If there is more than one item, show color items |
|
8045
|
|
|
if (active.length > 1) { |
|
8046
|
|
|
helpers.each(tooltipItems, function(tooltipItem) { |
|
8047
|
|
|
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance)); |
|
8048
|
|
|
}); |
|
8049
|
|
|
} |
|
8050
|
|
|
|
|
8051
|
|
|
// Build the Text Lines |
|
8052
|
|
|
helpers.extend(model, { |
|
8053
|
|
|
title: me.getTitle(tooltipItems, data), |
|
8054
|
|
|
beforeBody: me.getBeforeBody(tooltipItems, data), |
|
8055
|
|
|
body: me.getBody(tooltipItems, data), |
|
8056
|
|
|
afterBody: me.getAfterBody(tooltipItems, data), |
|
8057
|
|
|
footer: me.getFooter(tooltipItems, data), |
|
8058
|
|
|
x: Math.round(tooltipPosition.x), |
|
8059
|
|
|
y: Math.round(tooltipPosition.y), |
|
8060
|
|
|
caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2), |
|
8061
|
|
|
labelColors: labelColors |
|
8062
|
|
|
}); |
|
8063
|
|
|
|
|
8064
|
|
|
// We need to determine alignment of |
|
8065
|
|
|
var tooltipSize = me.getTooltipSize(model); |
|
8066
|
|
|
me.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas |
|
8067
|
|
|
|
|
8068
|
|
|
helpers.extend(model, me.getBackgroundPoint(model, tooltipSize)); |
|
8069
|
|
|
} else { |
|
8070
|
|
|
me._model.opacity = 0; |
|
8071
|
|
|
} |
|
8072
|
|
|
|
|
8073
|
|
|
if (changed && opts.custom) { |
|
8074
|
|
|
opts.custom.call(me, model); |
|
8075
|
|
|
} |
|
8076
|
|
|
|
|
8077
|
|
|
return me; |
|
8078
|
|
|
}, |
|
8079
|
|
|
getTooltipSize: function(vm) { |
|
8080
|
|
|
var ctx = this._chart.ctx; |
|
8081
|
|
|
|
|
8082
|
|
|
var size = { |
|
8083
|
|
|
height: vm.yPadding * 2, // Tooltip Padding |
|
8084
|
|
|
width: 0 |
|
8085
|
|
|
}; |
|
8086
|
|
|
|
|
8087
|
|
|
// Count of all lines in the body |
|
8088
|
|
|
var body = vm.body; |
|
8089
|
|
|
var combinedBodyLength = body.reduce(function(count, bodyItem) { |
|
8090
|
|
|
return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; |
|
8091
|
|
|
}, 0); |
|
8092
|
|
|
combinedBodyLength += vm.beforeBody.length + vm.afterBody.length; |
|
8093
|
|
|
|
|
8094
|
|
|
var titleLineCount = vm.title.length; |
|
8095
|
|
|
var footerLineCount = vm.footer.length; |
|
8096
|
|
|
var titleFontSize = vm.titleFontSize, |
|
8097
|
|
|
bodyFontSize = vm.bodyFontSize, |
|
8098
|
|
|
footerFontSize = vm.footerFontSize; |
|
8099
|
|
|
|
|
8100
|
|
|
size.height += titleLineCount * titleFontSize; // Title Lines |
|
8101
|
|
|
size.height += (titleLineCount - 1) * vm.titleSpacing; // Title Line Spacing |
|
8102
|
|
|
size.height += titleLineCount ? vm.titleMarginBottom : 0; // Title's bottom Margin |
|
8103
|
|
|
size.height += combinedBodyLength * bodyFontSize; // Body Lines |
|
8104
|
|
|
size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing |
|
8105
|
|
|
size.height += footerLineCount ? vm.footerMarginTop : 0; // Footer Margin |
|
8106
|
|
|
size.height += footerLineCount * (footerFontSize); // Footer Lines |
|
8107
|
|
|
size.height += footerLineCount ? (footerLineCount - 1) * vm.footerSpacing : 0; // Footer Line Spacing |
|
8108
|
|
|
|
|
8109
|
|
|
// Title width |
|
8110
|
|
|
var widthPadding = 0; |
|
8111
|
|
|
var maxLineWidth = function(line) { |
|
8112
|
|
|
size.width = Math.max(size.width, ctx.measureText(line).width + widthPadding); |
|
8113
|
|
|
}; |
|
8114
|
|
|
|
|
8115
|
|
|
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); |
|
8116
|
|
|
helpers.each(vm.title, maxLineWidth); |
|
8117
|
|
|
|
|
8118
|
|
|
// Body width |
|
8119
|
|
|
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); |
|
8120
|
|
|
helpers.each(vm.beforeBody.concat(vm.afterBody), maxLineWidth); |
|
8121
|
|
|
|
|
8122
|
|
|
// Body lines may include some extra width due to the color box |
|
8123
|
|
|
widthPadding = body.length > 1 ? (bodyFontSize + 2) : 0; |
|
8124
|
|
|
helpers.each(body, function(bodyItem) { |
|
8125
|
|
|
helpers.each(bodyItem.before, maxLineWidth); |
|
8126
|
|
|
helpers.each(bodyItem.lines, maxLineWidth); |
|
8127
|
|
|
helpers.each(bodyItem.after, maxLineWidth); |
|
8128
|
|
|
}); |
|
8129
|
|
|
|
|
8130
|
|
|
// Reset back to 0 |
|
8131
|
|
|
widthPadding = 0; |
|
8132
|
|
|
|
|
8133
|
|
|
// Footer width |
|
8134
|
|
|
ctx.font = helpers.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily); |
|
8135
|
|
|
helpers.each(vm.footer, maxLineWidth); |
|
8136
|
|
|
|
|
8137
|
|
|
// Add padding |
|
8138
|
|
|
size.width += 2 * vm.xPadding; |
|
8139
|
|
|
|
|
8140
|
|
|
return size; |
|
8141
|
|
|
}, |
|
8142
|
|
|
determineAlignment: function(size) { |
|
8143
|
|
|
var me = this; |
|
8144
|
|
|
var model = me._model; |
|
8145
|
|
|
var chart = me._chart; |
|
8146
|
|
|
var chartArea = me._chartInstance.chartArea; |
|
8147
|
|
|
|
|
8148
|
|
|
if (model.y < size.height) { |
|
8149
|
|
|
model.yAlign = 'top'; |
|
8150
|
|
|
} else if (model.y > (chart.height - size.height)) { |
|
8151
|
|
|
model.yAlign = 'bottom'; |
|
8152
|
|
|
} |
|
8153
|
|
|
|
|
8154
|
|
|
var lf, rf; // functions to determine left, right alignment |
|
8155
|
|
|
var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart |
|
8156
|
|
|
var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges |
|
8157
|
|
|
var midX = (chartArea.left + chartArea.right) / 2; |
|
8158
|
|
|
var midY = (chartArea.top + chartArea.bottom) / 2; |
|
8159
|
|
|
|
|
8160
|
|
|
if (model.yAlign === 'center') { |
|
8161
|
|
|
lf = function(x) { |
|
8162
|
|
|
return x <= midX; |
|
8163
|
|
|
}; |
|
8164
|
|
|
rf = function(x) { |
|
8165
|
|
|
return x > midX; |
|
8166
|
|
|
}; |
|
8167
|
|
|
} else { |
|
8168
|
|
|
lf = function(x) { |
|
8169
|
|
|
return x <= (size.width / 2); |
|
8170
|
|
|
}; |
|
8171
|
|
|
rf = function(x) { |
|
8172
|
|
|
return x >= (chart.width - (size.width / 2)); |
|
8173
|
|
|
}; |
|
8174
|
|
|
} |
|
8175
|
|
|
|
|
8176
|
|
|
olf = function(x) { |
|
8177
|
|
|
return x + size.width > chart.width; |
|
8178
|
|
|
}; |
|
8179
|
|
|
orf = function(x) { |
|
8180
|
|
|
return x - size.width < 0; |
|
8181
|
|
|
}; |
|
8182
|
|
|
yf = function(y) { |
|
8183
|
|
|
return y <= midY ? 'top' : 'bottom'; |
|
8184
|
|
|
}; |
|
8185
|
|
|
|
|
8186
|
|
|
if (lf(model.x)) { |
|
8187
|
|
|
model.xAlign = 'left'; |
|
8188
|
|
|
|
|
8189
|
|
|
// Is tooltip too wide and goes over the right side of the chart.? |
|
8190
|
|
|
if (olf(model.x)) { |
|
8191
|
|
|
model.xAlign = 'center'; |
|
8192
|
|
|
model.yAlign = yf(model.y); |
|
8193
|
|
|
} |
|
8194
|
|
|
} else if (rf(model.x)) { |
|
8195
|
|
|
model.xAlign = 'right'; |
|
8196
|
|
|
|
|
8197
|
|
|
// Is tooltip too wide and goes outside left edge of canvas? |
|
8198
|
|
|
if (orf(model.x)) { |
|
8199
|
|
|
model.xAlign = 'center'; |
|
8200
|
|
|
model.yAlign = yf(model.y); |
|
8201
|
|
|
} |
|
8202
|
|
|
} |
|
8203
|
|
|
}, |
|
8204
|
|
|
getBackgroundPoint: function(vm, size) { |
|
8205
|
|
|
// Background Position |
|
8206
|
|
|
var pt = { |
|
8207
|
|
|
x: vm.x, |
|
8208
|
|
|
y: vm.y |
|
8209
|
|
|
}; |
|
8210
|
|
|
|
|
8211
|
|
|
var caretSize = vm.caretSize, |
|
8212
|
|
|
caretPadding = vm.caretPadding, |
|
8213
|
|
|
cornerRadius = vm.cornerRadius, |
|
8214
|
|
|
xAlign = vm.xAlign, |
|
8215
|
|
|
yAlign = vm.yAlign, |
|
8216
|
|
|
paddingAndSize = caretSize + caretPadding, |
|
8217
|
|
|
radiusAndPadding = cornerRadius + caretPadding; |
|
8218
|
|
|
|
|
8219
|
|
|
if (xAlign === 'right') { |
|
8220
|
|
|
pt.x -= size.width; |
|
8221
|
|
|
} else if (xAlign === 'center') { |
|
8222
|
|
|
pt.x -= (size.width / 2); |
|
8223
|
|
|
} |
|
8224
|
|
|
|
|
8225
|
|
|
if (yAlign === 'top') { |
|
8226
|
|
|
pt.y += paddingAndSize; |
|
8227
|
|
|
} else if (yAlign === 'bottom') { |
|
8228
|
|
|
pt.y -= size.height + paddingAndSize; |
|
8229
|
|
|
} else { |
|
8230
|
|
|
pt.y -= (size.height / 2); |
|
8231
|
|
|
} |
|
8232
|
|
|
|
|
8233
|
|
|
if (yAlign === 'center') { |
|
8234
|
|
|
if (xAlign === 'left') { |
|
8235
|
|
|
pt.x += paddingAndSize; |
|
8236
|
|
|
} else if (xAlign === 'right') { |
|
8237
|
|
|
pt.x -= paddingAndSize; |
|
8238
|
|
|
} |
|
8239
|
|
|
} else { |
|
8240
|
|
|
if (xAlign === 'left') { |
|
8241
|
|
|
pt.x -= radiusAndPadding; |
|
8242
|
|
|
} else if (xAlign === 'right') { |
|
8243
|
|
|
pt.x += radiusAndPadding; |
|
8244
|
|
|
} |
|
8245
|
|
|
} |
|
8246
|
|
|
|
|
8247
|
|
|
return pt; |
|
8248
|
|
|
}, |
|
8249
|
|
|
drawCaret: function(tooltipPoint, size, opacity) { |
|
8250
|
|
|
var vm = this._view; |
|
8251
|
|
|
var ctx = this._chart.ctx; |
|
8252
|
|
|
var x1, x2, x3; |
|
8253
|
|
|
var y1, y2, y3; |
|
8254
|
|
|
var caretSize = vm.caretSize; |
|
8255
|
|
|
var cornerRadius = vm.cornerRadius; |
|
8256
|
|
|
var xAlign = vm.xAlign, |
|
8257
|
|
|
yAlign = vm.yAlign; |
|
8258
|
|
|
var ptX = tooltipPoint.x, |
|
8259
|
|
|
ptY = tooltipPoint.y; |
|
8260
|
|
|
var width = size.width, |
|
8261
|
|
|
height = size.height; |
|
8262
|
|
|
|
|
8263
|
|
|
if (yAlign === 'center') { |
|
8264
|
|
|
// Left or right side |
|
8265
|
|
|
if (xAlign === 'left') { |
|
8266
|
|
|
x1 = ptX; |
|
8267
|
|
|
x2 = x1 - caretSize; |
|
8268
|
|
|
x3 = x1; |
|
8269
|
|
|
} else { |
|
8270
|
|
|
x1 = ptX + width; |
|
8271
|
|
|
x2 = x1 + caretSize; |
|
8272
|
|
|
x3 = x1; |
|
8273
|
|
|
} |
|
8274
|
|
|
|
|
8275
|
|
|
y2 = ptY + (height / 2); |
|
8276
|
|
|
y1 = y2 - caretSize; |
|
8277
|
|
|
y3 = y2 + caretSize; |
|
8278
|
|
|
} else { |
|
8279
|
|
|
if (xAlign === 'left') { |
|
8280
|
|
|
x1 = ptX + cornerRadius; |
|
8281
|
|
|
x2 = x1 + caretSize; |
|
8282
|
|
|
x3 = x2 + caretSize; |
|
8283
|
|
|
} else if (xAlign === 'right') { |
|
8284
|
|
|
x1 = ptX + width - cornerRadius; |
|
8285
|
|
|
x2 = x1 - caretSize; |
|
8286
|
|
|
x3 = x2 - caretSize; |
|
8287
|
|
|
} else { |
|
8288
|
|
|
x2 = ptX + (width / 2); |
|
8289
|
|
|
x1 = x2 - caretSize; |
|
8290
|
|
|
x3 = x2 + caretSize; |
|
8291
|
|
|
} |
|
8292
|
|
|
|
|
8293
|
|
|
if (yAlign === 'top') { |
|
8294
|
|
|
y1 = ptY; |
|
8295
|
|
|
y2 = y1 - caretSize; |
|
8296
|
|
|
y3 = y1; |
|
8297
|
|
|
} else { |
|
8298
|
|
|
y1 = ptY + height; |
|
8299
|
|
|
y2 = y1 + caretSize; |
|
8300
|
|
|
y3 = y1; |
|
8301
|
|
|
} |
|
8302
|
|
|
} |
|
8303
|
|
|
|
|
8304
|
|
|
var bgColor = helpers.color(vm.backgroundColor); |
|
8305
|
|
|
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); |
|
8306
|
|
|
ctx.beginPath(); |
|
8307
|
|
|
ctx.moveTo(x1, y1); |
|
8308
|
|
|
ctx.lineTo(x2, y2); |
|
8309
|
|
|
ctx.lineTo(x3, y3); |
|
8310
|
|
|
ctx.closePath(); |
|
8311
|
|
|
ctx.fill(); |
|
8312
|
|
|
}, |
|
8313
|
|
|
drawTitle: function(pt, vm, ctx, opacity) { |
|
8314
|
|
|
var title = vm.title; |
|
8315
|
|
|
|
|
8316
|
|
|
if (title.length) { |
|
8317
|
|
|
ctx.textAlign = vm._titleAlign; |
|
8318
|
|
|
ctx.textBaseline = "top"; |
|
8319
|
|
|
|
|
8320
|
|
|
var titleFontSize = vm.titleFontSize, |
|
8321
|
|
|
titleSpacing = vm.titleSpacing; |
|
8322
|
|
|
|
|
8323
|
|
|
var titleFontColor = helpers.color(vm.titleFontColor); |
|
8324
|
|
|
ctx.fillStyle = titleFontColor.alpha(opacity * titleFontColor.alpha()).rgbString(); |
|
8325
|
|
|
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); |
|
8326
|
|
|
|
|
8327
|
|
|
var i, len; |
|
8328
|
|
|
for (i = 0, len = title.length; i < len; ++i) { |
|
8329
|
|
|
ctx.fillText(title[i], pt.x, pt.y); |
|
8330
|
|
|
pt.y += titleFontSize + titleSpacing; // Line Height and spacing |
|
8331
|
|
|
|
|
8332
|
|
|
if (i + 1 === title.length) { |
|
8333
|
|
|
pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing |
|
8334
|
|
|
} |
|
8335
|
|
|
} |
|
8336
|
|
|
} |
|
8337
|
|
|
}, |
|
8338
|
|
|
drawBody: function(pt, vm, ctx, opacity) { |
|
8339
|
|
|
var bodyFontSize = vm.bodyFontSize; |
|
8340
|
|
|
var bodySpacing = vm.bodySpacing; |
|
8341
|
|
|
var body = vm.body; |
|
8342
|
|
|
|
|
8343
|
|
|
ctx.textAlign = vm._bodyAlign; |
|
8344
|
|
|
ctx.textBaseline = "top"; |
|
8345
|
|
|
|
|
8346
|
|
|
var bodyFontColor = helpers.color(vm.bodyFontColor); |
|
8347
|
|
|
var textColor = bodyFontColor.alpha(opacity * bodyFontColor.alpha()).rgbString(); |
|
8348
|
|
|
ctx.fillStyle = textColor; |
|
8349
|
|
|
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); |
|
8350
|
|
|
|
|
8351
|
|
|
// Before Body |
|
8352
|
|
|
var xLinePadding = 0; |
|
8353
|
|
|
var fillLineOfText = function(line) { |
|
8354
|
|
|
ctx.fillText(line, pt.x + xLinePadding, pt.y); |
|
8355
|
|
|
pt.y += bodyFontSize + bodySpacing; |
|
8356
|
|
|
}; |
|
8357
|
|
|
|
|
8358
|
|
|
// Before body lines |
|
8359
|
|
|
helpers.each(vm.beforeBody, fillLineOfText); |
|
8360
|
|
|
|
|
8361
|
|
|
var drawColorBoxes = body.length > 1; |
|
8362
|
|
|
xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; |
|
8363
|
|
|
|
|
8364
|
|
|
// Draw body lines now |
|
8365
|
|
|
helpers.each(body, function(bodyItem, i) { |
|
8366
|
|
|
helpers.each(bodyItem.before, fillLineOfText); |
|
8367
|
|
|
|
|
8368
|
|
|
helpers.each(bodyItem.lines, function(line) { |
|
8369
|
|
|
// Draw Legend-like boxes if needed |
|
8370
|
|
|
if (drawColorBoxes) { |
|
8371
|
|
|
// Fill a white rect so that colours merge nicely if the opacity is < 1 |
|
8372
|
|
|
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString(); |
|
8373
|
|
|
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); |
|
8374
|
|
|
|
|
8375
|
|
|
// Border |
|
8376
|
|
|
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString(); |
|
8377
|
|
|
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); |
|
8378
|
|
|
|
|
8379
|
|
|
// Inner square |
|
8380
|
|
|
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString(); |
|
8381
|
|
|
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); |
|
8382
|
|
|
|
|
8383
|
|
|
ctx.fillStyle = textColor; |
|
8384
|
|
|
} |
|
8385
|
|
|
|
|
8386
|
|
|
fillLineOfText(line); |
|
8387
|
|
|
}); |
|
8388
|
|
|
|
|
8389
|
|
|
helpers.each(bodyItem.after, fillLineOfText); |
|
8390
|
|
|
}); |
|
8391
|
|
|
|
|
8392
|
|
|
// Reset back to 0 for after body |
|
8393
|
|
|
xLinePadding = 0; |
|
8394
|
|
|
|
|
8395
|
|
|
// After body lines |
|
8396
|
|
|
helpers.each(vm.afterBody, fillLineOfText); |
|
8397
|
|
|
pt.y -= bodySpacing; // Remove last body spacing |
|
8398
|
|
|
}, |
|
8399
|
|
|
drawFooter: function(pt, vm, ctx, opacity) { |
|
8400
|
|
|
var footer = vm.footer; |
|
8401
|
|
|
|
|
8402
|
|
|
if (footer.length) { |
|
8403
|
|
|
pt.y += vm.footerMarginTop; |
|
8404
|
|
|
|
|
8405
|
|
|
ctx.textAlign = vm._footerAlign; |
|
8406
|
|
|
ctx.textBaseline = "top"; |
|
8407
|
|
|
|
|
8408
|
|
|
var footerFontColor = helpers.color(vm.footerFontColor); |
|
8409
|
|
|
ctx.fillStyle = footerFontColor.alpha(opacity * footerFontColor.alpha()).rgbString(); |
|
8410
|
|
|
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); |
|
8411
|
|
|
|
|
8412
|
|
|
helpers.each(footer, function(line) { |
|
8413
|
|
|
ctx.fillText(line, pt.x, pt.y); |
|
8414
|
|
|
pt.y += vm.footerFontSize + vm.footerSpacing; |
|
8415
|
|
|
}); |
|
8416
|
|
|
} |
|
8417
|
|
|
}, |
|
8418
|
|
|
draw: function() { |
|
8419
|
|
|
var ctx = this._chart.ctx; |
|
8420
|
|
|
var vm = this._view; |
|
8421
|
|
|
|
|
8422
|
|
|
if (vm.opacity === 0) { |
|
8423
|
|
|
return; |
|
8424
|
|
|
} |
|
8425
|
|
|
|
|
8426
|
|
|
var tooltipSize = this.getTooltipSize(vm); |
|
8427
|
|
|
var pt = { |
|
8428
|
|
|
x: vm.x, |
|
8429
|
|
|
y: vm.y |
|
8430
|
|
|
}; |
|
8431
|
|
|
|
|
8432
|
|
|
// IE11/Edge does not like very small opacities, so snap to 0 |
|
8433
|
|
|
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; |
|
8434
|
|
|
|
|
8435
|
|
|
if (this._options.enabled) { |
|
8436
|
|
|
// Draw Background |
|
8437
|
|
|
var bgColor = helpers.color(vm.backgroundColor); |
|
8438
|
|
|
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); |
|
8439
|
|
|
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius); |
|
8440
|
|
|
ctx.fill(); |
|
8441
|
|
|
|
|
8442
|
|
|
// Draw Caret |
|
8443
|
|
|
this.drawCaret(pt, tooltipSize, opacity); |
|
8444
|
|
|
|
|
8445
|
|
|
// Draw Title, Body, and Footer |
|
8446
|
|
|
pt.x += vm.xPadding; |
|
8447
|
|
|
pt.y += vm.yPadding; |
|
8448
|
|
|
|
|
8449
|
|
|
// Titles |
|
8450
|
|
|
this.drawTitle(pt, vm, ctx, opacity); |
|
8451
|
|
|
|
|
8452
|
|
|
// Body |
|
8453
|
|
|
this.drawBody(pt, vm, ctx, opacity); |
|
8454
|
|
|
|
|
8455
|
|
|
// Footer |
|
8456
|
|
|
this.drawFooter(pt, vm, ctx, opacity); |
|
8457
|
|
|
} |
|
8458
|
|
|
} |
|
8459
|
|
|
}); |
|
8460
|
|
|
}; |
|
8461
|
|
|
|
|
8462
|
|
|
},{}],35:[function(require,module,exports){ |
|
8463
|
|
|
"use strict"; |
|
8464
|
|
|
|
|
8465
|
|
|
module.exports = function(Chart) { |
|
8466
|
|
|
|
|
8467
|
|
|
var helpers = Chart.helpers, |
|
8468
|
|
|
globalOpts = Chart.defaults.global; |
|
8469
|
|
|
|
|
8470
|
|
|
globalOpts.elements.arc = { |
|
8471
|
|
|
backgroundColor: globalOpts.defaultColor, |
|
8472
|
|
|
borderColor: "#fff", |
|
8473
|
|
|
borderWidth: 2 |
|
8474
|
|
|
}; |
|
8475
|
|
|
|
|
8476
|
|
|
Chart.elements.Arc = Chart.Element.extend({ |
|
8477
|
|
|
inLabelRange: function(mouseX) { |
|
8478
|
|
|
var vm = this._view; |
|
8479
|
|
|
|
|
8480
|
|
|
if (vm) { |
|
8481
|
|
|
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); |
|
8482
|
|
|
} else { |
|
8483
|
|
|
return false; |
|
8484
|
|
|
} |
|
8485
|
|
|
}, |
|
8486
|
|
|
inRange: function(chartX, chartY) { |
|
8487
|
|
|
var vm = this._view; |
|
8488
|
|
|
|
|
8489
|
|
|
if (vm) { |
|
8490
|
|
|
var pointRelativePosition = helpers.getAngleFromPoint(vm, { |
|
8491
|
|
|
x: chartX, |
|
8492
|
|
|
y: chartY |
|
8493
|
|
|
}), |
|
8494
|
|
|
angle = pointRelativePosition.angle, |
|
8495
|
|
|
distance = pointRelativePosition.distance; |
|
8496
|
|
|
|
|
8497
|
|
|
//Sanitise angle range |
|
8498
|
|
|
var startAngle = vm.startAngle; |
|
8499
|
|
|
var endAngle = vm.endAngle; |
|
8500
|
|
|
while (endAngle < startAngle) { |
|
8501
|
|
|
endAngle += 2.0 * Math.PI; |
|
8502
|
|
|
} |
|
8503
|
|
|
while (angle > endAngle) { |
|
8504
|
|
|
angle -= 2.0 * Math.PI; |
|
8505
|
|
|
} |
|
8506
|
|
|
while (angle < startAngle) { |
|
8507
|
|
|
angle += 2.0 * Math.PI; |
|
8508
|
|
|
} |
|
8509
|
|
|
|
|
8510
|
|
|
//Check if within the range of the open/close angle |
|
8511
|
|
|
var betweenAngles = (angle >= startAngle && angle <= endAngle), |
|
8512
|
|
|
withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); |
|
8513
|
|
|
|
|
8514
|
|
|
return (betweenAngles && withinRadius); |
|
8515
|
|
|
} else { |
|
8516
|
|
|
return false; |
|
8517
|
|
|
} |
|
8518
|
|
|
}, |
|
8519
|
|
|
tooltipPosition: function() { |
|
8520
|
|
|
var vm = this._view; |
|
8521
|
|
|
|
|
8522
|
|
|
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2), |
|
8523
|
|
|
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; |
|
8524
|
|
|
return { |
|
8525
|
|
|
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), |
|
8526
|
|
|
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) |
|
8527
|
|
|
}; |
|
8528
|
|
|
}, |
|
8529
|
|
|
draw: function() { |
|
8530
|
|
|
|
|
8531
|
|
|
var ctx = this._chart.ctx, |
|
8532
|
|
|
vm = this._view, |
|
8533
|
|
|
sA = vm.startAngle, |
|
8534
|
|
|
eA = vm.endAngle; |
|
8535
|
|
|
|
|
8536
|
|
|
ctx.beginPath(); |
|
8537
|
|
|
|
|
8538
|
|
|
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); |
|
8539
|
|
|
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); |
|
8540
|
|
|
|
|
8541
|
|
|
ctx.closePath(); |
|
8542
|
|
|
ctx.strokeStyle = vm.borderColor; |
|
8543
|
|
|
ctx.lineWidth = vm.borderWidth; |
|
8544
|
|
|
|
|
8545
|
|
|
ctx.fillStyle = vm.backgroundColor; |
|
8546
|
|
|
|
|
8547
|
|
|
ctx.fill(); |
|
8548
|
|
|
ctx.lineJoin = 'bevel'; |
|
8549
|
|
|
|
|
8550
|
|
|
if (vm.borderWidth) { |
|
8551
|
|
|
ctx.stroke(); |
|
8552
|
|
|
} |
|
8553
|
|
|
} |
|
8554
|
|
|
}); |
|
8555
|
|
|
}; |
|
8556
|
|
|
|
|
8557
|
|
|
},{}],36:[function(require,module,exports){ |
|
8558
|
|
|
"use strict"; |
|
8559
|
|
|
|
|
8560
|
|
|
module.exports = function(Chart) { |
|
8561
|
|
|
|
|
8562
|
|
|
var helpers = Chart.helpers; |
|
8563
|
|
|
var globalDefaults = Chart.defaults.global; |
|
8564
|
|
|
|
|
8565
|
|
|
Chart.defaults.global.elements.line = { |
|
8566
|
|
|
tension: 0.4, |
|
8567
|
|
|
backgroundColor: globalDefaults.defaultColor, |
|
8568
|
|
|
borderWidth: 3, |
|
8569
|
|
|
borderColor: globalDefaults.defaultColor, |
|
8570
|
|
|
borderCapStyle: 'butt', |
|
8571
|
|
|
borderDash: [], |
|
8572
|
|
|
borderDashOffset: 0.0, |
|
8573
|
|
|
borderJoinStyle: 'miter', |
|
8574
|
|
|
capBezierPoints: true, |
|
8575
|
|
|
fill: true // do we fill in the area between the line and its base axis |
|
8576
|
|
|
}; |
|
8577
|
|
|
|
|
8578
|
|
|
Chart.elements.Line = Chart.Element.extend({ |
|
8579
|
|
|
draw: function() { |
|
8580
|
|
|
var me = this; |
|
8581
|
|
|
var vm = me._view; |
|
8582
|
|
|
var spanGaps = vm.spanGaps; |
|
8583
|
|
|
var scaleZero = vm.scaleZero; |
|
8584
|
|
|
var loop = me._loop; |
|
8585
|
|
|
|
|
8586
|
|
|
var ctx = me._chart.ctx; |
|
8587
|
|
|
ctx.save(); |
|
8588
|
|
|
|
|
8589
|
|
|
// Helper function to draw a line to a point |
|
8590
|
|
|
function lineToPoint(previousPoint, point) { |
|
8591
|
|
|
var vm = point._view; |
|
8592
|
|
|
if (point._view.steppedLine === true) { |
|
8593
|
|
|
ctx.lineTo(point._view.x, previousPoint._view.y); |
|
8594
|
|
|
ctx.lineTo(point._view.x, point._view.y); |
|
8595
|
|
|
} else if (point._view.tension === 0) { |
|
8596
|
|
|
ctx.lineTo(vm.x, vm.y); |
|
8597
|
|
|
} else { |
|
8598
|
|
|
ctx.bezierCurveTo( |
|
8599
|
|
|
previousPoint._view.controlPointNextX, |
|
8600
|
|
|
previousPoint._view.controlPointNextY, |
|
8601
|
|
|
vm.controlPointPreviousX, |
|
8602
|
|
|
vm.controlPointPreviousY, |
|
8603
|
|
|
vm.x, |
|
8604
|
|
|
vm.y |
|
8605
|
|
|
); |
|
8606
|
|
|
} |
|
8607
|
|
|
} |
|
8608
|
|
|
|
|
8609
|
|
|
var points = me._children.slice(); // clone array |
|
8610
|
|
|
var lastDrawnIndex = -1; |
|
8611
|
|
|
|
|
8612
|
|
|
// If we are looping, adding the first point again |
|
8613
|
|
|
if (loop && points.length) { |
|
8614
|
|
|
points.push(points[0]); |
|
8615
|
|
|
} |
|
8616
|
|
|
|
|
8617
|
|
|
var index, current, previous, currentVM; |
|
8618
|
|
|
|
|
8619
|
|
|
// Fill Line |
|
8620
|
|
|
if (points.length && vm.fill) { |
|
8621
|
|
|
ctx.beginPath(); |
|
8622
|
|
|
|
|
8623
|
|
|
for (index = 0; index < points.length; ++index) { |
|
|
|
|
|
|
8624
|
|
|
current = points[index]; |
|
8625
|
|
|
previous = helpers.previousItem(points, index); |
|
8626
|
|
|
currentVM = current._view; |
|
8627
|
|
|
|
|
8628
|
|
|
// First point moves to it's starting position no matter what |
|
8629
|
|
|
if (index === 0) { |
|
8630
|
|
|
if (loop) { |
|
8631
|
|
|
ctx.moveTo(scaleZero.x, scaleZero.y); |
|
8632
|
|
|
} else { |
|
8633
|
|
|
ctx.moveTo(currentVM.x, scaleZero); |
|
8634
|
|
|
} |
|
8635
|
|
|
|
|
8636
|
|
|
if (!currentVM.skip) { |
|
8637
|
|
|
lastDrawnIndex = index; |
|
8638
|
|
|
ctx.lineTo(currentVM.x, currentVM.y); |
|
8639
|
|
|
} |
|
8640
|
|
|
} else { |
|
8641
|
|
|
previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; |
|
8642
|
|
|
|
|
8643
|
|
|
if (currentVM.skip) { |
|
8644
|
|
|
// Only do this if this is the first point that is skipped |
|
8645
|
|
|
if (!spanGaps && lastDrawnIndex === (index - 1)) { |
|
8646
|
|
|
if (loop) { |
|
8647
|
|
|
ctx.lineTo(scaleZero.x, scaleZero.y); |
|
8648
|
|
|
} else { |
|
8649
|
|
|
ctx.lineTo(previous._view.x, scaleZero); |
|
8650
|
|
|
} |
|
8651
|
|
|
} |
|
8652
|
|
|
} else { |
|
8653
|
|
|
if (lastDrawnIndex !== (index - 1)) { |
|
8654
|
|
|
// There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case. |
|
8655
|
|
|
// If the first data point is NaN, then there is no real gap to skip |
|
8656
|
|
|
if (spanGaps && lastDrawnIndex !== -1) { |
|
8657
|
|
|
// We are spanning the gap, so simple draw a line to this point |
|
8658
|
|
|
lineToPoint(previous, current); |
|
8659
|
|
|
} else { |
|
8660
|
|
|
if (loop) { |
|
8661
|
|
|
ctx.lineTo(currentVM.x, currentVM.y); |
|
8662
|
|
|
} else { |
|
8663
|
|
|
ctx.lineTo(currentVM.x, scaleZero); |
|
8664
|
|
|
ctx.lineTo(currentVM.x, currentVM.y); |
|
8665
|
|
|
} |
|
8666
|
|
|
} |
|
8667
|
|
|
} else { |
|
8668
|
|
|
// Line to next point |
|
8669
|
|
|
lineToPoint(previous, current); |
|
8670
|
|
|
} |
|
8671
|
|
|
lastDrawnIndex = index; |
|
8672
|
|
|
} |
|
8673
|
|
|
} |
|
8674
|
|
|
} |
|
8675
|
|
|
|
|
8676
|
|
|
if (!loop) { |
|
8677
|
|
|
ctx.lineTo(points[lastDrawnIndex]._view.x, scaleZero); |
|
8678
|
|
|
} |
|
8679
|
|
|
|
|
8680
|
|
|
ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor; |
|
8681
|
|
|
ctx.closePath(); |
|
8682
|
|
|
ctx.fill(); |
|
8683
|
|
|
} |
|
8684
|
|
|
|
|
8685
|
|
|
// Stroke Line Options |
|
8686
|
|
|
var globalOptionLineElements = globalDefaults.elements.line; |
|
8687
|
|
|
ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; |
|
8688
|
|
|
|
|
8689
|
|
|
// IE 9 and 10 do not support line dash |
|
8690
|
|
|
if (ctx.setLineDash) { |
|
8691
|
|
|
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); |
|
8692
|
|
|
} |
|
8693
|
|
|
|
|
8694
|
|
|
ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; |
|
8695
|
|
|
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; |
|
8696
|
|
|
ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; |
|
8697
|
|
|
ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; |
|
8698
|
|
|
|
|
8699
|
|
|
// Stroke Line |
|
8700
|
|
|
ctx.beginPath(); |
|
8701
|
|
|
lastDrawnIndex = -1; |
|
8702
|
|
|
|
|
8703
|
|
|
for (index = 0; index < points.length; ++index) { |
|
|
|
|
|
|
8704
|
|
|
current = points[index]; |
|
8705
|
|
|
previous = helpers.previousItem(points, index); |
|
8706
|
|
|
currentVM = current._view; |
|
8707
|
|
|
|
|
8708
|
|
|
// First point moves to it's starting position no matter what |
|
8709
|
|
|
if (index === 0) { |
|
8710
|
|
|
if (currentVM.skip) { |
|
|
|
|
|
|
8711
|
|
|
|
|
8712
|
|
|
} else { |
|
8713
|
|
|
ctx.moveTo(currentVM.x, currentVM.y); |
|
8714
|
|
|
lastDrawnIndex = index; |
|
8715
|
|
|
} |
|
8716
|
|
|
} else { |
|
8717
|
|
|
previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; |
|
8718
|
|
|
|
|
8719
|
|
|
if (!currentVM.skip) { |
|
8720
|
|
|
if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { |
|
8721
|
|
|
// There was a gap and this is the first point after the gap |
|
8722
|
|
|
ctx.moveTo(currentVM.x, currentVM.y); |
|
8723
|
|
|
} else { |
|
8724
|
|
|
// Line to next point |
|
8725
|
|
|
lineToPoint(previous, current); |
|
8726
|
|
|
} |
|
8727
|
|
|
lastDrawnIndex = index; |
|
8728
|
|
|
} |
|
8729
|
|
|
} |
|
8730
|
|
|
} |
|
8731
|
|
|
|
|
8732
|
|
|
ctx.stroke(); |
|
8733
|
|
|
ctx.restore(); |
|
8734
|
|
|
} |
|
8735
|
|
|
}); |
|
8736
|
|
|
}; |
|
8737
|
|
|
},{}],37:[function(require,module,exports){ |
|
8738
|
|
|
"use strict"; |
|
8739
|
|
|
|
|
8740
|
|
|
module.exports = function(Chart) { |
|
8741
|
|
|
|
|
8742
|
|
|
var helpers = Chart.helpers, |
|
8743
|
|
|
globalOpts = Chart.defaults.global, |
|
8744
|
|
|
defaultColor = globalOpts.defaultColor; |
|
8745
|
|
|
|
|
8746
|
|
|
globalOpts.elements.point = { |
|
8747
|
|
|
radius: 3, |
|
8748
|
|
|
pointStyle: 'circle', |
|
8749
|
|
|
backgroundColor: defaultColor, |
|
8750
|
|
|
borderWidth: 1, |
|
8751
|
|
|
borderColor: defaultColor, |
|
8752
|
|
|
// Hover |
|
8753
|
|
|
hitRadius: 1, |
|
8754
|
|
|
hoverRadius: 4, |
|
8755
|
|
|
hoverBorderWidth: 1 |
|
8756
|
|
|
}; |
|
8757
|
|
|
|
|
8758
|
|
|
Chart.elements.Point = Chart.Element.extend({ |
|
8759
|
|
|
inRange: function(mouseX, mouseY) { |
|
8760
|
|
|
var vm = this._view; |
|
8761
|
|
|
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; |
|
8762
|
|
|
}, |
|
8763
|
|
|
inLabelRange: function(mouseX) { |
|
8764
|
|
|
var vm = this._view; |
|
8765
|
|
|
return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; |
|
8766
|
|
|
}, |
|
8767
|
|
|
tooltipPosition: function() { |
|
8768
|
|
|
var vm = this._view; |
|
8769
|
|
|
return { |
|
8770
|
|
|
x: vm.x, |
|
8771
|
|
|
y: vm.y, |
|
8772
|
|
|
padding: vm.radius + vm.borderWidth |
|
8773
|
|
|
}; |
|
8774
|
|
|
}, |
|
8775
|
|
|
draw: function() { |
|
8776
|
|
|
var vm = this._view; |
|
8777
|
|
|
var ctx = this._chart.ctx; |
|
8778
|
|
|
var pointStyle = vm.pointStyle; |
|
8779
|
|
|
var radius = vm.radius; |
|
8780
|
|
|
var x = vm.x; |
|
8781
|
|
|
var y = vm.y; |
|
8782
|
|
|
|
|
8783
|
|
|
if (vm.skip) { |
|
8784
|
|
|
return; |
|
8785
|
|
|
} |
|
8786
|
|
|
|
|
8787
|
|
|
ctx.strokeStyle = vm.borderColor || defaultColor; |
|
8788
|
|
|
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth); |
|
8789
|
|
|
ctx.fillStyle = vm.backgroundColor || defaultColor; |
|
8790
|
|
|
|
|
8791
|
|
|
Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y); |
|
8792
|
|
|
} |
|
8793
|
|
|
}); |
|
8794
|
|
|
}; |
|
8795
|
|
|
|
|
8796
|
|
|
},{}],38:[function(require,module,exports){ |
|
8797
|
|
|
"use strict"; |
|
8798
|
|
|
|
|
8799
|
|
|
module.exports = function(Chart) { |
|
8800
|
|
|
|
|
8801
|
|
|
var globalOpts = Chart.defaults.global; |
|
8802
|
|
|
|
|
8803
|
|
|
globalOpts.elements.rectangle = { |
|
8804
|
|
|
backgroundColor: globalOpts.defaultColor, |
|
8805
|
|
|
borderWidth: 0, |
|
8806
|
|
|
borderColor: globalOpts.defaultColor, |
|
8807
|
|
|
borderSkipped: 'bottom' |
|
8808
|
|
|
}; |
|
8809
|
|
|
|
|
8810
|
|
|
Chart.elements.Rectangle = Chart.Element.extend({ |
|
8811
|
|
|
draw: function() { |
|
|
|
|
|
|
8812
|
|
|
var ctx = this._chart.ctx; |
|
8813
|
|
|
var vm = this._view; |
|
8814
|
|
|
|
|
8815
|
|
|
var halfWidth = vm.width / 2, |
|
8816
|
|
|
leftX = vm.x - halfWidth, |
|
8817
|
|
|
rightX = vm.x + halfWidth, |
|
8818
|
|
|
top = vm.base - (vm.base - vm.y), |
|
8819
|
|
|
halfStroke = vm.borderWidth / 2; |
|
8820
|
|
|
|
|
8821
|
|
|
// Canvas doesn't allow us to stroke inside the width so we can |
|
8822
|
|
|
// adjust the sizes to fit if we're setting a stroke on the line |
|
8823
|
|
|
if (vm.borderWidth) { |
|
8824
|
|
|
leftX += halfStroke; |
|
8825
|
|
|
rightX -= halfStroke; |
|
8826
|
|
|
top += halfStroke; |
|
8827
|
|
|
} |
|
8828
|
|
|
|
|
8829
|
|
|
ctx.beginPath(); |
|
8830
|
|
|
ctx.fillStyle = vm.backgroundColor; |
|
8831
|
|
|
ctx.strokeStyle = vm.borderColor; |
|
8832
|
|
|
ctx.lineWidth = vm.borderWidth; |
|
8833
|
|
|
|
|
8834
|
|
|
// Corner points, from bottom-left to bottom-right clockwise |
|
8835
|
|
|
// | 1 2 | |
|
8836
|
|
|
// | 0 3 | |
|
8837
|
|
|
var corners = [ |
|
8838
|
|
|
[leftX, vm.base], |
|
8839
|
|
|
[leftX, top], |
|
8840
|
|
|
[rightX, top], |
|
8841
|
|
|
[rightX, vm.base] |
|
8842
|
|
|
]; |
|
8843
|
|
|
|
|
8844
|
|
|
// Find first (starting) corner with fallback to 'bottom' |
|
8845
|
|
|
var borders = ['bottom', 'left', 'top', 'right']; |
|
8846
|
|
|
var startCorner = borders.indexOf(vm.borderSkipped, 0); |
|
8847
|
|
|
if (startCorner === -1) |
|
8848
|
|
|
startCorner = 0; |
|
|
|
|
|
|
8849
|
|
|
|
|
8850
|
|
|
function cornerAt(index) { |
|
8851
|
|
|
return corners[(startCorner + index) % 4]; |
|
8852
|
|
|
} |
|
8853
|
|
|
|
|
8854
|
|
|
// Draw rectangle from 'startCorner' |
|
8855
|
|
|
ctx.moveTo.apply(ctx, cornerAt(0)); |
|
8856
|
|
|
for (var i = 1; i < 4; i++) |
|
8857
|
|
|
ctx.lineTo.apply(ctx, cornerAt(i)); |
|
|
|
|
|
|
8858
|
|
|
|
|
8859
|
|
|
ctx.fill(); |
|
8860
|
|
|
if (vm.borderWidth) { |
|
8861
|
|
|
ctx.stroke(); |
|
8862
|
|
|
} |
|
8863
|
|
|
}, |
|
8864
|
|
|
height: function() { |
|
8865
|
|
|
var vm = this._view; |
|
8866
|
|
|
return vm.base - vm.y; |
|
8867
|
|
|
}, |
|
8868
|
|
|
inRange: function(mouseX, mouseY) { |
|
|
|
|
|
|
8869
|
|
|
var vm = this._view; |
|
8870
|
|
|
return vm ? |
|
8871
|
|
|
(vm.y < vm.base ? |
|
8872
|
|
|
(mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base) : |
|
8873
|
|
|
(mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y)) : |
|
8874
|
|
|
false; |
|
8875
|
|
|
}, |
|
8876
|
|
|
inLabelRange: function(mouseX) { |
|
8877
|
|
|
var vm = this._view; |
|
8878
|
|
|
return vm ? (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) : false; |
|
8879
|
|
|
}, |
|
8880
|
|
|
tooltipPosition: function() { |
|
8881
|
|
|
var vm = this._view; |
|
8882
|
|
|
return { |
|
8883
|
|
|
x: vm.x, |
|
8884
|
|
|
y: vm.y |
|
8885
|
|
|
}; |
|
8886
|
|
|
} |
|
8887
|
|
|
}); |
|
8888
|
|
|
|
|
8889
|
|
|
}; |
|
8890
|
|
|
},{}],39:[function(require,module,exports){ |
|
8891
|
|
|
"use strict"; |
|
8892
|
|
|
|
|
8893
|
|
|
module.exports = function(Chart) { |
|
8894
|
|
|
|
|
8895
|
|
|
var helpers = Chart.helpers; |
|
8896
|
|
|
// Default config for a category scale |
|
8897
|
|
|
var defaultConfig = { |
|
8898
|
|
|
position: "bottom" |
|
8899
|
|
|
}; |
|
8900
|
|
|
|
|
8901
|
|
|
var DatasetScale = Chart.Scale.extend({ |
|
8902
|
|
|
/** |
|
8903
|
|
|
* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use tose |
|
8904
|
|
|
* else fall back to data.labels |
|
8905
|
|
|
* @private |
|
8906
|
|
|
*/ |
|
8907
|
|
|
getLabels: function() { |
|
8908
|
|
|
var data = this.chart.data; |
|
8909
|
|
|
return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; |
|
8910
|
|
|
}, |
|
8911
|
|
|
// Implement this so that |
|
8912
|
|
|
determineDataLimits: function() { |
|
8913
|
|
|
var me = this; |
|
8914
|
|
|
var labels = me.getLabels(); |
|
8915
|
|
|
me.minIndex = 0; |
|
8916
|
|
|
me.maxIndex = labels.length - 1; |
|
8917
|
|
|
var findIndex; |
|
8918
|
|
|
|
|
8919
|
|
|
if (me.options.ticks.min !== undefined) { |
|
8920
|
|
|
// user specified min value |
|
8921
|
|
|
findIndex = helpers.indexOf(labels, me.options.ticks.min); |
|
8922
|
|
|
me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; |
|
8923
|
|
|
} |
|
8924
|
|
|
|
|
8925
|
|
|
if (me.options.ticks.max !== undefined) { |
|
8926
|
|
|
// user specified max value |
|
8927
|
|
|
findIndex = helpers.indexOf(labels, me.options.ticks.max); |
|
8928
|
|
|
me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; |
|
8929
|
|
|
} |
|
8930
|
|
|
|
|
8931
|
|
|
me.min = labels[me.minIndex]; |
|
8932
|
|
|
me.max = labels[me.maxIndex]; |
|
8933
|
|
|
}, |
|
8934
|
|
|
|
|
8935
|
|
|
buildTicks: function() { |
|
8936
|
|
|
var me = this; |
|
8937
|
|
|
var labels = me.getLabels(); |
|
8938
|
|
|
// If we are viewing some subset of labels, slice the original array |
|
8939
|
|
|
me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); |
|
8940
|
|
|
}, |
|
8941
|
|
|
|
|
8942
|
|
|
getLabelForIndex: function(index) { |
|
8943
|
|
|
return this.ticks[index]; |
|
8944
|
|
|
}, |
|
8945
|
|
|
|
|
8946
|
|
|
// Used to get data value locations. Value can either be an index or a numerical value |
|
8947
|
|
|
getPixelForValue: function(value, index, datasetIndex, includeOffset) { |
|
8948
|
|
|
var me = this; |
|
8949
|
|
|
// 1 is added because we need the length but we have the indexes |
|
8950
|
|
|
var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); |
|
8951
|
|
|
|
|
8952
|
|
|
if (value !== undefined) { |
|
8953
|
|
|
var labels = me.getLabels(); |
|
8954
|
|
|
var idx = labels.indexOf(value); |
|
8955
|
|
|
index = idx !== -1 ? idx : index; |
|
8956
|
|
|
} |
|
8957
|
|
|
|
|
8958
|
|
|
if (me.isHorizontal()) { |
|
8959
|
|
|
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
|
8960
|
|
|
var valueWidth = innerWidth / offsetAmt; |
|
8961
|
|
|
var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft; |
|
8962
|
|
|
|
|
8963
|
|
|
if (me.options.gridLines.offsetGridLines && includeOffset) { |
|
8964
|
|
|
widthOffset += (valueWidth / 2); |
|
8965
|
|
|
} |
|
8966
|
|
|
|
|
8967
|
|
|
return me.left + Math.round(widthOffset); |
|
8968
|
|
|
} else { |
|
8969
|
|
|
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); |
|
8970
|
|
|
var valueHeight = innerHeight / offsetAmt; |
|
8971
|
|
|
var heightOffset = (valueHeight * (index - me.minIndex)) + me.paddingTop; |
|
8972
|
|
|
|
|
8973
|
|
|
if (me.options.gridLines.offsetGridLines && includeOffset) { |
|
8974
|
|
|
heightOffset += (valueHeight / 2); |
|
8975
|
|
|
} |
|
8976
|
|
|
|
|
8977
|
|
|
return me.top + Math.round(heightOffset); |
|
8978
|
|
|
} |
|
8979
|
|
|
}, |
|
8980
|
|
|
getPixelForTick: function(index, includeOffset) { |
|
8981
|
|
|
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset); |
|
8982
|
|
|
}, |
|
8983
|
|
|
getValueForPixel: function(pixel) { |
|
|
|
|
|
|
8984
|
|
|
var me = this; |
|
8985
|
|
|
var value; |
|
8986
|
|
|
var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); |
|
8987
|
|
|
var horz = me.isHorizontal(); |
|
8988
|
|
|
var innerDimension = horz ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); |
|
8989
|
|
|
var valueDimension = innerDimension / offsetAmt; |
|
8990
|
|
|
|
|
8991
|
|
|
pixel -= horz ? me.left : me.top; |
|
8992
|
|
|
|
|
8993
|
|
|
if (me.options.gridLines.offsetGridLines) { |
|
8994
|
|
|
pixel -= (valueDimension / 2); |
|
8995
|
|
|
} |
|
8996
|
|
|
pixel -= horz ? me.paddingLeft : me.paddingTop; |
|
8997
|
|
|
|
|
8998
|
|
|
if (pixel <= 0) { |
|
8999
|
|
|
value = 0; |
|
9000
|
|
|
} else { |
|
9001
|
|
|
value = Math.round(pixel / valueDimension); |
|
9002
|
|
|
} |
|
9003
|
|
|
|
|
9004
|
|
|
return value; |
|
9005
|
|
|
}, |
|
9006
|
|
|
getBasePixel: function() { |
|
9007
|
|
|
return this.bottom; |
|
9008
|
|
|
} |
|
9009
|
|
|
}); |
|
9010
|
|
|
|
|
9011
|
|
|
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig); |
|
9012
|
|
|
|
|
9013
|
|
|
}; |
|
9014
|
|
|
},{}],40:[function(require,module,exports){ |
|
9015
|
|
|
"use strict"; |
|
9016
|
|
|
|
|
9017
|
|
|
module.exports = function(Chart) { |
|
9018
|
|
|
|
|
9019
|
|
|
var helpers = Chart.helpers; |
|
9020
|
|
|
|
|
9021
|
|
|
var defaultConfig = { |
|
9022
|
|
|
position: "left", |
|
9023
|
|
|
ticks: { |
|
9024
|
|
|
callback: function(tickValue, index, ticks) { |
|
9025
|
|
|
// If we have lots of ticks, don't use the ones |
|
9026
|
|
|
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; |
|
9027
|
|
|
|
|
9028
|
|
|
// If we have a number like 2.5 as the delta, figure out how many decimal places we need |
|
9029
|
|
|
if (Math.abs(delta) > 1) { |
|
9030
|
|
|
if (tickValue !== Math.floor(tickValue)) { |
|
9031
|
|
|
// not an integer |
|
9032
|
|
|
delta = tickValue - Math.floor(tickValue); |
|
9033
|
|
|
} |
|
9034
|
|
|
} |
|
9035
|
|
|
|
|
9036
|
|
|
var logDelta = helpers.log10(Math.abs(delta)); |
|
9037
|
|
|
var tickString = ''; |
|
9038
|
|
|
|
|
9039
|
|
|
if (tickValue !== 0) { |
|
9040
|
|
|
var numDecimal = -1 * Math.floor(logDelta); |
|
9041
|
|
|
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places |
|
9042
|
|
|
tickString = tickValue.toFixed(numDecimal); |
|
9043
|
|
|
} else { |
|
9044
|
|
|
tickString = '0'; // never show decimal places for 0 |
|
9045
|
|
|
} |
|
9046
|
|
|
|
|
9047
|
|
|
return tickString; |
|
9048
|
|
|
} |
|
9049
|
|
|
} |
|
9050
|
|
|
}; |
|
9051
|
|
|
|
|
9052
|
|
|
var LinearScale = Chart.LinearScaleBase.extend({ |
|
9053
|
|
|
determineDataLimits: function() { |
|
|
|
|
|
|
9054
|
|
|
var me = this; |
|
9055
|
|
|
var opts = me.options; |
|
9056
|
|
|
var chart = me.chart; |
|
9057
|
|
|
var data = chart.data; |
|
9058
|
|
|
var datasets = data.datasets; |
|
9059
|
|
|
var isHorizontal = me.isHorizontal(); |
|
9060
|
|
|
|
|
9061
|
|
|
function IDMatches(meta) { |
|
9062
|
|
|
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; |
|
9063
|
|
|
} |
|
9064
|
|
|
|
|
9065
|
|
|
// First Calculate the range |
|
9066
|
|
|
me.min = null; |
|
9067
|
|
|
me.max = null; |
|
9068
|
|
|
|
|
9069
|
|
|
if (opts.stacked) { |
|
9070
|
|
|
var valuesPerType = {}; |
|
9071
|
|
|
var hasPositiveValues = false; |
|
|
|
|
|
|
9072
|
|
|
var hasNegativeValues = false; |
|
|
|
|
|
|
9073
|
|
|
|
|
9074
|
|
|
helpers.each(datasets, function(dataset, datasetIndex) { |
|
9075
|
|
|
var meta = chart.getDatasetMeta(datasetIndex); |
|
9076
|
|
|
if (valuesPerType[meta.type] === undefined) { |
|
9077
|
|
|
valuesPerType[meta.type] = { |
|
9078
|
|
|
positiveValues: [], |
|
9079
|
|
|
negativeValues: [] |
|
9080
|
|
|
}; |
|
9081
|
|
|
} |
|
9082
|
|
|
|
|
9083
|
|
|
// Store these per type |
|
9084
|
|
|
var positiveValues = valuesPerType[meta.type].positiveValues; |
|
9085
|
|
|
var negativeValues = valuesPerType[meta.type].negativeValues; |
|
9086
|
|
|
|
|
9087
|
|
|
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
|
9088
|
|
|
helpers.each(dataset.data, function(rawValue, index) { |
|
9089
|
|
|
var value = +me.getRightValue(rawValue); |
|
9090
|
|
|
if (isNaN(value) || meta.data[index].hidden) { |
|
9091
|
|
|
return; |
|
9092
|
|
|
} |
|
9093
|
|
|
|
|
9094
|
|
|
positiveValues[index] = positiveValues[index] || 0; |
|
9095
|
|
|
negativeValues[index] = negativeValues[index] || 0; |
|
9096
|
|
|
|
|
9097
|
|
|
if (opts.relativePoints) { |
|
9098
|
|
|
positiveValues[index] = 100; |
|
9099
|
|
|
} else { |
|
9100
|
|
|
if (value < 0) { |
|
9101
|
|
|
hasNegativeValues = true; |
|
|
|
|
|
|
9102
|
|
|
negativeValues[index] += value; |
|
9103
|
|
|
} else { |
|
9104
|
|
|
hasPositiveValues = true; |
|
|
|
|
|
|
9105
|
|
|
positiveValues[index] += value; |
|
9106
|
|
|
} |
|
9107
|
|
|
} |
|
9108
|
|
|
}); |
|
9109
|
|
|
} |
|
9110
|
|
|
}); |
|
9111
|
|
|
|
|
9112
|
|
|
helpers.each(valuesPerType, function(valuesForType) { |
|
9113
|
|
|
var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); |
|
9114
|
|
|
var minVal = helpers.min(values); |
|
9115
|
|
|
var maxVal = helpers.max(values); |
|
9116
|
|
|
me.min = me.min === null ? minVal : Math.min(me.min, minVal); |
|
9117
|
|
|
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); |
|
9118
|
|
|
}); |
|
9119
|
|
|
|
|
9120
|
|
|
} else { |
|
9121
|
|
|
helpers.each(datasets, function(dataset, datasetIndex) { |
|
9122
|
|
|
var meta = chart.getDatasetMeta(datasetIndex); |
|
9123
|
|
|
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
|
9124
|
|
|
helpers.each(dataset.data, function(rawValue, index) { |
|
9125
|
|
|
var value = +me.getRightValue(rawValue); |
|
9126
|
|
|
if (isNaN(value) || meta.data[index].hidden) { |
|
9127
|
|
|
return; |
|
9128
|
|
|
} |
|
9129
|
|
|
|
|
9130
|
|
|
if (me.min === null) { |
|
9131
|
|
|
me.min = value; |
|
9132
|
|
|
} else if (value < me.min) { |
|
9133
|
|
|
me.min = value; |
|
9134
|
|
|
} |
|
9135
|
|
|
|
|
9136
|
|
|
if (me.max === null) { |
|
9137
|
|
|
me.max = value; |
|
9138
|
|
|
} else if (value > me.max) { |
|
9139
|
|
|
me.max = value; |
|
9140
|
|
|
} |
|
9141
|
|
|
}); |
|
9142
|
|
|
} |
|
9143
|
|
|
}); |
|
9144
|
|
|
} |
|
9145
|
|
|
|
|
9146
|
|
|
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero |
|
9147
|
|
|
this.handleTickRangeOptions(); |
|
9148
|
|
|
}, |
|
9149
|
|
|
getTickLimit: function() { |
|
9150
|
|
|
var maxTicks; |
|
9151
|
|
|
var me = this; |
|
9152
|
|
|
var tickOpts = me.options.ticks; |
|
9153
|
|
|
|
|
9154
|
|
|
if (me.isHorizontal()) { |
|
9155
|
|
|
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); |
|
9156
|
|
|
} else { |
|
9157
|
|
|
// The factor of 2 used to scale the font size has been experimentally determined. |
|
9158
|
|
|
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize); |
|
9159
|
|
|
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); |
|
9160
|
|
|
} |
|
9161
|
|
|
|
|
9162
|
|
|
return maxTicks; |
|
9163
|
|
|
}, |
|
9164
|
|
|
// Called after the ticks are built. We need |
|
9165
|
|
|
handleDirectionalChanges: function() { |
|
9166
|
|
|
if (!this.isHorizontal()) { |
|
9167
|
|
|
// We are in a vertical orientation. The top value is the highest. So reverse the array |
|
9168
|
|
|
this.ticks.reverse(); |
|
9169
|
|
|
} |
|
9170
|
|
|
}, |
|
9171
|
|
|
getLabelForIndex: function(index, datasetIndex) { |
|
9172
|
|
|
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
|
9173
|
|
|
}, |
|
9174
|
|
|
// Utils |
|
9175
|
|
|
getPixelForValue: function(value) { |
|
|
|
|
|
|
9176
|
|
|
// This must be called after fit has been run so that |
|
9177
|
|
|
// this.left, this.top, this.right, and this.bottom have been defined |
|
9178
|
|
|
var me = this; |
|
9179
|
|
|
var paddingLeft = me.paddingLeft; |
|
9180
|
|
|
var paddingBottom = me.paddingBottom; |
|
9181
|
|
|
var start = me.start; |
|
9182
|
|
|
|
|
9183
|
|
|
var rightValue = +me.getRightValue(value); |
|
9184
|
|
|
var pixel; |
|
9185
|
|
|
var innerDimension; |
|
9186
|
|
|
var range = me.end - start; |
|
9187
|
|
|
|
|
9188
|
|
|
if (me.isHorizontal()) { |
|
9189
|
|
|
innerDimension = me.width - (paddingLeft + me.paddingRight); |
|
9190
|
|
|
pixel = me.left + (innerDimension / range * (rightValue - start)); |
|
9191
|
|
|
return Math.round(pixel + paddingLeft); |
|
9192
|
|
|
} else { |
|
9193
|
|
|
innerDimension = me.height - (me.paddingTop + paddingBottom); |
|
9194
|
|
|
pixel = (me.bottom - paddingBottom) - (innerDimension / range * (rightValue - start)); |
|
9195
|
|
|
return Math.round(pixel); |
|
9196
|
|
|
} |
|
9197
|
|
|
}, |
|
9198
|
|
|
getValueForPixel: function(pixel) { |
|
9199
|
|
|
var me = this; |
|
9200
|
|
|
var isHorizontal = me.isHorizontal(); |
|
9201
|
|
|
var paddingLeft = me.paddingLeft; |
|
9202
|
|
|
var paddingBottom = me.paddingBottom; |
|
9203
|
|
|
var innerDimension = isHorizontal ? me.width - (paddingLeft + me.paddingRight) : me.height - (me.paddingTop + paddingBottom); |
|
9204
|
|
|
var offset = (isHorizontal ? pixel - me.left - paddingLeft : me.bottom - paddingBottom - pixel) / innerDimension; |
|
9205
|
|
|
return me.start + ((me.end - me.start) * offset); |
|
9206
|
|
|
}, |
|
9207
|
|
|
getPixelForTick: function(index) { |
|
9208
|
|
|
return this.getPixelForValue(this.ticksAsNumbers[index]); |
|
9209
|
|
|
} |
|
9210
|
|
|
}); |
|
9211
|
|
|
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig); |
|
9212
|
|
|
|
|
9213
|
|
|
}; |
|
9214
|
|
|
},{}],41:[function(require,module,exports){ |
|
9215
|
|
|
"use strict"; |
|
9216
|
|
|
|
|
9217
|
|
|
module.exports = function(Chart) { |
|
9218
|
|
|
|
|
9219
|
|
|
var helpers = Chart.helpers, |
|
9220
|
|
|
noop = helpers.noop; |
|
9221
|
|
|
|
|
9222
|
|
|
Chart.LinearScaleBase = Chart.Scale.extend({ |
|
9223
|
|
|
handleTickRangeOptions: function() { |
|
9224
|
|
|
var me = this; |
|
9225
|
|
|
var opts = me.options; |
|
9226
|
|
|
var tickOpts = opts.ticks; |
|
9227
|
|
|
|
|
9228
|
|
|
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart, |
|
9229
|
|
|
// do nothing since that would make the chart weird. If the user really wants a weird chart |
|
9230
|
|
|
// axis, they can manually override it |
|
9231
|
|
|
if (tickOpts.beginAtZero) { |
|
9232
|
|
|
var minSign = helpers.sign(me.min); |
|
9233
|
|
|
var maxSign = helpers.sign(me.max); |
|
9234
|
|
|
|
|
9235
|
|
|
if (minSign < 0 && maxSign < 0) { |
|
9236
|
|
|
// move the top up to 0 |
|
9237
|
|
|
me.max = 0; |
|
9238
|
|
|
} else if (minSign > 0 && maxSign > 0) { |
|
9239
|
|
|
// move the botttom down to 0 |
|
9240
|
|
|
me.min = 0; |
|
9241
|
|
|
} |
|
9242
|
|
|
} |
|
9243
|
|
|
|
|
9244
|
|
|
if (tickOpts.min !== undefined) { |
|
9245
|
|
|
me.min = tickOpts.min; |
|
9246
|
|
|
} else if (tickOpts.suggestedMin !== undefined) { |
|
9247
|
|
|
me.min = Math.min(me.min, tickOpts.suggestedMin); |
|
9248
|
|
|
} |
|
9249
|
|
|
|
|
9250
|
|
|
if (tickOpts.max !== undefined) { |
|
9251
|
|
|
me.max = tickOpts.max; |
|
9252
|
|
|
} else if (tickOpts.suggestedMax !== undefined) { |
|
9253
|
|
|
me.max = Math.max(me.max, tickOpts.suggestedMax); |
|
9254
|
|
|
} |
|
9255
|
|
|
|
|
9256
|
|
|
if (me.min === me.max) { |
|
9257
|
|
|
me.max++; |
|
9258
|
|
|
|
|
9259
|
|
|
if (!tickOpts.beginAtZero) { |
|
9260
|
|
|
me.min--; |
|
9261
|
|
|
} |
|
9262
|
|
|
} |
|
9263
|
|
|
}, |
|
9264
|
|
|
getTickLimit: noop, |
|
9265
|
|
|
handleDirectionalChanges: noop, |
|
9266
|
|
|
|
|
9267
|
|
|
buildTicks: function() { |
|
9268
|
|
|
var me = this; |
|
9269
|
|
|
var opts = me.options; |
|
9270
|
|
|
var ticks = me.ticks = []; |
|
9271
|
|
|
var tickOpts = opts.ticks; |
|
9272
|
|
|
var getValueOrDefault = helpers.getValueOrDefault; |
|
9273
|
|
|
|
|
9274
|
|
|
// Figure out what the max number of ticks we can support it is based on the size of |
|
9275
|
|
|
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50 |
|
9276
|
|
|
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on |
|
9277
|
|
|
// the graph |
|
9278
|
|
|
|
|
9279
|
|
|
var maxTicks = me.getTickLimit(); |
|
9280
|
|
|
|
|
9281
|
|
|
// Make sure we always have at least 2 ticks |
|
9282
|
|
|
maxTicks = Math.max(2, maxTicks); |
|
9283
|
|
|
|
|
9284
|
|
|
// To get a "nice" value for the tick spacing, we will use the appropriately named |
|
9285
|
|
|
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks |
|
9286
|
|
|
// for details. |
|
9287
|
|
|
|
|
9288
|
|
|
var spacing; |
|
9289
|
|
|
var fixedStepSizeSet = (tickOpts.fixedStepSize && tickOpts.fixedStepSize > 0) || (tickOpts.stepSize && tickOpts.stepSize > 0); |
|
9290
|
|
|
if (fixedStepSizeSet) { |
|
9291
|
|
|
spacing = getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize); |
|
9292
|
|
|
} else { |
|
9293
|
|
|
var niceRange = helpers.niceNum(me.max - me.min, false); |
|
9294
|
|
|
spacing = helpers.niceNum(niceRange / (maxTicks - 1), true); |
|
9295
|
|
|
} |
|
9296
|
|
|
var niceMin = Math.floor(me.min / spacing) * spacing; |
|
9297
|
|
|
var niceMax = Math.ceil(me.max / spacing) * spacing; |
|
9298
|
|
|
var numSpaces = (niceMax - niceMin) / spacing; |
|
9299
|
|
|
|
|
9300
|
|
|
// If very close to our rounded value, use it. |
|
9301
|
|
|
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { |
|
9302
|
|
|
numSpaces = Math.round(numSpaces); |
|
9303
|
|
|
} else { |
|
9304
|
|
|
numSpaces = Math.ceil(numSpaces); |
|
9305
|
|
|
} |
|
9306
|
|
|
|
|
9307
|
|
|
// Put the values into the ticks array |
|
9308
|
|
|
ticks.push(tickOpts.min !== undefined ? tickOpts.min : niceMin); |
|
9309
|
|
|
for (var j = 1; j < numSpaces; ++j) { |
|
9310
|
|
|
ticks.push(niceMin + (j * spacing)); |
|
9311
|
|
|
} |
|
9312
|
|
|
ticks.push(tickOpts.max !== undefined ? tickOpts.max : niceMax); |
|
9313
|
|
|
|
|
9314
|
|
|
me.handleDirectionalChanges(); |
|
9315
|
|
|
|
|
9316
|
|
|
// At this point, we need to update our max and min given the tick values since we have expanded the |
|
9317
|
|
|
// range of the scale |
|
9318
|
|
|
me.max = helpers.max(ticks); |
|
9319
|
|
|
me.min = helpers.min(ticks); |
|
9320
|
|
|
|
|
9321
|
|
|
if (tickOpts.reverse) { |
|
9322
|
|
|
ticks.reverse(); |
|
9323
|
|
|
|
|
9324
|
|
|
me.start = me.max; |
|
9325
|
|
|
me.end = me.min; |
|
9326
|
|
|
} else { |
|
9327
|
|
|
me.start = me.min; |
|
9328
|
|
|
me.end = me.max; |
|
9329
|
|
|
} |
|
9330
|
|
|
}, |
|
9331
|
|
|
convertTicksToLabels: function() { |
|
9332
|
|
|
var me = this; |
|
9333
|
|
|
me.ticksAsNumbers = me.ticks.slice(); |
|
9334
|
|
|
me.zeroLineIndex = me.ticks.indexOf(0); |
|
9335
|
|
|
|
|
9336
|
|
|
Chart.Scale.prototype.convertTicksToLabels.call(me); |
|
9337
|
|
|
}, |
|
9338
|
|
|
}); |
|
9339
|
|
|
}; |
|
9340
|
|
|
},{}],42:[function(require,module,exports){ |
|
9341
|
|
|
"use strict"; |
|
9342
|
|
|
|
|
9343
|
|
|
module.exports = function(Chart) { |
|
9344
|
|
|
|
|
9345
|
|
|
var helpers = Chart.helpers; |
|
9346
|
|
|
|
|
9347
|
|
|
var defaultConfig = { |
|
9348
|
|
|
position: "left", |
|
9349
|
|
|
|
|
9350
|
|
|
// label settings |
|
9351
|
|
|
ticks: { |
|
9352
|
|
|
callback: function(value, index, arr) { |
|
9353
|
|
|
var remain = value / (Math.pow(10, Math.floor(helpers.log10(value)))); |
|
9354
|
|
|
|
|
9355
|
|
|
if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) { |
|
9356
|
|
|
return value.toExponential(); |
|
9357
|
|
|
} else { |
|
9358
|
|
|
return ''; |
|
9359
|
|
|
} |
|
9360
|
|
|
} |
|
9361
|
|
|
} |
|
9362
|
|
|
}; |
|
9363
|
|
|
|
|
9364
|
|
|
var LogarithmicScale = Chart.Scale.extend({ |
|
9365
|
|
|
determineDataLimits: function() { |
|
|
|
|
|
|
9366
|
|
|
var me = this; |
|
9367
|
|
|
var opts = me.options; |
|
9368
|
|
|
var tickOpts = opts.ticks; |
|
9369
|
|
|
var chart = me.chart; |
|
9370
|
|
|
var data = chart.data; |
|
9371
|
|
|
var datasets = data.datasets; |
|
9372
|
|
|
var getValueOrDefault = helpers.getValueOrDefault; |
|
9373
|
|
|
var isHorizontal = me.isHorizontal(); |
|
9374
|
|
|
function IDMatches(meta) { |
|
9375
|
|
|
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; |
|
9376
|
|
|
} |
|
9377
|
|
|
|
|
9378
|
|
|
// Calculate Range |
|
9379
|
|
|
me.min = null; |
|
9380
|
|
|
me.max = null; |
|
9381
|
|
|
|
|
9382
|
|
|
if (opts.stacked) { |
|
9383
|
|
|
var valuesPerType = {}; |
|
9384
|
|
|
|
|
9385
|
|
|
helpers.each(datasets, function(dataset, datasetIndex) { |
|
9386
|
|
|
var meta = chart.getDatasetMeta(datasetIndex); |
|
9387
|
|
|
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
|
9388
|
|
|
if (valuesPerType[meta.type] === undefined) { |
|
9389
|
|
|
valuesPerType[meta.type] = []; |
|
9390
|
|
|
} |
|
9391
|
|
|
|
|
9392
|
|
|
helpers.each(dataset.data, function(rawValue, index) { |
|
9393
|
|
|
var values = valuesPerType[meta.type]; |
|
9394
|
|
|
var value = +me.getRightValue(rawValue); |
|
9395
|
|
|
if (isNaN(value) || meta.data[index].hidden) { |
|
9396
|
|
|
return; |
|
9397
|
|
|
} |
|
9398
|
|
|
|
|
9399
|
|
|
values[index] = values[index] || 0; |
|
9400
|
|
|
|
|
9401
|
|
|
if (opts.relativePoints) { |
|
9402
|
|
|
values[index] = 100; |
|
9403
|
|
|
} else { |
|
9404
|
|
|
// Don't need to split positive and negative since the log scale can't handle a 0 crossing |
|
9405
|
|
|
values[index] += value; |
|
9406
|
|
|
} |
|
9407
|
|
|
}); |
|
9408
|
|
|
} |
|
9409
|
|
|
}); |
|
9410
|
|
|
|
|
9411
|
|
|
helpers.each(valuesPerType, function(valuesForType) { |
|
9412
|
|
|
var minVal = helpers.min(valuesForType); |
|
9413
|
|
|
var maxVal = helpers.max(valuesForType); |
|
9414
|
|
|
me.min = me.min === null ? minVal : Math.min(me.min, minVal); |
|
9415
|
|
|
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); |
|
9416
|
|
|
}); |
|
9417
|
|
|
|
|
9418
|
|
|
} else { |
|
9419
|
|
|
helpers.each(datasets, function(dataset, datasetIndex) { |
|
9420
|
|
|
var meta = chart.getDatasetMeta(datasetIndex); |
|
9421
|
|
|
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
|
9422
|
|
|
helpers.each(dataset.data, function(rawValue, index) { |
|
9423
|
|
|
var value = +me.getRightValue(rawValue); |
|
9424
|
|
|
if (isNaN(value) || meta.data[index].hidden) { |
|
9425
|
|
|
return; |
|
9426
|
|
|
} |
|
9427
|
|
|
|
|
9428
|
|
|
if (me.min === null) { |
|
9429
|
|
|
me.min = value; |
|
9430
|
|
|
} else if (value < me.min) { |
|
9431
|
|
|
me.min = value; |
|
9432
|
|
|
} |
|
9433
|
|
|
|
|
9434
|
|
|
if (me.max === null) { |
|
9435
|
|
|
me.max = value; |
|
9436
|
|
|
} else if (value > me.max) { |
|
9437
|
|
|
me.max = value; |
|
9438
|
|
|
} |
|
9439
|
|
|
}); |
|
9440
|
|
|
} |
|
9441
|
|
|
}); |
|
9442
|
|
|
} |
|
9443
|
|
|
|
|
9444
|
|
|
me.min = getValueOrDefault(tickOpts.min, me.min); |
|
9445
|
|
|
me.max = getValueOrDefault(tickOpts.max, me.max); |
|
9446
|
|
|
|
|
9447
|
|
|
if (me.min === me.max) { |
|
9448
|
|
|
if (me.min !== 0 && me.min !== null) { |
|
9449
|
|
|
me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); |
|
9450
|
|
|
me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); |
|
9451
|
|
|
} else { |
|
9452
|
|
|
me.min = 1; |
|
9453
|
|
|
me.max = 10; |
|
9454
|
|
|
} |
|
9455
|
|
|
} |
|
9456
|
|
|
}, |
|
9457
|
|
|
buildTicks: function() { |
|
9458
|
|
|
var me = this; |
|
9459
|
|
|
var opts = me.options; |
|
9460
|
|
|
var tickOpts = opts.ticks; |
|
9461
|
|
|
var getValueOrDefault = helpers.getValueOrDefault; |
|
9462
|
|
|
|
|
9463
|
|
|
// Reset the ticks array. Later on, we will draw a grid line at these positions |
|
9464
|
|
|
// The array simply contains the numerical value of the spots where ticks will be |
|
9465
|
|
|
var ticks = me.ticks = []; |
|
9466
|
|
|
|
|
9467
|
|
|
// Figure out what the max number of ticks we can support it is based on the size of |
|
9468
|
|
|
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50 |
|
9469
|
|
|
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on |
|
9470
|
|
|
// the graph |
|
9471
|
|
|
|
|
9472
|
|
|
var tickVal = getValueOrDefault(tickOpts.min, Math.pow(10, Math.floor(helpers.log10(me.min)))); |
|
9473
|
|
|
|
|
9474
|
|
|
while (tickVal < me.max) { |
|
9475
|
|
|
ticks.push(tickVal); |
|
9476
|
|
|
|
|
9477
|
|
|
var exp = Math.floor(helpers.log10(tickVal)); |
|
9478
|
|
|
var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1; |
|
9479
|
|
|
|
|
9480
|
|
|
if (significand === 10) { |
|
9481
|
|
|
significand = 1; |
|
9482
|
|
|
++exp; |
|
9483
|
|
|
} |
|
9484
|
|
|
|
|
9485
|
|
|
tickVal = significand * Math.pow(10, exp); |
|
9486
|
|
|
} |
|
9487
|
|
|
|
|
9488
|
|
|
var lastTick = getValueOrDefault(tickOpts.max, tickVal); |
|
9489
|
|
|
ticks.push(lastTick); |
|
9490
|
|
|
|
|
9491
|
|
|
if (!me.isHorizontal()) { |
|
9492
|
|
|
// We are in a vertical orientation. The top value is the highest. So reverse the array |
|
9493
|
|
|
ticks.reverse(); |
|
9494
|
|
|
} |
|
9495
|
|
|
|
|
9496
|
|
|
// At this point, we need to update our max and min given the tick values since we have expanded the |
|
9497
|
|
|
// range of the scale |
|
9498
|
|
|
me.max = helpers.max(ticks); |
|
9499
|
|
|
me.min = helpers.min(ticks); |
|
9500
|
|
|
|
|
9501
|
|
|
if (tickOpts.reverse) { |
|
9502
|
|
|
ticks.reverse(); |
|
9503
|
|
|
|
|
9504
|
|
|
me.start = me.max; |
|
9505
|
|
|
me.end = me.min; |
|
9506
|
|
|
} else { |
|
9507
|
|
|
me.start = me.min; |
|
9508
|
|
|
me.end = me.max; |
|
9509
|
|
|
} |
|
9510
|
|
|
}, |
|
9511
|
|
|
convertTicksToLabels: function() { |
|
9512
|
|
|
this.tickValues = this.ticks.slice(); |
|
9513
|
|
|
|
|
9514
|
|
|
Chart.Scale.prototype.convertTicksToLabels.call(this); |
|
9515
|
|
|
}, |
|
9516
|
|
|
// Get the correct tooltip label |
|
9517
|
|
|
getLabelForIndex: function(index, datasetIndex) { |
|
9518
|
|
|
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
|
9519
|
|
|
}, |
|
9520
|
|
|
getPixelForTick: function(index) { |
|
9521
|
|
|
return this.getPixelForValue(this.tickValues[index]); |
|
9522
|
|
|
}, |
|
9523
|
|
|
getPixelForValue: function(value) { |
|
|
|
|
|
|
9524
|
|
|
var me = this; |
|
9525
|
|
|
var innerDimension; |
|
9526
|
|
|
var pixel; |
|
9527
|
|
|
|
|
9528
|
|
|
var start = me.start; |
|
9529
|
|
|
var newVal = +me.getRightValue(value); |
|
9530
|
|
|
var range = helpers.log10(me.end) - helpers.log10(start); |
|
9531
|
|
|
var paddingTop = me.paddingTop; |
|
9532
|
|
|
var paddingBottom = me.paddingBottom; |
|
9533
|
|
|
var paddingLeft = me.paddingLeft; |
|
9534
|
|
|
|
|
9535
|
|
|
if (me.isHorizontal()) { |
|
9536
|
|
|
|
|
9537
|
|
|
if (newVal === 0) { |
|
9538
|
|
|
pixel = me.left + paddingLeft; |
|
9539
|
|
|
} else { |
|
9540
|
|
|
innerDimension = me.width - (paddingLeft + me.paddingRight); |
|
9541
|
|
|
pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); |
|
9542
|
|
|
pixel += paddingLeft; |
|
9543
|
|
|
} |
|
9544
|
|
|
} else { |
|
9545
|
|
|
// Bottom - top since pixels increase downard on a screen |
|
9546
|
|
|
if (newVal === 0) { |
|
9547
|
|
|
pixel = me.top + paddingTop; |
|
9548
|
|
|
} else { |
|
9549
|
|
|
innerDimension = me.height - (paddingTop + paddingBottom); |
|
9550
|
|
|
pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); |
|
9551
|
|
|
} |
|
9552
|
|
|
} |
|
9553
|
|
|
|
|
9554
|
|
|
return pixel; |
|
9555
|
|
|
}, |
|
9556
|
|
|
getValueForPixel: function(pixel) { |
|
9557
|
|
|
var me = this; |
|
9558
|
|
|
var range = helpers.log10(me.end) - helpers.log10(me.start); |
|
9559
|
|
|
var value, innerDimension; |
|
9560
|
|
|
|
|
9561
|
|
|
if (me.isHorizontal()) { |
|
9562
|
|
|
innerDimension = me.width - (me.paddingLeft + me.paddingRight); |
|
9563
|
|
|
value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension); |
|
9564
|
|
|
} else { |
|
9565
|
|
|
innerDimension = me.height - (me.paddingTop + me.paddingBottom); |
|
9566
|
|
|
value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start; |
|
9567
|
|
|
} |
|
9568
|
|
|
|
|
9569
|
|
|
return value; |
|
9570
|
|
|
} |
|
9571
|
|
|
}); |
|
9572
|
|
|
Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig); |
|
9573
|
|
|
|
|
9574
|
|
|
}; |
|
9575
|
|
|
},{}],43:[function(require,module,exports){ |
|
9576
|
|
|
"use strict"; |
|
9577
|
|
|
|
|
9578
|
|
|
module.exports = function(Chart) { |
|
9579
|
|
|
|
|
9580
|
|
|
var helpers = Chart.helpers; |
|
9581
|
|
|
var globalDefaults = Chart.defaults.global; |
|
9582
|
|
|
|
|
9583
|
|
|
var defaultConfig = { |
|
9584
|
|
|
display: true, |
|
9585
|
|
|
|
|
9586
|
|
|
//Boolean - Whether to animate scaling the chart from the centre |
|
9587
|
|
|
animate: true, |
|
9588
|
|
|
lineArc: false, |
|
9589
|
|
|
position: "chartArea", |
|
9590
|
|
|
|
|
9591
|
|
|
angleLines: { |
|
9592
|
|
|
display: true, |
|
9593
|
|
|
color: "rgba(0, 0, 0, 0.1)", |
|
9594
|
|
|
lineWidth: 1 |
|
9595
|
|
|
}, |
|
9596
|
|
|
|
|
9597
|
|
|
// label settings |
|
9598
|
|
|
ticks: { |
|
9599
|
|
|
//Boolean - Show a backdrop to the scale label |
|
9600
|
|
|
showLabelBackdrop: true, |
|
9601
|
|
|
|
|
9602
|
|
|
//String - The colour of the label backdrop |
|
9603
|
|
|
backdropColor: "rgba(255,255,255,0.75)", |
|
9604
|
|
|
|
|
9605
|
|
|
//Number - The backdrop padding above & below the label in pixels |
|
9606
|
|
|
backdropPaddingY: 2, |
|
9607
|
|
|
|
|
9608
|
|
|
//Number - The backdrop padding to the side of the label in pixels |
|
9609
|
|
|
backdropPaddingX: 2 |
|
9610
|
|
|
}, |
|
9611
|
|
|
|
|
9612
|
|
|
pointLabels: { |
|
9613
|
|
|
//Number - Point label font size in pixels |
|
9614
|
|
|
fontSize: 10, |
|
9615
|
|
|
|
|
9616
|
|
|
//Function - Used to convert point labels |
|
9617
|
|
|
callback: function(label) { |
|
9618
|
|
|
return label; |
|
9619
|
|
|
} |
|
9620
|
|
|
} |
|
9621
|
|
|
}; |
|
9622
|
|
|
|
|
9623
|
|
|
var LinearRadialScale = Chart.LinearScaleBase.extend({ |
|
9624
|
|
|
getValueCount: function() { |
|
9625
|
|
|
return this.chart.data.labels.length; |
|
9626
|
|
|
}, |
|
9627
|
|
|
setDimensions: function() { |
|
9628
|
|
|
var me = this; |
|
9629
|
|
|
var opts = me.options; |
|
9630
|
|
|
var tickOpts = opts.ticks; |
|
9631
|
|
|
// Set the unconstrained dimension before label rotation |
|
9632
|
|
|
me.width = me.maxWidth; |
|
9633
|
|
|
me.height = me.maxHeight; |
|
9634
|
|
|
me.xCenter = Math.round(me.width / 2); |
|
9635
|
|
|
me.yCenter = Math.round(me.height / 2); |
|
9636
|
|
|
|
|
9637
|
|
|
var minSize = helpers.min([me.height, me.width]); |
|
9638
|
|
|
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
|
9639
|
|
|
me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); |
|
9640
|
|
|
}, |
|
9641
|
|
|
determineDataLimits: function() { |
|
9642
|
|
|
var me = this; |
|
9643
|
|
|
var chart = me.chart; |
|
9644
|
|
|
me.min = null; |
|
9645
|
|
|
me.max = null; |
|
9646
|
|
|
|
|
9647
|
|
|
|
|
9648
|
|
|
helpers.each(chart.data.datasets, function(dataset, datasetIndex) { |
|
9649
|
|
|
if (chart.isDatasetVisible(datasetIndex)) { |
|
9650
|
|
|
var meta = chart.getDatasetMeta(datasetIndex); |
|
9651
|
|
|
|
|
9652
|
|
|
helpers.each(dataset.data, function(rawValue, index) { |
|
9653
|
|
|
var value = +me.getRightValue(rawValue); |
|
9654
|
|
|
if (isNaN(value) || meta.data[index].hidden) { |
|
9655
|
|
|
return; |
|
9656
|
|
|
} |
|
9657
|
|
|
|
|
9658
|
|
|
if (me.min === null) { |
|
9659
|
|
|
me.min = value; |
|
9660
|
|
|
} else if (value < me.min) { |
|
9661
|
|
|
me.min = value; |
|
9662
|
|
|
} |
|
9663
|
|
|
|
|
9664
|
|
|
if (me.max === null) { |
|
9665
|
|
|
me.max = value; |
|
9666
|
|
|
} else if (value > me.max) { |
|
9667
|
|
|
me.max = value; |
|
9668
|
|
|
} |
|
9669
|
|
|
}); |
|
9670
|
|
|
} |
|
9671
|
|
|
}); |
|
9672
|
|
|
|
|
9673
|
|
|
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero |
|
9674
|
|
|
me.handleTickRangeOptions(); |
|
9675
|
|
|
}, |
|
9676
|
|
|
getTickLimit: function() { |
|
9677
|
|
|
var tickOpts = this.options.ticks; |
|
9678
|
|
|
var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
|
9679
|
|
|
return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); |
|
9680
|
|
|
}, |
|
9681
|
|
|
convertTicksToLabels: function() { |
|
9682
|
|
|
var me = this; |
|
9683
|
|
|
Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); |
|
9684
|
|
|
|
|
9685
|
|
|
// Point labels |
|
9686
|
|
|
me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); |
|
9687
|
|
|
}, |
|
9688
|
|
|
getLabelForIndex: function(index, datasetIndex) { |
|
9689
|
|
|
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
|
9690
|
|
|
}, |
|
9691
|
|
|
fit: function() { |
|
9692
|
|
|
/* |
|
9693
|
|
|
* Right, this is really confusing and there is a lot of maths going on here |
|
9694
|
|
|
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 |
|
9695
|
|
|
* |
|
9696
|
|
|
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif |
|
9697
|
|
|
* |
|
9698
|
|
|
* Solution: |
|
9699
|
|
|
* |
|
9700
|
|
|
* We assume the radius of the polygon is half the size of the canvas at first |
|
9701
|
|
|
* at each index we check if the text overlaps. |
|
9702
|
|
|
* |
|
9703
|
|
|
* Where it does, we store that angle and that index. |
|
9704
|
|
|
* |
|
9705
|
|
|
* After finding the largest index and angle we calculate how much we need to remove |
|
9706
|
|
|
* from the shape radius to move the point inwards by that x. |
|
9707
|
|
|
* |
|
9708
|
|
|
* We average the left and right distances to get the maximum shape radius that can fit in the box |
|
9709
|
|
|
* along with labels. |
|
9710
|
|
|
* |
|
9711
|
|
|
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion |
|
9712
|
|
|
* on each side, removing that from the size, halving it and adding the left x protrusion width. |
|
9713
|
|
|
* |
|
9714
|
|
|
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels |
|
9715
|
|
|
* and position it in the most space efficient manner |
|
9716
|
|
|
* |
|
9717
|
|
|
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif |
|
9718
|
|
|
*/ |
|
9719
|
|
|
|
|
9720
|
|
|
var pointLabels = this.options.pointLabels; |
|
9721
|
|
|
var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize); |
|
9722
|
|
|
var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle); |
|
9723
|
|
|
var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily); |
|
9724
|
|
|
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily); |
|
9725
|
|
|
|
|
9726
|
|
|
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. |
|
9727
|
|
|
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points |
|
9728
|
|
|
var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]), |
|
9729
|
|
|
pointPosition, |
|
9730
|
|
|
i, |
|
9731
|
|
|
textWidth, |
|
9732
|
|
|
halfTextWidth, |
|
9733
|
|
|
furthestRight = this.width, |
|
9734
|
|
|
furthestRightIndex, |
|
9735
|
|
|
furthestRightAngle, |
|
9736
|
|
|
furthestLeft = 0, |
|
9737
|
|
|
furthestLeftIndex, |
|
9738
|
|
|
furthestLeftAngle, |
|
9739
|
|
|
xProtrusionLeft, |
|
9740
|
|
|
xProtrusionRight, |
|
9741
|
|
|
radiusReductionRight, |
|
9742
|
|
|
radiusReductionLeft; |
|
9743
|
|
|
this.ctx.font = pointLabeFont; |
|
9744
|
|
|
|
|
9745
|
|
|
for (i = 0; i < this.getValueCount(); i++) { |
|
9746
|
|
|
// 5px to space the text slightly out - similar to what we do in the draw function. |
|
9747
|
|
|
pointPosition = this.getPointPosition(i, largestPossibleRadius); |
|
9748
|
|
|
textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5; |
|
9749
|
|
|
|
|
9750
|
|
|
// Add quarter circle to make degree 0 mean top of circle |
|
9751
|
|
|
var angleRadians = this.getIndexAngle(i) + (Math.PI / 2); |
|
9752
|
|
|
var angle = (angleRadians * 360 / (2 * Math.PI)) % 360; |
|
9753
|
|
|
|
|
9754
|
|
|
if (angle === 0 || angle === 180) { |
|
9755
|
|
|
// At angle 0 and 180, we're at exactly the top/bottom |
|
9756
|
|
|
// of the radar chart, so text will be aligned centrally, so we'll half it and compare |
|
9757
|
|
|
// w/left and right text sizes |
|
9758
|
|
|
halfTextWidth = textWidth / 2; |
|
9759
|
|
|
if (pointPosition.x + halfTextWidth > furthestRight) { |
|
9760
|
|
|
furthestRight = pointPosition.x + halfTextWidth; |
|
9761
|
|
|
furthestRightIndex = i; |
|
9762
|
|
|
} |
|
9763
|
|
|
if (pointPosition.x - halfTextWidth < furthestLeft) { |
|
9764
|
|
|
furthestLeft = pointPosition.x - halfTextWidth; |
|
9765
|
|
|
furthestLeftIndex = i; |
|
9766
|
|
|
} |
|
9767
|
|
|
} else if (angle < 180) { |
|
9768
|
|
|
// Less than half the values means we'll left align the text |
|
9769
|
|
|
if (pointPosition.x + textWidth > furthestRight) { |
|
9770
|
|
|
furthestRight = pointPosition.x + textWidth; |
|
9771
|
|
|
furthestRightIndex = i; |
|
9772
|
|
|
} |
|
9773
|
|
|
} else { |
|
9774
|
|
|
// More than half the values means we'll right align the text |
|
9775
|
|
|
if (pointPosition.x - textWidth < furthestLeft) { |
|
9776
|
|
|
furthestLeft = pointPosition.x - textWidth; |
|
9777
|
|
|
furthestLeftIndex = i; |
|
9778
|
|
|
} |
|
9779
|
|
|
} |
|
9780
|
|
|
} |
|
9781
|
|
|
|
|
9782
|
|
|
xProtrusionLeft = furthestLeft; |
|
9783
|
|
|
xProtrusionRight = Math.ceil(furthestRight - this.width); |
|
9784
|
|
|
|
|
9785
|
|
|
furthestRightAngle = this.getIndexAngle(furthestRightIndex); |
|
|
|
|
|
|
9786
|
|
|
furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); |
|
|
|
|
|
|
9787
|
|
|
|
|
9788
|
|
|
radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2); |
|
9789
|
|
|
radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2); |
|
9790
|
|
|
|
|
9791
|
|
|
// Ensure we actually need to reduce the size of the chart |
|
9792
|
|
|
radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0; |
|
9793
|
|
|
radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; |
|
9794
|
|
|
|
|
9795
|
|
|
this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2); |
|
9796
|
|
|
this.setCenterPoint(radiusReductionLeft, radiusReductionRight); |
|
9797
|
|
|
}, |
|
9798
|
|
|
setCenterPoint: function(leftMovement, rightMovement) { |
|
9799
|
|
|
var me = this; |
|
9800
|
|
|
var maxRight = me.width - rightMovement - me.drawingArea, |
|
9801
|
|
|
maxLeft = leftMovement + me.drawingArea; |
|
9802
|
|
|
|
|
9803
|
|
|
me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); |
|
9804
|
|
|
// Always vertically in the centre as the text height doesn't change |
|
9805
|
|
|
me.yCenter = Math.round((me.height / 2) + me.top); |
|
9806
|
|
|
}, |
|
9807
|
|
|
|
|
9808
|
|
|
getIndexAngle: function(index) { |
|
9809
|
|
|
var angleMultiplier = (Math.PI * 2) / this.getValueCount(); |
|
9810
|
|
|
var startAngle = this.chart.options && this.chart.options.startAngle ? |
|
9811
|
|
|
this.chart.options.startAngle : |
|
9812
|
|
|
0; |
|
9813
|
|
|
|
|
9814
|
|
|
var startAngleRadians = startAngle * Math.PI * 2 / 360; |
|
9815
|
|
|
|
|
9816
|
|
|
// Start from the top instead of right, so remove a quarter of the circle |
|
9817
|
|
|
return index * angleMultiplier - (Math.PI / 2) + startAngleRadians; |
|
9818
|
|
|
}, |
|
9819
|
|
|
getDistanceFromCenterForValue: function(value) { |
|
9820
|
|
|
var me = this; |
|
9821
|
|
|
|
|
9822
|
|
|
if (value === null) { |
|
9823
|
|
|
return 0; // null always in center |
|
9824
|
|
|
} |
|
9825
|
|
|
|
|
9826
|
|
|
// Take into account half font size + the yPadding of the top value |
|
9827
|
|
|
var scalingFactor = me.drawingArea / (me.max - me.min); |
|
9828
|
|
|
if (me.options.reverse) { |
|
9829
|
|
|
return (me.max - value) * scalingFactor; |
|
9830
|
|
|
} else { |
|
9831
|
|
|
return (value - me.min) * scalingFactor; |
|
9832
|
|
|
} |
|
9833
|
|
|
}, |
|
9834
|
|
|
getPointPosition: function(index, distanceFromCenter) { |
|
9835
|
|
|
var me = this; |
|
9836
|
|
|
var thisAngle = me.getIndexAngle(index); |
|
9837
|
|
|
return { |
|
9838
|
|
|
x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, |
|
9839
|
|
|
y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter |
|
9840
|
|
|
}; |
|
9841
|
|
|
}, |
|
9842
|
|
|
getPointPositionForValue: function(index, value) { |
|
9843
|
|
|
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); |
|
9844
|
|
|
}, |
|
9845
|
|
|
|
|
9846
|
|
|
getBasePosition: function() { |
|
9847
|
|
|
var me = this; |
|
9848
|
|
|
var min = me.min; |
|
9849
|
|
|
var max = me.max; |
|
9850
|
|
|
|
|
9851
|
|
|
return me.getPointPositionForValue(0, |
|
9852
|
|
|
me.beginAtZero? 0: |
|
9853
|
|
|
min < 0 && max < 0? max : |
|
9854
|
|
|
min > 0 && max > 0? min : |
|
9855
|
|
|
0); |
|
9856
|
|
|
}, |
|
9857
|
|
|
|
|
9858
|
|
|
draw: function() { |
|
9859
|
|
|
var me = this; |
|
9860
|
|
|
var opts = me.options; |
|
9861
|
|
|
var gridLineOpts = opts.gridLines; |
|
9862
|
|
|
var tickOpts = opts.ticks; |
|
9863
|
|
|
var angleLineOpts = opts.angleLines; |
|
9864
|
|
|
var pointLabelOpts = opts.pointLabels; |
|
9865
|
|
|
var getValueOrDefault = helpers.getValueOrDefault; |
|
9866
|
|
|
|
|
9867
|
|
|
if (opts.display) { |
|
9868
|
|
|
var ctx = me.ctx; |
|
9869
|
|
|
|
|
9870
|
|
|
// Tick Font |
|
9871
|
|
|
var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
|
9872
|
|
|
var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); |
|
9873
|
|
|
var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); |
|
9874
|
|
|
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
|
9875
|
|
|
|
|
9876
|
|
|
helpers.each(me.ticks, function(label, index) { |
|
9877
|
|
|
// Don't draw a centre value (if it is minimum) |
|
9878
|
|
|
if (index > 0 || opts.reverse) { |
|
9879
|
|
|
var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); |
|
9880
|
|
|
var yHeight = me.yCenter - yCenterOffset; |
|
9881
|
|
|
|
|
9882
|
|
|
// Draw circular lines around the scale |
|
9883
|
|
|
if (gridLineOpts.display && index !== 0) { |
|
9884
|
|
|
ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1); |
|
9885
|
|
|
ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); |
|
9886
|
|
|
|
|
9887
|
|
|
if (opts.lineArc) { |
|
9888
|
|
|
// Draw circular arcs between the points |
|
9889
|
|
|
ctx.beginPath(); |
|
9890
|
|
|
ctx.arc(me.xCenter, me.yCenter, yCenterOffset, 0, Math.PI * 2); |
|
9891
|
|
|
ctx.closePath(); |
|
9892
|
|
|
ctx.stroke(); |
|
9893
|
|
|
} else { |
|
9894
|
|
|
// Draw straight lines connecting each index |
|
9895
|
|
|
ctx.beginPath(); |
|
9896
|
|
|
for (var i = 0; i < me.getValueCount(); i++) { |
|
9897
|
|
|
var pointPosition = me.getPointPosition(i, yCenterOffset); |
|
9898
|
|
|
if (i === 0) { |
|
9899
|
|
|
ctx.moveTo(pointPosition.x, pointPosition.y); |
|
9900
|
|
|
} else { |
|
9901
|
|
|
ctx.lineTo(pointPosition.x, pointPosition.y); |
|
9902
|
|
|
} |
|
9903
|
|
|
} |
|
9904
|
|
|
ctx.closePath(); |
|
9905
|
|
|
ctx.stroke(); |
|
9906
|
|
|
} |
|
9907
|
|
|
} |
|
9908
|
|
|
|
|
9909
|
|
|
if (tickOpts.display) { |
|
9910
|
|
|
var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); |
|
9911
|
|
|
ctx.font = tickLabelFont; |
|
9912
|
|
|
|
|
9913
|
|
|
if (tickOpts.showLabelBackdrop) { |
|
9914
|
|
|
var labelWidth = ctx.measureText(label).width; |
|
9915
|
|
|
ctx.fillStyle = tickOpts.backdropColor; |
|
9916
|
|
|
ctx.fillRect( |
|
9917
|
|
|
me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX, |
|
9918
|
|
|
yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY, |
|
9919
|
|
|
labelWidth + tickOpts.backdropPaddingX * 2, |
|
9920
|
|
|
tickFontSize + tickOpts.backdropPaddingY * 2 |
|
9921
|
|
|
); |
|
9922
|
|
|
} |
|
9923
|
|
|
|
|
9924
|
|
|
ctx.textAlign = 'center'; |
|
9925
|
|
|
ctx.textBaseline = "middle"; |
|
9926
|
|
|
ctx.fillStyle = tickFontColor; |
|
9927
|
|
|
ctx.fillText(label, me.xCenter, yHeight); |
|
9928
|
|
|
} |
|
9929
|
|
|
} |
|
9930
|
|
|
}); |
|
9931
|
|
|
|
|
9932
|
|
|
if (!opts.lineArc) { |
|
9933
|
|
|
ctx.lineWidth = angleLineOpts.lineWidth; |
|
9934
|
|
|
ctx.strokeStyle = angleLineOpts.color; |
|
9935
|
|
|
|
|
9936
|
|
|
var outerDistance = me.getDistanceFromCenterForValue(opts.reverse ? me.min : me.max); |
|
9937
|
|
|
|
|
9938
|
|
|
// Point Label Font |
|
9939
|
|
|
var pointLabelFontSize = getValueOrDefault(pointLabelOpts.fontSize, globalDefaults.defaultFontSize); |
|
9940
|
|
|
var pointLabeFontStyle = getValueOrDefault(pointLabelOpts.fontStyle, globalDefaults.defaultFontStyle); |
|
9941
|
|
|
var pointLabeFontFamily = getValueOrDefault(pointLabelOpts.fontFamily, globalDefaults.defaultFontFamily); |
|
9942
|
|
|
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily); |
|
9943
|
|
|
|
|
9944
|
|
|
for (var i = me.getValueCount() - 1; i >= 0; i--) { |
|
9945
|
|
|
if (angleLineOpts.display) { |
|
9946
|
|
|
var outerPosition = me.getPointPosition(i, outerDistance); |
|
9947
|
|
|
ctx.beginPath(); |
|
9948
|
|
|
ctx.moveTo(me.xCenter, me.yCenter); |
|
9949
|
|
|
ctx.lineTo(outerPosition.x, outerPosition.y); |
|
9950
|
|
|
ctx.stroke(); |
|
9951
|
|
|
ctx.closePath(); |
|
9952
|
|
|
} |
|
9953
|
|
|
// Extra 3px out for some label spacing |
|
9954
|
|
|
var pointLabelPosition = me.getPointPosition(i, outerDistance + 5); |
|
9955
|
|
|
|
|
9956
|
|
|
// Keep this in loop since we may support array properties here |
|
9957
|
|
|
var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); |
|
9958
|
|
|
ctx.font = pointLabeFont; |
|
9959
|
|
|
ctx.fillStyle = pointLabelFontColor; |
|
9960
|
|
|
|
|
9961
|
|
|
var pointLabels = me.pointLabels; |
|
9962
|
|
|
|
|
9963
|
|
|
// Add quarter circle to make degree 0 mean top of circle |
|
9964
|
|
|
var angleRadians = this.getIndexAngle(i) + (Math.PI / 2); |
|
9965
|
|
|
var angle = (angleRadians * 360 / (2 * Math.PI)) % 360; |
|
9966
|
|
|
|
|
9967
|
|
|
if (angle === 0 || angle === 180) { |
|
9968
|
|
|
ctx.textAlign = 'center'; |
|
9969
|
|
|
} else if (angle < 180) { |
|
9970
|
|
|
ctx.textAlign = 'left'; |
|
9971
|
|
|
} else { |
|
9972
|
|
|
ctx.textAlign = 'right'; |
|
9973
|
|
|
} |
|
9974
|
|
|
|
|
9975
|
|
|
// Set the correct text baseline based on outer positioning |
|
9976
|
|
|
if (angle === 90 || angle === 270) { |
|
9977
|
|
|
ctx.textBaseline = 'middle'; |
|
9978
|
|
|
} else if (angle > 270 || angle < 90) { |
|
9979
|
|
|
ctx.textBaseline = 'bottom'; |
|
9980
|
|
|
} else { |
|
9981
|
|
|
ctx.textBaseline = 'top'; |
|
9982
|
|
|
} |
|
9983
|
|
|
|
|
9984
|
|
|
ctx.fillText(pointLabels[i] ? pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y); |
|
9985
|
|
|
} |
|
9986
|
|
|
} |
|
9987
|
|
|
} |
|
9988
|
|
|
} |
|
9989
|
|
|
}); |
|
9990
|
|
|
Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig); |
|
9991
|
|
|
|
|
9992
|
|
|
}; |
|
9993
|
|
|
|
|
9994
|
|
|
},{}],44:[function(require,module,exports){ |
|
9995
|
|
|
/*global window: false */ |
|
9996
|
|
|
"use strict"; |
|
9997
|
|
|
|
|
9998
|
|
|
var moment = require(1); |
|
9999
|
|
|
moment = typeof(moment) === 'function' ? moment : window.moment; |
|
10000
|
|
|
|
|
10001
|
|
|
module.exports = function(Chart) { |
|
10002
|
|
|
|
|
10003
|
|
|
var helpers = Chart.helpers; |
|
10004
|
|
|
var time = { |
|
10005
|
|
|
units: [{ |
|
10006
|
|
|
name: 'millisecond', |
|
10007
|
|
|
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] |
|
10008
|
|
|
}, { |
|
10009
|
|
|
name: 'second', |
|
10010
|
|
|
steps: [1, 2, 5, 10, 30] |
|
10011
|
|
|
}, { |
|
10012
|
|
|
name: 'minute', |
|
10013
|
|
|
steps: [1, 2, 5, 10, 30] |
|
10014
|
|
|
}, { |
|
10015
|
|
|
name: 'hour', |
|
10016
|
|
|
steps: [1, 2, 3, 6, 12] |
|
10017
|
|
|
}, { |
|
10018
|
|
|
name: 'day', |
|
10019
|
|
|
steps: [1, 2, 5] |
|
10020
|
|
|
}, { |
|
10021
|
|
|
name: 'week', |
|
10022
|
|
|
maxStep: 4 |
|
10023
|
|
|
}, { |
|
10024
|
|
|
name: 'month', |
|
10025
|
|
|
maxStep: 3 |
|
10026
|
|
|
}, { |
|
10027
|
|
|
name: 'quarter', |
|
10028
|
|
|
maxStep: 4 |
|
10029
|
|
|
}, { |
|
10030
|
|
|
name: 'year', |
|
10031
|
|
|
maxStep: false |
|
10032
|
|
|
}] |
|
10033
|
|
|
}; |
|
10034
|
|
|
|
|
10035
|
|
|
var defaultConfig = { |
|
10036
|
|
|
position: "bottom", |
|
10037
|
|
|
|
|
10038
|
|
|
time: { |
|
10039
|
|
|
parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment |
|
10040
|
|
|
format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ |
|
10041
|
|
|
unit: false, // false == automatic or override with week, month, year, etc. |
|
10042
|
|
|
round: false, // none, or override with week, month, year, etc. |
|
10043
|
|
|
displayFormat: false, // DEPRECATED |
|
10044
|
|
|
isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ |
|
10045
|
|
|
|
|
10046
|
|
|
// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ |
|
10047
|
|
|
displayFormats: { |
|
10048
|
|
|
'millisecond': 'h:mm:ss.SSS a', // 11:20:01.123 AM, |
|
10049
|
|
|
'second': 'h:mm:ss a', // 11:20:01 AM |
|
10050
|
|
|
'minute': 'h:mm:ss a', // 11:20:01 AM |
|
10051
|
|
|
'hour': 'MMM D, hA', // Sept 4, 5PM |
|
10052
|
|
|
'day': 'll', // Sep 4 2015 |
|
10053
|
|
|
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ? |
|
10054
|
|
|
'month': 'MMM YYYY', // Sept 2015 |
|
10055
|
|
|
'quarter': '[Q]Q - YYYY', // Q3 |
|
10056
|
|
|
'year': 'YYYY' // 2015 |
|
10057
|
|
|
} |
|
10058
|
|
|
}, |
|
10059
|
|
|
ticks: { |
|
10060
|
|
|
autoSkip: false |
|
10061
|
|
|
} |
|
10062
|
|
|
}; |
|
10063
|
|
|
|
|
10064
|
|
|
var TimeScale = Chart.Scale.extend({ |
|
10065
|
|
|
initialize: function() { |
|
10066
|
|
|
if (!moment) { |
|
10067
|
|
|
throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com'); |
|
10068
|
|
|
} |
|
10069
|
|
|
|
|
10070
|
|
|
Chart.Scale.prototype.initialize.call(this); |
|
10071
|
|
|
}, |
|
10072
|
|
|
getLabelMoment: function(datasetIndex, index) { |
|
10073
|
|
|
if (typeof this.labelMoments[datasetIndex] != 'undefined') { |
|
10074
|
|
|
return this.labelMoments[datasetIndex][index]; |
|
10075
|
|
|
} |
|
10076
|
|
|
|
|
10077
|
|
|
return null; |
|
10078
|
|
|
}, |
|
10079
|
|
|
getMomentStartOf: function(tick) { |
|
10080
|
|
|
var me = this; |
|
10081
|
|
|
if (me.options.time.unit === 'week' && me.options.time.isoWeekday !== false) { |
|
10082
|
|
|
return tick.clone().startOf('isoWeek').isoWeekday(me.options.time.isoWeekday); |
|
10083
|
|
|
} else { |
|
10084
|
|
|
return tick.clone().startOf(me.tickUnit); |
|
10085
|
|
|
} |
|
10086
|
|
|
}, |
|
10087
|
|
|
determineDataLimits: function() { |
|
10088
|
|
|
var me = this; |
|
10089
|
|
|
me.labelMoments = []; |
|
10090
|
|
|
|
|
10091
|
|
|
// Only parse these once. If the dataset does not have data as x,y pairs, we will use |
|
10092
|
|
|
// these |
|
10093
|
|
|
var scaleLabelMoments = []; |
|
10094
|
|
|
if (me.chart.data.labels && me.chart.data.labels.length > 0) { |
|
10095
|
|
|
helpers.each(me.chart.data.labels, function(label) { |
|
10096
|
|
|
var labelMoment = me.parseTime(label); |
|
10097
|
|
|
|
|
10098
|
|
|
if (labelMoment.isValid()) { |
|
10099
|
|
|
if (me.options.time.round) { |
|
10100
|
|
|
labelMoment.startOf(me.options.time.round); |
|
10101
|
|
|
} |
|
10102
|
|
|
scaleLabelMoments.push(labelMoment); |
|
10103
|
|
|
} |
|
10104
|
|
|
}, me); |
|
10105
|
|
|
|
|
10106
|
|
|
me.firstTick = moment.min.call(me, scaleLabelMoments); |
|
10107
|
|
|
me.lastTick = moment.max.call(me, scaleLabelMoments); |
|
10108
|
|
|
} else { |
|
10109
|
|
|
me.firstTick = null; |
|
10110
|
|
|
me.lastTick = null; |
|
10111
|
|
|
} |
|
10112
|
|
|
|
|
10113
|
|
|
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { |
|
10114
|
|
|
var momentsForDataset = []; |
|
10115
|
|
|
var datasetVisible = me.chart.isDatasetVisible(datasetIndex); |
|
10116
|
|
|
|
|
10117
|
|
|
if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) { |
|
10118
|
|
|
helpers.each(dataset.data, function(value) { |
|
10119
|
|
|
var labelMoment = me.parseTime(me.getRightValue(value)); |
|
10120
|
|
|
|
|
10121
|
|
|
if (labelMoment.isValid()) { |
|
10122
|
|
|
if (me.options.time.round) { |
|
10123
|
|
|
labelMoment.startOf(me.options.time.round); |
|
10124
|
|
|
} |
|
10125
|
|
|
momentsForDataset.push(labelMoment); |
|
10126
|
|
|
|
|
10127
|
|
|
if (datasetVisible) { |
|
10128
|
|
|
// May have gone outside the scale ranges, make sure we keep the first and last ticks updated |
|
10129
|
|
|
me.firstTick = me.firstTick !== null ? moment.min(me.firstTick, labelMoment) : labelMoment; |
|
10130
|
|
|
me.lastTick = me.lastTick !== null ? moment.max(me.lastTick, labelMoment) : labelMoment; |
|
10131
|
|
|
} |
|
10132
|
|
|
} |
|
10133
|
|
|
}, me); |
|
10134
|
|
|
} else { |
|
10135
|
|
|
// We have no labels. Use the ones from the scale |
|
10136
|
|
|
momentsForDataset = scaleLabelMoments; |
|
10137
|
|
|
} |
|
10138
|
|
|
|
|
10139
|
|
|
me.labelMoments.push(momentsForDataset); |
|
10140
|
|
|
}, me); |
|
10141
|
|
|
|
|
10142
|
|
|
// Set these after we've done all the data |
|
10143
|
|
|
if (me.options.time.min) { |
|
10144
|
|
|
me.firstTick = me.parseTime(me.options.time.min); |
|
10145
|
|
|
} |
|
10146
|
|
|
|
|
10147
|
|
|
if (me.options.time.max) { |
|
10148
|
|
|
me.lastTick = me.parseTime(me.options.time.max); |
|
10149
|
|
|
} |
|
10150
|
|
|
|
|
10151
|
|
|
// We will modify these, so clone for later |
|
10152
|
|
|
me.firstTick = (me.firstTick || moment()).clone(); |
|
10153
|
|
|
me.lastTick = (me.lastTick || moment()).clone(); |
|
10154
|
|
|
}, |
|
10155
|
|
|
buildTicks: function() { |
|
10156
|
|
|
var me = this; |
|
10157
|
|
|
|
|
10158
|
|
|
me.ctx.save(); |
|
10159
|
|
|
var tickFontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); |
|
10160
|
|
|
var tickFontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle); |
|
10161
|
|
|
var tickFontFamily = helpers.getValueOrDefault(me.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily); |
|
10162
|
|
|
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
|
10163
|
|
|
me.ctx.font = tickLabelFont; |
|
10164
|
|
|
|
|
10165
|
|
|
me.ticks = []; |
|
10166
|
|
|
me.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step |
|
10167
|
|
|
me.scaleSizeInUnits = 0; // How large the scale is in the base unit (seconds, minutes, etc) |
|
10168
|
|
|
|
|
10169
|
|
|
// Set unit override if applicable |
|
10170
|
|
|
if (me.options.time.unit) { |
|
10171
|
|
|
me.tickUnit = me.options.time.unit || 'day'; |
|
10172
|
|
|
me.displayFormat = me.options.time.displayFormats[me.tickUnit]; |
|
10173
|
|
|
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); |
|
10174
|
|
|
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, 1); |
|
10175
|
|
|
} else { |
|
10176
|
|
|
// Determine the smallest needed unit of the time |
|
10177
|
|
|
var innerWidth = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); |
|
10178
|
|
|
|
|
10179
|
|
|
// Crude approximation of what the label length might be |
|
10180
|
|
|
var tempFirstLabel = me.tickFormatFunction(me.firstTick, 0, []); |
|
10181
|
|
|
var tickLabelWidth = me.ctx.measureText(tempFirstLabel).width; |
|
10182
|
|
|
var cosRotation = Math.cos(helpers.toRadians(me.options.ticks.maxRotation)); |
|
10183
|
|
|
var sinRotation = Math.sin(helpers.toRadians(me.options.ticks.maxRotation)); |
|
10184
|
|
|
tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); |
|
10185
|
|
|
var labelCapacity = innerWidth / (tickLabelWidth); |
|
10186
|
|
|
|
|
10187
|
|
|
// Start as small as possible |
|
10188
|
|
|
me.tickUnit = 'millisecond'; |
|
10189
|
|
|
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); |
|
10190
|
|
|
me.displayFormat = me.options.time.displayFormats[me.tickUnit]; |
|
10191
|
|
|
|
|
10192
|
|
|
var unitDefinitionIndex = 0; |
|
10193
|
|
|
var unitDefinition = time.units[unitDefinitionIndex]; |
|
10194
|
|
|
|
|
10195
|
|
|
// While we aren't ideal and we don't have units left |
|
10196
|
|
|
while (unitDefinitionIndex < time.units.length) { |
|
10197
|
|
|
// Can we scale this unit. If `false` we can scale infinitely |
|
10198
|
|
|
me.unitScale = 1; |
|
10199
|
|
|
|
|
10200
|
|
|
if (helpers.isArray(unitDefinition.steps) && Math.ceil(me.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) { |
|
10201
|
|
|
// Use one of the prefedined steps |
|
10202
|
|
|
for (var idx = 0; idx < unitDefinition.steps.length; ++idx) { |
|
10203
|
|
|
if (unitDefinition.steps[idx] >= Math.ceil(me.scaleSizeInUnits / labelCapacity)) { |
|
10204
|
|
|
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, unitDefinition.steps[idx]); |
|
10205
|
|
|
break; |
|
10206
|
|
|
} |
|
10207
|
|
|
} |
|
10208
|
|
|
|
|
10209
|
|
|
break; |
|
10210
|
|
|
} else if ((unitDefinition.maxStep === false) || (Math.ceil(me.scaleSizeInUnits / labelCapacity) < unitDefinition.maxStep)) { |
|
10211
|
|
|
// We have a max step. Scale this unit |
|
10212
|
|
|
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, Math.ceil(me.scaleSizeInUnits / labelCapacity)); |
|
10213
|
|
|
break; |
|
10214
|
|
|
} else { |
|
10215
|
|
|
// Move to the next unit up |
|
10216
|
|
|
++unitDefinitionIndex; |
|
10217
|
|
|
unitDefinition = time.units[unitDefinitionIndex]; |
|
10218
|
|
|
|
|
10219
|
|
|
me.tickUnit = unitDefinition.name; |
|
10220
|
|
|
var leadingUnitBuffer = me.firstTick.diff(me.getMomentStartOf(me.firstTick), me.tickUnit, true); |
|
10221
|
|
|
var trailingUnitBuffer = me.getMomentStartOf(me.lastTick.clone().add(1, me.tickUnit)).diff(me.lastTick, me.tickUnit, true); |
|
10222
|
|
|
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true) + leadingUnitBuffer + trailingUnitBuffer; |
|
10223
|
|
|
me.displayFormat = me.options.time.displayFormats[unitDefinition.name]; |
|
10224
|
|
|
} |
|
10225
|
|
|
} |
|
10226
|
|
|
} |
|
10227
|
|
|
|
|
10228
|
|
|
var roundedStart; |
|
10229
|
|
|
|
|
10230
|
|
|
// Only round the first tick if we have no hard minimum |
|
10231
|
|
|
if (!me.options.time.min) { |
|
10232
|
|
|
me.firstTick = me.getMomentStartOf(me.firstTick); |
|
10233
|
|
|
roundedStart = me.firstTick; |
|
10234
|
|
|
} else { |
|
10235
|
|
|
roundedStart = me.getMomentStartOf(me.firstTick); |
|
10236
|
|
|
} |
|
10237
|
|
|
|
|
10238
|
|
|
// Only round the last tick if we have no hard maximum |
|
10239
|
|
|
if (!me.options.time.max) { |
|
10240
|
|
|
var roundedEnd = me.getMomentStartOf(me.lastTick); |
|
10241
|
|
|
var delta = roundedEnd.diff(me.lastTick, me.tickUnit, true); |
|
10242
|
|
|
if (delta < 0) { |
|
10243
|
|
|
// Do not use end of because we need me to be in the next time unit |
|
10244
|
|
|
me.lastTick = me.getMomentStartOf(me.lastTick.add(1, me.tickUnit)); |
|
10245
|
|
|
} else if (delta >= 0) { |
|
10246
|
|
|
me.lastTick = roundedEnd; |
|
10247
|
|
|
} |
|
10248
|
|
|
|
|
10249
|
|
|
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); |
|
10250
|
|
|
} |
|
10251
|
|
|
|
|
10252
|
|
|
me.smallestLabelSeparation = me.width; |
|
10253
|
|
|
|
|
10254
|
|
|
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { |
|
10255
|
|
|
for (var i = 1; i < me.labelMoments[datasetIndex].length; i++) { |
|
10256
|
|
|
me.smallestLabelSeparation = Math.min(me.smallestLabelSeparation, me.labelMoments[datasetIndex][i].diff(me.labelMoments[datasetIndex][i - 1], me.tickUnit, true)); |
|
10257
|
|
|
} |
|
10258
|
|
|
}, me); |
|
10259
|
|
|
|
|
10260
|
|
|
// Tick displayFormat override |
|
10261
|
|
|
if (me.options.time.displayFormat) { |
|
10262
|
|
|
me.displayFormat = me.options.time.displayFormat; |
|
10263
|
|
|
} |
|
10264
|
|
|
|
|
10265
|
|
|
// first tick. will have been rounded correctly if options.time.min is not specified |
|
10266
|
|
|
me.ticks.push(me.firstTick.clone()); |
|
10267
|
|
|
|
|
10268
|
|
|
// For every unit in between the first and last moment, create a moment and add it to the ticks tick |
|
10269
|
|
|
for (var i = 1; i <= me.scaleSizeInUnits; ++i) { |
|
10270
|
|
|
var newTick = roundedStart.clone().add(i, me.tickUnit); |
|
10271
|
|
|
|
|
10272
|
|
|
// Are we greater than the max time |
|
10273
|
|
|
if (me.options.time.max && newTick.diff(me.lastTick, me.tickUnit, true) >= 0) { |
|
10274
|
|
|
break; |
|
10275
|
|
|
} |
|
10276
|
|
|
|
|
10277
|
|
|
if (i % me.unitScale === 0) { |
|
10278
|
|
|
me.ticks.push(newTick); |
|
10279
|
|
|
} |
|
10280
|
|
|
} |
|
10281
|
|
|
|
|
10282
|
|
|
// Always show the right tick |
|
10283
|
|
|
var diff = me.ticks[me.ticks.length - 1].diff(me.lastTick, me.tickUnit); |
|
10284
|
|
|
if (diff !== 0 || me.scaleSizeInUnits === 0) { |
|
10285
|
|
|
// this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart |
|
10286
|
|
|
// but the last tick was not rounded. |
|
10287
|
|
|
if (me.options.time.max) { |
|
10288
|
|
|
me.ticks.push(me.lastTick.clone()); |
|
10289
|
|
|
me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true); |
|
10290
|
|
|
} else { |
|
10291
|
|
|
me.ticks.push(me.lastTick.clone()); |
|
10292
|
|
|
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); |
|
10293
|
|
|
} |
|
10294
|
|
|
} |
|
10295
|
|
|
|
|
10296
|
|
|
me.ctx.restore(); |
|
10297
|
|
|
}, |
|
10298
|
|
|
// Get tooltip label |
|
10299
|
|
|
getLabelForIndex: function(index, datasetIndex) { |
|
10300
|
|
|
var me = this; |
|
10301
|
|
|
var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : ''; |
|
10302
|
|
|
|
|
10303
|
|
|
if (typeof me.chart.data.datasets[datasetIndex].data[0] === 'object') { |
|
10304
|
|
|
label = me.getRightValue(me.chart.data.datasets[datasetIndex].data[index]); |
|
10305
|
|
|
} |
|
10306
|
|
|
|
|
10307
|
|
|
// Format nicely |
|
10308
|
|
|
if (me.options.time.tooltipFormat) { |
|
10309
|
|
|
label = me.parseTime(label).format(me.options.time.tooltipFormat); |
|
10310
|
|
|
} |
|
10311
|
|
|
|
|
10312
|
|
|
return label; |
|
10313
|
|
|
}, |
|
10314
|
|
|
// Function to format an individual tick mark |
|
10315
|
|
|
tickFormatFunction: function(tick, index, ticks) { |
|
10316
|
|
|
var formattedTick = tick.format(this.displayFormat); |
|
10317
|
|
|
var tickOpts = this.options.ticks; |
|
10318
|
|
|
var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback); |
|
10319
|
|
|
|
|
10320
|
|
|
if (callback) { |
|
10321
|
|
|
return callback(formattedTick, index, ticks); |
|
10322
|
|
|
} else { |
|
10323
|
|
|
return formattedTick; |
|
10324
|
|
|
} |
|
10325
|
|
|
}, |
|
10326
|
|
|
convertTicksToLabels: function() { |
|
10327
|
|
|
var me = this; |
|
10328
|
|
|
me.tickMoments = me.ticks; |
|
10329
|
|
|
me.ticks = me.ticks.map(me.tickFormatFunction, me); |
|
10330
|
|
|
}, |
|
10331
|
|
|
getPixelForValue: function(value, index, datasetIndex) { |
|
10332
|
|
|
var me = this; |
|
10333
|
|
|
if (!value || !value.isValid) { |
|
10334
|
|
|
// not already a moment object |
|
10335
|
|
|
value = moment(me.getRightValue(value)); |
|
10336
|
|
|
} |
|
10337
|
|
|
var labelMoment = value && value.isValid && value.isValid() ? value : me.getLabelMoment(datasetIndex, index); |
|
10338
|
|
|
|
|
10339
|
|
|
if (labelMoment) { |
|
|
|
|
|
|
10340
|
|
|
var offset = labelMoment.diff(me.firstTick, me.tickUnit, true); |
|
10341
|
|
|
|
|
10342
|
|
|
var decimal = offset !== 0 ? offset / me.scaleSizeInUnits : offset; |
|
10343
|
|
|
|
|
10344
|
|
|
if (me.isHorizontal()) { |
|
10345
|
|
|
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
|
10346
|
|
|
var valueOffset = (innerWidth * decimal) + me.paddingLeft; |
|
10347
|
|
|
|
|
10348
|
|
|
return me.left + Math.round(valueOffset); |
|
10349
|
|
|
} else { |
|
10350
|
|
|
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); |
|
10351
|
|
|
var heightOffset = (innerHeight * decimal) + me.paddingTop; |
|
10352
|
|
|
|
|
10353
|
|
|
return me.top + Math.round(heightOffset); |
|
10354
|
|
|
} |
|
10355
|
|
|
} |
|
10356
|
|
|
}, |
|
10357
|
|
|
getPixelForTick: function(index) { |
|
10358
|
|
|
return this.getPixelForValue(this.tickMoments[index], null, null); |
|
10359
|
|
|
}, |
|
10360
|
|
|
getValueForPixel: function(pixel) { |
|
10361
|
|
|
var me = this; |
|
10362
|
|
|
var innerDimension = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); |
|
10363
|
|
|
var offset = (pixel - (me.isHorizontal() ? me.left + me.paddingLeft : me.top + me.paddingTop)) / innerDimension; |
|
10364
|
|
|
offset *= me.scaleSizeInUnits; |
|
10365
|
|
|
return me.firstTick.clone().add(moment.duration(offset, me.tickUnit).asSeconds(), 'seconds'); |
|
10366
|
|
|
}, |
|
10367
|
|
|
parseTime: function(label) { |
|
10368
|
|
|
var me = this; |
|
10369
|
|
|
if (typeof me.options.time.parser === 'string') { |
|
10370
|
|
|
return moment(label, me.options.time.parser); |
|
10371
|
|
|
} |
|
10372
|
|
|
if (typeof me.options.time.parser === 'function') { |
|
10373
|
|
|
return me.options.time.parser(label); |
|
10374
|
|
|
} |
|
10375
|
|
|
// Date objects |
|
10376
|
|
|
if (typeof label.getMonth === 'function' || typeof label === 'number') { |
|
10377
|
|
|
return moment(label); |
|
10378
|
|
|
} |
|
10379
|
|
|
// Moment support |
|
10380
|
|
|
if (label.isValid && label.isValid()) { |
|
10381
|
|
|
return label; |
|
10382
|
|
|
} |
|
10383
|
|
|
// Custom parsing (return an instance of moment) |
|
10384
|
|
|
if (typeof me.options.time.format !== 'string' && me.options.time.format.call) { |
|
10385
|
|
|
console.warn("options.time.format is deprecated and replaced by options.time.parser. See http://nnnick.github.io/Chart.js/docs-v2/#scales-time-scale"); |
|
10386
|
|
|
return me.options.time.format(label); |
|
10387
|
|
|
} |
|
10388
|
|
|
// Moment format parsing |
|
10389
|
|
|
return moment(label, me.options.time.format); |
|
10390
|
|
|
} |
|
10391
|
|
|
}); |
|
10392
|
|
|
Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig); |
|
10393
|
|
|
|
|
10394
|
|
|
}; |
|
10395
|
|
|
|
|
10396
|
|
|
},{"1":1}]},{},[7])(7) |
|
10397
|
|
|
}); |
This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.