| 1 |
/*! |
| 2 |
* Chart.js |
| 3 |
* http://chartjs.org/ |
| 4 |
* Version: 2.7.0 |
| 5 |
* |
| 6 |
* Copyright 2017 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 |
/* MIT license */ |
| 12 |
var colorNames = require(5); |
| 13 |
|
| 14 |
module.exports = { |
| 15 |
getRgba: getRgba, |
| 16 |
getHsla: getHsla, |
| 17 |
getRgb: getRgb, |
| 18 |
getHsl: getHsl, |
| 19 |
getHwb: getHwb, |
| 20 |
getAlpha: getAlpha, |
| 21 |
|
| 22 |
hexString: hexString, |
| 23 |
rgbString: rgbString, |
| 24 |
rgbaString: rgbaString, |
| 25 |
percentString: percentString, |
| 26 |
percentaString: percentaString, |
| 27 |
hslString: hslString, |
| 28 |
hslaString: hslaString, |
| 29 |
hwbString: hwbString, |
| 30 |
keyword: keyword |
| 31 |
} |
| 32 |
|
| 33 |
function getRgba(string) { |
| 34 |
if (!string) { |
| 35 |
return; |
| 36 |
} |
| 37 |
var abbr = /^#([a-fA-F0-9]{3})$/i, |
| 38 |
hex = /^#([a-fA-F0-9]{6})$/i, |
| 39 |
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, |
| 40 |
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, |
| 41 |
keyword = /(\w+)/; |
| 42 |
|
| 43 |
var rgb = [0, 0, 0], |
| 44 |
a = 1, |
| 45 |
match = string.match(abbr); |
| 46 |
if (match) { |
| 47 |
match = match[1]; |
| 48 |
for (var i = 0; i < rgb.length; i++) { |
| 49 |
rgb[i] = parseInt(match[i] + match[i], 16); |
| 50 |
} |
| 51 |
} |
| 52 |
else if (match = string.match(hex)) { |
| 53 |
match = match[1]; |
| 54 |
for (var i = 0; i < rgb.length; i++) { |
| 55 |
rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); |
| 56 |
} |
| 57 |
} |
| 58 |
else if (match = string.match(rgba)) { |
| 59 |
for (var i = 0; i < rgb.length; i++) { |
| 60 |
rgb[i] = parseInt(match[i + 1]); |
| 61 |
} |
| 62 |
a = parseFloat(match[4]); |
| 63 |
} |
| 64 |
else if (match = string.match(per)) { |
| 65 |
for (var i = 0; i < rgb.length; i++) { |
| 66 |
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); |
| 67 |
} |
| 68 |
a = parseFloat(match[4]); |
| 69 |
} |
| 70 |
else if (match = string.match(keyword)) { |
| 71 |
if (match[1] == "transparent") { |
| 72 |
return [0, 0, 0, 0]; |
| 73 |
} |
| 74 |
rgb = colorNames[match[1]]; |
| 75 |
if (!rgb) { |
| 76 |
return; |
| 77 |
} |
| 78 |
} |
| 79 |
|
| 80 |
for (var i = 0; i < rgb.length; i++) { |
| 81 |
rgb[i] = scale(rgb[i], 0, 255); |
| 82 |
} |
| 83 |
if (!a && a != 0) { |
| 84 |
a = 1; |
| 85 |
} |
| 86 |
else { |
| 87 |
a = scale(a, 0, 1); |
| 88 |
} |
| 89 |
rgb[3] = a; |
| 90 |
return rgb; |
| 91 |
} |
| 92 |
|
| 93 |
function getHsla(string) { |
| 94 |
if (!string) { |
| 95 |
return; |
| 96 |
} |
| 97 |
var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; |
| 98 |
var match = string.match(hsl); |
| 99 |
if (match) { |
| 100 |
var alpha = parseFloat(match[4]); |
| 101 |
var h = scale(parseInt(match[1]), 0, 360), |
| 102 |
s = scale(parseFloat(match[2]), 0, 100), |
| 103 |
l = scale(parseFloat(match[3]), 0, 100), |
| 104 |
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); |
| 105 |
return [h, s, l, a]; |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
function getHwb(string) { |
| 110 |
if (!string) { |
| 111 |
return; |
| 112 |
} |
| 113 |
var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; |
| 114 |
var match = string.match(hwb); |
| 115 |
if (match) { |
| 116 |
var alpha = parseFloat(match[4]); |
| 117 |
var h = scale(parseInt(match[1]), 0, 360), |
| 118 |
w = scale(parseFloat(match[2]), 0, 100), |
| 119 |
b = scale(parseFloat(match[3]), 0, 100), |
| 120 |
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); |
| 121 |
return [h, w, b, a]; |
| 122 |
} |
| 123 |
} |
| 124 |
|
| 125 |
function getRgb(string) { |
| 126 |
var rgba = getRgba(string); |
| 127 |
return rgba && rgba.slice(0, 3); |
| 128 |
} |
| 129 |
|
| 130 |
function getHsl(string) { |
| 131 |
var hsla = getHsla(string); |
| 132 |
return hsla && hsla.slice(0, 3); |
| 133 |
} |
| 134 |
|
| 135 |
function getAlpha(string) { |
| 136 |
var vals = getRgba(string); |
| 137 |
if (vals) { |
| 138 |
return vals[3]; |
| 139 |
} |
| 140 |
else if (vals = getHsla(string)) { |
| 141 |
return vals[3]; |
| 142 |
} |
| 143 |
else if (vals = getHwb(string)) { |
| 144 |
return vals[3]; |
| 145 |
} |
| 146 |
} |
| 147 |
|
| 148 |
// generators |
| 149 |
function hexString(rgb) { |
| 150 |
return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) |
| 151 |
+ hexDouble(rgb[2]); |
| 152 |
} |
| 153 |
|
| 154 |
function rgbString(rgba, alpha) { |
| 155 |
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { |
| 156 |
return rgbaString(rgba, alpha); |
| 157 |
} |
| 158 |
return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; |
| 159 |
} |
| 160 |
|
| 161 |
function rgbaString(rgba, alpha) { |
| 162 |
if (alpha === undefined) { |
| 163 |
alpha = (rgba[3] !== undefined ? rgba[3] : 1); |
| 164 |
} |
| 165 |
return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] |
| 166 |
+ ", " + alpha + ")"; |
| 167 |
} |
| 168 |
|
| 169 |
function percentString(rgba, alpha) { |
| 170 |
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { |
| 171 |
return percentaString(rgba, alpha); |
| 172 |
} |
| 173 |
var r = Math.round(rgba[0]/255 * 100), |
| 174 |
g = Math.round(rgba[1]/255 * 100), |
| 175 |
b = Math.round(rgba[2]/255 * 100); |
| 176 |
|
| 177 |
return "rgb(" + r + "%, " + g + "%, " + b + "%)"; |
| 178 |
} |
| 179 |
|
| 180 |
function percentaString(rgba, alpha) { |
| 181 |
var r = Math.round(rgba[0]/255 * 100), |
| 182 |
g = Math.round(rgba[1]/255 * 100), |
| 183 |
b = Math.round(rgba[2]/255 * 100); |
| 184 |
return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; |
| 185 |
} |
| 186 |
|
| 187 |
function hslString(hsla, alpha) { |
| 188 |
if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { |
| 189 |
return hslaString(hsla, alpha); |
| 190 |
} |
| 191 |
return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; |
| 192 |
} |
| 193 |
|
| 194 |
function hslaString(hsla, alpha) { |
| 195 |
if (alpha === undefined) { |
| 196 |
alpha = (hsla[3] !== undefined ? hsla[3] : 1); |
| 197 |
} |
| 198 |
return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " |
| 199 |
+ alpha + ")"; |
| 200 |
} |
| 201 |
|
| 202 |
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax |
| 203 |
// (hwb have alpha optional & 1 is default value) |
| 204 |
function hwbString(hwb, alpha) { |
| 205 |
if (alpha === undefined) { |
| 206 |
alpha = (hwb[3] !== undefined ? hwb[3] : 1); |
| 207 |
} |
| 208 |
return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" |
| 209 |
+ (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; |
| 210 |
} |
| 211 |
|
| 212 |
function keyword(rgb) { |
| 213 |
return reverseNames[rgb.slice(0, 3)]; |
| 214 |
} |
| 215 |
|
| 216 |
// helpers |
| 217 |
function scale(num, min, max) { |
| 218 |
return Math.min(Math.max(min, num), max); |
| 219 |
} |
| 220 |
|
| 221 |
function hexDouble(num) { |
| 222 |
var str = num.toString(16).toUpperCase(); |
| 223 |
return (str.length < 2) ? "0" + str : str; |
| 224 |
} |
| 225 |
|
| 226 |
|
| 227 |
//create a list of reverse color names |
| 228 |
var reverseNames = {}; |
| 229 |
for (var name in colorNames) { |
| 230 |
reverseNames[colorNames[name]] = name; |
| 231 |
} |
| 232 |
|
| 233 |
},{"5":5}],2:[function(require,module,exports){ |
| 234 |
/* MIT license */ |
| 235 |
var convert = require(4); |
| 236 |
var string = require(1); |
| 237 |
|
| 238 |
var Color = function (obj) { |
| 239 |
if (obj instanceof Color) { |
| 240 |
return obj; |
| 241 |
} |
| 242 |
if (!(this instanceof Color)) { |
| 243 |
return new Color(obj); |
| 244 |
} |
| 245 |
|
| 246 |
this.valid = false; |
| 247 |
this.values = { |
| 248 |
rgb: [0, 0, 0], |
| 249 |
hsl: [0, 0, 0], |
| 250 |
hsv: [0, 0, 0], |
| 251 |
hwb: [0, 0, 0], |
| 252 |
cmyk: [0, 0, 0, 0], |
| 253 |
alpha: 1 |
| 254 |
}; |
| 255 |
|
| 256 |
// parse Color() argument |
| 257 |
var vals; |
| 258 |
if (typeof obj === 'string') { |
| 259 |
vals = string.getRgba(obj); |
| 260 |
if (vals) { |
| 261 |
this.setValues('rgb', vals); |
| 262 |
} else if (vals = string.getHsla(obj)) { |
| 263 |
this.setValues('hsl', vals); |
| 264 |
} else if (vals = string.getHwb(obj)) { |
| 265 |
this.setValues('hwb', vals); |
| 266 |
} |
| 267 |
} else if (typeof obj === 'object') { |
| 268 |
vals = obj; |
| 269 |
if (vals.r !== undefined || vals.red !== undefined) { |
| 270 |
this.setValues('rgb', vals); |
| 271 |
} else if (vals.l !== undefined || vals.lightness !== undefined) { |
| 272 |
this.setValues('hsl', vals); |
| 273 |
} else if (vals.v !== undefined || vals.value !== undefined) { |
| 274 |
this.setValues('hsv', vals); |
| 275 |
} else if (vals.w !== undefined || vals.whiteness !== undefined) { |
| 276 |
this.setValues('hwb', vals); |
| 277 |
} else if (vals.c !== undefined || vals.cyan !== undefined) { |
| 278 |
this.setValues('cmyk', vals); |
| 279 |
} |
| 280 |
} |
| 281 |
}; |
| 282 |
|
| 283 |
Color.prototype = { |
| 284 |
isValid: function () { |
| 285 |
return this.valid; |
| 286 |
}, |
| 287 |
rgb: function () { |
| 288 |
return this.setSpace('rgb', arguments); |
| 289 |
}, |
| 290 |
hsl: function () { |
| 291 |
return this.setSpace('hsl', arguments); |
| 292 |
}, |
| 293 |
hsv: function () { |
| 294 |
return this.setSpace('hsv', arguments); |
| 295 |
}, |
| 296 |
hwb: function () { |
| 297 |
return this.setSpace('hwb', arguments); |
| 298 |
}, |
| 299 |
cmyk: function () { |
| 300 |
return this.setSpace('cmyk', arguments); |
| 301 |
}, |
| 302 |
|
| 303 |
rgbArray: function () { |
| 304 |
return this.values.rgb; |
| 305 |
}, |
| 306 |
hslArray: function () { |
| 307 |
return this.values.hsl; |
| 308 |
}, |
| 309 |
hsvArray: function () { |
| 310 |
return this.values.hsv; |
| 311 |
}, |
| 312 |
hwbArray: function () { |
| 313 |
var values = this.values; |
| 314 |
if (values.alpha !== 1) { |
| 315 |
return values.hwb.concat([values.alpha]); |
| 316 |
} |
| 317 |
return values.hwb; |
| 318 |
}, |
| 319 |
cmykArray: function () { |
| 320 |
return this.values.cmyk; |
| 321 |
}, |
| 322 |
rgbaArray: function () { |
| 323 |
var values = this.values; |
| 324 |
return values.rgb.concat([values.alpha]); |
| 325 |
}, |
| 326 |
hslaArray: function () { |
| 327 |
var values = this.values; |
| 328 |
return values.hsl.concat([values.alpha]); |
| 329 |
}, |
| 330 |
alpha: function (val) { |
| 331 |
if (val === undefined) { |
| 332 |
return this.values.alpha; |
| 333 |
} |
| 334 |
this.setValues('alpha', val); |
| 335 |
return this; |
| 336 |
}, |
| 337 |
|
| 338 |
red: function (val) { |
| 339 |
return this.setChannel('rgb', 0, val); |
| 340 |
}, |
| 341 |
green: function (val) { |
| 342 |
return this.setChannel('rgb', 1, val); |
| 343 |
}, |
| 344 |
blue: function (val) { |
| 345 |
return this.setChannel('rgb', 2, val); |
| 346 |
}, |
| 347 |
hue: function (val) { |
| 348 |
if (val) { |
| 349 |
val %= 360; |
| 350 |
val = val < 0 ? 360 + val : val; |
| 351 |
} |
| 352 |
return this.setChannel('hsl', 0, val); |
| 353 |
}, |
| 354 |
saturation: function (val) { |
| 355 |
return this.setChannel('hsl', 1, val); |
| 356 |
}, |
| 357 |
lightness: function (val) { |
| 358 |
return this.setChannel('hsl', 2, val); |
| 359 |
}, |
| 360 |
saturationv: function (val) { |
| 361 |
return this.setChannel('hsv', 1, val); |
| 362 |
}, |
| 363 |
whiteness: function (val) { |
| 364 |
return this.setChannel('hwb', 1, val); |
| 365 |
}, |
| 366 |
blackness: function (val) { |
| 367 |
return this.setChannel('hwb', 2, val); |
| 368 |
}, |
| 369 |
value: function (val) { |
| 370 |
return this.setChannel('hsv', 2, val); |
| 371 |
}, |
| 372 |
cyan: function (val) { |
| 373 |
return this.setChannel('cmyk', 0, val); |
| 374 |
}, |
| 375 |
magenta: function (val) { |
| 376 |
return this.setChannel('cmyk', 1, val); |
| 377 |
}, |
| 378 |
yellow: function (val) { |
| 379 |
return this.setChannel('cmyk', 2, val); |
| 380 |
}, |
| 381 |
black: function (val) { |
| 382 |
return this.setChannel('cmyk', 3, val); |
| 383 |
}, |
| 384 |
|
| 385 |
hexString: function () { |
| 386 |
return string.hexString(this.values.rgb); |
| 387 |
}, |
| 388 |
rgbString: function () { |
| 389 |
return string.rgbString(this.values.rgb, this.values.alpha); |
| 390 |
}, |
| 391 |
rgbaString: function () { |
| 392 |
return string.rgbaString(this.values.rgb, this.values.alpha); |
| 393 |
}, |
| 394 |
percentString: function () { |
| 395 |
return string.percentString(this.values.rgb, this.values.alpha); |
| 396 |
}, |
| 397 |
hslString: function () { |
| 398 |
return string.hslString(this.values.hsl, this.values.alpha); |
| 399 |
}, |
| 400 |
hslaString: function () { |
| 401 |
return string.hslaString(this.values.hsl, this.values.alpha); |
| 402 |
}, |
| 403 |
hwbString: function () { |
| 404 |
return string.hwbString(this.values.hwb, this.values.alpha); |
| 405 |
}, |
| 406 |
keyword: function () { |
| 407 |
return string.keyword(this.values.rgb, this.values.alpha); |
| 408 |
}, |
| 409 |
|
| 410 |
rgbNumber: function () { |
| 411 |
var rgb = this.values.rgb; |
| 412 |
return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; |
| 413 |
}, |
| 414 |
|
| 415 |
luminosity: function () { |
| 416 |
// http://www.w3.org/TR/WCAG20/#relativeluminancedef |
| 417 |
var rgb = this.values.rgb; |
| 418 |
var lum = []; |
| 419 |
for (var i = 0; i < rgb.length; i++) { |
| 420 |
var chan = rgb[i] / 255; |
| 421 |
lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); |
| 422 |
} |
| 423 |
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; |
| 424 |
}, |
| 425 |
|
| 426 |
contrast: function (color2) { |
| 427 |
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef |
| 428 |
var lum1 = this.luminosity(); |
| 429 |
var lum2 = color2.luminosity(); |
| 430 |
if (lum1 > lum2) { |
| 431 |
return (lum1 + 0.05) / (lum2 + 0.05); |
| 432 |
} |
| 433 |
return (lum2 + 0.05) / (lum1 + 0.05); |
| 434 |
}, |
| 435 |
|
| 436 |
level: function (color2) { |
| 437 |
var contrastRatio = this.contrast(color2); |
| 438 |
if (contrastRatio >= 7.1) { |
| 439 |
return 'AAA'; |
| 440 |
} |
| 441 |
|
| 442 |
return (contrastRatio >= 4.5) ? 'AA' : ''; |
| 443 |
}, |
| 444 |
|
| 445 |
dark: function () { |
| 446 |
// YIQ equation from http://24ways.org/2010/calculating-color-contrast |
| 447 |
var rgb = this.values.rgb; |
| 448 |
var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; |
| 449 |
return yiq < 128; |
| 450 |
}, |
| 451 |
|
| 452 |
light: function () { |
| 453 |
return !this.dark(); |
| 454 |
}, |
| 455 |
|
| 456 |
negate: function () { |
| 457 |
var rgb = []; |
| 458 |
for (var i = 0; i < 3; i++) { |
| 459 |
rgb[i] = 255 - this.values.rgb[i]; |
| 460 |
} |
| 461 |
this.setValues('rgb', rgb); |
| 462 |
return this; |
| 463 |
}, |
| 464 |
|
| 465 |
lighten: function (ratio) { |
| 466 |
var hsl = this.values.hsl; |
| 467 |
hsl[2] += hsl[2] * ratio; |
| 468 |
this.setValues('hsl', hsl); |
| 469 |
return this; |
| 470 |
}, |
| 471 |
|
| 472 |
darken: function (ratio) { |
| 473 |
var hsl = this.values.hsl; |
| 474 |
hsl[2] -= hsl[2] * ratio; |
| 475 |
this.setValues('hsl', hsl); |
| 476 |
return this; |
| 477 |
}, |
| 478 |
|
| 479 |
saturate: function (ratio) { |
| 480 |
var hsl = this.values.hsl; |
| 481 |
hsl[1] += hsl[1] * ratio; |
| 482 |
this.setValues('hsl', hsl); |
| 483 |
return this; |
| 484 |
}, |
| 485 |
|
| 486 |
desaturate: function (ratio) { |
| 487 |
var hsl = this.values.hsl; |
| 488 |
hsl[1] -= hsl[1] * ratio; |
| 489 |
this.setValues('hsl', hsl); |
| 490 |
return this; |
| 491 |
}, |
| 492 |
|
| 493 |
whiten: function (ratio) { |
| 494 |
var hwb = this.values.hwb; |
| 495 |
hwb[1] += hwb[1] * ratio; |
| 496 |
this.setValues('hwb', hwb); |
| 497 |
return this; |
| 498 |
}, |
| 499 |
|
| 500 |
blacken: function (ratio) { |
| 501 |
var hwb = this.values.hwb; |
| 502 |
hwb[2] += hwb[2] * ratio; |
| 503 |
this.setValues('hwb', hwb); |
| 504 |
return this; |
| 505 |
}, |
| 506 |
|
| 507 |
greyscale: function () { |
| 508 |
var rgb = this.values.rgb; |
| 509 |
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale |
| 510 |
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; |
| 511 |
this.setValues('rgb', [val, val, val]); |
| 512 |
return this; |
| 513 |
}, |
| 514 |
|
| 515 |
clearer: function (ratio) { |
| 516 |
var alpha = this.values.alpha; |
| 517 |
this.setValues('alpha', alpha - (alpha * ratio)); |
| 518 |
return this; |
| 519 |
}, |
| 520 |
|
| 521 |
opaquer: function (ratio) { |
| 522 |
var alpha = this.values.alpha; |
| 523 |
this.setValues('alpha', alpha + (alpha * ratio)); |
| 524 |
return this; |
| 525 |
}, |
| 526 |
|
| 527 |
rotate: function (degrees) { |
| 528 |
var hsl = this.values.hsl; |
| 529 |
var hue = (hsl[0] + degrees) % 360; |
| 530 |
hsl[0] = hue < 0 ? 360 + hue : hue; |
| 531 |
this.setValues('hsl', hsl); |
| 532 |
return this; |
| 533 |
}, |
| 534 |
|
| 535 |
/** |
| 536 |
* Ported from sass implementation in C |
| 537 |
* https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 |
| 538 |
*/ |
| 539 |
mix: function (mixinColor, weight) { |
| 540 |
var color1 = this; |
| 541 |
var color2 = mixinColor; |
| 542 |
var p = weight === undefined ? 0.5 : weight; |
| 543 |
|
| 544 |
var w = 2 * p - 1; |
| 545 |
var a = color1.alpha() - color2.alpha(); |
| 546 |
|
| 547 |
var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; |
| 548 |
var w2 = 1 - w1; |
| 549 |
|
| 550 |
return this |
| 551 |
.rgb( |
| 552 |
w1 * color1.red() + w2 * color2.red(), |
| 553 |
w1 * color1.green() + w2 * color2.green(), |
| 554 |
w1 * color1.blue() + w2 * color2.blue() |
| 555 |
) |
| 556 |
.alpha(color1.alpha() * p + color2.alpha() * (1 - p)); |
| 557 |
}, |
| 558 |
|
| 559 |
toJSON: function () { |
| 560 |
return this.rgb(); |
| 561 |
}, |
| 562 |
|
| 563 |
clone: function () { |
| 564 |
// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, |
| 565 |
// making the final build way to big to embed in Chart.js. So let's do it manually, |
| 566 |
// assuming that values to clone are 1 dimension arrays containing only numbers, |
| 567 |
// except 'alpha' which is a number. |
| 568 |
var result = new Color(); |
| 569 |
var source = this.values; |
| 570 |
var target = result.values; |
| 571 |
var value, type; |
| 572 |
|
| 573 |
for (var prop in source) { |
| 574 |
if (source.hasOwnProperty(prop)) { |
| 575 |
value = source[prop]; |
| 576 |
type = ({}).toString.call(value); |
| 577 |
if (type === '[object Array]') { |
| 578 |
target[prop] = value.slice(0); |
| 579 |
} else if (type === '[object Number]') { |
| 580 |
target[prop] = value; |
| 581 |
} else { |
| 582 |
console.error('unexpected color value:', value); |
| 583 |
} |
| 584 |
} |
| 585 |
} |
| 586 |
|
| 587 |
return result; |
| 588 |
} |
| 589 |
}; |
| 590 |
|
| 591 |
Color.prototype.spaces = { |
| 592 |
rgb: ['red', 'green', 'blue'], |
| 593 |
hsl: ['hue', 'saturation', 'lightness'], |
| 594 |
hsv: ['hue', 'saturation', 'value'], |
| 595 |
hwb: ['hue', 'whiteness', 'blackness'], |
| 596 |
cmyk: ['cyan', 'magenta', 'yellow', 'black'] |
| 597 |
}; |
| 598 |
|
| 599 |
Color.prototype.maxes = { |
| 600 |
rgb: [255, 255, 255], |
| 601 |
hsl: [360, 100, 100], |
| 602 |
hsv: [360, 100, 100], |
| 603 |
hwb: [360, 100, 100], |
| 604 |
cmyk: [100, 100, 100, 100] |
| 605 |
}; |
| 606 |
|
| 607 |
Color.prototype.getValues = function (space) { |
| 608 |
var values = this.values; |
| 609 |
var vals = {}; |
| 610 |
|
| 611 |
for (var i = 0; i < space.length; i++) { |
| 612 |
vals[space.charAt(i)] = values[space][i]; |
| 613 |
} |
| 614 |
|
| 615 |
if (values.alpha !== 1) { |
| 616 |
vals.a = values.alpha; |
| 617 |
} |
| 618 |
|
| 619 |
// {r: 255, g: 255, b: 255, a: 0.4} |
| 620 |
return vals; |
| 621 |
}; |
| 622 |
|
| 623 |
Color.prototype.setValues = function (space, vals) { |
| 624 |
var values = this.values; |
| 625 |
var spaces = this.spaces; |
| 626 |
var maxes = this.maxes; |
| 627 |
var alpha = 1; |
| 628 |
var i; |
| 629 |
|
| 630 |
this.valid = true; |
| 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 |
},{"1":1,"4":4}],3:[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 |
},{}],4:[function(require,module,exports){ |
| 1421 |
var conversions = require(3); |
| 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 |
},{"3":3}],5:[function(require,module,exports){ |
| 1514 |
'use strict' |
| 1515 |
|
| 1516 |
module.exports = { |
| 1517 |
"aliceblue": [240, 248, 255], |
| 1518 |
"antiquewhite": [250, 235, 215], |
| 1519 |
"aqua": [0, 255, 255], |
| 1520 |
"aquamarine": [127, 255, 212], |
| 1521 |
"azure": [240, 255, 255], |
| 1522 |
"beige": [245, 245, 220], |
| 1523 |
"bisque": [255, 228, 196], |
| 1524 |
"black": [0, 0, 0], |
| 1525 |
"blanchedalmond": [255, 235, 205], |
| 1526 |
"blue": [0, 0, 255], |
| 1527 |
"blueviolet": [138, 43, 226], |
| 1528 |
"brown": [165, 42, 42], |
| 1529 |
"burlywood": [222, 184, 135], |
| 1530 |
"cadetblue": [95, 158, 160], |
| 1531 |
"chartreuse": [127, 255, 0], |
| 1532 |
"chocolate": [210, 105, 30], |
| 1533 |
"coral": [255, 127, 80], |
| 1534 |
"cornflowerblue": [100, 149, 237], |
| 1535 |
"cornsilk": [255, 248, 220], |
| 1536 |
"crimson": [220, 20, 60], |
| 1537 |
"cyan": [0, 255, 255], |
| 1538 |
"darkblue": [0, 0, 139], |
| 1539 |
"darkcyan": [0, 139, 139], |
| 1540 |
"darkgoldenrod": [184, 134, 11], |
| 1541 |
"darkgray": [169, 169, 169], |
| 1542 |
"darkgreen": [0, 100, 0], |
| 1543 |
"darkgrey": [169, 169, 169], |
| 1544 |
"darkkhaki": [189, 183, 107], |
| 1545 |
"darkmagenta": [139, 0, 139], |
| 1546 |
"darkolivegreen": [85, 107, 47], |
| 1547 |
"darkorange": [255, 140, 0], |
| 1548 |
"darkorchid": [153, 50, 204], |
| 1549 |
"darkred": [139, 0, 0], |
| 1550 |
"darksalmon": [233, 150, 122], |
| 1551 |
"darkseagreen": [143, 188, 143], |
| 1552 |
"darkslateblue": [72, 61, 139], |
| 1553 |
"darkslategray": [47, 79, 79], |
| 1554 |
"darkslategrey": [47, 79, 79], |
| 1555 |
"darkturquoise": [0, 206, 209], |
| 1556 |
"darkviolet": [148, 0, 211], |
| 1557 |
"deeppink": [255, 20, 147], |
| 1558 |
"deepskyblue": [0, 191, 255], |
| 1559 |
"dimgray": [105, 105, 105], |
| 1560 |
"dimgrey": [105, 105, 105], |
| 1561 |
"dodgerblue": [30, 144, 255], |
| 1562 |
"firebrick": [178, 34, 34], |
| 1563 |
"floralwhite": [255, 250, 240], |
| 1564 |
"forestgreen": [34, 139, 34], |
| 1565 |
"fuchsia": [255, 0, 255], |
| 1566 |
"gainsboro": [220, 220, 220], |
| 1567 |
"ghostwhite": [248, 248, 255], |
| 1568 |
"gold": [255, 215, 0], |
| 1569 |
"goldenrod": [218, 165, 32], |
| 1570 |
"gray": [128, 128, 128], |
| 1571 |
"green": [0, 128, 0], |
| 1572 |
"greenyellow": [173, 255, 47], |
| 1573 |
"grey": [128, 128, 128], |
| 1574 |
"honeydew": [240, 255, 240], |
| 1575 |
"hotpink": [255, 105, 180], |
| 1576 |
"indianred": [205, 92, 92], |
| 1577 |
"indigo": [75, 0, 130], |
| 1578 |
"ivory": [255, 255, 240], |
| 1579 |
"khaki": [240, 230, 140], |
| 1580 |
"lavender": [230, 230, 250], |
| 1581 |
"lavenderblush": [255, 240, 245], |
| 1582 |
"lawngreen": [124, 252, 0], |
| 1583 |
"lemonchiffon": [255, 250, 205], |
| 1584 |
"lightblue": [173, 216, 230], |
| 1585 |
"lightcoral": [240, 128, 128], |
| 1586 |
"lightcyan": [224, 255, 255], |
| 1587 |
"lightgoldenrodyellow": [250, 250, 210], |
| 1588 |
"lightgray": [211, 211, 211], |
| 1589 |
"lightgreen": [144, 238, 144], |
| 1590 |
"lightgrey": [211, 211, 211], |
| 1591 |
"lightpink": [255, 182, 193], |
| 1592 |
"lightsalmon": [255, 160, 122], |
| 1593 |
"lightseagreen": [32, 178, 170], |
| 1594 |
"lightskyblue": [135, 206, 250], |
| 1595 |
"lightslategray": [119, 136, 153], |
| 1596 |
"lightslategrey": [119, 136, 153], |
| 1597 |
"lightsteelblue": [176, 196, 222], |
| 1598 |
"lightyellow": [255, 255, 224], |
| 1599 |
"lime": [0, 255, 0], |
| 1600 |
"limegreen": [50, 205, 50], |
| 1601 |
"linen": [250, 240, 230], |
| 1602 |
"magenta": [255, 0, 255], |
| 1603 |
"maroon": [128, 0, 0], |
| 1604 |
"mediumaquamarine": [102, 205, 170], |
| 1605 |
"mediumblue": [0, 0, 205], |
| 1606 |
"mediumorchid": [186, 85, 211], |
| 1607 |
"mediumpurple": [147, 112, 219], |
| 1608 |
"mediumseagreen": [60, 179, 113], |
| 1609 |
"mediumslateblue": [123, 104, 238], |
| 1610 |
"mediumspringgreen": [0, 250, 154], |
| 1611 |
"mediumturquoise": [72, 209, 204], |
| 1612 |
"mediumvioletred": [199, 21, 133], |
| 1613 |
"midnightblue": [25, 25, 112], |
| 1614 |
"mintcream": [245, 255, 250], |
| 1615 |
"mistyrose": [255, 228, 225], |
| 1616 |
"moccasin": [255, 228, 181], |
| 1617 |
"navajowhite": [255, 222, 173], |
| 1618 |
"navy": [0, 0, 128], |
| 1619 |
"oldlace": [253, 245, 230], |
| 1620 |
"olive": [128, 128, 0], |
| 1621 |
"olivedrab": [107, 142, 35], |
| 1622 |
"orange": [255, 165, 0], |
| 1623 |
"orangered": [255, 69, 0], |
| 1624 |
"orchid": [218, 112, 214], |
| 1625 |
"palegoldenrod": [238, 232, 170], |
| 1626 |
"palegreen": [152, 251, 152], |
| 1627 |
"paleturquoise": [175, 238, 238], |
| 1628 |
"palevioletred": [219, 112, 147], |
| 1629 |
"papayawhip": [255, 239, 213], |
| 1630 |
"peachpuff": [255, 218, 185], |
| 1631 |
"peru": [205, 133, 63], |
| 1632 |
"pink": [255, 192, 203], |
| 1633 |
"plum": [221, 160, 221], |
| 1634 |
"powderblue": [176, 224, 230], |
| 1635 |
"purple": [128, 0, 128], |
| 1636 |
"rebeccapurple": [102, 51, 153], |
| 1637 |
"red": [255, 0, 0], |
| 1638 |
"rosybrown": [188, 143, 143], |
| 1639 |
"royalblue": [65, 105, 225], |
| 1640 |
"saddlebrown": [139, 69, 19], |
| 1641 |
"salmon": [250, 128, 114], |
| 1642 |
"sandybrown": [244, 164, 96], |
| 1643 |
"seagreen": [46, 139, 87], |
| 1644 |
"seashell": [255, 245, 238], |
| 1645 |
"sienna": [160, 82, 45], |
| 1646 |
"silver": [192, 192, 192], |
| 1647 |
"skyblue": [135, 206, 235], |
| 1648 |
"slateblue": [106, 90, 205], |
| 1649 |
"slategray": [112, 128, 144], |
| 1650 |
"slategrey": [112, 128, 144], |
| 1651 |
"snow": [255, 250, 250], |
| 1652 |
"springgreen": [0, 255, 127], |
| 1653 |
"steelblue": [70, 130, 180], |
| 1654 |
"tan": [210, 180, 140], |
| 1655 |
"teal": [0, 128, 128], |
| 1656 |
"thistle": [216, 191, 216], |
| 1657 |
"tomato": [255, 99, 71], |
| 1658 |
"turquoise": [64, 224, 208], |
| 1659 |
"violet": [238, 130, 238], |
| 1660 |
"wheat": [245, 222, 179], |
| 1661 |
"white": [255, 255, 255], |
| 1662 |
"whitesmoke": [245, 245, 245], |
| 1663 |
"yellow": [255, 255, 0], |
| 1664 |
"yellowgreen": [154, 205, 50] |
| 1665 |
}; |
| 1666 |
|
| 1667 |
},{}],6:[function(require,module,exports){ |
| 1668 |
//! moment.js |
| 1669 |
//! version : 2.18.1 |
| 1670 |
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors |
| 1671 |
//! license : MIT |
| 1672 |
//! momentjs.com |
| 1673 |
|
| 1674 |
;(function (global, factory) { |
| 1675 |
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
| 1676 |
typeof define === 'function' && define.amd ? define(factory) : |
| 1677 |
global.moment = factory() |
| 1678 |
}(this, (function () { 'use strict'; |
| 1679 |
|
| 1680 |
var hookCallback; |
| 1681 |
|
| 1682 |
function hooks () { |
| 1683 |
return hookCallback.apply(null, arguments); |
| 1684 |
} |
| 1685 |
|
| 1686 |
// This is done to register the method called with moment() |
| 1687 |
// without creating circular dependencies. |
| 1688 |
function setHookCallback (callback) { |
| 1689 |
hookCallback = callback; |
| 1690 |
} |
| 1691 |
|
| 1692 |
function isArray(input) { |
| 1693 |
return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; |
| 1694 |
} |
| 1695 |
|
| 1696 |
function isObject(input) { |
| 1697 |
// IE8 will treat undefined and null as object if it wasn't for |
| 1698 |
// input != null |
| 1699 |
return input != null && Object.prototype.toString.call(input) === '[object Object]'; |
| 1700 |
} |
| 1701 |
|
| 1702 |
function isObjectEmpty(obj) { |
| 1703 |
var k; |
| 1704 |
for (k in obj) { |
| 1705 |
// even if its not own property I'd still call it non-empty |
| 1706 |
return false; |
| 1707 |
} |
| 1708 |
return true; |
| 1709 |
} |
| 1710 |
|
| 1711 |
function isUndefined(input) { |
| 1712 |
return input === void 0; |
| 1713 |
} |
| 1714 |
|
| 1715 |
function isNumber(input) { |
| 1716 |
return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; |
| 1717 |
} |
| 1718 |
|
| 1719 |
function isDate(input) { |
| 1720 |
return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; |
| 1721 |
} |
| 1722 |
|
| 1723 |
function map(arr, fn) { |
| 1724 |
var res = [], i; |
| 1725 |
for (i = 0; i < arr.length; ++i) { |
| 1726 |
res.push(fn(arr[i], i)); |
| 1727 |
} |
| 1728 |
return res; |
| 1729 |
} |
| 1730 |
|
| 1731 |
function hasOwnProp(a, b) { |
| 1732 |
return Object.prototype.hasOwnProperty.call(a, b); |
| 1733 |
} |
| 1734 |
|
| 1735 |
function extend(a, b) { |
| 1736 |
for (var i in b) { |
| 1737 |
if (hasOwnProp(b, i)) { |
| 1738 |
a[i] = b[i]; |
| 1739 |
} |
| 1740 |
} |
| 1741 |
|
| 1742 |
if (hasOwnProp(b, 'toString')) { |
| 1743 |
a.toString = b.toString; |
| 1744 |
} |
| 1745 |
|
| 1746 |
if (hasOwnProp(b, 'valueOf')) { |
| 1747 |
a.valueOf = b.valueOf; |
| 1748 |
} |
| 1749 |
|
| 1750 |
return a; |
| 1751 |
} |
| 1752 |
|
| 1753 |
function createUTC (input, format, locale, strict) { |
| 1754 |
return createLocalOrUTC(input, format, locale, strict, true).utc(); |
| 1755 |
} |
| 1756 |
|
| 1757 |
function defaultParsingFlags() { |
| 1758 |
// We need to deep clone this object. |
| 1759 |
return { |
| 1760 |
empty : false, |
| 1761 |
unusedTokens : [], |
| 1762 |
unusedInput : [], |
| 1763 |
overflow : -2, |
| 1764 |
charsLeftOver : 0, |
| 1765 |
nullInput : false, |
| 1766 |
invalidMonth : null, |
| 1767 |
invalidFormat : false, |
| 1768 |
userInvalidated : false, |
| 1769 |
iso : false, |
| 1770 |
parsedDateParts : [], |
| 1771 |
meridiem : null, |
| 1772 |
rfc2822 : false, |
| 1773 |
weekdayMismatch : false |
| 1774 |
}; |
| 1775 |
} |
| 1776 |
|
| 1777 |
function getParsingFlags(m) { |
| 1778 |
if (m._pf == null) { |
| 1779 |
m._pf = defaultParsingFlags(); |
| 1780 |
} |
| 1781 |
return m._pf; |
| 1782 |
} |
| 1783 |
|
| 1784 |
var some; |
| 1785 |
if (Array.prototype.some) { |
| 1786 |
some = Array.prototype.some; |
| 1787 |
} else { |
| 1788 |
some = function (fun) { |
| 1789 |
var t = Object(this); |
| 1790 |
var len = t.length >>> 0; |
| 1791 |
|
| 1792 |
for (var i = 0; i < len; i++) { |
| 1793 |
if (i in t && fun.call(this, t[i], i, t)) { |
| 1794 |
return true; |
| 1795 |
} |
| 1796 |
} |
| 1797 |
|
| 1798 |
return false; |
| 1799 |
}; |
| 1800 |
} |
| 1801 |
|
| 1802 |
var some$1 = some; |
| 1803 |
|
| 1804 |
function isValid(m) { |
| 1805 |
if (m._isValid == null) { |
| 1806 |
var flags = getParsingFlags(m); |
| 1807 |
var parsedParts = some$1.call(flags.parsedDateParts, function (i) { |
| 1808 |
return i != null; |
| 1809 |
}); |
| 1810 |
var isNowValid = !isNaN(m._d.getTime()) && |
| 1811 |
flags.overflow < 0 && |
| 1812 |
!flags.empty && |
| 1813 |
!flags.invalidMonth && |
| 1814 |
!flags.invalidWeekday && |
| 1815 |
!flags.nullInput && |
| 1816 |
!flags.invalidFormat && |
| 1817 |
!flags.userInvalidated && |
| 1818 |
(!flags.meridiem || (flags.meridiem && parsedParts)); |
| 1819 |
|
| 1820 |
if (m._strict) { |
| 1821 |
isNowValid = isNowValid && |
| 1822 |
flags.charsLeftOver === 0 && |
| 1823 |
flags.unusedTokens.length === 0 && |
| 1824 |
flags.bigHour === undefined; |
| 1825 |
} |
| 1826 |
|
| 1827 |
if (Object.isFrozen == null || !Object.isFrozen(m)) { |
| 1828 |
m._isValid = isNowValid; |
| 1829 |
} |
| 1830 |
else { |
| 1831 |
return isNowValid; |
| 1832 |
} |
| 1833 |
} |
| 1834 |
return m._isValid; |
| 1835 |
} |
| 1836 |
|
| 1837 |
function createInvalid (flags) { |
| 1838 |
var m = createUTC(NaN); |
| 1839 |
if (flags != null) { |
| 1840 |
extend(getParsingFlags(m), flags); |
| 1841 |
} |
| 1842 |
else { |
| 1843 |
getParsingFlags(m).userInvalidated = true; |
| 1844 |
} |
| 1845 |
|
| 1846 |
return m; |
| 1847 |
} |
| 1848 |
|
| 1849 |
// Plugins that add properties should also add the key here (null value), |
| 1850 |
// so we can properly clone ourselves. |
| 1851 |
var momentProperties = hooks.momentProperties = []; |
| 1852 |
|
| 1853 |
function copyConfig(to, from) { |
| 1854 |
var i, prop, val; |
| 1855 |
|
| 1856 |
if (!isUndefined(from._isAMomentObject)) { |
| 1857 |
to._isAMomentObject = from._isAMomentObject; |
| 1858 |
} |
| 1859 |
if (!isUndefined(from._i)) { |
| 1860 |
to._i = from._i; |
| 1861 |
} |
| 1862 |
if (!isUndefined(from._f)) { |
| 1863 |
to._f = from._f; |
| 1864 |
} |
| 1865 |
if (!isUndefined(from._l)) { |
| 1866 |
to._l = from._l; |
| 1867 |
} |
| 1868 |
if (!isUndefined(from._strict)) { |
| 1869 |
to._strict = from._strict; |
| 1870 |
} |
| 1871 |
if (!isUndefined(from._tzm)) { |
| 1872 |
to._tzm = from._tzm; |
| 1873 |
} |
| 1874 |
if (!isUndefined(from._isUTC)) { |
| 1875 |
to._isUTC = from._isUTC; |
| 1876 |
} |
| 1877 |
if (!isUndefined(from._offset)) { |
| 1878 |
to._offset = from._offset; |
| 1879 |
} |
| 1880 |
if (!isUndefined(from._pf)) { |
| 1881 |
to._pf = getParsingFlags(from); |
| 1882 |
} |
| 1883 |
if (!isUndefined(from._locale)) { |
| 1884 |
to._locale = from._locale; |
| 1885 |
} |
| 1886 |
|
| 1887 |
if (momentProperties.length > 0) { |
| 1888 |
for (i = 0; i < momentProperties.length; i++) { |
| 1889 |
prop = momentProperties[i]; |
| 1890 |
val = from[prop]; |
| 1891 |
if (!isUndefined(val)) { |
| 1892 |
to[prop] = val; |
| 1893 |
} |
| 1894 |
} |
| 1895 |
} |
| 1896 |
|
| 1897 |
return to; |
| 1898 |
} |
| 1899 |
|
| 1900 |
var updateInProgress = false; |
| 1901 |
|
| 1902 |
// Moment prototype object |
| 1903 |
function Moment(config) { |
| 1904 |
copyConfig(this, config); |
| 1905 |
this._d = new Date(config._d != null ? config._d.getTime() : NaN); |
| 1906 |
if (!this.isValid()) { |
| 1907 |
this._d = new Date(NaN); |
| 1908 |
} |
| 1909 |
// Prevent infinite loop in case updateOffset creates new moment |
| 1910 |
// objects. |
| 1911 |
if (updateInProgress === false) { |
| 1912 |
updateInProgress = true; |
| 1913 |
hooks.updateOffset(this); |
| 1914 |
updateInProgress = false; |
| 1915 |
} |
| 1916 |
} |
| 1917 |
|
| 1918 |
function isMoment (obj) { |
| 1919 |
return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); |
| 1920 |
} |
| 1921 |
|
| 1922 |
function absFloor (number) { |
| 1923 |
if (number < 0) { |
| 1924 |
// -0 -> 0 |
| 1925 |
return Math.ceil(number) || 0; |
| 1926 |
} else { |
| 1927 |
return Math.floor(number); |
| 1928 |
} |
| 1929 |
} |
| 1930 |
|
| 1931 |
function toInt(argumentForCoercion) { |
| 1932 |
var coercedNumber = +argumentForCoercion, |
| 1933 |
value = 0; |
| 1934 |
|
| 1935 |
if (coercedNumber !== 0 && isFinite(coercedNumber)) { |
| 1936 |
value = absFloor(coercedNumber); |
| 1937 |
} |
| 1938 |
|
| 1939 |
return value; |
| 1940 |
} |
| 1941 |
|
| 1942 |
// compare two arrays, return the number of differences |
| 1943 |
function compareArrays(array1, array2, dontConvert) { |
| 1944 |
var len = Math.min(array1.length, array2.length), |
| 1945 |
lengthDiff = Math.abs(array1.length - array2.length), |
| 1946 |
diffs = 0, |
| 1947 |
i; |
| 1948 |
for (i = 0; i < len; i++) { |
| 1949 |
if ((dontConvert && array1[i] !== array2[i]) || |
| 1950 |
(!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { |
| 1951 |
diffs++; |
| 1952 |
} |
| 1953 |
} |
| 1954 |
return diffs + lengthDiff; |
| 1955 |
} |
| 1956 |
|
| 1957 |
function warn(msg) { |
| 1958 |
if (hooks.suppressDeprecationWarnings === false && |
| 1959 |
(typeof console !== 'undefined') && console.warn) { |
| 1960 |
console.warn('Deprecation warning: ' + msg); |
| 1961 |
} |
| 1962 |
} |
| 1963 |
|
| 1964 |
function deprecate(msg, fn) { |
| 1965 |
var firstTime = true; |
| 1966 |
|
| 1967 |
return extend(function () { |
| 1968 |
if (hooks.deprecationHandler != null) { |
| 1969 |
hooks.deprecationHandler(null, msg); |
| 1970 |
} |
| 1971 |
if (firstTime) { |
| 1972 |
var args = []; |
| 1973 |
var arg; |
| 1974 |
for (var i = 0; i < arguments.length; i++) { |
| 1975 |
arg = ''; |
| 1976 |
if (typeof arguments[i] === 'object') { |
| 1977 |
arg += '\n[' + i + '] '; |
| 1978 |
for (var key in arguments[0]) { |
| 1979 |
arg += key + ': ' + arguments[0][key] + ', '; |
| 1980 |
} |
| 1981 |
arg = arg.slice(0, -2); // Remove trailing comma and space |
| 1982 |
} else { |
| 1983 |
arg = arguments[i]; |
| 1984 |
} |
| 1985 |
args.push(arg); |
| 1986 |
} |
| 1987 |
warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); |
| 1988 |
firstTime = false; |
| 1989 |
} |
| 1990 |
return fn.apply(this, arguments); |
| 1991 |
}, fn); |
| 1992 |
} |
| 1993 |
|
| 1994 |
var deprecations = {}; |
| 1995 |
|
| 1996 |
function deprecateSimple(name, msg) { |
| 1997 |
if (hooks.deprecationHandler != null) { |
| 1998 |
hooks.deprecationHandler(name, msg); |
| 1999 |
} |
| 2000 |
if (!deprecations[name]) { |
| 2001 |
warn(msg); |
| 2002 |
deprecations[name] = true; |
| 2003 |
} |
| 2004 |
} |
| 2005 |
|
| 2006 |
hooks.suppressDeprecationWarnings = false; |
| 2007 |
hooks.deprecationHandler = null; |
| 2008 |
|
| 2009 |
function isFunction(input) { |
| 2010 |
return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; |
| 2011 |
} |
| 2012 |
|
| 2013 |
function set (config) { |
| 2014 |
var prop, i; |
| 2015 |
for (i in config) { |
| 2016 |
prop = config[i]; |
| 2017 |
if (isFunction(prop)) { |
| 2018 |
this[i] = prop; |
| 2019 |
} else { |
| 2020 |
this['_' + i] = prop; |
| 2021 |
} |
| 2022 |
} |
| 2023 |
this._config = config; |
| 2024 |
// Lenient ordinal parsing accepts just a number in addition to |
| 2025 |
// number + (possibly) stuff coming from _dayOfMonthOrdinalParse. |
| 2026 |
// TODO: Remove "ordinalParse" fallback in next major release. |
| 2027 |
this._dayOfMonthOrdinalParseLenient = new RegExp( |
| 2028 |
(this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + |
| 2029 |
'|' + (/\d{1,2}/).source); |
| 2030 |
} |
| 2031 |
|
| 2032 |
function mergeConfigs(parentConfig, childConfig) { |
| 2033 |
var res = extend({}, parentConfig), prop; |
| 2034 |
for (prop in childConfig) { |
| 2035 |
if (hasOwnProp(childConfig, prop)) { |
| 2036 |
if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { |
| 2037 |
res[prop] = {}; |
| 2038 |
extend(res[prop], parentConfig[prop]); |
| 2039 |
extend(res[prop], childConfig[prop]); |
| 2040 |
} else if (childConfig[prop] != null) { |
| 2041 |
res[prop] = childConfig[prop]; |
| 2042 |
} else { |
| 2043 |
delete res[prop]; |
| 2044 |
} |
| 2045 |
} |
| 2046 |
} |
| 2047 |
for (prop in parentConfig) { |
| 2048 |
if (hasOwnProp(parentConfig, prop) && |
| 2049 |
!hasOwnProp(childConfig, prop) && |
| 2050 |
isObject(parentConfig[prop])) { |
| 2051 |
// make sure changes to properties don't modify parent config |
| 2052 |
res[prop] = extend({}, res[prop]); |
| 2053 |
} |
| 2054 |
} |
| 2055 |
return res; |
| 2056 |
} |
| 2057 |
|
| 2058 |
function Locale(config) { |
| 2059 |
if (config != null) { |
| 2060 |
this.set(config); |
| 2061 |
} |
| 2062 |
} |
| 2063 |
|
| 2064 |
var keys; |
| 2065 |
|
| 2066 |
if (Object.keys) { |
| 2067 |
keys = Object.keys; |
| 2068 |
} else { |
| 2069 |
keys = function (obj) { |
| 2070 |
var i, res = []; |
| 2071 |
for (i in obj) { |
| 2072 |
if (hasOwnProp(obj, i)) { |
| 2073 |
res.push(i); |
| 2074 |
} |
| 2075 |
} |
| 2076 |
return res; |
| 2077 |
}; |
| 2078 |
} |
| 2079 |
|
| 2080 |
var keys$1 = keys; |
| 2081 |
|
| 2082 |
var defaultCalendar = { |
| 2083 |
sameDay : '[Today at] LT', |
| 2084 |
nextDay : '[Tomorrow at] LT', |
| 2085 |
nextWeek : 'dddd [at] LT', |
| 2086 |
lastDay : '[Yesterday at] LT', |
| 2087 |
lastWeek : '[Last] dddd [at] LT', |
| 2088 |
sameElse : 'L' |
| 2089 |
}; |
| 2090 |
|
| 2091 |
function calendar (key, mom, now) { |
| 2092 |
var output = this._calendar[key] || this._calendar['sameElse']; |
| 2093 |
return isFunction(output) ? output.call(mom, now) : output; |
| 2094 |
} |
| 2095 |
|
| 2096 |
var defaultLongDateFormat = { |
| 2097 |
LTS : 'h:mm:ss A', |
| 2098 |
LT : 'h:mm A', |
| 2099 |
L : 'MM/DD/YYYY', |
| 2100 |
LL : 'MMMM D, YYYY', |
| 2101 |
LLL : 'MMMM D, YYYY h:mm A', |
| 2102 |
LLLL : 'dddd, MMMM D, YYYY h:mm A' |
| 2103 |
}; |
| 2104 |
|
| 2105 |
function longDateFormat (key) { |
| 2106 |
var format = this._longDateFormat[key], |
| 2107 |
formatUpper = this._longDateFormat[key.toUpperCase()]; |
| 2108 |
|
| 2109 |
if (format || !formatUpper) { |
| 2110 |
return format; |
| 2111 |
} |
| 2112 |
|
| 2113 |
this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { |
| 2114 |
return val.slice(1); |
| 2115 |
}); |
| 2116 |
|
| 2117 |
return this._longDateFormat[key]; |
| 2118 |
} |
| 2119 |
|
| 2120 |
var defaultInvalidDate = 'Invalid date'; |
| 2121 |
|
| 2122 |
function invalidDate () { |
| 2123 |
return this._invalidDate; |
| 2124 |
} |
| 2125 |
|
| 2126 |
var defaultOrdinal = '%d'; |
| 2127 |
var defaultDayOfMonthOrdinalParse = /\d{1,2}/; |
| 2128 |
|
| 2129 |
function ordinal (number) { |
| 2130 |
return this._ordinal.replace('%d', number); |
| 2131 |
} |
| 2132 |
|
| 2133 |
var defaultRelativeTime = { |
| 2134 |
future : 'in %s', |
| 2135 |
past : '%s ago', |
| 2136 |
s : 'a few seconds', |
| 2137 |
ss : '%d seconds', |
| 2138 |
m : 'a minute', |
| 2139 |
mm : '%d minutes', |
| 2140 |
h : 'an hour', |
| 2141 |
hh : '%d hours', |
| 2142 |
d : 'a day', |
| 2143 |
dd : '%d days', |
| 2144 |
M : 'a month', |
| 2145 |
MM : '%d months', |
| 2146 |
y : 'a year', |
| 2147 |
yy : '%d years' |
| 2148 |
}; |
| 2149 |
|
| 2150 |
function relativeTime (number, withoutSuffix, string, isFuture) { |
| 2151 |
var output = this._relativeTime[string]; |
| 2152 |
return (isFunction(output)) ? |
| 2153 |
output(number, withoutSuffix, string, isFuture) : |
| 2154 |
output.replace(/%d/i, number); |
| 2155 |
} |
| 2156 |
|
| 2157 |
function pastFuture (diff, output) { |
| 2158 |
var format = this._relativeTime[diff > 0 ? 'future' : 'past']; |
| 2159 |
return isFunction(format) ? format(output) : format.replace(/%s/i, output); |
| 2160 |
} |
| 2161 |
|
| 2162 |
var aliases = {}; |
| 2163 |
|
| 2164 |
function addUnitAlias (unit, shorthand) { |
| 2165 |
var lowerCase = unit.toLowerCase(); |
| 2166 |
aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; |
| 2167 |
} |
| 2168 |
|
| 2169 |
function normalizeUnits(units) { |
| 2170 |
return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; |
| 2171 |
} |
| 2172 |
|
| 2173 |
function normalizeObjectUnits(inputObject) { |
| 2174 |
var normalizedInput = {}, |
| 2175 |
normalizedProp, |
| 2176 |
prop; |
| 2177 |
|
| 2178 |
for (prop in inputObject) { |
| 2179 |
if (hasOwnProp(inputObject, prop)) { |
| 2180 |
normalizedProp = normalizeUnits(prop); |
| 2181 |
if (normalizedProp) { |
| 2182 |
normalizedInput[normalizedProp] = inputObject[prop]; |
| 2183 |
} |
| 2184 |
} |
| 2185 |
} |
| 2186 |
|
| 2187 |
return normalizedInput; |
| 2188 |
} |
| 2189 |
|
| 2190 |
var priorities = {}; |
| 2191 |
|
| 2192 |
function addUnitPriority(unit, priority) { |
| 2193 |
priorities[unit] = priority; |
| 2194 |
} |
| 2195 |
|
| 2196 |
function getPrioritizedUnits(unitsObj) { |
| 2197 |
var units = []; |
| 2198 |
for (var u in unitsObj) { |
| 2199 |
units.push({unit: u, priority: priorities[u]}); |
| 2200 |
} |
| 2201 |
units.sort(function (a, b) { |
| 2202 |
return a.priority - b.priority; |
| 2203 |
}); |
| 2204 |
return units; |
| 2205 |
} |
| 2206 |
|
| 2207 |
function makeGetSet (unit, keepTime) { |
| 2208 |
return function (value) { |
| 2209 |
if (value != null) { |
| 2210 |
set$1(this, unit, value); |
| 2211 |
hooks.updateOffset(this, keepTime); |
| 2212 |
return this; |
| 2213 |
} else { |
| 2214 |
return get(this, unit); |
| 2215 |
} |
| 2216 |
}; |
| 2217 |
} |
| 2218 |
|
| 2219 |
function get (mom, unit) { |
| 2220 |
return mom.isValid() ? |
| 2221 |
mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; |
| 2222 |
} |
| 2223 |
|
| 2224 |
function set$1 (mom, unit, value) { |
| 2225 |
if (mom.isValid()) { |
| 2226 |
mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); |
| 2227 |
} |
| 2228 |
} |
| 2229 |
|
| 2230 |
// MOMENTS |
| 2231 |
|
| 2232 |
function stringGet (units) { |
| 2233 |
units = normalizeUnits(units); |
| 2234 |
if (isFunction(this[units])) { |
| 2235 |
return this[units](); |
| 2236 |
} |
| 2237 |
return this; |
| 2238 |
} |
| 2239 |
|
| 2240 |
|
| 2241 |
function stringSet (units, value) { |
| 2242 |
if (typeof units === 'object') { |
| 2243 |
units = normalizeObjectUnits(units); |
| 2244 |
var prioritized = getPrioritizedUnits(units); |
| 2245 |
for (var i = 0; i < prioritized.length; i++) { |
| 2246 |
this[prioritized[i].unit](units[prioritized[i].unit]); |
| 2247 |
} |
| 2248 |
} else { |
| 2249 |
units = normalizeUnits(units); |
| 2250 |
if (isFunction(this[units])) { |
| 2251 |
return this[units](value); |
| 2252 |
} |
| 2253 |
} |
| 2254 |
return this; |
| 2255 |
} |
| 2256 |
|
| 2257 |
function zeroFill(number, targetLength, forceSign) { |
| 2258 |
var absNumber = '' + Math.abs(number), |
| 2259 |
zerosToFill = targetLength - absNumber.length, |
| 2260 |
sign = number >= 0; |
| 2261 |
return (sign ? (forceSign ? '+' : '') : '-') + |
| 2262 |
Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; |
| 2263 |
} |
| 2264 |
|
| 2265 |
var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; |
| 2266 |
|
| 2267 |
var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; |
| 2268 |
|
| 2269 |
var formatFunctions = {}; |
| 2270 |
|
| 2271 |
var formatTokenFunctions = {}; |
| 2272 |
|
| 2273 |
// token: 'M' |
| 2274 |
// padded: ['MM', 2] |
| 2275 |
// ordinal: 'Mo' |
| 2276 |
// callback: function () { this.month() + 1 } |
| 2277 |
function addFormatToken (token, padded, ordinal, callback) { |
| 2278 |
var func = callback; |
| 2279 |
if (typeof callback === 'string') { |
| 2280 |
func = function () { |
| 2281 |
return this[callback](); |
| 2282 |
}; |
| 2283 |
} |
| 2284 |
if (token) { |
| 2285 |
formatTokenFunctions[token] = func; |
| 2286 |
} |
| 2287 |
if (padded) { |
| 2288 |
formatTokenFunctions[padded[0]] = function () { |
| 2289 |
return zeroFill(func.apply(this, arguments), padded[1], padded[2]); |
| 2290 |
}; |
| 2291 |
} |
| 2292 |
if (ordinal) { |
| 2293 |
formatTokenFunctions[ordinal] = function () { |
| 2294 |
return this.localeData().ordinal(func.apply(this, arguments), token); |
| 2295 |
}; |
| 2296 |
} |
| 2297 |
} |
| 2298 |
|
| 2299 |
function removeFormattingTokens(input) { |
| 2300 |
if (input.match(/\[[\s\S]/)) { |
| 2301 |
return input.replace(/^\[|\]$/g, ''); |
| 2302 |
} |
| 2303 |
return input.replace(/\\/g, ''); |
| 2304 |
} |
| 2305 |
|
| 2306 |
function makeFormatFunction(format) { |
| 2307 |
var array = format.match(formattingTokens), i, length; |
| 2308 |
|
| 2309 |
for (i = 0, length = array.length; i < length; i++) { |
| 2310 |
if (formatTokenFunctions[array[i]]) { |
| 2311 |
array[i] = formatTokenFunctions[array[i]]; |
| 2312 |
} else { |
| 2313 |
array[i] = removeFormattingTokens(array[i]); |
| 2314 |
} |
| 2315 |
} |
| 2316 |
|
| 2317 |
return function (mom) { |
| 2318 |
var output = '', i; |
| 2319 |
for (i = 0; i < length; i++) { |
| 2320 |
output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; |
| 2321 |
} |
| 2322 |
return output; |
| 2323 |
}; |
| 2324 |
} |
| 2325 |
|
| 2326 |
// format date using native date object |
| 2327 |
function formatMoment(m, format) { |
| 2328 |
if (!m.isValid()) { |
| 2329 |
return m.localeData().invalidDate(); |
| 2330 |
} |
| 2331 |
|
| 2332 |
format = expandFormat(format, m.localeData()); |
| 2333 |
formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); |
| 2334 |
|
| 2335 |
return formatFunctions[format](m); |
| 2336 |
} |
| 2337 |
|
| 2338 |
function expandFormat(format, locale) { |
| 2339 |
var i = 5; |
| 2340 |
|
| 2341 |
function replaceLongDateFormatTokens(input) { |
| 2342 |
return locale.longDateFormat(input) || input; |
| 2343 |
} |
| 2344 |
|
| 2345 |
localFormattingTokens.lastIndex = 0; |
| 2346 |
while (i >= 0 && localFormattingTokens.test(format)) { |
| 2347 |
format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); |
| 2348 |
localFormattingTokens.lastIndex = 0; |
| 2349 |
i -= 1; |
| 2350 |
} |
| 2351 |
|
| 2352 |
return format; |
| 2353 |
} |
| 2354 |
|
| 2355 |
var match1 = /\d/; // 0 - 9 |
| 2356 |
var match2 = /\d\d/; // 00 - 99 |
| 2357 |
var match3 = /\d{3}/; // 000 - 999 |
| 2358 |
var match4 = /\d{4}/; // 0000 - 9999 |
| 2359 |
var match6 = /[+-]?\d{6}/; // -999999 - 999999 |
| 2360 |
var match1to2 = /\d\d?/; // 0 - 99 |
| 2361 |
var match3to4 = /\d\d\d\d?/; // 999 - 9999 |
| 2362 |
var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 |
| 2363 |
var match1to3 = /\d{1,3}/; // 0 - 999 |
| 2364 |
var match1to4 = /\d{1,4}/; // 0 - 9999 |
| 2365 |
var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 |
| 2366 |
|
| 2367 |
var matchUnsigned = /\d+/; // 0 - inf |
| 2368 |
var matchSigned = /[+-]?\d+/; // -inf - inf |
| 2369 |
|
| 2370 |
var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z |
| 2371 |
var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z |
| 2372 |
|
| 2373 |
var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 |
| 2374 |
|
| 2375 |
// any word (or two) characters or numbers including two/three word month in arabic. |
| 2376 |
// includes scottish gaelic two word and hyphenated months |
| 2377 |
var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; |
| 2378 |
|
| 2379 |
|
| 2380 |
var regexes = {}; |
| 2381 |
|
| 2382 |
function addRegexToken (token, regex, strictRegex) { |
| 2383 |
regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { |
| 2384 |
return (isStrict && strictRegex) ? strictRegex : regex; |
| 2385 |
}; |
| 2386 |
} |
| 2387 |
|
| 2388 |
function getParseRegexForToken (token, config) { |
| 2389 |
if (!hasOwnProp(regexes, token)) { |
| 2390 |
return new RegExp(unescapeFormat(token)); |
| 2391 |
} |
| 2392 |
|
| 2393 |
return regexes[token](config._strict, config._locale); |
| 2394 |
} |
| 2395 |
|
| 2396 |
// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript |
| 2397 |
function unescapeFormat(s) { |
| 2398 |
return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { |
| 2399 |
return p1 || p2 || p3 || p4; |
| 2400 |
})); |
| 2401 |
} |
| 2402 |
|
| 2403 |
function regexEscape(s) { |
| 2404 |
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); |
| 2405 |
} |
| 2406 |
|
| 2407 |
var tokens = {}; |
| 2408 |
|
| 2409 |
function addParseToken (token, callback) { |
| 2410 |
var i, func = callback; |
| 2411 |
if (typeof token === 'string') { |
| 2412 |
token = [token]; |
| 2413 |
} |
| 2414 |
if (isNumber(callback)) { |
| 2415 |
func = function (input, array) { |
| 2416 |
array[callback] = toInt(input); |
| 2417 |
}; |
| 2418 |
} |
| 2419 |
for (i = 0; i < token.length; i++) { |
| 2420 |
tokens[token[i]] = func; |
| 2421 |
} |
| 2422 |
} |
| 2423 |
|
| 2424 |
function addWeekParseToken (token, callback) { |
| 2425 |
addParseToken(token, function (input, array, config, token) { |
| 2426 |
config._w = config._w || {}; |
| 2427 |
callback(input, config._w, config, token); |
| 2428 |
}); |
| 2429 |
} |
| 2430 |
|
| 2431 |
function addTimeToArrayFromToken(token, input, config) { |
| 2432 |
if (input != null && hasOwnProp(tokens, token)) { |
| 2433 |
tokens[token](input, config._a, config, token); |
| 2434 |
} |
| 2435 |
} |
| 2436 |
|
| 2437 |
var YEAR = 0; |
| 2438 |
var MONTH = 1; |
| 2439 |
var DATE = 2; |
| 2440 |
var HOUR = 3; |
| 2441 |
var MINUTE = 4; |
| 2442 |
var SECOND = 5; |
| 2443 |
var MILLISECOND = 6; |
| 2444 |
var WEEK = 7; |
| 2445 |
var WEEKDAY = 8; |
| 2446 |
|
| 2447 |
var indexOf; |
| 2448 |
|
| 2449 |
if (Array.prototype.indexOf) { |
| 2450 |
indexOf = Array.prototype.indexOf; |
| 2451 |
} else { |
| 2452 |
indexOf = function (o) { |
| 2453 |
// I know |
| 2454 |
var i; |
| 2455 |
for (i = 0; i < this.length; ++i) { |
| 2456 |
if (this[i] === o) { |
| 2457 |
return i; |
| 2458 |
} |
| 2459 |
} |
| 2460 |
return -1; |
| 2461 |
}; |
| 2462 |
} |
| 2463 |
|
| 2464 |
var indexOf$1 = indexOf; |
| 2465 |
|
| 2466 |
function daysInMonth(year, month) { |
| 2467 |
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); |
| 2468 |
} |
| 2469 |
|
| 2470 |
// FORMATTING |
| 2471 |
|
| 2472 |
addFormatToken('M', ['MM', 2], 'Mo', function () { |
| 2473 |
return this.month() + 1; |
| 2474 |
}); |
| 2475 |
|
| 2476 |
addFormatToken('MMM', 0, 0, function (format) { |
| 2477 |
return this.localeData().monthsShort(this, format); |
| 2478 |
}); |
| 2479 |
|
| 2480 |
addFormatToken('MMMM', 0, 0, function (format) { |
| 2481 |
return this.localeData().months(this, format); |
| 2482 |
}); |
| 2483 |
|
| 2484 |
// ALIASES |
| 2485 |
|
| 2486 |
addUnitAlias('month', 'M'); |
| 2487 |
|
| 2488 |
// PRIORITY |
| 2489 |
|
| 2490 |
addUnitPriority('month', 8); |
| 2491 |
|
| 2492 |
// PARSING |
| 2493 |
|
| 2494 |
addRegexToken('M', match1to2); |
| 2495 |
addRegexToken('MM', match1to2, match2); |
| 2496 |
addRegexToken('MMM', function (isStrict, locale) { |
| 2497 |
return locale.monthsShortRegex(isStrict); |
| 2498 |
}); |
| 2499 |
addRegexToken('MMMM', function (isStrict, locale) { |
| 2500 |
return locale.monthsRegex(isStrict); |
| 2501 |
}); |
| 2502 |
|
| 2503 |
addParseToken(['M', 'MM'], function (input, array) { |
| 2504 |
array[MONTH] = toInt(input) - 1; |
| 2505 |
}); |
| 2506 |
|
| 2507 |
addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { |
| 2508 |
var month = config._locale.monthsParse(input, token, config._strict); |
| 2509 |
// if we didn't find a month name, mark the date as invalid. |
| 2510 |
if (month != null) { |
| 2511 |
array[MONTH] = month; |
| 2512 |
} else { |
| 2513 |
getParsingFlags(config).invalidMonth = input; |
| 2514 |
} |
| 2515 |
}); |
| 2516 |
|
| 2517 |
// LOCALES |
| 2518 |
|
| 2519 |
var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; |
| 2520 |
var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); |
| 2521 |
function localeMonths (m, format) { |
| 2522 |
if (!m) { |
| 2523 |
return isArray(this._months) ? this._months : |
| 2524 |
this._months['standalone']; |
| 2525 |
} |
| 2526 |
return isArray(this._months) ? this._months[m.month()] : |
| 2527 |
this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; |
| 2528 |
} |
| 2529 |
|
| 2530 |
var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); |
| 2531 |
function localeMonthsShort (m, format) { |
| 2532 |
if (!m) { |
| 2533 |
return isArray(this._monthsShort) ? this._monthsShort : |
| 2534 |
this._monthsShort['standalone']; |
| 2535 |
} |
| 2536 |
return isArray(this._monthsShort) ? this._monthsShort[m.month()] : |
| 2537 |
this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; |
| 2538 |
} |
| 2539 |
|
| 2540 |
function handleStrictParse(monthName, format, strict) { |
| 2541 |
var i, ii, mom, llc = monthName.toLocaleLowerCase(); |
| 2542 |
if (!this._monthsParse) { |
| 2543 |
// this is not used |
| 2544 |
this._monthsParse = []; |
| 2545 |
this._longMonthsParse = []; |
| 2546 |
this._shortMonthsParse = []; |
| 2547 |
for (i = 0; i < 12; ++i) { |
| 2548 |
mom = createUTC([2000, i]); |
| 2549 |
this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); |
| 2550 |
this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); |
| 2551 |
} |
| 2552 |
} |
| 2553 |
|
| 2554 |
if (strict) { |
| 2555 |
if (format === 'MMM') { |
| 2556 |
ii = indexOf$1.call(this._shortMonthsParse, llc); |
| 2557 |
return ii !== -1 ? ii : null; |
| 2558 |
} else { |
| 2559 |
ii = indexOf$1.call(this._longMonthsParse, llc); |
| 2560 |
return ii !== -1 ? ii : null; |
| 2561 |
} |
| 2562 |
} else { |
| 2563 |
if (format === 'MMM') { |
| 2564 |
ii = indexOf$1.call(this._shortMonthsParse, llc); |
| 2565 |
if (ii !== -1) { |
| 2566 |
return ii; |
| 2567 |
} |
| 2568 |
ii = indexOf$1.call(this._longMonthsParse, llc); |
| 2569 |
return ii !== -1 ? ii : null; |
| 2570 |
} else { |
| 2571 |
ii = indexOf$1.call(this._longMonthsParse, llc); |
| 2572 |
if (ii !== -1) { |
| 2573 |
return ii; |
| 2574 |
} |
| 2575 |
ii = indexOf$1.call(this._shortMonthsParse, llc); |
| 2576 |
return ii !== -1 ? ii : null; |
| 2577 |
} |
| 2578 |
} |
| 2579 |
} |
| 2580 |
|
| 2581 |
function localeMonthsParse (monthName, format, strict) { |
| 2582 |
var i, mom, regex; |
| 2583 |
|
| 2584 |
if (this._monthsParseExact) { |
| 2585 |
return handleStrictParse.call(this, monthName, format, strict); |
| 2586 |
} |
| 2587 |
|
| 2588 |
if (!this._monthsParse) { |
| 2589 |
this._monthsParse = []; |
| 2590 |
this._longMonthsParse = []; |
| 2591 |
this._shortMonthsParse = []; |
| 2592 |
} |
| 2593 |
|
| 2594 |
// TODO: add sorting |
| 2595 |
// Sorting makes sure if one month (or abbr) is a prefix of another |
| 2596 |
// see sorting in computeMonthsParse |
| 2597 |
for (i = 0; i < 12; i++) { |
| 2598 |
// make the regex if we don't have it already |
| 2599 |
mom = createUTC([2000, i]); |
| 2600 |
if (strict && !this._longMonthsParse[i]) { |
| 2601 |
this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); |
| 2602 |
this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); |
| 2603 |
} |
| 2604 |
if (!strict && !this._monthsParse[i]) { |
| 2605 |
regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); |
| 2606 |
this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); |
| 2607 |
} |
| 2608 |
// test the regex |
| 2609 |
if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { |
| 2610 |
return i; |
| 2611 |
} else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { |
| 2612 |
return i; |
| 2613 |
} else if (!strict && this._monthsParse[i].test(monthName)) { |
| 2614 |
return i; |
| 2615 |
} |
| 2616 |
} |
| 2617 |
} |
| 2618 |
|
| 2619 |
// MOMENTS |
| 2620 |
|
| 2621 |
function setMonth (mom, value) { |
| 2622 |
var dayOfMonth; |
| 2623 |
|
| 2624 |
if (!mom.isValid()) { |
| 2625 |
// No op |
| 2626 |
return mom; |
| 2627 |
} |
| 2628 |
|
| 2629 |
if (typeof value === 'string') { |
| 2630 |
if (/^\d+$/.test(value)) { |
| 2631 |
value = toInt(value); |
| 2632 |
} else { |
| 2633 |
value = mom.localeData().monthsParse(value); |
| 2634 |
// TODO: Another silent failure? |
| 2635 |
if (!isNumber(value)) { |
| 2636 |
return mom; |
| 2637 |
} |
| 2638 |
} |
| 2639 |
} |
| 2640 |
|
| 2641 |
dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); |
| 2642 |
mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); |
| 2643 |
return mom; |
| 2644 |
} |
| 2645 |
|
| 2646 |
function getSetMonth (value) { |
| 2647 |
if (value != null) { |
| 2648 |
setMonth(this, value); |
| 2649 |
hooks.updateOffset(this, true); |
| 2650 |
return this; |
| 2651 |
} else { |
| 2652 |
return get(this, 'Month'); |
| 2653 |
} |
| 2654 |
} |
| 2655 |
|
| 2656 |
function getDaysInMonth () { |
| 2657 |
return daysInMonth(this.year(), this.month()); |
| 2658 |
} |
| 2659 |
|
| 2660 |
var defaultMonthsShortRegex = matchWord; |
| 2661 |
function monthsShortRegex (isStrict) { |
| 2662 |
if (this._monthsParseExact) { |
| 2663 |
if (!hasOwnProp(this, '_monthsRegex')) { |
| 2664 |
computeMonthsParse.call(this); |
| 2665 |
} |
| 2666 |
if (isStrict) { |
| 2667 |
return this._monthsShortStrictRegex; |
| 2668 |
} else { |
| 2669 |
return this._monthsShortRegex; |
| 2670 |
} |
| 2671 |
} else { |
| 2672 |
if (!hasOwnProp(this, '_monthsShortRegex')) { |
| 2673 |
this._monthsShortRegex = defaultMonthsShortRegex; |
| 2674 |
} |
| 2675 |
return this._monthsShortStrictRegex && isStrict ? |
| 2676 |
this._monthsShortStrictRegex : this._monthsShortRegex; |
| 2677 |
} |
| 2678 |
} |
| 2679 |
|
| 2680 |
var defaultMonthsRegex = matchWord; |
| 2681 |
function monthsRegex (isStrict) { |
| 2682 |
if (this._monthsParseExact) { |
| 2683 |
if (!hasOwnProp(this, '_monthsRegex')) { |
| 2684 |
computeMonthsParse.call(this); |
| 2685 |
} |
| 2686 |
if (isStrict) { |
| 2687 |
return this._monthsStrictRegex; |
| 2688 |
} else { |
| 2689 |
return this._monthsRegex; |
| 2690 |
} |
| 2691 |
} else { |
| 2692 |
if (!hasOwnProp(this, '_monthsRegex')) { |
| 2693 |
this._monthsRegex = defaultMonthsRegex; |
| 2694 |
} |
| 2695 |
return this._monthsStrictRegex && isStrict ? |
| 2696 |
this._monthsStrictRegex : this._monthsRegex; |
| 2697 |
} |
| 2698 |
} |
| 2699 |
|
| 2700 |
function computeMonthsParse () { |
| 2701 |
function cmpLenRev(a, b) { |
| 2702 |
return b.length - a.length; |
| 2703 |
} |
| 2704 |
|
| 2705 |
var shortPieces = [], longPieces = [], mixedPieces = [], |
| 2706 |
i, mom; |
| 2707 |
for (i = 0; i < 12; i++) { |
| 2708 |
// make the regex if we don't have it already |
| 2709 |
mom = createUTC([2000, i]); |
| 2710 |
shortPieces.push(this.monthsShort(mom, '')); |
| 2711 |
longPieces.push(this.months(mom, '')); |
| 2712 |
mixedPieces.push(this.months(mom, '')); |
| 2713 |
mixedPieces.push(this.monthsShort(mom, '')); |
| 2714 |
} |
| 2715 |
// Sorting makes sure if one month (or abbr) is a prefix of another it |
| 2716 |
// will match the longer piece. |
| 2717 |
shortPieces.sort(cmpLenRev); |
| 2718 |
longPieces.sort(cmpLenRev); |
| 2719 |
mixedPieces.sort(cmpLenRev); |
| 2720 |
for (i = 0; i < 12; i++) { |
| 2721 |
shortPieces[i] = regexEscape(shortPieces[i]); |
| 2722 |
longPieces[i] = regexEscape(longPieces[i]); |
| 2723 |
} |
| 2724 |
for (i = 0; i < 24; i++) { |
| 2725 |
mixedPieces[i] = regexEscape(mixedPieces[i]); |
| 2726 |
} |
| 2727 |
|
| 2728 |
this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); |
| 2729 |
this._monthsShortRegex = this._monthsRegex; |
| 2730 |
this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); |
| 2731 |
this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); |
| 2732 |
} |
| 2733 |
|
| 2734 |
// FORMATTING |
| 2735 |
|
| 2736 |
addFormatToken('Y', 0, 0, function () { |
| 2737 |
var y = this.year(); |
| 2738 |
return y <= 9999 ? '' + y : '+' + y; |
| 2739 |
}); |
| 2740 |
|
| 2741 |
addFormatToken(0, ['YY', 2], 0, function () { |
| 2742 |
return this.year() % 100; |
| 2743 |
}); |
| 2744 |
|
| 2745 |
addFormatToken(0, ['YYYY', 4], 0, 'year'); |
| 2746 |
addFormatToken(0, ['YYYYY', 5], 0, 'year'); |
| 2747 |
addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); |
| 2748 |
|
| 2749 |
// ALIASES |
| 2750 |
|
| 2751 |
addUnitAlias('year', 'y'); |
| 2752 |
|
| 2753 |
// PRIORITIES |
| 2754 |
|
| 2755 |
addUnitPriority('year', 1); |
| 2756 |
|
| 2757 |
// PARSING |
| 2758 |
|
| 2759 |
addRegexToken('Y', matchSigned); |
| 2760 |
addRegexToken('YY', match1to2, match2); |
| 2761 |
addRegexToken('YYYY', match1to4, match4); |
| 2762 |
addRegexToken('YYYYY', match1to6, match6); |
| 2763 |
addRegexToken('YYYYYY', match1to6, match6); |
| 2764 |
|
| 2765 |
addParseToken(['YYYYY', 'YYYYYY'], YEAR); |
| 2766 |
addParseToken('YYYY', function (input, array) { |
| 2767 |
array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); |
| 2768 |
}); |
| 2769 |
addParseToken('YY', function (input, array) { |
| 2770 |
array[YEAR] = hooks.parseTwoDigitYear(input); |
| 2771 |
}); |
| 2772 |
addParseToken('Y', function (input, array) { |
| 2773 |
array[YEAR] = parseInt(input, 10); |
| 2774 |
}); |
| 2775 |
|
| 2776 |
// HELPERS |
| 2777 |
|
| 2778 |
function daysInYear(year) { |
| 2779 |
return isLeapYear(year) ? 366 : 365; |
| 2780 |
} |
| 2781 |
|
| 2782 |
function isLeapYear(year) { |
| 2783 |
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; |
| 2784 |
} |
| 2785 |
|
| 2786 |
// HOOKS |
| 2787 |
|
| 2788 |
hooks.parseTwoDigitYear = function (input) { |
| 2789 |
return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); |
| 2790 |
}; |
| 2791 |
|
| 2792 |
// MOMENTS |
| 2793 |
|
| 2794 |
var getSetYear = makeGetSet('FullYear', true); |
| 2795 |
|
| 2796 |
function getIsLeapYear () { |
| 2797 |
return isLeapYear(this.year()); |
| 2798 |
} |
| 2799 |
|
| 2800 |
function createDate (y, m, d, h, M, s, ms) { |
| 2801 |
// can't just apply() to create a date: |
| 2802 |
// https://stackoverflow.com/q/181348 |
| 2803 |
var date = new Date(y, m, d, h, M, s, ms); |
| 2804 |
|
| 2805 |
// the date constructor remaps years 0-99 to 1900-1999 |
| 2806 |
if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { |
| 2807 |
date.setFullYear(y); |
| 2808 |
} |
| 2809 |
return date; |
| 2810 |
} |
| 2811 |
|
| 2812 |
function createUTCDate (y) { |
| 2813 |
var date = new Date(Date.UTC.apply(null, arguments)); |
| 2814 |
|
| 2815 |
// the Date.UTC function remaps years 0-99 to 1900-1999 |
| 2816 |
if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { |
| 2817 |
date.setUTCFullYear(y); |
| 2818 |
} |
| 2819 |
return date; |
| 2820 |
} |
| 2821 |
|
| 2822 |
// start-of-first-week - start-of-year |
| 2823 |
function firstWeekOffset(year, dow, doy) { |
| 2824 |
var // first-week day -- which january is always in the first week (4 for iso, 1 for other) |
| 2825 |
fwd = 7 + dow - doy, |
| 2826 |
// first-week day local weekday -- which local weekday is fwd |
| 2827 |
fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; |
| 2828 |
|
| 2829 |
return -fwdlw + fwd - 1; |
| 2830 |
} |
| 2831 |
|
| 2832 |
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday |
| 2833 |
function dayOfYearFromWeeks(year, week, weekday, dow, doy) { |
| 2834 |
var localWeekday = (7 + weekday - dow) % 7, |
| 2835 |
weekOffset = firstWeekOffset(year, dow, doy), |
| 2836 |
dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, |
| 2837 |
resYear, resDayOfYear; |
| 2838 |
|
| 2839 |
if (dayOfYear <= 0) { |
| 2840 |
resYear = year - 1; |
| 2841 |
resDayOfYear = daysInYear(resYear) + dayOfYear; |
| 2842 |
} else if (dayOfYear > daysInYear(year)) { |
| 2843 |
resYear = year + 1; |
| 2844 |
resDayOfYear = dayOfYear - daysInYear(year); |
| 2845 |
} else { |
| 2846 |
resYear = year; |
| 2847 |
resDayOfYear = dayOfYear; |
| 2848 |
} |
| 2849 |
|
| 2850 |
return { |
| 2851 |
year: resYear, |
| 2852 |
dayOfYear: resDayOfYear |
| 2853 |
}; |
| 2854 |
} |
| 2855 |
|
| 2856 |
function weekOfYear(mom, dow, doy) { |
| 2857 |
var weekOffset = firstWeekOffset(mom.year(), dow, doy), |
| 2858 |
week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, |
| 2859 |
resWeek, resYear; |
| 2860 |
|
| 2861 |
if (week < 1) { |
| 2862 |
resYear = mom.year() - 1; |
| 2863 |
resWeek = week + weeksInYear(resYear, dow, doy); |
| 2864 |
} else if (week > weeksInYear(mom.year(), dow, doy)) { |
| 2865 |
resWeek = week - weeksInYear(mom.year(), dow, doy); |
| 2866 |
resYear = mom.year() + 1; |
| 2867 |
} else { |
| 2868 |
resYear = mom.year(); |
| 2869 |
resWeek = week; |
| 2870 |
} |
| 2871 |
|
| 2872 |
return { |
| 2873 |
week: resWeek, |
| 2874 |
year: resYear |
| 2875 |
}; |
| 2876 |
} |
| 2877 |
|
| 2878 |
function weeksInYear(year, dow, doy) { |
| 2879 |
var weekOffset = firstWeekOffset(year, dow, doy), |
| 2880 |
weekOffsetNext = firstWeekOffset(year + 1, dow, doy); |
| 2881 |
return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; |
| 2882 |
} |
| 2883 |
|
| 2884 |
// FORMATTING |
| 2885 |
|
| 2886 |
addFormatToken('w', ['ww', 2], 'wo', 'week'); |
| 2887 |
addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); |
| 2888 |
|
| 2889 |
// ALIASES |
| 2890 |
|
| 2891 |
addUnitAlias('week', 'w'); |
| 2892 |
addUnitAlias('isoWeek', 'W'); |
| 2893 |
|
| 2894 |
// PRIORITIES |
| 2895 |
|
| 2896 |
addUnitPriority('week', 5); |
| 2897 |
addUnitPriority('isoWeek', 5); |
| 2898 |
|
| 2899 |
// PARSING |
| 2900 |
|
| 2901 |
addRegexToken('w', match1to2); |
| 2902 |
addRegexToken('ww', match1to2, match2); |
| 2903 |
addRegexToken('W', match1to2); |
| 2904 |
addRegexToken('WW', match1to2, match2); |
| 2905 |
|
| 2906 |
addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { |
| 2907 |
week[token.substr(0, 1)] = toInt(input); |
| 2908 |
}); |
| 2909 |
|
| 2910 |
// HELPERS |
| 2911 |
|
| 2912 |
// LOCALES |
| 2913 |
|
| 2914 |
function localeWeek (mom) { |
| 2915 |
return weekOfYear(mom, this._week.dow, this._week.doy).week; |
| 2916 |
} |
| 2917 |
|
| 2918 |
var defaultLocaleWeek = { |
| 2919 |
dow : 0, // Sunday is the first day of the week. |
| 2920 |
doy : 6 // The week that contains Jan 1st is the first week of the year. |
| 2921 |
}; |
| 2922 |
|
| 2923 |
function localeFirstDayOfWeek () { |
| 2924 |
return this._week.dow; |
| 2925 |
} |
| 2926 |
|
| 2927 |
function localeFirstDayOfYear () { |
| 2928 |
return this._week.doy; |
| 2929 |
} |
| 2930 |
|
| 2931 |
// MOMENTS |
| 2932 |
|
| 2933 |
function getSetWeek (input) { |
| 2934 |
var week = this.localeData().week(this); |
| 2935 |
return input == null ? week : this.add((input - week) * 7, 'd'); |
| 2936 |
} |
| 2937 |
|
| 2938 |
function getSetISOWeek (input) { |
| 2939 |
var week = weekOfYear(this, 1, 4).week; |
| 2940 |
return input == null ? week : this.add((input - week) * 7, 'd'); |
| 2941 |
} |
| 2942 |
|
| 2943 |
// FORMATTING |
| 2944 |
|
| 2945 |
addFormatToken('d', 0, 'do', 'day'); |
| 2946 |
|
| 2947 |
addFormatToken('dd', 0, 0, function (format) { |
| 2948 |
return this.localeData().weekdaysMin(this, format); |
| 2949 |
}); |
| 2950 |
|
| 2951 |
addFormatToken('ddd', 0, 0, function (format) { |
| 2952 |
return this.localeData().weekdaysShort(this, format); |
| 2953 |
}); |
| 2954 |
|
| 2955 |
addFormatToken('dddd', 0, 0, function (format) { |
| 2956 |
return this.localeData().weekdays(this, format); |
| 2957 |
}); |
| 2958 |
|
| 2959 |
addFormatToken('e', 0, 0, 'weekday'); |
| 2960 |
addFormatToken('E', 0, 0, 'isoWeekday'); |
| 2961 |
|
| 2962 |
// ALIASES |
| 2963 |
|
| 2964 |
addUnitAlias('day', 'd'); |
| 2965 |
addUnitAlias('weekday', 'e'); |
| 2966 |
addUnitAlias('isoWeekday', 'E'); |
| 2967 |
|
| 2968 |
// PRIORITY |
| 2969 |
addUnitPriority('day', 11); |
| 2970 |
addUnitPriority('weekday', 11); |
| 2971 |
addUnitPriority('isoWeekday', 11); |
| 2972 |
|
| 2973 |
// PARSING |
| 2974 |
|
| 2975 |
addRegexToken('d', match1to2); |
| 2976 |
addRegexToken('e', match1to2); |
| 2977 |
addRegexToken('E', match1to2); |
| 2978 |
addRegexToken('dd', function (isStrict, locale) { |
| 2979 |
return locale.weekdaysMinRegex(isStrict); |
| 2980 |
}); |
| 2981 |
addRegexToken('ddd', function (isStrict, locale) { |
| 2982 |
return locale.weekdaysShortRegex(isStrict); |
| 2983 |
}); |
| 2984 |
addRegexToken('dddd', function (isStrict, locale) { |
| 2985 |
return locale.weekdaysRegex(isStrict); |
| 2986 |
}); |
| 2987 |
|
| 2988 |
addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { |
| 2989 |
var weekday = config._locale.weekdaysParse(input, token, config._strict); |
| 2990 |
// if we didn't get a weekday name, mark the date as invalid |
| 2991 |
if (weekday != null) { |
| 2992 |
week.d = weekday; |
| 2993 |
} else { |
| 2994 |
getParsingFlags(config).invalidWeekday = input; |
| 2995 |
} |
| 2996 |
}); |
| 2997 |
|
| 2998 |
addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { |
| 2999 |
week[token] = toInt(input); |
| 3000 |
}); |
| 3001 |
|
| 3002 |
// HELPERS |
| 3003 |
|
| 3004 |
function parseWeekday(input, locale) { |
| 3005 |
if (typeof input !== 'string') { |
| 3006 |
return input; |
| 3007 |
} |
| 3008 |
|
| 3009 |
if (!isNaN(input)) { |
| 3010 |
return parseInt(input, 10); |
| 3011 |
} |
| 3012 |
|
| 3013 |
input = locale.weekdaysParse(input); |
| 3014 |
if (typeof input === 'number') { |
| 3015 |
return input; |
| 3016 |
} |
| 3017 |
|
| 3018 |
return null; |
| 3019 |
} |
| 3020 |
|
| 3021 |
function parseIsoWeekday(input, locale) { |
| 3022 |
if (typeof input === 'string') { |
| 3023 |
return locale.weekdaysParse(input) % 7 || 7; |
| 3024 |
} |
| 3025 |
return isNaN(input) ? null : input; |
| 3026 |
} |
| 3027 |
|
| 3028 |
// LOCALES |
| 3029 |
|
| 3030 |
var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); |
| 3031 |
function localeWeekdays (m, format) { |
| 3032 |
if (!m) { |
| 3033 |
return isArray(this._weekdays) ? this._weekdays : |
| 3034 |
this._weekdays['standalone']; |
| 3035 |
} |
| 3036 |
return isArray(this._weekdays) ? this._weekdays[m.day()] : |
| 3037 |
this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; |
| 3038 |
} |
| 3039 |
|
| 3040 |
var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); |
| 3041 |
function localeWeekdaysShort (m) { |
| 3042 |
return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; |
| 3043 |
} |
| 3044 |
|
| 3045 |
var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); |
| 3046 |
function localeWeekdaysMin (m) { |
| 3047 |
return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; |
| 3048 |
} |
| 3049 |
|
| 3050 |
function handleStrictParse$1(weekdayName, format, strict) { |
| 3051 |
var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); |
| 3052 |
if (!this._weekdaysParse) { |
| 3053 |
this._weekdaysParse = []; |
| 3054 |
this._shortWeekdaysParse = []; |
| 3055 |
this._minWeekdaysParse = []; |
| 3056 |
|
| 3057 |
for (i = 0; i < 7; ++i) { |
| 3058 |
mom = createUTC([2000, 1]).day(i); |
| 3059 |
this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); |
| 3060 |
this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); |
| 3061 |
this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); |
| 3062 |
} |
| 3063 |
} |
| 3064 |
|
| 3065 |
if (strict) { |
| 3066 |
if (format === 'dddd') { |
| 3067 |
ii = indexOf$1.call(this._weekdaysParse, llc); |
| 3068 |
return ii !== -1 ? ii : null; |
| 3069 |
} else if (format === 'ddd') { |
| 3070 |
ii = indexOf$1.call(this._shortWeekdaysParse, llc); |
| 3071 |
return ii !== -1 ? ii : null; |
| 3072 |
} else { |
| 3073 |
ii = indexOf$1.call(this._minWeekdaysParse, llc); |
| 3074 |
return ii !== -1 ? ii : null; |
| 3075 |
} |
| 3076 |
} else { |
| 3077 |
if (format === 'dddd') { |
| 3078 |
ii = indexOf$1.call(this._weekdaysParse, llc); |
| 3079 |
if (ii !== -1) { |
| 3080 |
return ii; |
| 3081 |
} |
| 3082 |
ii = indexOf$1.call(this._shortWeekdaysParse, llc); |
| 3083 |
if (ii !== -1) { |
| 3084 |
return ii; |
| 3085 |
} |
| 3086 |
ii = indexOf$1.call(this._minWeekdaysParse, llc); |
| 3087 |
return ii !== -1 ? ii : null; |
| 3088 |
} else if (format === 'ddd') { |
| 3089 |
ii = indexOf$1.call(this._shortWeekdaysParse, llc); |
| 3090 |
if (ii !== -1) { |
| 3091 |
return ii; |
| 3092 |
} |
| 3093 |
ii = indexOf$1.call(this._weekdaysParse, llc); |
| 3094 |
if (ii !== -1) { |
| 3095 |
return ii; |
| 3096 |
} |
| 3097 |
ii = indexOf$1.call(this._minWeekdaysParse, llc); |
| 3098 |
return ii !== -1 ? ii : null; |
| 3099 |
} else { |
| 3100 |
ii = indexOf$1.call(this._minWeekdaysParse, llc); |
| 3101 |
if (ii !== -1) { |
| 3102 |
return ii; |
| 3103 |
} |
| 3104 |
ii = indexOf$1.call(this._weekdaysParse, llc); |
| 3105 |
if (ii !== -1) { |
| 3106 |
return ii; |
| 3107 |
} |
| 3108 |
ii = indexOf$1.call(this._shortWeekdaysParse, llc); |
| 3109 |
return ii !== -1 ? ii : null; |
| 3110 |
} |
| 3111 |
} |
| 3112 |
} |
| 3113 |
|
| 3114 |
function localeWeekdaysParse (weekdayName, format, strict) { |
| 3115 |
var i, mom, regex; |
| 3116 |
|
| 3117 |
if (this._weekdaysParseExact) { |
| 3118 |
return handleStrictParse$1.call(this, weekdayName, format, strict); |
| 3119 |
} |
| 3120 |
|
| 3121 |
if (!this._weekdaysParse) { |
| 3122 |
this._weekdaysParse = []; |
| 3123 |
this._minWeekdaysParse = []; |
| 3124 |
this._shortWeekdaysParse = []; |
| 3125 |
this._fullWeekdaysParse = []; |
| 3126 |
} |
| 3127 |
|
| 3128 |
for (i = 0; i < 7; i++) { |
| 3129 |
// make the regex if we don't have it already |
| 3130 |
|
| 3131 |
mom = createUTC([2000, 1]).day(i); |
| 3132 |
if (strict && !this._fullWeekdaysParse[i]) { |
| 3133 |
this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); |
| 3134 |
this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); |
| 3135 |
this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); |
| 3136 |
} |
| 3137 |
if (!this._weekdaysParse[i]) { |
| 3138 |
regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); |
| 3139 |
this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); |
| 3140 |
} |
| 3141 |
// test the regex |
| 3142 |
if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { |
| 3143 |
return i; |
| 3144 |
} else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { |
| 3145 |
return i; |
| 3146 |
} else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { |
| 3147 |
return i; |
| 3148 |
} else if (!strict && this._weekdaysParse[i].test(weekdayName)) { |
| 3149 |
return i; |
| 3150 |
} |
| 3151 |
} |
| 3152 |
} |
| 3153 |
|
| 3154 |
// MOMENTS |
| 3155 |
|
| 3156 |
function getSetDayOfWeek (input) { |
| 3157 |
if (!this.isValid()) { |
| 3158 |
return input != null ? this : NaN; |
| 3159 |
} |
| 3160 |
var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); |
| 3161 |
if (input != null) { |
| 3162 |
input = parseWeekday(input, this.localeData()); |
| 3163 |
return this.add(input - day, 'd'); |
| 3164 |
} else { |
| 3165 |
return day; |
| 3166 |
} |
| 3167 |
} |
| 3168 |
|
| 3169 |
function getSetLocaleDayOfWeek (input) { |
| 3170 |
if (!this.isValid()) { |
| 3171 |
return input != null ? this : NaN; |
| 3172 |
} |
| 3173 |
var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; |
| 3174 |
return input == null ? weekday : this.add(input - weekday, 'd'); |
| 3175 |
} |
| 3176 |
|
| 3177 |
function getSetISODayOfWeek (input) { |
| 3178 |
if (!this.isValid()) { |
| 3179 |
return input != null ? this : NaN; |
| 3180 |
} |
| 3181 |
|
| 3182 |
// behaves the same as moment#day except |
| 3183 |
// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) |
| 3184 |
// as a setter, sunday should belong to the previous week. |
| 3185 |
|
| 3186 |
if (input != null) { |
| 3187 |
var weekday = parseIsoWeekday(input, this.localeData()); |
| 3188 |
return this.day(this.day() % 7 ? weekday : weekday - 7); |
| 3189 |
} else { |
| 3190 |
return this.day() || 7; |
| 3191 |
} |
| 3192 |
} |
| 3193 |
|
| 3194 |
var defaultWeekdaysRegex = matchWord; |
| 3195 |
function weekdaysRegex (isStrict) { |
| 3196 |
if (this._weekdaysParseExact) { |
| 3197 |
if (!hasOwnProp(this, '_weekdaysRegex')) { |
| 3198 |
computeWeekdaysParse.call(this); |
| 3199 |
} |
| 3200 |
if (isStrict) { |
| 3201 |
return this._weekdaysStrictRegex; |
| 3202 |
} else { |
| 3203 |
return this._weekdaysRegex; |
| 3204 |
} |
| 3205 |
} else { |
| 3206 |
if (!hasOwnProp(this, '_weekdaysRegex')) { |
| 3207 |
this._weekdaysRegex = defaultWeekdaysRegex; |
| 3208 |
} |
| 3209 |
return this._weekdaysStrictRegex && isStrict ? |
| 3210 |
this._weekdaysStrictRegex : this._weekdaysRegex; |
| 3211 |
} |
| 3212 |
} |
| 3213 |
|
| 3214 |
var defaultWeekdaysShortRegex = matchWord; |
| 3215 |
function weekdaysShortRegex (isStrict) { |
| 3216 |
if (this._weekdaysParseExact) { |
| 3217 |
if (!hasOwnProp(this, '_weekdaysRegex')) { |
| 3218 |
computeWeekdaysParse.call(this); |
| 3219 |
} |
| 3220 |
if (isStrict) { |
| 3221 |
return this._weekdaysShortStrictRegex; |
| 3222 |
} else { |
| 3223 |
return this._weekdaysShortRegex; |
| 3224 |
} |
| 3225 |
} else { |
| 3226 |
if (!hasOwnProp(this, '_weekdaysShortRegex')) { |
| 3227 |
this._weekdaysShortRegex = defaultWeekdaysShortRegex; |
| 3228 |
} |
| 3229 |
return this._weekdaysShortStrictRegex && isStrict ? |
| 3230 |
this._weekdaysShortStrictRegex : this._weekdaysShortRegex; |
| 3231 |
} |
| 3232 |
} |
| 3233 |
|
| 3234 |
var defaultWeekdaysMinRegex = matchWord; |
| 3235 |
function weekdaysMinRegex (isStrict) { |
| 3236 |
if (this._weekdaysParseExact) { |
| 3237 |
if (!hasOwnProp(this, '_weekdaysRegex')) { |
| 3238 |
computeWeekdaysParse.call(this); |
| 3239 |
} |
| 3240 |
if (isStrict) { |
| 3241 |
return this._weekdaysMinStrictRegex; |
| 3242 |
} else { |
| 3243 |
return this._weekdaysMinRegex; |
| 3244 |
} |
| 3245 |
} else { |
| 3246 |
if (!hasOwnProp(this, '_weekdaysMinRegex')) { |
| 3247 |
this._weekdaysMinRegex = defaultWeekdaysMinRegex; |
| 3248 |
} |
| 3249 |
return this._weekdaysMinStrictRegex && isStrict ? |
| 3250 |
this._weekdaysMinStrictRegex : this._weekdaysMinRegex; |
| 3251 |
} |
| 3252 |
} |
| 3253 |
|
| 3254 |
|
| 3255 |
function computeWeekdaysParse () { |
| 3256 |
function cmpLenRev(a, b) { |
| 3257 |
return b.length - a.length; |
| 3258 |
} |
| 3259 |
|
| 3260 |
var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], |
| 3261 |
i, mom, minp, shortp, longp; |
| 3262 |
for (i = 0; i < 7; i++) { |
| 3263 |
// make the regex if we don't have it already |
| 3264 |
mom = createUTC([2000, 1]).day(i); |
| 3265 |
minp = this.weekdaysMin(mom, ''); |
| 3266 |
shortp = this.weekdaysShort(mom, ''); |
| 3267 |
longp = this.weekdays(mom, ''); |
| 3268 |
minPieces.push(minp); |
| 3269 |
shortPieces.push(shortp); |
| 3270 |
longPieces.push(longp); |
| 3271 |
mixedPieces.push(minp); |
| 3272 |
mixedPieces.push(shortp); |
| 3273 |
mixedPieces.push(longp); |
| 3274 |
} |
| 3275 |
// Sorting makes sure if one weekday (or abbr) is a prefix of another it |
| 3276 |
// will match the longer piece. |
| 3277 |
minPieces.sort(cmpLenRev); |
| 3278 |
shortPieces.sort(cmpLenRev); |
| 3279 |
longPieces.sort(cmpLenRev); |
| 3280 |
mixedPieces.sort(cmpLenRev); |
| 3281 |
for (i = 0; i < 7; i++) { |
| 3282 |
shortPieces[i] = regexEscape(shortPieces[i]); |
| 3283 |
longPieces[i] = regexEscape(longPieces[i]); |
| 3284 |
mixedPieces[i] = regexEscape(mixedPieces[i]); |
| 3285 |
} |
| 3286 |
|
| 3287 |
this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); |
| 3288 |
this._weekdaysShortRegex = this._weekdaysRegex; |
| 3289 |
this._weekdaysMinRegex = this._weekdaysRegex; |
| 3290 |
|
| 3291 |
this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); |
| 3292 |
this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); |
| 3293 |
this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); |
| 3294 |
} |
| 3295 |
|
| 3296 |
// FORMATTING |
| 3297 |
|
| 3298 |
function hFormat() { |
| 3299 |
return this.hours() % 12 || 12; |
| 3300 |
} |
| 3301 |
|
| 3302 |
function kFormat() { |
| 3303 |
return this.hours() || 24; |
| 3304 |
} |
| 3305 |
|
| 3306 |
addFormatToken('H', ['HH', 2], 0, 'hour'); |
| 3307 |
addFormatToken('h', ['hh', 2], 0, hFormat); |
| 3308 |
addFormatToken('k', ['kk', 2], 0, kFormat); |
| 3309 |
|
| 3310 |
addFormatToken('hmm', 0, 0, function () { |
| 3311 |
return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); |
| 3312 |
}); |
| 3313 |
|
| 3314 |
addFormatToken('hmmss', 0, 0, function () { |
| 3315 |
return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + |
| 3316 |
zeroFill(this.seconds(), 2); |
| 3317 |
}); |
| 3318 |
|
| 3319 |
addFormatToken('Hmm', 0, 0, function () { |
| 3320 |
return '' + this.hours() + zeroFill(this.minutes(), 2); |
| 3321 |
}); |
| 3322 |
|
| 3323 |
addFormatToken('Hmmss', 0, 0, function () { |
| 3324 |
return '' + this.hours() + zeroFill(this.minutes(), 2) + |
| 3325 |
zeroFill(this.seconds(), 2); |
| 3326 |
}); |
| 3327 |
|
| 3328 |
function meridiem (token, lowercase) { |
| 3329 |
addFormatToken(token, 0, 0, function () { |
| 3330 |
return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); |
| 3331 |
}); |
| 3332 |
} |
| 3333 |
|
| 3334 |
meridiem('a', true); |
| 3335 |
meridiem('A', false); |
| 3336 |
|
| 3337 |
// ALIASES |
| 3338 |
|
| 3339 |
addUnitAlias('hour', 'h'); |
| 3340 |
|
| 3341 |
// PRIORITY |
| 3342 |
addUnitPriority('hour', 13); |
| 3343 |
|
| 3344 |
// PARSING |
| 3345 |
|
| 3346 |
function matchMeridiem (isStrict, locale) { |
| 3347 |
return locale._meridiemParse; |
| 3348 |
} |
| 3349 |
|
| 3350 |
addRegexToken('a', matchMeridiem); |
| 3351 |
addRegexToken('A', matchMeridiem); |
| 3352 |
addRegexToken('H', match1to2); |
| 3353 |
addRegexToken('h', match1to2); |
| 3354 |
addRegexToken('k', match1to2); |
| 3355 |
addRegexToken('HH', match1to2, match2); |
| 3356 |
addRegexToken('hh', match1to2, match2); |
| 3357 |
addRegexToken('kk', match1to2, match2); |
| 3358 |
|
| 3359 |
addRegexToken('hmm', match3to4); |
| 3360 |
addRegexToken('hmmss', match5to6); |
| 3361 |
addRegexToken('Hmm', match3to4); |
| 3362 |
addRegexToken('Hmmss', match5to6); |
| 3363 |
|
| 3364 |
addParseToken(['H', 'HH'], HOUR); |
| 3365 |
addParseToken(['k', 'kk'], function (input, array, config) { |
| 3366 |
var kInput = toInt(input); |
| 3367 |
array[HOUR] = kInput === 24 ? 0 : kInput; |
| 3368 |
}); |
| 3369 |
addParseToken(['a', 'A'], function (input, array, config) { |
| 3370 |
config._isPm = config._locale.isPM(input); |
| 3371 |
config._meridiem = input; |
| 3372 |
}); |
| 3373 |
addParseToken(['h', 'hh'], function (input, array, config) { |
| 3374 |
array[HOUR] = toInt(input); |
| 3375 |
getParsingFlags(config).bigHour = true; |
| 3376 |
}); |
| 3377 |
addParseToken('hmm', function (input, array, config) { |
| 3378 |
var pos = input.length - 2; |
| 3379 |
array[HOUR] = toInt(input.substr(0, pos)); |
| 3380 |
array[MINUTE] = toInt(input.substr(pos)); |
| 3381 |
getParsingFlags(config).bigHour = true; |
| 3382 |
}); |
| 3383 |
addParseToken('hmmss', function (input, array, config) { |
| 3384 |
var pos1 = input.length - 4; |
| 3385 |
var pos2 = input.length - 2; |
| 3386 |
array[HOUR] = toInt(input.substr(0, pos1)); |
| 3387 |
array[MINUTE] = toInt(input.substr(pos1, 2)); |
| 3388 |
array[SECOND] = toInt(input.substr(pos2)); |
| 3389 |
getParsingFlags(config).bigHour = true; |
| 3390 |
}); |
| 3391 |
addParseToken('Hmm', function (input, array, config) { |
| 3392 |
var pos = input.length - 2; |
| 3393 |
array[HOUR] = toInt(input.substr(0, pos)); |
| 3394 |
array[MINUTE] = toInt(input.substr(pos)); |
| 3395 |
}); |
| 3396 |
addParseToken('Hmmss', function (input, array, config) { |
| 3397 |
var pos1 = input.length - 4; |
| 3398 |
var pos2 = input.length - 2; |
| 3399 |
array[HOUR] = toInt(input.substr(0, pos1)); |
| 3400 |
array[MINUTE] = toInt(input.substr(pos1, 2)); |
| 3401 |
array[SECOND] = toInt(input.substr(pos2)); |
| 3402 |
}); |
| 3403 |
|
| 3404 |
// LOCALES |
| 3405 |
|
| 3406 |
function localeIsPM (input) { |
| 3407 |
// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays |
| 3408 |
// Using charAt should be more compatible. |
| 3409 |
return ((input + '').toLowerCase().charAt(0) === 'p'); |
| 3410 |
} |
| 3411 |
|
| 3412 |
var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; |
| 3413 |
function localeMeridiem (hours, minutes, isLower) { |
| 3414 |
if (hours > 11) { |
| 3415 |
return isLower ? 'pm' : 'PM'; |
| 3416 |
} else { |
| 3417 |
return isLower ? 'am' : 'AM'; |
| 3418 |
} |
| 3419 |
} |
| 3420 |
|
| 3421 |
|
| 3422 |
// MOMENTS |
| 3423 |
|
| 3424 |
// Setting the hour should keep the time, because the user explicitly |
| 3425 |
// specified which hour he wants. So trying to maintain the same hour (in |
| 3426 |
// a new timezone) makes sense. Adding/subtracting hours does not follow |
| 3427 |
// this rule. |
| 3428 |
var getSetHour = makeGetSet('Hours', true); |
| 3429 |
|
| 3430 |
// months |
| 3431 |
// week |
| 3432 |
// weekdays |
| 3433 |
// meridiem |
| 3434 |
var baseConfig = { |
| 3435 |
calendar: defaultCalendar, |
| 3436 |
longDateFormat: defaultLongDateFormat, |
| 3437 |
invalidDate: defaultInvalidDate, |
| 3438 |
ordinal: defaultOrdinal, |
| 3439 |
dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, |
| 3440 |
relativeTime: defaultRelativeTime, |
| 3441 |
|
| 3442 |
months: defaultLocaleMonths, |
| 3443 |
monthsShort: defaultLocaleMonthsShort, |
| 3444 |
|
| 3445 |
week: defaultLocaleWeek, |
| 3446 |
|
| 3447 |
weekdays: defaultLocaleWeekdays, |
| 3448 |
weekdaysMin: defaultLocaleWeekdaysMin, |
| 3449 |
weekdaysShort: defaultLocaleWeekdaysShort, |
| 3450 |
|
| 3451 |
meridiemParse: defaultLocaleMeridiemParse |
| 3452 |
}; |
| 3453 |
|
| 3454 |
// internal storage for locale config files |
| 3455 |
var locales = {}; |
| 3456 |
var localeFamilies = {}; |
| 3457 |
var globalLocale; |
| 3458 |
|
| 3459 |
function normalizeLocale(key) { |
| 3460 |
return key ? key.toLowerCase().replace('_', '-') : key; |
| 3461 |
} |
| 3462 |
|
| 3463 |
// pick the locale from the array |
| 3464 |
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each |
| 3465 |
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root |
| 3466 |
function chooseLocale(names) { |
| 3467 |
var i = 0, j, next, locale, split; |
| 3468 |
|
| 3469 |
while (i < names.length) { |
| 3470 |
split = normalizeLocale(names[i]).split('-'); |
| 3471 |
j = split.length; |
| 3472 |
next = normalizeLocale(names[i + 1]); |
| 3473 |
next = next ? next.split('-') : null; |
| 3474 |
while (j > 0) { |
| 3475 |
locale = loadLocale(split.slice(0, j).join('-')); |
| 3476 |
if (locale) { |
| 3477 |
return locale; |
| 3478 |
} |
| 3479 |
if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { |
| 3480 |
//the next array item is better than a shallower substring of this one |
| 3481 |
break; |
| 3482 |
} |
| 3483 |
j--; |
| 3484 |
} |
| 3485 |
i++; |
| 3486 |
} |
| 3487 |
return null; |
| 3488 |
} |
| 3489 |
|
| 3490 |
function loadLocale(name) { |
| 3491 |
var oldLocale = null; |
| 3492 |
// TODO: Find a better way to register and load all the locales in Node |
| 3493 |
if (!locales[name] && (typeof module !== 'undefined') && |
| 3494 |
module && module.exports) { |
| 3495 |
try { |
| 3496 |
oldLocale = globalLocale._abbr; |
| 3497 |
require('./locale/' + name); |
| 3498 |
// because defineLocale currently also sets the global locale, we |
| 3499 |
// want to undo that for lazy loaded locales |
| 3500 |
getSetGlobalLocale(oldLocale); |
| 3501 |
} catch (e) { } |
| 3502 |
} |
| 3503 |
return locales[name]; |
| 3504 |
} |
| 3505 |
|
| 3506 |
// This function will load locale and then set the global locale. If |
| 3507 |
// no arguments are passed in, it will simply return the current global |
| 3508 |
// locale key. |
| 3509 |
function getSetGlobalLocale (key, values) { |
| 3510 |
var data; |
| 3511 |
if (key) { |
| 3512 |
if (isUndefined(values)) { |
| 3513 |
data = getLocale(key); |
| 3514 |
} |
| 3515 |
else { |
| 3516 |
data = defineLocale(key, values); |
| 3517 |
} |
| 3518 |
|
| 3519 |
if (data) { |
| 3520 |
// moment.duration._locale = moment._locale = data; |
| 3521 |
globalLocale = data; |
| 3522 |
} |
| 3523 |
} |
| 3524 |
|
| 3525 |
return globalLocale._abbr; |
| 3526 |
} |
| 3527 |
|
| 3528 |
function defineLocale (name, config) { |
| 3529 |
if (config !== null) { |
| 3530 |
var parentConfig = baseConfig; |
| 3531 |
config.abbr = name; |
| 3532 |
if (locales[name] != null) { |
| 3533 |
deprecateSimple('defineLocaleOverride', |
| 3534 |
'use moment.updateLocale(localeName, config) to change ' + |
| 3535 |
'an existing locale. moment.defineLocale(localeName, ' + |
| 3536 |
'config) should only be used for creating a new locale ' + |
| 3537 |
'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); |
| 3538 |
parentConfig = locales[name]._config; |
| 3539 |
} else if (config.parentLocale != null) { |
| 3540 |
if (locales[config.parentLocale] != null) { |
| 3541 |
parentConfig = locales[config.parentLocale]._config; |
| 3542 |
} else { |
| 3543 |
if (!localeFamilies[config.parentLocale]) { |
| 3544 |
localeFamilies[config.parentLocale] = []; |
| 3545 |
} |
| 3546 |
localeFamilies[config.parentLocale].push({ |
| 3547 |
name: name, |
| 3548 |
config: config |
| 3549 |
}); |
| 3550 |
return null; |
| 3551 |
} |
| 3552 |
} |
| 3553 |
locales[name] = new Locale(mergeConfigs(parentConfig, config)); |
| 3554 |
|
| 3555 |
if (localeFamilies[name]) { |
| 3556 |
localeFamilies[name].forEach(function (x) { |
| 3557 |
defineLocale(x.name, x.config); |
| 3558 |
}); |
| 3559 |
} |
| 3560 |
|
| 3561 |
// backwards compat for now: also set the locale |
| 3562 |
// make sure we set the locale AFTER all child locales have been |
| 3563 |
// created, so we won't end up with the child locale set. |
| 3564 |
getSetGlobalLocale(name); |
| 3565 |
|
| 3566 |
|
| 3567 |
return locales[name]; |
| 3568 |
} else { |
| 3569 |
// useful for testing |
| 3570 |
delete locales[name]; |
| 3571 |
return null; |
| 3572 |
} |
| 3573 |
} |
| 3574 |
|
| 3575 |
function updateLocale(name, config) { |
| 3576 |
if (config != null) { |
| 3577 |
var locale, parentConfig = baseConfig; |
| 3578 |
// MERGE |
| 3579 |
if (locales[name] != null) { |
| 3580 |
parentConfig = locales[name]._config; |
| 3581 |
} |
| 3582 |
config = mergeConfigs(parentConfig, config); |
| 3583 |
locale = new Locale(config); |
| 3584 |
locale.parentLocale = locales[name]; |
| 3585 |
locales[name] = locale; |
| 3586 |
|
| 3587 |
// backwards compat for now: also set the locale |
| 3588 |
getSetGlobalLocale(name); |
| 3589 |
} else { |
| 3590 |
// pass null for config to unupdate, useful for tests |
| 3591 |
if (locales[name] != null) { |
| 3592 |
if (locales[name].parentLocale != null) { |
| 3593 |
locales[name] = locales[name].parentLocale; |
| 3594 |
} else if (locales[name] != null) { |
| 3595 |
delete locales[name]; |
| 3596 |
} |
| 3597 |
} |
| 3598 |
} |
| 3599 |
return locales[name]; |
| 3600 |
} |
| 3601 |
|
| 3602 |
// returns locale data |
| 3603 |
function getLocale (key) { |
| 3604 |
var locale; |
| 3605 |
|
| 3606 |
if (key && key._locale && key._locale._abbr) { |
| 3607 |
key = key._locale._abbr; |
| 3608 |
} |
| 3609 |
|
| 3610 |
if (!key) { |
| 3611 |
return globalLocale; |
| 3612 |
} |
| 3613 |
|
| 3614 |
if (!isArray(key)) { |
| 3615 |
//short-circuit everything else |
| 3616 |
locale = loadLocale(key); |
| 3617 |
if (locale) { |
| 3618 |
return locale; |
| 3619 |
} |
| 3620 |
key = [key]; |
| 3621 |
} |
| 3622 |
|
| 3623 |
return chooseLocale(key); |
| 3624 |
} |
| 3625 |
|
| 3626 |
function listLocales() { |
| 3627 |
return keys$1(locales); |
| 3628 |
} |
| 3629 |
|
| 3630 |
function checkOverflow (m) { |
| 3631 |
var overflow; |
| 3632 |
var a = m._a; |
| 3633 |
|
| 3634 |
if (a && getParsingFlags(m).overflow === -2) { |
| 3635 |
overflow = |
| 3636 |
a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : |
| 3637 |
a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : |
| 3638 |
a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : |
| 3639 |
a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : |
| 3640 |
a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : |
| 3641 |
a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : |
| 3642 |
-1; |
| 3643 |
|
| 3644 |
if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { |
| 3645 |
overflow = DATE; |
| 3646 |
} |
| 3647 |
if (getParsingFlags(m)._overflowWeeks && overflow === -1) { |
| 3648 |
overflow = WEEK; |
| 3649 |
} |
| 3650 |
if (getParsingFlags(m)._overflowWeekday && overflow === -1) { |
| 3651 |
overflow = WEEKDAY; |
| 3652 |
} |
| 3653 |
|
| 3654 |
getParsingFlags(m).overflow = overflow; |
| 3655 |
} |
| 3656 |
|
| 3657 |
return m; |
| 3658 |
} |
| 3659 |
|
| 3660 |
// iso 8601 regex |
| 3661 |
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) |
| 3662 |
var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; |
| 3663 |
var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; |
| 3664 |
|
| 3665 |
var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; |
| 3666 |
|
| 3667 |
var isoDates = [ |
| 3668 |
['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], |
| 3669 |
['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], |
| 3670 |
['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], |
| 3671 |
['GGGG-[W]WW', /\d{4}-W\d\d/, false], |
| 3672 |
['YYYY-DDD', /\d{4}-\d{3}/], |
| 3673 |
['YYYY-MM', /\d{4}-\d\d/, false], |
| 3674 |
['YYYYYYMMDD', /[+-]\d{10}/], |
| 3675 |
['YYYYMMDD', /\d{8}/], |
| 3676 |
// YYYYMM is NOT allowed by the standard |
| 3677 |
['GGGG[W]WWE', /\d{4}W\d{3}/], |
| 3678 |
['GGGG[W]WW', /\d{4}W\d{2}/, false], |
| 3679 |
['YYYYDDD', /\d{7}/] |
| 3680 |
]; |
| 3681 |
|
| 3682 |
// iso time formats and regexes |
| 3683 |
var isoTimes = [ |
| 3684 |
['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], |
| 3685 |
['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], |
| 3686 |
['HH:mm:ss', /\d\d:\d\d:\d\d/], |
| 3687 |
['HH:mm', /\d\d:\d\d/], |
| 3688 |
['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], |
| 3689 |
['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], |
| 3690 |
['HHmmss', /\d\d\d\d\d\d/], |
| 3691 |
['HHmm', /\d\d\d\d/], |
| 3692 |
['HH', /\d\d/] |
| 3693 |
]; |
| 3694 |
|
| 3695 |
var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; |
| 3696 |
|
| 3697 |
// date from iso format |
| 3698 |
function configFromISO(config) { |
| 3699 |
var i, l, |
| 3700 |
string = config._i, |
| 3701 |
match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), |
| 3702 |
allowTime, dateFormat, timeFormat, tzFormat; |
| 3703 |
|
| 3704 |
if (match) { |
| 3705 |
getParsingFlags(config).iso = true; |
| 3706 |
|
| 3707 |
for (i = 0, l = isoDates.length; i < l; i++) { |
| 3708 |
if (isoDates[i][1].exec(match[1])) { |
| 3709 |
dateFormat = isoDates[i][0]; |
| 3710 |
allowTime = isoDates[i][2] !== false; |
| 3711 |
break; |
| 3712 |
} |
| 3713 |
} |
| 3714 |
if (dateFormat == null) { |
| 3715 |
config._isValid = false; |
| 3716 |
return; |
| 3717 |
} |
| 3718 |
if (match[3]) { |
| 3719 |
for (i = 0, l = isoTimes.length; i < l; i++) { |
| 3720 |
if (isoTimes[i][1].exec(match[3])) { |
| 3721 |
// match[2] should be 'T' or space |
| 3722 |
timeFormat = (match[2] || ' ') + isoTimes[i][0]; |
| 3723 |
break; |
| 3724 |
} |
| 3725 |
} |
| 3726 |
if (timeFormat == null) { |
| 3727 |
config._isValid = false; |
| 3728 |
return; |
| 3729 |
} |
| 3730 |
} |
| 3731 |
if (!allowTime && timeFormat != null) { |
| 3732 |
config._isValid = false; |
| 3733 |
return; |
| 3734 |
} |
| 3735 |
if (match[4]) { |
| 3736 |
if (tzRegex.exec(match[4])) { |
| 3737 |
tzFormat = 'Z'; |
| 3738 |
} else { |
| 3739 |
config._isValid = false; |
| 3740 |
return; |
| 3741 |
} |
| 3742 |
} |
| 3743 |
config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); |
| 3744 |
configFromStringAndFormat(config); |
| 3745 |
} else { |
| 3746 |
config._isValid = false; |
| 3747 |
} |
| 3748 |
} |
| 3749 |
|
| 3750 |
// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 |
| 3751 |
var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/; |
| 3752 |
|
| 3753 |
// date and time from ref 2822 format |
| 3754 |
function configFromRFC2822(config) { |
| 3755 |
var string, match, dayFormat, |
| 3756 |
dateFormat, timeFormat, tzFormat; |
| 3757 |
var timezones = { |
| 3758 |
' GMT': ' +0000', |
| 3759 |
' EDT': ' -0400', |
| 3760 |
' EST': ' -0500', |
| 3761 |
' CDT': ' -0500', |
| 3762 |
' CST': ' -0600', |
| 3763 |
' MDT': ' -0600', |
| 3764 |
' MST': ' -0700', |
| 3765 |
' PDT': ' -0700', |
| 3766 |
' PST': ' -0800' |
| 3767 |
}; |
| 3768 |
var military = 'YXWVUTSRQPONZABCDEFGHIKLM'; |
| 3769 |
var timezone, timezoneIndex; |
| 3770 |
|
| 3771 |
string = config._i |
| 3772 |
.replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace |
| 3773 |
.replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space |
| 3774 |
.replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces |
| 3775 |
match = basicRfcRegex.exec(string); |
| 3776 |
|
| 3777 |
if (match) { |
| 3778 |
dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : ''; |
| 3779 |
dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY '); |
| 3780 |
timeFormat = 'HH:mm' + (match[4] ? ':ss' : ''); |
| 3781 |
|
| 3782 |
// TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. |
| 3783 |
if (match[1]) { // day of week given |
| 3784 |
var momentDate = new Date(match[2]); |
| 3785 |
var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()]; |
| 3786 |
|
| 3787 |
if (match[1].substr(0,3) !== momentDay) { |
| 3788 |
getParsingFlags(config).weekdayMismatch = true; |
| 3789 |
config._isValid = false; |
| 3790 |
return; |
| 3791 |
} |
| 3792 |
} |
| 3793 |
|
| 3794 |
switch (match[5].length) { |
| 3795 |
case 2: // military |
| 3796 |
if (timezoneIndex === 0) { |
| 3797 |
timezone = ' +0000'; |
| 3798 |
} else { |
| 3799 |
timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12; |
| 3800 |
timezone = ((timezoneIndex < 0) ? ' -' : ' +') + |
| 3801 |
(('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00'; |
| 3802 |
} |
| 3803 |
break; |
| 3804 |
case 4: // Zone |
| 3805 |
timezone = timezones[match[5]]; |
| 3806 |
break; |
| 3807 |
default: // UT or +/-9999 |
| 3808 |
timezone = timezones[' GMT']; |
| 3809 |
} |
| 3810 |
match[5] = timezone; |
| 3811 |
config._i = match.splice(1).join(''); |
| 3812 |
tzFormat = ' ZZ'; |
| 3813 |
config._f = dayFormat + dateFormat + timeFormat + tzFormat; |
| 3814 |
configFromStringAndFormat(config); |
| 3815 |
getParsingFlags(config).rfc2822 = true; |
| 3816 |
} else { |
| 3817 |
config._isValid = false; |
| 3818 |
} |
| 3819 |
} |
| 3820 |
|
| 3821 |
// date from iso format or fallback |
| 3822 |
function configFromString(config) { |
| 3823 |
var matched = aspNetJsonRegex.exec(config._i); |
| 3824 |
|
| 3825 |
if (matched !== null) { |
| 3826 |
config._d = new Date(+matched[1]); |
| 3827 |
return; |
| 3828 |
} |
| 3829 |
|
| 3830 |
configFromISO(config); |
| 3831 |
if (config._isValid === false) { |
| 3832 |
delete config._isValid; |
| 3833 |
} else { |
| 3834 |
return; |
| 3835 |
} |
| 3836 |
|
| 3837 |
configFromRFC2822(config); |
| 3838 |
if (config._isValid === false) { |
| 3839 |
delete config._isValid; |
| 3840 |
} else { |
| 3841 |
return; |
| 3842 |
} |
| 3843 |
|
| 3844 |
// Final attempt, use Input Fallback |
| 3845 |
hooks.createFromInputFallback(config); |
| 3846 |
} |
| 3847 |
|
| 3848 |
hooks.createFromInputFallback = deprecate( |
| 3849 |
'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + |
| 3850 |
'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + |
| 3851 |
'discouraged and will be removed in an upcoming major release. Please refer to ' + |
| 3852 |
'http://momentjs.com/guides/#/warnings/js-date/ for more info.', |
| 3853 |
function (config) { |
| 3854 |
config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); |
| 3855 |
} |
| 3856 |
); |
| 3857 |
|
| 3858 |
// Pick the first defined of two or three arguments. |
| 3859 |
function defaults(a, b, c) { |
| 3860 |
if (a != null) { |
| 3861 |
return a; |
| 3862 |
} |
| 3863 |
if (b != null) { |
| 3864 |
return b; |
| 3865 |
} |
| 3866 |
return c; |
| 3867 |
} |
| 3868 |
|
| 3869 |
function currentDateArray(config) { |
| 3870 |
// hooks is actually the exported moment object |
| 3871 |
var nowValue = new Date(hooks.now()); |
| 3872 |
if (config._useUTC) { |
| 3873 |
return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; |
| 3874 |
} |
| 3875 |
return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; |
| 3876 |
} |
| 3877 |
|
| 3878 |
// convert an array to a date. |
| 3879 |
// the array should mirror the parameters below |
| 3880 |
// note: all values past the year are optional and will default to the lowest possible value. |
| 3881 |
// [year, month, day , hour, minute, second, millisecond] |
| 3882 |
function configFromArray (config) { |
| 3883 |
var i, date, input = [], currentDate, yearToUse; |
| 3884 |
|
| 3885 |
if (config._d) { |
| 3886 |
return; |
| 3887 |
} |
| 3888 |
|
| 3889 |
currentDate = currentDateArray(config); |
| 3890 |
|
| 3891 |
//compute day of the year from weeks and weekdays |
| 3892 |
if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { |
| 3893 |
dayOfYearFromWeekInfo(config); |
| 3894 |
} |
| 3895 |
|
| 3896 |
//if the day of the year is set, figure out what it is |
| 3897 |
if (config._dayOfYear != null) { |
| 3898 |
yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); |
| 3899 |
|
| 3900 |
if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { |
| 3901 |
getParsingFlags(config)._overflowDayOfYear = true; |
| 3902 |
} |
| 3903 |
|
| 3904 |
date = createUTCDate(yearToUse, 0, config._dayOfYear); |
| 3905 |
config._a[MONTH] = date.getUTCMonth(); |
| 3906 |
config._a[DATE] = date.getUTCDate(); |
| 3907 |
} |
| 3908 |
|
| 3909 |
// Default to current date. |
| 3910 |
// * if no year, month, day of month are given, default to today |
| 3911 |
// * if day of month is given, default month and year |
| 3912 |
// * if month is given, default only year |
| 3913 |
// * if year is given, don't default anything |
| 3914 |
for (i = 0; i < 3 && config._a[i] == null; ++i) { |
| 3915 |
config._a[i] = input[i] = currentDate[i]; |
| 3916 |
} |
| 3917 |
|
| 3918 |
// Zero out whatever was not defaulted, including time |
| 3919 |
for (; i < 7; i++) { |
| 3920 |
config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; |
| 3921 |
} |
| 3922 |
|
| 3923 |
// Check for 24:00:00.000 |
| 3924 |
if (config._a[HOUR] === 24 && |
| 3925 |
config._a[MINUTE] === 0 && |
| 3926 |
config._a[SECOND] === 0 && |
| 3927 |
config._a[MILLISECOND] === 0) { |
| 3928 |
config._nextDay = true; |
| 3929 |
config._a[HOUR] = 0; |
| 3930 |
} |
| 3931 |
|
| 3932 |
config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); |
| 3933 |
// Apply timezone offset from input. The actual utcOffset can be changed |
| 3934 |
// with parseZone. |
| 3935 |
if (config._tzm != null) { |
| 3936 |
config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); |
| 3937 |
} |
| 3938 |
|
| 3939 |
if (config._nextDay) { |
| 3940 |
config._a[HOUR] = 24; |
| 3941 |
} |
| 3942 |
} |
| 3943 |
|
| 3944 |
function dayOfYearFromWeekInfo(config) { |
| 3945 |
var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; |
| 3946 |
|
| 3947 |
w = config._w; |
| 3948 |
if (w.GG != null || w.W != null || w.E != null) { |
| 3949 |
dow = 1; |
| 3950 |
doy = 4; |
| 3951 |
|
| 3952 |
// TODO: We need to take the current isoWeekYear, but that depends on |
| 3953 |
// how we interpret now (local, utc, fixed offset). So create |
| 3954 |
// a now version of current config (take local/utc/offset flags, and |
| 3955 |
// create now). |
| 3956 |
weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); |
| 3957 |
week = defaults(w.W, 1); |
| 3958 |
weekday = defaults(w.E, 1); |
| 3959 |
if (weekday < 1 || weekday > 7) { |
| 3960 |
weekdayOverflow = true; |
| 3961 |
} |
| 3962 |
} else { |
| 3963 |
dow = config._locale._week.dow; |
| 3964 |
doy = config._locale._week.doy; |
| 3965 |
|
| 3966 |
var curWeek = weekOfYear(createLocal(), dow, doy); |
| 3967 |
|
| 3968 |
weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); |
| 3969 |
|
| 3970 |
// Default to current week. |
| 3971 |
week = defaults(w.w, curWeek.week); |
| 3972 |
|
| 3973 |
if (w.d != null) { |
| 3974 |
// weekday -- low day numbers are considered next week |
| 3975 |
weekday = w.d; |
| 3976 |
if (weekday < 0 || weekday > 6) { |
| 3977 |
weekdayOverflow = true; |
| 3978 |
} |
| 3979 |
} else if (w.e != null) { |
| 3980 |
// local weekday -- counting starts from begining of week |
| 3981 |
weekday = w.e + dow; |
| 3982 |
if (w.e < 0 || w.e > 6) { |
| 3983 |
weekdayOverflow = true; |
| 3984 |
} |
| 3985 |
} else { |
| 3986 |
// default to begining of week |
| 3987 |
weekday = dow; |
| 3988 |
} |
| 3989 |
} |
| 3990 |
if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { |
| 3991 |
getParsingFlags(config)._overflowWeeks = true; |
| 3992 |
} else if (weekdayOverflow != null) { |
| 3993 |
getParsingFlags(config)._overflowWeekday = true; |
| 3994 |
} else { |
| 3995 |
temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); |
| 3996 |
config._a[YEAR] = temp.year; |
| 3997 |
config._dayOfYear = temp.dayOfYear; |
| 3998 |
} |
| 3999 |
} |
| 4000 |
|
| 4001 |
// constant that refers to the ISO standard |
| 4002 |
hooks.ISO_8601 = function () {}; |
| 4003 |
|
| 4004 |
// constant that refers to the RFC 2822 form |
| 4005 |
hooks.RFC_2822 = function () {}; |
| 4006 |
|
| 4007 |
// date from string and format string |
| 4008 |
function configFromStringAndFormat(config) { |
| 4009 |
// TODO: Move this to another part of the creation flow to prevent circular deps |
| 4010 |
if (config._f === hooks.ISO_8601) { |
| 4011 |
configFromISO(config); |
| 4012 |
return; |
| 4013 |
} |
| 4014 |
if (config._f === hooks.RFC_2822) { |
| 4015 |
configFromRFC2822(config); |
| 4016 |
return; |
| 4017 |
} |
| 4018 |
config._a = []; |
| 4019 |
getParsingFlags(config).empty = true; |
| 4020 |
|
| 4021 |
// This array is used to make a Date, either with `new Date` or `Date.UTC` |
| 4022 |
var string = '' + config._i, |
| 4023 |
i, parsedInput, tokens, token, skipped, |
| 4024 |
stringLength = string.length, |
| 4025 |
totalParsedInputLength = 0; |
| 4026 |
|
| 4027 |
tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; |
| 4028 |
|
| 4029 |
for (i = 0; i < tokens.length; i++) { |
| 4030 |
token = tokens[i]; |
| 4031 |
parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; |
| 4032 |
// console.log('token', token, 'parsedInput', parsedInput, |
| 4033 |
// 'regex', getParseRegexForToken(token, config)); |
| 4034 |
if (parsedInput) { |
| 4035 |
skipped = string.substr(0, string.indexOf(parsedInput)); |
| 4036 |
if (skipped.length > 0) { |
| 4037 |
getParsingFlags(config).unusedInput.push(skipped); |
| 4038 |
} |
| 4039 |
string = string.slice(string.indexOf(parsedInput) + parsedInput.length); |
| 4040 |
totalParsedInputLength += parsedInput.length; |
| 4041 |
} |
| 4042 |
// don't parse if it's not a known token |
| 4043 |
if (formatTokenFunctions[token]) { |
| 4044 |
if (parsedInput) { |
| 4045 |
getParsingFlags(config).empty = false; |
| 4046 |
} |
| 4047 |
else { |
| 4048 |
getParsingFlags(config).unusedTokens.push(token); |
| 4049 |
} |
| 4050 |
addTimeToArrayFromToken(token, parsedInput, config); |
| 4051 |
} |
| 4052 |
else if (config._strict && !parsedInput) { |
| 4053 |
getParsingFlags(config).unusedTokens.push(token); |
| 4054 |
} |
| 4055 |
} |
| 4056 |
|
| 4057 |
// add remaining unparsed input length to the string |
| 4058 |
getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; |
| 4059 |
if (string.length > 0) { |
| 4060 |
getParsingFlags(config).unusedInput.push(string); |
| 4061 |
} |
| 4062 |
|
| 4063 |
// clear _12h flag if hour is <= 12 |
| 4064 |
if (config._a[HOUR] <= 12 && |
| 4065 |
getParsingFlags(config).bigHour === true && |
| 4066 |
config._a[HOUR] > 0) { |
| 4067 |
getParsingFlags(config).bigHour = undefined; |
| 4068 |
} |
| 4069 |
|
| 4070 |
getParsingFlags(config).parsedDateParts = config._a.slice(0); |
| 4071 |
getParsingFlags(config).meridiem = config._meridiem; |
| 4072 |
// handle meridiem |
| 4073 |
config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); |
| 4074 |
|
| 4075 |
configFromArray(config); |
| 4076 |
checkOverflow(config); |
| 4077 |
} |
| 4078 |
|
| 4079 |
|
| 4080 |
function meridiemFixWrap (locale, hour, meridiem) { |
| 4081 |
var isPm; |
| 4082 |
|
| 4083 |
if (meridiem == null) { |
| 4084 |
// nothing to do |
| 4085 |
return hour; |
| 4086 |
} |
| 4087 |
if (locale.meridiemHour != null) { |
| 4088 |
return locale.meridiemHour(hour, meridiem); |
| 4089 |
} else if (locale.isPM != null) { |
| 4090 |
// Fallback |
| 4091 |
isPm = locale.isPM(meridiem); |
| 4092 |
if (isPm && hour < 12) { |
| 4093 |
hour += 12; |
| 4094 |
} |
| 4095 |
if (!isPm && hour === 12) { |
| 4096 |
hour = 0; |
| 4097 |
} |
| 4098 |
return hour; |
| 4099 |
} else { |
| 4100 |
// this is not supposed to happen |
| 4101 |
return hour; |
| 4102 |
} |
| 4103 |
} |
| 4104 |
|
| 4105 |
// date from string and array of format strings |
| 4106 |
function configFromStringAndArray(config) { |
| 4107 |
var tempConfig, |
| 4108 |
bestMoment, |
| 4109 |
|
| 4110 |
scoreToBeat, |
| 4111 |
i, |
| 4112 |
currentScore; |
| 4113 |
|
| 4114 |
if (config._f.length === 0) { |
| 4115 |
getParsingFlags(config).invalidFormat = true; |
| 4116 |
config._d = new Date(NaN); |
| 4117 |
return; |
| 4118 |
} |
| 4119 |
|
| 4120 |
for (i = 0; i < config._f.length; i++) { |
| 4121 |
currentScore = 0; |
| 4122 |
tempConfig = copyConfig({}, config); |
| 4123 |
if (config._useUTC != null) { |
| 4124 |
tempConfig._useUTC = config._useUTC; |
| 4125 |
} |
| 4126 |
tempConfig._f = config._f[i]; |
| 4127 |
configFromStringAndFormat(tempConfig); |
| 4128 |
|
| 4129 |
if (!isValid(tempConfig)) { |
| 4130 |
continue; |
| 4131 |
} |
| 4132 |
|
| 4133 |
// if there is any input that was not parsed add a penalty for that format |
| 4134 |
currentScore += getParsingFlags(tempConfig).charsLeftOver; |
| 4135 |
|
| 4136 |
//or tokens |
| 4137 |
currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; |
| 4138 |
|
| 4139 |
getParsingFlags(tempConfig).score = currentScore; |
| 4140 |
|
| 4141 |
if (scoreToBeat == null || currentScore < scoreToBeat) { |
| 4142 |
scoreToBeat = currentScore; |
| 4143 |
bestMoment = tempConfig; |
| 4144 |
} |
| 4145 |
} |
| 4146 |
|
| 4147 |
extend(config, bestMoment || tempConfig); |
| 4148 |
} |
| 4149 |
|
| 4150 |
function configFromObject(config) { |
| 4151 |
if (config._d) { |
| 4152 |
return; |
| 4153 |
} |
| 4154 |
|
| 4155 |
var i = normalizeObjectUnits(config._i); |
| 4156 |
config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { |
| 4157 |
return obj && parseInt(obj, 10); |
| 4158 |
}); |
| 4159 |
|
| 4160 |
configFromArray(config); |
| 4161 |
} |
| 4162 |
|
| 4163 |
function createFromConfig (config) { |
| 4164 |
var res = new Moment(checkOverflow(prepareConfig(config))); |
| 4165 |
if (res._nextDay) { |
| 4166 |
// Adding is smart enough around DST |
| 4167 |
res.add(1, 'd'); |
| 4168 |
res._nextDay = undefined; |
| 4169 |
} |
| 4170 |
|
| 4171 |
return res; |
| 4172 |
} |
| 4173 |
|
| 4174 |
function prepareConfig (config) { |
| 4175 |
var input = config._i, |
| 4176 |
format = config._f; |
| 4177 |
|
| 4178 |
config._locale = config._locale || getLocale(config._l); |
| 4179 |
|
| 4180 |
if (input === null || (format === undefined && input === '')) { |
| 4181 |
return createInvalid({nullInput: true}); |
| 4182 |
} |
| 4183 |
|
| 4184 |
if (typeof input === 'string') { |
| 4185 |
config._i = input = config._locale.preparse(input); |
| 4186 |
} |
| 4187 |
|
| 4188 |
if (isMoment(input)) { |
| 4189 |
return new Moment(checkOverflow(input)); |
| 4190 |
} else if (isDate(input)) { |
| 4191 |
config._d = input; |
| 4192 |
} else if (isArray(format)) { |
| 4193 |
configFromStringAndArray(config); |
| 4194 |
} else if (format) { |
| 4195 |
configFromStringAndFormat(config); |
| 4196 |
} else { |
| 4197 |
configFromInput(config); |
| 4198 |
} |
| 4199 |
|
| 4200 |
if (!isValid(config)) { |
| 4201 |
config._d = null; |
| 4202 |
} |
| 4203 |
|
| 4204 |
return config; |
| 4205 |
} |
| 4206 |
|
| 4207 |
function configFromInput(config) { |
| 4208 |
var input = config._i; |
| 4209 |
if (isUndefined(input)) { |
| 4210 |
config._d = new Date(hooks.now()); |
| 4211 |
} else if (isDate(input)) { |
| 4212 |
config._d = new Date(input.valueOf()); |
| 4213 |
} else if (typeof input === 'string') { |
| 4214 |
configFromString(config); |
| 4215 |
} else if (isArray(input)) { |
| 4216 |
config._a = map(input.slice(0), function (obj) { |
| 4217 |
return parseInt(obj, 10); |
| 4218 |
}); |
| 4219 |
configFromArray(config); |
| 4220 |
} else if (isObject(input)) { |
| 4221 |
configFromObject(config); |
| 4222 |
} else if (isNumber(input)) { |
| 4223 |
// from milliseconds |
| 4224 |
config._d = new Date(input); |
| 4225 |
} else { |
| 4226 |
hooks.createFromInputFallback(config); |
| 4227 |
} |
| 4228 |
} |
| 4229 |
|
| 4230 |
function createLocalOrUTC (input, format, locale, strict, isUTC) { |
| 4231 |
var c = {}; |
| 4232 |
|
| 4233 |
if (locale === true || locale === false) { |
| 4234 |
strict = locale; |
| 4235 |
locale = undefined; |
| 4236 |
} |
| 4237 |
|
| 4238 |
if ((isObject(input) && isObjectEmpty(input)) || |
| 4239 |
(isArray(input) && input.length === 0)) { |
| 4240 |
input = undefined; |
| 4241 |
} |
| 4242 |
// object construction must be done this way. |
| 4243 |
// https://github.com/moment/moment/issues/1423 |
| 4244 |
c._isAMomentObject = true; |
| 4245 |
c._useUTC = c._isUTC = isUTC; |
| 4246 |
c._l = locale; |
| 4247 |
c._i = input; |
| 4248 |
c._f = format; |
| 4249 |
c._strict = strict; |
| 4250 |
|
| 4251 |
return createFromConfig(c); |
| 4252 |
} |
| 4253 |
|
| 4254 |
function createLocal (input, format, locale, strict) { |
| 4255 |
return createLocalOrUTC(input, format, locale, strict, false); |
| 4256 |
} |
| 4257 |
|
| 4258 |
var prototypeMin = deprecate( |
| 4259 |
'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', |
| 4260 |
function () { |
| 4261 |
var other = createLocal.apply(null, arguments); |
| 4262 |
if (this.isValid() && other.isValid()) { |
| 4263 |
return other < this ? this : other; |
| 4264 |
} else { |
| 4265 |
return createInvalid(); |
| 4266 |
} |
| 4267 |
} |
| 4268 |
); |
| 4269 |
|
| 4270 |
var prototypeMax = deprecate( |
| 4271 |
'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', |
| 4272 |
function () { |
| 4273 |
var other = createLocal.apply(null, arguments); |
| 4274 |
if (this.isValid() && other.isValid()) { |
| 4275 |
return other > this ? this : other; |
| 4276 |
} else { |
| 4277 |
return createInvalid(); |
| 4278 |
} |
| 4279 |
} |
| 4280 |
); |
| 4281 |
|
| 4282 |
// Pick a moment m from moments so that m[fn](other) is true for all |
| 4283 |
// other. This relies on the function fn to be transitive. |
| 4284 |
// |
| 4285 |
// moments should either be an array of moment objects or an array, whose |
| 4286 |
// first element is an array of moment objects. |
| 4287 |
function pickBy(fn, moments) { |
| 4288 |
var res, i; |
| 4289 |
if (moments.length === 1 && isArray(moments[0])) { |
| 4290 |
moments = moments[0]; |
| 4291 |
} |
| 4292 |
if (!moments.length) { |
| 4293 |
return createLocal(); |
| 4294 |
} |
| 4295 |
res = moments[0]; |
| 4296 |
for (i = 1; i < moments.length; ++i) { |
| 4297 |
if (!moments[i].isValid() || moments[i][fn](res)) { |
| 4298 |
res = moments[i]; |
| 4299 |
} |
| 4300 |
} |
| 4301 |
return res; |
| 4302 |
} |
| 4303 |
|
| 4304 |
// TODO: Use [].sort instead? |
| 4305 |
function min () { |
| 4306 |
var args = [].slice.call(arguments, 0); |
| 4307 |
|
| 4308 |
return pickBy('isBefore', args); |
| 4309 |
} |
| 4310 |
|
| 4311 |
function max () { |
| 4312 |
var args = [].slice.call(arguments, 0); |
| 4313 |
|
| 4314 |
return pickBy('isAfter', args); |
| 4315 |
} |
| 4316 |
|
| 4317 |
var now = function () { |
| 4318 |
return Date.now ? Date.now() : +(new Date()); |
| 4319 |
}; |
| 4320 |
|
| 4321 |
var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; |
| 4322 |
|
| 4323 |
function isDurationValid(m) { |
| 4324 |
for (var key in m) { |
| 4325 |
if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key])))) { |
| 4326 |
return false; |
| 4327 |
} |
| 4328 |
} |
| 4329 |
|
| 4330 |
var unitHasDecimal = false; |
| 4331 |
for (var i = 0; i < ordering.length; ++i) { |
| 4332 |
if (m[ordering[i]]) { |
| 4333 |
if (unitHasDecimal) { |
| 4334 |
return false; // only allow non-integers for smallest unit |
| 4335 |
} |
| 4336 |
if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { |
| 4337 |
unitHasDecimal = true; |
| 4338 |
} |
| 4339 |
} |
| 4340 |
} |
| 4341 |
|
| 4342 |
return true; |
| 4343 |
} |
| 4344 |
|
| 4345 |
function isValid$1() { |
| 4346 |
return this._isValid; |
| 4347 |
} |
| 4348 |
|
| 4349 |
function createInvalid$1() { |
| 4350 |
return createDuration(NaN); |
| 4351 |
} |
| 4352 |
|
| 4353 |
function Duration (duration) { |
| 4354 |
var normalizedInput = normalizeObjectUnits(duration), |
| 4355 |
years = normalizedInput.year || 0, |
| 4356 |
quarters = normalizedInput.quarter || 0, |
| 4357 |
months = normalizedInput.month || 0, |
| 4358 |
weeks = normalizedInput.week || 0, |
| 4359 |
days = normalizedInput.day || 0, |
| 4360 |
hours = normalizedInput.hour || 0, |
| 4361 |
minutes = normalizedInput.minute || 0, |
| 4362 |
seconds = normalizedInput.second || 0, |
| 4363 |
milliseconds = normalizedInput.millisecond || 0; |
| 4364 |
|
| 4365 |
this._isValid = isDurationValid(normalizedInput); |
| 4366 |
|
| 4367 |
// representation for dateAddRemove |
| 4368 |
this._milliseconds = +milliseconds + |
| 4369 |
seconds * 1e3 + // 1000 |
| 4370 |
minutes * 6e4 + // 1000 * 60 |
| 4371 |
hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 |
| 4372 |
// Because of dateAddRemove treats 24 hours as different from a |
| 4373 |
// day when working around DST, we need to store them separately |
| 4374 |
this._days = +days + |
| 4375 |
weeks * 7; |
| 4376 |
// It is impossible translate months into days without knowing |
| 4377 |
// which months you are are talking about, so we have to store |
| 4378 |
// it separately. |
| 4379 |
this._months = +months + |
| 4380 |
quarters * 3 + |
| 4381 |
years * 12; |
| 4382 |
|
| 4383 |
this._data = {}; |
| 4384 |
|
| 4385 |
this._locale = getLocale(); |
| 4386 |
|
| 4387 |
this._bubble(); |
| 4388 |
} |
| 4389 |
|
| 4390 |
function isDuration (obj) { |
| 4391 |
return obj instanceof Duration; |
| 4392 |
} |
| 4393 |
|
| 4394 |
function absRound (number) { |
| 4395 |
if (number < 0) { |
| 4396 |
return Math.round(-1 * number) * -1; |
| 4397 |
} else { |
| 4398 |
return Math.round(number); |
| 4399 |
} |
| 4400 |
} |
| 4401 |
|
| 4402 |
// FORMATTING |
| 4403 |
|
| 4404 |
function offset (token, separator) { |
| 4405 |
addFormatToken(token, 0, 0, function () { |
| 4406 |
var offset = this.utcOffset(); |
| 4407 |
var sign = '+'; |
| 4408 |
if (offset < 0) { |
| 4409 |
offset = -offset; |
| 4410 |
sign = '-'; |
| 4411 |
} |
| 4412 |
return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); |
| 4413 |
}); |
| 4414 |
} |
| 4415 |
|
| 4416 |
offset('Z', ':'); |
| 4417 |
offset('ZZ', ''); |
| 4418 |
|
| 4419 |
// PARSING |
| 4420 |
|
| 4421 |
addRegexToken('Z', matchShortOffset); |
| 4422 |
addRegexToken('ZZ', matchShortOffset); |
| 4423 |
addParseToken(['Z', 'ZZ'], function (input, array, config) { |
| 4424 |
config._useUTC = true; |
| 4425 |
config._tzm = offsetFromString(matchShortOffset, input); |
| 4426 |
}); |
| 4427 |
|
| 4428 |
// HELPERS |
| 4429 |
|
| 4430 |
// timezone chunker |
| 4431 |
// '+10:00' > ['10', '00'] |
| 4432 |
// '-1530' > ['-15', '30'] |
| 4433 |
var chunkOffset = /([\+\-]|\d\d)/gi; |
| 4434 |
|
| 4435 |
function offsetFromString(matcher, string) { |
| 4436 |
var matches = (string || '').match(matcher); |
| 4437 |
|
| 4438 |
if (matches === null) { |
| 4439 |
return null; |
| 4440 |
} |
| 4441 |
|
| 4442 |
var chunk = matches[matches.length - 1] || []; |
| 4443 |
var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; |
| 4444 |
var minutes = +(parts[1] * 60) + toInt(parts[2]); |
| 4445 |
|
| 4446 |
return minutes === 0 ? |
| 4447 |
0 : |
| 4448 |
parts[0] === '+' ? minutes : -minutes; |
| 4449 |
} |
| 4450 |
|
| 4451 |
// Return a moment from input, that is local/utc/zone equivalent to model. |
| 4452 |
function cloneWithOffset(input, model) { |
| 4453 |
var res, diff; |
| 4454 |
if (model._isUTC) { |
| 4455 |
res = model.clone(); |
| 4456 |
diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); |
| 4457 |
// Use low-level api, because this fn is low-level api. |
| 4458 |
res._d.setTime(res._d.valueOf() + diff); |
| 4459 |
hooks.updateOffset(res, false); |
| 4460 |
return res; |
| 4461 |
} else { |
| 4462 |
return createLocal(input).local(); |
| 4463 |
} |
| 4464 |
} |
| 4465 |
|
| 4466 |
function getDateOffset (m) { |
| 4467 |
// On Firefox.24 Date#getTimezoneOffset returns a floating point. |
| 4468 |
// https://github.com/moment/moment/pull/1871 |
| 4469 |
return -Math.round(m._d.getTimezoneOffset() / 15) * 15; |
| 4470 |
} |
| 4471 |
|
| 4472 |
// HOOKS |
| 4473 |
|
| 4474 |
// This function will be called whenever a moment is mutated. |
| 4475 |
// It is intended to keep the offset in sync with the timezone. |
| 4476 |
hooks.updateOffset = function () {}; |
| 4477 |
|
| 4478 |
// MOMENTS |
| 4479 |
|
| 4480 |
// keepLocalTime = true means only change the timezone, without |
| 4481 |
// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> |
| 4482 |
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset |
| 4483 |
// +0200, so we adjust the time as needed, to be valid. |
| 4484 |
// |
| 4485 |
// Keeping the time actually adds/subtracts (one hour) |
| 4486 |
// from the actual represented time. That is why we call updateOffset |
| 4487 |
// a second time. In case it wants us to change the offset again |
| 4488 |
// _changeInProgress == true case, then we have to adjust, because |
| 4489 |
// there is no such time in the given timezone. |
| 4490 |
function getSetOffset (input, keepLocalTime, keepMinutes) { |
| 4491 |
var offset = this._offset || 0, |
| 4492 |
localAdjust; |
| 4493 |
if (!this.isValid()) { |
| 4494 |
return input != null ? this : NaN; |
| 4495 |
} |
| 4496 |
if (input != null) { |
| 4497 |
if (typeof input === 'string') { |
| 4498 |
input = offsetFromString(matchShortOffset, input); |
| 4499 |
if (input === null) { |
| 4500 |
return this; |
| 4501 |
} |
| 4502 |
} else if (Math.abs(input) < 16 && !keepMinutes) { |
| 4503 |
input = input * 60; |
| 4504 |
} |
| 4505 |
if (!this._isUTC && keepLocalTime) { |
| 4506 |
localAdjust = getDateOffset(this); |
| 4507 |
} |
| 4508 |
this._offset = input; |
| 4509 |
this._isUTC = true; |
| 4510 |
if (localAdjust != null) { |
| 4511 |
this.add(localAdjust, 'm'); |
| 4512 |
} |
| 4513 |
if (offset !== input) { |
| 4514 |
if (!keepLocalTime || this._changeInProgress) { |
| 4515 |
addSubtract(this, createDuration(input - offset, 'm'), 1, false); |
| 4516 |
} else if (!this._changeInProgress) { |
| 4517 |
this._changeInProgress = true; |
| 4518 |
hooks.updateOffset(this, true); |
| 4519 |
this._changeInProgress = null; |
| 4520 |
} |
| 4521 |
} |
| 4522 |
return this; |
| 4523 |
} else { |
| 4524 |
return this._isUTC ? offset : getDateOffset(this); |
| 4525 |
} |
| 4526 |
} |
| 4527 |
|
| 4528 |
function getSetZone (input, keepLocalTime) { |
| 4529 |
if (input != null) { |
| 4530 |
if (typeof input !== 'string') { |
| 4531 |
input = -input; |
| 4532 |
} |
| 4533 |
|
| 4534 |
this.utcOffset(input, keepLocalTime); |
| 4535 |
|
| 4536 |
return this; |
| 4537 |
} else { |
| 4538 |
return -this.utcOffset(); |
| 4539 |
} |
| 4540 |
} |
| 4541 |
|
| 4542 |
function setOffsetToUTC (keepLocalTime) { |
| 4543 |
return this.utcOffset(0, keepLocalTime); |
| 4544 |
} |
| 4545 |
|
| 4546 |
function setOffsetToLocal (keepLocalTime) { |
| 4547 |
if (this._isUTC) { |
| 4548 |
this.utcOffset(0, keepLocalTime); |
| 4549 |
this._isUTC = false; |
| 4550 |
|
| 4551 |
if (keepLocalTime) { |
| 4552 |
this.subtract(getDateOffset(this), 'm'); |
| 4553 |
} |
| 4554 |
} |
| 4555 |
return this; |
| 4556 |
} |
| 4557 |
|
| 4558 |
function setOffsetToParsedOffset () { |
| 4559 |
if (this._tzm != null) { |
| 4560 |
this.utcOffset(this._tzm, false, true); |
| 4561 |
} else if (typeof this._i === 'string') { |
| 4562 |
var tZone = offsetFromString(matchOffset, this._i); |
| 4563 |
if (tZone != null) { |
| 4564 |
this.utcOffset(tZone); |
| 4565 |
} |
| 4566 |
else { |
| 4567 |
this.utcOffset(0, true); |
| 4568 |
} |
| 4569 |
} |
| 4570 |
return this; |
| 4571 |
} |
| 4572 |
|
| 4573 |
function hasAlignedHourOffset (input) { |
| 4574 |
if (!this.isValid()) { |
| 4575 |
return false; |
| 4576 |
} |
| 4577 |
input = input ? createLocal(input).utcOffset() : 0; |
| 4578 |
|
| 4579 |
return (this.utcOffset() - input) % 60 === 0; |
| 4580 |
} |
| 4581 |
|
| 4582 |
function isDaylightSavingTime () { |
| 4583 |
return ( |
| 4584 |
this.utcOffset() > this.clone().month(0).utcOffset() || |
| 4585 |
this.utcOffset() > this.clone().month(5).utcOffset() |
| 4586 |
); |
| 4587 |
} |
| 4588 |
|
| 4589 |
function isDaylightSavingTimeShifted () { |
| 4590 |
if (!isUndefined(this._isDSTShifted)) { |
| 4591 |
return this._isDSTShifted; |
| 4592 |
} |
| 4593 |
|
| 4594 |
var c = {}; |
| 4595 |
|
| 4596 |
copyConfig(c, this); |
| 4597 |
c = prepareConfig(c); |
| 4598 |
|
| 4599 |
if (c._a) { |
| 4600 |
var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); |
| 4601 |
this._isDSTShifted = this.isValid() && |
| 4602 |
compareArrays(c._a, other.toArray()) > 0; |
| 4603 |
} else { |
| 4604 |
this._isDSTShifted = false; |
| 4605 |
} |
| 4606 |
|
| 4607 |
return this._isDSTShifted; |
| 4608 |
} |
| 4609 |
|
| 4610 |
function isLocal () { |
| 4611 |
return this.isValid() ? !this._isUTC : false; |
| 4612 |
} |
| 4613 |
|
| 4614 |
function isUtcOffset () { |
| 4615 |
return this.isValid() ? this._isUTC : false; |
| 4616 |
} |
| 4617 |
|
| 4618 |
function isUtc () { |
| 4619 |
return this.isValid() ? this._isUTC && this._offset === 0 : false; |
| 4620 |
} |
| 4621 |
|
| 4622 |
// ASP.NET json date format regex |
| 4623 |
var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; |
| 4624 |
|
| 4625 |
// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html |
| 4626 |
// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere |
| 4627 |
// and further modified to allow for strings containing both week and day |
| 4628 |
var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; |
| 4629 |
|
| 4630 |
function createDuration (input, key) { |
| 4631 |
var duration = input, |
| 4632 |
// matching against regexp is expensive, do it on demand |
| 4633 |
match = null, |
| 4634 |
sign, |
| 4635 |
ret, |
| 4636 |
diffRes; |
| 4637 |
|
| 4638 |
if (isDuration(input)) { |
| 4639 |
duration = { |
| 4640 |
ms : input._milliseconds, |
| 4641 |
d : input._days, |
| 4642 |
M : input._months |
| 4643 |
}; |
| 4644 |
} else if (isNumber(input)) { |
| 4645 |
duration = {}; |
| 4646 |
if (key) { |
| 4647 |
duration[key] = input; |
| 4648 |
} else { |
| 4649 |
duration.milliseconds = input; |
| 4650 |
} |
| 4651 |
} else if (!!(match = aspNetRegex.exec(input))) { |
| 4652 |
sign = (match[1] === '-') ? -1 : 1; |
| 4653 |
duration = { |
| 4654 |
y : 0, |
| 4655 |
d : toInt(match[DATE]) * sign, |
| 4656 |
h : toInt(match[HOUR]) * sign, |
| 4657 |
m : toInt(match[MINUTE]) * sign, |
| 4658 |
s : toInt(match[SECOND]) * sign, |
| 4659 |
ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match |
| 4660 |
}; |
| 4661 |
} else if (!!(match = isoRegex.exec(input))) { |
| 4662 |
sign = (match[1] === '-') ? -1 : 1; |
| 4663 |
duration = { |
| 4664 |
y : parseIso(match[2], sign), |
| 4665 |
M : parseIso(match[3], sign), |
| 4666 |
w : parseIso(match[4], sign), |
| 4667 |
d : parseIso(match[5], sign), |
| 4668 |
h : parseIso(match[6], sign), |
| 4669 |
m : parseIso(match[7], sign), |
| 4670 |
s : parseIso(match[8], sign) |
| 4671 |
}; |
| 4672 |
} else if (duration == null) {// checks for null or undefined |
| 4673 |
duration = {}; |
| 4674 |
} else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { |
| 4675 |
diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); |
| 4676 |
|
| 4677 |
duration = {}; |
| 4678 |
duration.ms = diffRes.milliseconds; |
| 4679 |
duration.M = diffRes.months; |
| 4680 |
} |
| 4681 |
|
| 4682 |
ret = new Duration(duration); |
| 4683 |
|
| 4684 |
if (isDuration(input) && hasOwnProp(input, '_locale')) { |
| 4685 |
ret._locale = input._locale; |
| 4686 |
} |
| 4687 |
|
| 4688 |
return ret; |
| 4689 |
} |
| 4690 |
|
| 4691 |
createDuration.fn = Duration.prototype; |
| 4692 |
createDuration.invalid = createInvalid$1; |
| 4693 |
|
| 4694 |
function parseIso (inp, sign) { |
| 4695 |
// We'd normally use ~~inp for this, but unfortunately it also |
| 4696 |
// converts floats to ints. |
| 4697 |
// inp may be undefined, so careful calling replace on it. |
| 4698 |
var res = inp && parseFloat(inp.replace(',', '.')); |
| 4699 |
// apply sign while we're at it |
| 4700 |
return (isNaN(res) ? 0 : res) * sign; |
| 4701 |
} |
| 4702 |
|
| 4703 |
function positiveMomentsDifference(base, other) { |
| 4704 |
var res = {milliseconds: 0, months: 0}; |
| 4705 |
|
| 4706 |
res.months = other.month() - base.month() + |
| 4707 |
(other.year() - base.year()) * 12; |
| 4708 |
if (base.clone().add(res.months, 'M').isAfter(other)) { |
| 4709 |
--res.months; |
| 4710 |
} |
| 4711 |
|
| 4712 |
res.milliseconds = +other - +(base.clone().add(res.months, 'M')); |
| 4713 |
|
| 4714 |
return res; |
| 4715 |
} |
| 4716 |
|
| 4717 |
function momentsDifference(base, other) { |
| 4718 |
var res; |
| 4719 |
if (!(base.isValid() && other.isValid())) { |
| 4720 |
return {milliseconds: 0, months: 0}; |
| 4721 |
} |
| 4722 |
|
| 4723 |
other = cloneWithOffset(other, base); |
| 4724 |
if (base.isBefore(other)) { |
| 4725 |
res = positiveMomentsDifference(base, other); |
| 4726 |
} else { |
| 4727 |
res = positiveMomentsDifference(other, base); |
| 4728 |
res.milliseconds = -res.milliseconds; |
| 4729 |
res.months = -res.months; |
| 4730 |
} |
| 4731 |
|
| 4732 |
return res; |
| 4733 |
} |
| 4734 |
|
| 4735 |
// TODO: remove 'name' arg after deprecation is removed |
| 4736 |
function createAdder(direction, name) { |
| 4737 |
return function (val, period) { |
| 4738 |
var dur, tmp; |
| 4739 |
//invert the arguments, but complain about it |
| 4740 |
if (period !== null && !isNaN(+period)) { |
| 4741 |
deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + |
| 4742 |
'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); |
| 4743 |
tmp = val; val = period; period = tmp; |
| 4744 |
} |
| 4745 |
|
| 4746 |
val = typeof val === 'string' ? +val : val; |
| 4747 |
dur = createDuration(val, period); |
| 4748 |
addSubtract(this, dur, direction); |
| 4749 |
return this; |
| 4750 |
}; |
| 4751 |
} |
| 4752 |
|
| 4753 |
function addSubtract (mom, duration, isAdding, updateOffset) { |
| 4754 |
var milliseconds = duration._milliseconds, |
| 4755 |
days = absRound(duration._days), |
| 4756 |
months = absRound(duration._months); |
| 4757 |
|
| 4758 |
if (!mom.isValid()) { |
| 4759 |
// No op |
| 4760 |
return; |
| 4761 |
} |
| 4762 |
|
| 4763 |
updateOffset = updateOffset == null ? true : updateOffset; |
| 4764 |
|
| 4765 |
if (milliseconds) { |
| 4766 |
mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); |
| 4767 |
} |
| 4768 |
if (days) { |
| 4769 |
set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); |
| 4770 |
} |
| 4771 |
if (months) { |
| 4772 |
setMonth(mom, get(mom, 'Month') + months * isAdding); |
| 4773 |
} |
| 4774 |
if (updateOffset) { |
| 4775 |
hooks.updateOffset(mom, days || months); |
| 4776 |
} |
| 4777 |
} |
| 4778 |
|
| 4779 |
var add = createAdder(1, 'add'); |
| 4780 |
var subtract = createAdder(-1, 'subtract'); |
| 4781 |
|
| 4782 |
function getCalendarFormat(myMoment, now) { |
| 4783 |
var diff = myMoment.diff(now, 'days', true); |
| 4784 |
return diff < -6 ? 'sameElse' : |
| 4785 |
diff < -1 ? 'lastWeek' : |
| 4786 |
diff < 0 ? 'lastDay' : |
| 4787 |
diff < 1 ? 'sameDay' : |
| 4788 |
diff < 2 ? 'nextDay' : |
| 4789 |
diff < 7 ? 'nextWeek' : 'sameElse'; |
| 4790 |
} |
| 4791 |
|
| 4792 |
function calendar$1 (time, formats) { |
| 4793 |
// We want to compare the start of today, vs this. |
| 4794 |
// Getting start-of-today depends on whether we're local/utc/offset or not. |
| 4795 |
var now = time || createLocal(), |
| 4796 |
sod = cloneWithOffset(now, this).startOf('day'), |
| 4797 |
format = hooks.calendarFormat(this, sod) || 'sameElse'; |
| 4798 |
|
| 4799 |
var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); |
| 4800 |
|
| 4801 |
return this.format(output || this.localeData().calendar(format, this, createLocal(now))); |
| 4802 |
} |
| 4803 |
|
| 4804 |
function clone () { |
| 4805 |
return new Moment(this); |
| 4806 |
} |
| 4807 |
|
| 4808 |
function isAfter (input, units) { |
| 4809 |
var localInput = isMoment(input) ? input : createLocal(input); |
| 4810 |
if (!(this.isValid() && localInput.isValid())) { |
| 4811 |
return false; |
| 4812 |
} |
| 4813 |
units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); |
| 4814 |
if (units === 'millisecond') { |
| 4815 |
return this.valueOf() > localInput.valueOf(); |
| 4816 |
} else { |
| 4817 |
return localInput.valueOf() < this.clone().startOf(units).valueOf(); |
| 4818 |
} |
| 4819 |
} |
| 4820 |
|
| 4821 |
function isBefore (input, units) { |
| 4822 |
var localInput = isMoment(input) ? input : createLocal(input); |
| 4823 |
if (!(this.isValid() && localInput.isValid())) { |
| 4824 |
return false; |
| 4825 |
} |
| 4826 |
units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); |
| 4827 |
if (units === 'millisecond') { |
| 4828 |
return this.valueOf() < localInput.valueOf(); |
| 4829 |
} else { |
| 4830 |
return this.clone().endOf(units).valueOf() < localInput.valueOf(); |
| 4831 |
} |
| 4832 |
} |
| 4833 |
|
| 4834 |
function isBetween (from, to, units, inclusivity) { |
| 4835 |
inclusivity = inclusivity || '()'; |
| 4836 |
return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && |
| 4837 |
(inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); |
| 4838 |
} |
| 4839 |
|
| 4840 |
function isSame (input, units) { |
| 4841 |
var localInput = isMoment(input) ? input : createLocal(input), |
| 4842 |
inputMs; |
| 4843 |
if (!(this.isValid() && localInput.isValid())) { |
| 4844 |
return false; |
| 4845 |
} |
| 4846 |
units = normalizeUnits(units || 'millisecond'); |
| 4847 |
if (units === 'millisecond') { |
| 4848 |
return this.valueOf() === localInput.valueOf(); |
| 4849 |
} else { |
| 4850 |
inputMs = localInput.valueOf(); |
| 4851 |
return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); |
| 4852 |
} |
| 4853 |
} |
| 4854 |
|
| 4855 |
function isSameOrAfter (input, units) { |
| 4856 |
return this.isSame(input, units) || this.isAfter(input,units); |
| 4857 |
} |
| 4858 |
|
| 4859 |
function isSameOrBefore (input, units) { |
| 4860 |
return this.isSame(input, units) || this.isBefore(input,units); |
| 4861 |
} |
| 4862 |
|
| 4863 |
function diff (input, units, asFloat) { |
| 4864 |
var that, |
| 4865 |
zoneDelta, |
| 4866 |
delta, output; |
| 4867 |
|
| 4868 |
if (!this.isValid()) { |
| 4869 |
return NaN; |
| 4870 |
} |
| 4871 |
|
| 4872 |
that = cloneWithOffset(input, this); |
| 4873 |
|
| 4874 |
if (!that.isValid()) { |
| 4875 |
return NaN; |
| 4876 |
} |
| 4877 |
|
| 4878 |
zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; |
| 4879 |
|
| 4880 |
units = normalizeUnits(units); |
| 4881 |
|
| 4882 |
if (units === 'year' || units === 'month' || units === 'quarter') { |
| 4883 |
output = monthDiff(this, that); |
| 4884 |
if (units === 'quarter') { |
| 4885 |
output = output / 3; |
| 4886 |
} else if (units === 'year') { |
| 4887 |
output = output / 12; |
| 4888 |
} |
| 4889 |
} else { |
| 4890 |
delta = this - that; |
| 4891 |
output = units === 'second' ? delta / 1e3 : // 1000 |
| 4892 |
units === 'minute' ? delta / 6e4 : // 1000 * 60 |
| 4893 |
units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 |
| 4894 |
units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst |
| 4895 |
units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst |
| 4896 |
delta; |
| 4897 |
} |
| 4898 |
return asFloat ? output : absFloor(output); |
| 4899 |
} |
| 4900 |
|
| 4901 |
function monthDiff (a, b) { |
| 4902 |
// difference in months |
| 4903 |
var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), |
| 4904 |
// b is in (anchor - 1 month, anchor + 1 month) |
| 4905 |
anchor = a.clone().add(wholeMonthDiff, 'months'), |
| 4906 |
anchor2, adjust; |
| 4907 |
|
| 4908 |
if (b - anchor < 0) { |
| 4909 |
anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); |
| 4910 |
// linear across the month |
| 4911 |
adjust = (b - anchor) / (anchor - anchor2); |
| 4912 |
} else { |
| 4913 |
anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); |
| 4914 |
// linear across the month |
| 4915 |
adjust = (b - anchor) / (anchor2 - anchor); |
| 4916 |
} |
| 4917 |
|
| 4918 |
//check for negative zero, return zero if negative zero |
| 4919 |
return -(wholeMonthDiff + adjust) || 0; |
| 4920 |
} |
| 4921 |
|
| 4922 |
hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; |
| 4923 |
hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; |
| 4924 |
|
| 4925 |
function toString () { |
| 4926 |
return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); |
| 4927 |
} |
| 4928 |
|
| 4929 |
function toISOString() { |
| 4930 |
if (!this.isValid()) { |
| 4931 |
return null; |
| 4932 |
} |
| 4933 |
var m = this.clone().utc(); |
| 4934 |
if (m.year() < 0 || m.year() > 9999) { |
| 4935 |
return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); |
| 4936 |
} |
| 4937 |
if (isFunction(Date.prototype.toISOString)) { |
| 4938 |
// native implementation is ~50x faster, use it when we can |
| 4939 |
return this.toDate().toISOString(); |
| 4940 |
} |
| 4941 |
return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); |
| 4942 |
} |
| 4943 |
|
| 4944 |
/** |
| 4945 |
* Return a human readable representation of a moment that can |
| 4946 |
* also be evaluated to get a new moment which is the same |
| 4947 |
* |
| 4948 |
* @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects |
| 4949 |
*/ |
| 4950 |
function inspect () { |
| 4951 |
if (!this.isValid()) { |
| 4952 |
return 'moment.invalid(/* ' + this._i + ' */)'; |
| 4953 |
} |
| 4954 |
var func = 'moment'; |
| 4955 |
var zone = ''; |
| 4956 |
if (!this.isLocal()) { |
| 4957 |
func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; |
| 4958 |
zone = 'Z'; |
| 4959 |
} |
| 4960 |
var prefix = '[' + func + '("]'; |
| 4961 |
var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; |
| 4962 |
var datetime = '-MM-DD[T]HH:mm:ss.SSS'; |
| 4963 |
var suffix = zone + '[")]'; |
| 4964 |
|
| 4965 |
return this.format(prefix + year + datetime + suffix); |
| 4966 |
} |
| 4967 |
|
| 4968 |
function format (inputString) { |
| 4969 |
if (!inputString) { |
| 4970 |
inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; |
| 4971 |
} |
| 4972 |
var output = formatMoment(this, inputString); |
| 4973 |
return this.localeData().postformat(output); |
| 4974 |
} |
| 4975 |
|
| 4976 |
function from (time, withoutSuffix) { |
| 4977 |
if (this.isValid() && |
| 4978 |
((isMoment(time) && time.isValid()) || |
| 4979 |
createLocal(time).isValid())) { |
| 4980 |
return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); |
| 4981 |
} else { |
| 4982 |
return this.localeData().invalidDate(); |
| 4983 |
} |
| 4984 |
} |
| 4985 |
|
| 4986 |
function fromNow (withoutSuffix) { |
| 4987 |
return this.from(createLocal(), withoutSuffix); |
| 4988 |
} |
| 4989 |
|
| 4990 |
function to (time, withoutSuffix) { |
| 4991 |
if (this.isValid() && |
| 4992 |
((isMoment(time) && time.isValid()) || |
| 4993 |
createLocal(time).isValid())) { |
| 4994 |
return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); |
| 4995 |
} else { |
| 4996 |
return this.localeData().invalidDate(); |
| 4997 |
} |
| 4998 |
} |
| 4999 |
|
| 5000 |
function toNow (withoutSuffix) { |
| 5001 |
return this.to(createLocal(), withoutSuffix); |
| 5002 |
} |
| 5003 |
|
| 5004 |
// If passed a locale key, it will set the locale for this |
| 5005 |
// instance. Otherwise, it will return the locale configuration |
| 5006 |
// variables for this instance. |
| 5007 |
function locale (key) { |
| 5008 |
var newLocaleData; |
| 5009 |
|
| 5010 |
if (key === undefined) { |
| 5011 |
return this._locale._abbr; |
| 5012 |
} else { |
| 5013 |
newLocaleData = getLocale(key); |
| 5014 |
if (newLocaleData != null) { |
| 5015 |
this._locale = newLocaleData; |
| 5016 |
} |
| 5017 |
return this; |
| 5018 |
} |
| 5019 |
} |
| 5020 |
|
| 5021 |
var lang = deprecate( |
| 5022 |
'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', |
| 5023 |
function (key) { |
| 5024 |
if (key === undefined) { |
| 5025 |
return this.localeData(); |
| 5026 |
} else { |
| 5027 |
return this.locale(key); |
| 5028 |
} |
| 5029 |
} |
| 5030 |
); |
| 5031 |
|
| 5032 |
function localeData () { |
| 5033 |
return this._locale; |
| 5034 |
} |
| 5035 |
|
| 5036 |
function startOf (units) { |
| 5037 |
units = normalizeUnits(units); |
| 5038 |
// the following switch intentionally omits break keywords |
| 5039 |
// to utilize falling through the cases. |
| 5040 |
switch (units) { |
| 5041 |
case 'year': |
| 5042 |
this.month(0); |
| 5043 |
/* falls through */ |
| 5044 |
case 'quarter': |
| 5045 |
case 'month': |
| 5046 |
this.date(1); |
| 5047 |
/* falls through */ |
| 5048 |
case 'week': |
| 5049 |
case 'isoWeek': |
| 5050 |
case 'day': |
| 5051 |
case 'date': |
| 5052 |
this.hours(0); |
| 5053 |
/* falls through */ |
| 5054 |
case 'hour': |
| 5055 |
this.minutes(0); |
| 5056 |
/* falls through */ |
| 5057 |
case 'minute': |
| 5058 |
this.seconds(0); |
| 5059 |
/* falls through */ |
| 5060 |
case 'second': |
| 5061 |
this.milliseconds(0); |
| 5062 |
} |
| 5063 |
|
| 5064 |
// weeks are a special case |
| 5065 |
if (units === 'week') { |
| 5066 |
this.weekday(0); |
| 5067 |
} |
| 5068 |
if (units === 'isoWeek') { |
| 5069 |
this.isoWeekday(1); |
| 5070 |
} |
| 5071 |
|
| 5072 |
// quarters are also special |
| 5073 |
if (units === 'quarter') { |
| 5074 |
this.month(Math.floor(this.month() / 3) * 3); |
| 5075 |
} |
| 5076 |
|
| 5077 |
return this; |
| 5078 |
} |
| 5079 |
|
| 5080 |
function endOf (units) { |
| 5081 |
units = normalizeUnits(units); |
| 5082 |
if (units === undefined || units === 'millisecond') { |
| 5083 |
return this; |
| 5084 |
} |
| 5085 |
|
| 5086 |
// 'date' is an alias for 'day', so it should be considered as such. |
| 5087 |
if (units === 'date') { |
| 5088 |
units = 'day'; |
| 5089 |
} |
| 5090 |
|
| 5091 |
return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); |
| 5092 |
} |
| 5093 |
|
| 5094 |
function valueOf () { |
| 5095 |
return this._d.valueOf() - ((this._offset || 0) * 60000); |
| 5096 |
} |
| 5097 |
|
| 5098 |
function unix () { |
| 5099 |
return Math.floor(this.valueOf() / 1000); |
| 5100 |
} |
| 5101 |
|
| 5102 |
function toDate () { |
| 5103 |
return new Date(this.valueOf()); |
| 5104 |
} |
| 5105 |
|
| 5106 |
function toArray () { |
| 5107 |
var m = this; |
| 5108 |
return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; |
| 5109 |
} |
| 5110 |
|
| 5111 |
function toObject () { |
| 5112 |
var m = this; |
| 5113 |
return { |
| 5114 |
years: m.year(), |
| 5115 |
months: m.month(), |
| 5116 |
date: m.date(), |
| 5117 |
hours: m.hours(), |
| 5118 |
minutes: m.minutes(), |
| 5119 |
seconds: m.seconds(), |
| 5120 |
milliseconds: m.milliseconds() |
| 5121 |
}; |
| 5122 |
} |
| 5123 |
|
| 5124 |
function toJSON () { |
| 5125 |
// new Date(NaN).toJSON() === null |
| 5126 |
return this.isValid() ? this.toISOString() : null; |
| 5127 |
} |
| 5128 |
|
| 5129 |
function isValid$2 () { |
| 5130 |
return isValid(this); |
| 5131 |
} |
| 5132 |
|
| 5133 |
function parsingFlags () { |
| 5134 |
return extend({}, getParsingFlags(this)); |
| 5135 |
} |
| 5136 |
|
| 5137 |
function invalidAt () { |
| 5138 |
return getParsingFlags(this).overflow; |
| 5139 |
} |
| 5140 |
|
| 5141 |
function creationData() { |
| 5142 |
return { |
| 5143 |
input: this._i, |
| 5144 |
format: this._f, |
| 5145 |
locale: this._locale, |
| 5146 |
isUTC: this._isUTC, |
| 5147 |
strict: this._strict |
| 5148 |
}; |
| 5149 |
} |
| 5150 |
|
| 5151 |
// FORMATTING |
| 5152 |
|
| 5153 |
addFormatToken(0, ['gg', 2], 0, function () { |
| 5154 |
return this.weekYear() % 100; |
| 5155 |
}); |
| 5156 |
|
| 5157 |
addFormatToken(0, ['GG', 2], 0, function () { |
| 5158 |
return this.isoWeekYear() % 100; |
| 5159 |
}); |
| 5160 |
|
| 5161 |
function addWeekYearFormatToken (token, getter) { |
| 5162 |
addFormatToken(0, [token, token.length], 0, getter); |
| 5163 |
} |
| 5164 |
|
| 5165 |
addWeekYearFormatToken('gggg', 'weekYear'); |
| 5166 |
addWeekYearFormatToken('ggggg', 'weekYear'); |
| 5167 |
addWeekYearFormatToken('GGGG', 'isoWeekYear'); |
| 5168 |
addWeekYearFormatToken('GGGGG', 'isoWeekYear'); |
| 5169 |
|
| 5170 |
// ALIASES |
| 5171 |
|
| 5172 |
addUnitAlias('weekYear', 'gg'); |
| 5173 |
addUnitAlias('isoWeekYear', 'GG'); |
| 5174 |
|
| 5175 |
// PRIORITY |
| 5176 |
|
| 5177 |
addUnitPriority('weekYear', 1); |
| 5178 |
addUnitPriority('isoWeekYear', 1); |
| 5179 |
|
| 5180 |
|
| 5181 |
// PARSING |
| 5182 |
|
| 5183 |
addRegexToken('G', matchSigned); |
| 5184 |
addRegexToken('g', matchSigned); |
| 5185 |
addRegexToken('GG', match1to2, match2); |
| 5186 |
addRegexToken('gg', match1to2, match2); |
| 5187 |
addRegexToken('GGGG', match1to4, match4); |
| 5188 |
addRegexToken('gggg', match1to4, match4); |
| 5189 |
addRegexToken('GGGGG', match1to6, match6); |
| 5190 |
addRegexToken('ggggg', match1to6, match6); |
| 5191 |
|
| 5192 |
addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { |
| 5193 |
week[token.substr(0, 2)] = toInt(input); |
| 5194 |
}); |
| 5195 |
|
| 5196 |
addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { |
| 5197 |
week[token] = hooks.parseTwoDigitYear(input); |
| 5198 |
}); |
| 5199 |
|
| 5200 |
// MOMENTS |
| 5201 |
|
| 5202 |
function getSetWeekYear (input) { |
| 5203 |
return getSetWeekYearHelper.call(this, |
| 5204 |
input, |
| 5205 |
this.week(), |
| 5206 |
this.weekday(), |
| 5207 |
this.localeData()._week.dow, |
| 5208 |
this.localeData()._week.doy); |
| 5209 |
} |
| 5210 |
|
| 5211 |
function getSetISOWeekYear (input) { |
| 5212 |
return getSetWeekYearHelper.call(this, |
| 5213 |
input, this.isoWeek(), this.isoWeekday(), 1, 4); |
| 5214 |
} |
| 5215 |
|
| 5216 |
function getISOWeeksInYear () { |
| 5217 |
return weeksInYear(this.year(), 1, 4); |
| 5218 |
} |
| 5219 |
|
| 5220 |
function getWeeksInYear () { |
| 5221 |
var weekInfo = this.localeData()._week; |
| 5222 |
return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); |
| 5223 |
} |
| 5224 |
|
| 5225 |
function getSetWeekYearHelper(input, week, weekday, dow, doy) { |
| 5226 |
var weeksTarget; |
| 5227 |
if (input == null) { |
| 5228 |
return weekOfYear(this, dow, doy).year; |
| 5229 |
} else { |
| 5230 |
weeksTarget = weeksInYear(input, dow, doy); |
| 5231 |
if (week > weeksTarget) { |
| 5232 |
week = weeksTarget; |
| 5233 |
} |
| 5234 |
return setWeekAll.call(this, input, week, weekday, dow, doy); |
| 5235 |
} |
| 5236 |
} |
| 5237 |
|
| 5238 |
function setWeekAll(weekYear, week, weekday, dow, doy) { |
| 5239 |
var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), |
| 5240 |
date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); |
| 5241 |
|
| 5242 |
this.year(date.getUTCFullYear()); |
| 5243 |
this.month(date.getUTCMonth()); |
| 5244 |
this.date(date.getUTCDate()); |
| 5245 |
return this; |
| 5246 |
} |
| 5247 |
|
| 5248 |
// FORMATTING |
| 5249 |
|
| 5250 |
addFormatToken('Q', 0, 'Qo', 'quarter'); |
| 5251 |
|
| 5252 |
// ALIASES |
| 5253 |
|
| 5254 |
addUnitAlias('quarter', 'Q'); |
| 5255 |
|
| 5256 |
// PRIORITY |
| 5257 |
|
| 5258 |
addUnitPriority('quarter', 7); |
| 5259 |
|
| 5260 |
// PARSING |
| 5261 |
|
| 5262 |
addRegexToken('Q', match1); |
| 5263 |
addParseToken('Q', function (input, array) { |
| 5264 |
array[MONTH] = (toInt(input) - 1) * 3; |
| 5265 |
}); |
| 5266 |
|
| 5267 |
// MOMENTS |
| 5268 |
|
| 5269 |
function getSetQuarter (input) { |
| 5270 |
return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); |
| 5271 |
} |
| 5272 |
|
| 5273 |
// FORMATTING |
| 5274 |
|
| 5275 |
addFormatToken('D', ['DD', 2], 'Do', 'date'); |
| 5276 |
|
| 5277 |
// ALIASES |
| 5278 |
|
| 5279 |
addUnitAlias('date', 'D'); |
| 5280 |
|
| 5281 |
// PRIOROITY |
| 5282 |
addUnitPriority('date', 9); |
| 5283 |
|
| 5284 |
// PARSING |
| 5285 |
|
| 5286 |
addRegexToken('D', match1to2); |
| 5287 |
addRegexToken('DD', match1to2, match2); |
| 5288 |
addRegexToken('Do', function (isStrict, locale) { |
| 5289 |
// TODO: Remove "ordinalParse" fallback in next major release. |
| 5290 |
return isStrict ? |
| 5291 |
(locale._dayOfMonthOrdinalParse || locale._ordinalParse) : |
| 5292 |
locale._dayOfMonthOrdinalParseLenient; |
| 5293 |
}); |
| 5294 |
|
| 5295 |
addParseToken(['D', 'DD'], DATE); |
| 5296 |
addParseToken('Do', function (input, array) { |
| 5297 |
array[DATE] = toInt(input.match(match1to2)[0], 10); |
| 5298 |
}); |
| 5299 |
|
| 5300 |
// MOMENTS |
| 5301 |
|
| 5302 |
var getSetDayOfMonth = makeGetSet('Date', true); |
| 5303 |
|
| 5304 |
// FORMATTING |
| 5305 |
|
| 5306 |
addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); |
| 5307 |
|
| 5308 |
// ALIASES |
| 5309 |
|
| 5310 |
addUnitAlias('dayOfYear', 'DDD'); |
| 5311 |
|
| 5312 |
// PRIORITY |
| 5313 |
addUnitPriority('dayOfYear', 4); |
| 5314 |
|
| 5315 |
// PARSING |
| 5316 |
|
| 5317 |
addRegexToken('DDD', match1to3); |
| 5318 |
addRegexToken('DDDD', match3); |
| 5319 |
addParseToken(['DDD', 'DDDD'], function (input, array, config) { |
| 5320 |
config._dayOfYear = toInt(input); |
| 5321 |
}); |
| 5322 |
|
| 5323 |
// HELPERS |
| 5324 |
|
| 5325 |
// MOMENTS |
| 5326 |
|
| 5327 |
function getSetDayOfYear (input) { |
| 5328 |
var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; |
| 5329 |
return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); |
| 5330 |
} |
| 5331 |
|
| 5332 |
// FORMATTING |
| 5333 |
|
| 5334 |
addFormatToken('m', ['mm', 2], 0, 'minute'); |
| 5335 |
|
| 5336 |
// ALIASES |
| 5337 |
|
| 5338 |
addUnitAlias('minute', 'm'); |
| 5339 |
|
| 5340 |
// PRIORITY |
| 5341 |
|
| 5342 |
addUnitPriority('minute', 14); |
| 5343 |
|
| 5344 |
// PARSING |
| 5345 |
|
| 5346 |
addRegexToken('m', match1to2); |
| 5347 |
addRegexToken('mm', match1to2, match2); |
| 5348 |
addParseToken(['m', 'mm'], MINUTE); |
| 5349 |
|
| 5350 |
// MOMENTS |
| 5351 |
|
| 5352 |
var getSetMinute = makeGetSet('Minutes', false); |
| 5353 |
|
| 5354 |
// FORMATTING |
| 5355 |
|
| 5356 |
addFormatToken('s', ['ss', 2], 0, 'second'); |
| 5357 |
|
| 5358 |
// ALIASES |
| 5359 |
|
| 5360 |
addUnitAlias('second', 's'); |
| 5361 |
|
| 5362 |
// PRIORITY |
| 5363 |
|
| 5364 |
addUnitPriority('second', 15); |
| 5365 |
|
| 5366 |
// PARSING |
| 5367 |
|
| 5368 |
addRegexToken('s', match1to2); |
| 5369 |
addRegexToken('ss', match1to2, match2); |
| 5370 |
addParseToken(['s', 'ss'], SECOND); |
| 5371 |
|
| 5372 |
// MOMENTS |
| 5373 |
|
| 5374 |
var getSetSecond = makeGetSet('Seconds', false); |
| 5375 |
|
| 5376 |
// FORMATTING |
| 5377 |
|
| 5378 |
addFormatToken('S', 0, 0, function () { |
| 5379 |
return ~~(this.millisecond() / 100); |
| 5380 |
}); |
| 5381 |
|
| 5382 |
addFormatToken(0, ['SS', 2], 0, function () { |
| 5383 |
return ~~(this.millisecond() / 10); |
| 5384 |
}); |
| 5385 |
|
| 5386 |
addFormatToken(0, ['SSS', 3], 0, 'millisecond'); |
| 5387 |
addFormatToken(0, ['SSSS', 4], 0, function () { |
| 5388 |
return this.millisecond() * 10; |
| 5389 |
}); |
| 5390 |
addFormatToken(0, ['SSSSS', 5], 0, function () { |
| 5391 |
return this.millisecond() * 100; |
| 5392 |
}); |
| 5393 |
addFormatToken(0, ['SSSSSS', 6], 0, function () { |
| 5394 |
return this.millisecond() * 1000; |
| 5395 |
}); |
| 5396 |
addFormatToken(0, ['SSSSSSS', 7], 0, function () { |
| 5397 |
return this.millisecond() * 10000; |
| 5398 |
}); |
| 5399 |
addFormatToken(0, ['SSSSSSSS', 8], 0, function () { |
| 5400 |
return this.millisecond() * 100000; |
| 5401 |
}); |
| 5402 |
addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { |
| 5403 |
return this.millisecond() * 1000000; |
| 5404 |
}); |
| 5405 |
|
| 5406 |
|
| 5407 |
// ALIASES |
| 5408 |
|
| 5409 |
addUnitAlias('millisecond', 'ms'); |
| 5410 |
|
| 5411 |
// PRIORITY |
| 5412 |
|
| 5413 |
addUnitPriority('millisecond', 16); |
| 5414 |
|
| 5415 |
// PARSING |
| 5416 |
|
| 5417 |
addRegexToken('S', match1to3, match1); |
| 5418 |
addRegexToken('SS', match1to3, match2); |
| 5419 |
addRegexToken('SSS', match1to3, match3); |
| 5420 |
|
| 5421 |
var token; |
| 5422 |
for (token = 'SSSS'; token.length <= 9; token += 'S') { |
| 5423 |
addRegexToken(token, matchUnsigned); |
| 5424 |
} |
| 5425 |
|
| 5426 |
function parseMs(input, array) { |
| 5427 |
array[MILLISECOND] = toInt(('0.' + input) * 1000); |
| 5428 |
} |
| 5429 |
|
| 5430 |
for (token = 'S'; token.length <= 9; token += 'S') { |
| 5431 |
addParseToken(token, parseMs); |
| 5432 |
} |
| 5433 |
// MOMENTS |
| 5434 |
|
| 5435 |
var getSetMillisecond = makeGetSet('Milliseconds', false); |
| 5436 |
|
| 5437 |
// FORMATTING |
| 5438 |
|
| 5439 |
addFormatToken('z', 0, 0, 'zoneAbbr'); |
| 5440 |
addFormatToken('zz', 0, 0, 'zoneName'); |
| 5441 |
|
| 5442 |
// MOMENTS |
| 5443 |
|
| 5444 |
function getZoneAbbr () { |
| 5445 |
return this._isUTC ? 'UTC' : ''; |
| 5446 |
} |
| 5447 |
|
| 5448 |
function getZoneName () { |
| 5449 |
return this._isUTC ? 'Coordinated Universal Time' : ''; |
| 5450 |
} |
| 5451 |
|
| 5452 |
var proto = Moment.prototype; |
| 5453 |
|
| 5454 |
proto.add = add; |
| 5455 |
proto.calendar = calendar$1; |
| 5456 |
proto.clone = clone; |
| 5457 |
proto.diff = diff; |
| 5458 |
proto.endOf = endOf; |
| 5459 |
proto.format = format; |
| 5460 |
proto.from = from; |
| 5461 |
proto.fromNow = fromNow; |
| 5462 |
proto.to = to; |
| 5463 |
proto.toNow = toNow; |
| 5464 |
proto.get = stringGet; |
| 5465 |
proto.invalidAt = invalidAt; |
| 5466 |
proto.isAfter = isAfter; |
| 5467 |
proto.isBefore = isBefore; |
| 5468 |
proto.isBetween = isBetween; |
| 5469 |
proto.isSame = isSame; |
| 5470 |
proto.isSameOrAfter = isSameOrAfter; |
| 5471 |
proto.isSameOrBefore = isSameOrBefore; |
| 5472 |
proto.isValid = isValid$2; |
| 5473 |
proto.lang = lang; |
| 5474 |
proto.locale = locale; |
| 5475 |
proto.localeData = localeData; |
| 5476 |
proto.max = prototypeMax; |
| 5477 |
proto.min = prototypeMin; |
| 5478 |
proto.parsingFlags = parsingFlags; |
| 5479 |
proto.set = stringSet; |
| 5480 |
proto.startOf = startOf; |
| 5481 |
proto.subtract = subtract; |
| 5482 |
proto.toArray = toArray; |
| 5483 |
proto.toObject = toObject; |
| 5484 |
proto.toDate = toDate; |
| 5485 |
proto.toISOString = toISOString; |
| 5486 |
proto.inspect = inspect; |
| 5487 |
proto.toJSON = toJSON; |
| 5488 |
proto.toString = toString; |
| 5489 |
proto.unix = unix; |
| 5490 |
proto.valueOf = valueOf; |
| 5491 |
proto.creationData = creationData; |
| 5492 |
|
| 5493 |
// Year |
| 5494 |
proto.year = getSetYear; |
| 5495 |
proto.isLeapYear = getIsLeapYear; |
| 5496 |
|
| 5497 |
// Week Year |
| 5498 |
proto.weekYear = getSetWeekYear; |
| 5499 |
proto.isoWeekYear = getSetISOWeekYear; |
| 5500 |
|
| 5501 |
// Quarter |
| 5502 |
proto.quarter = proto.quarters = getSetQuarter; |
| 5503 |
|
| 5504 |
// Month |
| 5505 |
proto.month = getSetMonth; |
| 5506 |
proto.daysInMonth = getDaysInMonth; |
| 5507 |
|
| 5508 |
// Week |
| 5509 |
proto.week = proto.weeks = getSetWeek; |
| 5510 |
proto.isoWeek = proto.isoWeeks = getSetISOWeek; |
| 5511 |
proto.weeksInYear = getWeeksInYear; |
| 5512 |
proto.isoWeeksInYear = getISOWeeksInYear; |
| 5513 |
|
| 5514 |
// Day |
| 5515 |
proto.date = getSetDayOfMonth; |
| 5516 |
proto.day = proto.days = getSetDayOfWeek; |
| 5517 |
proto.weekday = getSetLocaleDayOfWeek; |
| 5518 |
proto.isoWeekday = getSetISODayOfWeek; |
| 5519 |
proto.dayOfYear = getSetDayOfYear; |
| 5520 |
|
| 5521 |
// Hour |
| 5522 |
proto.hour = proto.hours = getSetHour; |
| 5523 |
|
| 5524 |
// Minute |
| 5525 |
proto.minute = proto.minutes = getSetMinute; |
| 5526 |
|
| 5527 |
// Second |
| 5528 |
proto.second = proto.seconds = getSetSecond; |
| 5529 |
|
| 5530 |
// Millisecond |
| 5531 |
proto.millisecond = proto.milliseconds = getSetMillisecond; |
| 5532 |
|
| 5533 |
// Offset |
| 5534 |
proto.utcOffset = getSetOffset; |
| 5535 |
proto.utc = setOffsetToUTC; |
| 5536 |
proto.local = setOffsetToLocal; |
| 5537 |
proto.parseZone = setOffsetToParsedOffset; |
| 5538 |
proto.hasAlignedHourOffset = hasAlignedHourOffset; |
| 5539 |
proto.isDST = isDaylightSavingTime; |
| 5540 |
proto.isLocal = isLocal; |
| 5541 |
proto.isUtcOffset = isUtcOffset; |
| 5542 |
proto.isUtc = isUtc; |
| 5543 |
proto.isUTC = isUtc; |
| 5544 |
|
| 5545 |
// Timezone |
| 5546 |
proto.zoneAbbr = getZoneAbbr; |
| 5547 |
proto.zoneName = getZoneName; |
| 5548 |
|
| 5549 |
// Deprecations |
| 5550 |
proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); |
| 5551 |
proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); |
| 5552 |
proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); |
| 5553 |
proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); |
| 5554 |
proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); |
| 5555 |
|
| 5556 |
function createUnix (input) { |
| 5557 |
return createLocal(input * 1000); |
| 5558 |
} |
| 5559 |
|
| 5560 |
function createInZone () { |
| 5561 |
return createLocal.apply(null, arguments).parseZone(); |
| 5562 |
} |
| 5563 |
|
| 5564 |
function preParsePostFormat (string) { |
| 5565 |
return string; |
| 5566 |
} |
| 5567 |
|
| 5568 |
var proto$1 = Locale.prototype; |
| 5569 |
|
| 5570 |
proto$1.calendar = calendar; |
| 5571 |
proto$1.longDateFormat = longDateFormat; |
| 5572 |
proto$1.invalidDate = invalidDate; |
| 5573 |
proto$1.ordinal = ordinal; |
| 5574 |
proto$1.preparse = preParsePostFormat; |
| 5575 |
proto$1.postformat = preParsePostFormat; |
| 5576 |
proto$1.relativeTime = relativeTime; |
| 5577 |
proto$1.pastFuture = pastFuture; |
| 5578 |
proto$1.set = set; |
| 5579 |
|
| 5580 |
// Month |
| 5581 |
proto$1.months = localeMonths; |
| 5582 |
proto$1.monthsShort = localeMonthsShort; |
| 5583 |
proto$1.monthsParse = localeMonthsParse; |
| 5584 |
proto$1.monthsRegex = monthsRegex; |
| 5585 |
proto$1.monthsShortRegex = monthsShortRegex; |
| 5586 |
|
| 5587 |
// Week |
| 5588 |
proto$1.week = localeWeek; |
| 5589 |
proto$1.firstDayOfYear = localeFirstDayOfYear; |
| 5590 |
proto$1.firstDayOfWeek = localeFirstDayOfWeek; |
| 5591 |
|
| 5592 |
// Day of Week |
| 5593 |
proto$1.weekdays = localeWeekdays; |
| 5594 |
proto$1.weekdaysMin = localeWeekdaysMin; |
| 5595 |
proto$1.weekdaysShort = localeWeekdaysShort; |
| 5596 |
proto$1.weekdaysParse = localeWeekdaysParse; |
| 5597 |
|
| 5598 |
proto$1.weekdaysRegex = weekdaysRegex; |
| 5599 |
proto$1.weekdaysShortRegex = weekdaysShortRegex; |
| 5600 |
proto$1.weekdaysMinRegex = weekdaysMinRegex; |
| 5601 |
|
| 5602 |
// Hours |
| 5603 |
proto$1.isPM = localeIsPM; |
| 5604 |
proto$1.meridiem = localeMeridiem; |
| 5605 |
|
| 5606 |
function get$1 (format, index, field, setter) { |
| 5607 |
var locale = getLocale(); |
| 5608 |
var utc = createUTC().set(setter, index); |
| 5609 |
return locale[field](utc, format); |
| 5610 |
} |
| 5611 |
|
| 5612 |
function listMonthsImpl (format, index, field) { |
| 5613 |
if (isNumber(format)) { |
| 5614 |
index = format; |
| 5615 |
format = undefined; |
| 5616 |
} |
| 5617 |
|
| 5618 |
format = format || ''; |
| 5619 |
|
| 5620 |
if (index != null) { |
| 5621 |
return get$1(format, index, field, 'month'); |
| 5622 |
} |
| 5623 |
|
| 5624 |
var i; |
| 5625 |
var out = []; |
| 5626 |
for (i = 0; i < 12; i++) { |
| 5627 |
out[i] = get$1(format, i, field, 'month'); |
| 5628 |
} |
| 5629 |
return out; |
| 5630 |
} |
| 5631 |
|
| 5632 |
// () |
| 5633 |
// (5) |
| 5634 |
// (fmt, 5) |
| 5635 |
// (fmt) |
| 5636 |
// (true) |
| 5637 |
// (true, 5) |
| 5638 |
// (true, fmt, 5) |
| 5639 |
// (true, fmt) |
| 5640 |
function listWeekdaysImpl (localeSorted, format, index, field) { |
| 5641 |
if (typeof localeSorted === 'boolean') { |
| 5642 |
if (isNumber(format)) { |
| 5643 |
index = format; |
| 5644 |
format = undefined; |
| 5645 |
} |
| 5646 |
|
| 5647 |
format = format || ''; |
| 5648 |
} else { |
| 5649 |
format = localeSorted; |
| 5650 |
index = format; |
| 5651 |
localeSorted = false; |
| 5652 |
|
| 5653 |
if (isNumber(format)) { |
| 5654 |
index = format; |
| 5655 |
format = undefined; |
| 5656 |
} |
| 5657 |
|
| 5658 |
format = format || ''; |
| 5659 |
} |
| 5660 |
|
| 5661 |
var locale = getLocale(), |
| 5662 |
shift = localeSorted ? locale._week.dow : 0; |
| 5663 |
|
| 5664 |
if (index != null) { |
| 5665 |
return get$1(format, (index + shift) % 7, field, 'day'); |
| 5666 |
} |
| 5667 |
|
| 5668 |
var i; |
| 5669 |
var out = []; |
| 5670 |
for (i = 0; i < 7; i++) { |
| 5671 |
out[i] = get$1(format, (i + shift) % 7, field, 'day'); |
| 5672 |
} |
| 5673 |
return out; |
| 5674 |
} |
| 5675 |
|
| 5676 |
function listMonths (format, index) { |
| 5677 |
return listMonthsImpl(format, index, 'months'); |
| 5678 |
} |
| 5679 |
|
| 5680 |
function listMonthsShort (format, index) { |
| 5681 |
return listMonthsImpl(format, index, 'monthsShort'); |
| 5682 |
} |
| 5683 |
|
| 5684 |
function listWeekdays (localeSorted, format, index) { |
| 5685 |
return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); |
| 5686 |
} |
| 5687 |
|
| 5688 |
function listWeekdaysShort (localeSorted, format, index) { |
| 5689 |
return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); |
| 5690 |
} |
| 5691 |
|
| 5692 |
function listWeekdaysMin (localeSorted, format, index) { |
| 5693 |
return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); |
| 5694 |
} |
| 5695 |
|
| 5696 |
getSetGlobalLocale('en', { |
| 5697 |
dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, |
| 5698 |
ordinal : function (number) { |
| 5699 |
var b = number % 10, |
| 5700 |
output = (toInt(number % 100 / 10) === 1) ? 'th' : |
| 5701 |
(b === 1) ? 'st' : |
| 5702 |
(b === 2) ? 'nd' : |
| 5703 |
(b === 3) ? 'rd' : 'th'; |
| 5704 |
return number + output; |
| 5705 |
} |
| 5706 |
}); |
| 5707 |
|
| 5708 |
// Side effect imports |
| 5709 |
hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); |
| 5710 |
hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); |
| 5711 |
|
| 5712 |
var mathAbs = Math.abs; |
| 5713 |
|
| 5714 |
function abs () { |
| 5715 |
var data = this._data; |
| 5716 |
|
| 5717 |
this._milliseconds = mathAbs(this._milliseconds); |
| 5718 |
this._days = mathAbs(this._days); |
| 5719 |
this._months = mathAbs(this._months); |
| 5720 |
|
| 5721 |
data.milliseconds = mathAbs(data.milliseconds); |
| 5722 |
data.seconds = mathAbs(data.seconds); |
| 5723 |
data.minutes = mathAbs(data.minutes); |
| 5724 |
data.hours = mathAbs(data.hours); |
| 5725 |
data.months = mathAbs(data.months); |
| 5726 |
data.years = mathAbs(data.years); |
| 5727 |
|
| 5728 |
return this; |
| 5729 |
} |
| 5730 |
|
| 5731 |
function addSubtract$1 (duration, input, value, direction) { |
| 5732 |
var other = createDuration(input, value); |
| 5733 |
|
| 5734 |
duration._milliseconds += direction * other._milliseconds; |
| 5735 |
duration._days += direction * other._days; |
| 5736 |
duration._months += direction * other._months; |
| 5737 |
|
| 5738 |
return duration._bubble(); |
| 5739 |
} |
| 5740 |
|
| 5741 |
// supports only 2.0-style add(1, 's') or add(duration) |
| 5742 |
function add$1 (input, value) { |
| 5743 |
return addSubtract$1(this, input, value, 1); |
| 5744 |
} |
| 5745 |
|
| 5746 |
// supports only 2.0-style subtract(1, 's') or subtract(duration) |
| 5747 |
function subtract$1 (input, value) { |
| 5748 |
return addSubtract$1(this, input, value, -1); |
| 5749 |
} |
| 5750 |
|
| 5751 |
function absCeil (number) { |
| 5752 |
if (number < 0) { |
| 5753 |
return Math.floor(number); |
| 5754 |
} else { |
| 5755 |
return Math.ceil(number); |
| 5756 |
} |
| 5757 |
} |
| 5758 |
|
| 5759 |
function bubble () { |
| 5760 |
var milliseconds = this._milliseconds; |
| 5761 |
var days = this._days; |
| 5762 |
var months = this._months; |
| 5763 |
var data = this._data; |
| 5764 |
var seconds, minutes, hours, years, monthsFromDays; |
| 5765 |
|
| 5766 |
// if we have a mix of positive and negative values, bubble down first |
| 5767 |
// check: https://github.com/moment/moment/issues/2166 |
| 5768 |
if (!((milliseconds >= 0 && days >= 0 && months >= 0) || |
| 5769 |
(milliseconds <= 0 && days <= 0 && months <= 0))) { |
| 5770 |
milliseconds += absCeil(monthsToDays(months) + days) * 864e5; |
| 5771 |
days = 0; |
| 5772 |
months = 0; |
| 5773 |
} |
| 5774 |
|
| 5775 |
// The following code bubbles up values, see the tests for |
| 5776 |
// examples of what that means. |
| 5777 |
data.milliseconds = milliseconds % 1000; |
| 5778 |
|
| 5779 |
seconds = absFloor(milliseconds / 1000); |
| 5780 |
data.seconds = seconds % 60; |
| 5781 |
|
| 5782 |
minutes = absFloor(seconds / 60); |
| 5783 |
data.minutes = minutes % 60; |
| 5784 |
|
| 5785 |
hours = absFloor(minutes / 60); |
| 5786 |
data.hours = hours % 24; |
| 5787 |
|
| 5788 |
days += absFloor(hours / 24); |
| 5789 |
|
| 5790 |
// convert days to months |
| 5791 |
monthsFromDays = absFloor(daysToMonths(days)); |
| 5792 |
months += monthsFromDays; |
| 5793 |
days -= absCeil(monthsToDays(monthsFromDays)); |
| 5794 |
|
| 5795 |
// 12 months -> 1 year |
| 5796 |
years = absFloor(months / 12); |
| 5797 |
months %= 12; |
| 5798 |
|
| 5799 |
data.days = days; |
| 5800 |
data.months = months; |
| 5801 |
data.years = years; |
| 5802 |
|
| 5803 |
return this; |
| 5804 |
} |
| 5805 |
|
| 5806 |
function daysToMonths (days) { |
| 5807 |
// 400 years have 146097 days (taking into account leap year rules) |
| 5808 |
// 400 years have 12 months === 4800 |
| 5809 |
return days * 4800 / 146097; |
| 5810 |
} |
| 5811 |
|
| 5812 |
function monthsToDays (months) { |
| 5813 |
// the reverse of daysToMonths |
| 5814 |
return months * 146097 / 4800; |
| 5815 |
} |
| 5816 |
|
| 5817 |
function as (units) { |
| 5818 |
if (!this.isValid()) { |
| 5819 |
return NaN; |
| 5820 |
} |
| 5821 |
var days; |
| 5822 |
var months; |
| 5823 |
var milliseconds = this._milliseconds; |
| 5824 |
|
| 5825 |
units = normalizeUnits(units); |
| 5826 |
|
| 5827 |
if (units === 'month' || units === 'year') { |
| 5828 |
days = this._days + milliseconds / 864e5; |
| 5829 |
months = this._months + daysToMonths(days); |
| 5830 |
return units === 'month' ? months : months / 12; |
| 5831 |
} else { |
| 5832 |
// handle milliseconds separately because of floating point math errors (issue #1867) |
| 5833 |
days = this._days + Math.round(monthsToDays(this._months)); |
| 5834 |
switch (units) { |
| 5835 |
case 'week' : return days / 7 + milliseconds / 6048e5; |
| 5836 |
case 'day' : return days + milliseconds / 864e5; |
| 5837 |
case 'hour' : return days * 24 + milliseconds / 36e5; |
| 5838 |
case 'minute' : return days * 1440 + milliseconds / 6e4; |
| 5839 |
case 'second' : return days * 86400 + milliseconds / 1000; |
| 5840 |
// Math.floor prevents floating point math errors here |
| 5841 |
case 'millisecond': return Math.floor(days * 864e5) + milliseconds; |
| 5842 |
default: throw new Error('Unknown unit ' + units); |
| 5843 |
} |
| 5844 |
} |
| 5845 |
} |
| 5846 |
|
| 5847 |
// TODO: Use this.as('ms')? |
| 5848 |
function valueOf$1 () { |
| 5849 |
if (!this.isValid()) { |
| 5850 |
return NaN; |
| 5851 |
} |
| 5852 |
return ( |
| 5853 |
this._milliseconds + |
| 5854 |
this._days * 864e5 + |
| 5855 |
(this._months % 12) * 2592e6 + |
| 5856 |
toInt(this._months / 12) * 31536e6 |
| 5857 |
); |
| 5858 |
} |
| 5859 |
|
| 5860 |
function makeAs (alias) { |
| 5861 |
return function () { |
| 5862 |
return this.as(alias); |
| 5863 |
}; |
| 5864 |
} |
| 5865 |
|
| 5866 |
var asMilliseconds = makeAs('ms'); |
| 5867 |
var asSeconds = makeAs('s'); |
| 5868 |
var asMinutes = makeAs('m'); |
| 5869 |
var asHours = makeAs('h'); |
| 5870 |
var asDays = makeAs('d'); |
| 5871 |
var asWeeks = makeAs('w'); |
| 5872 |
var asMonths = makeAs('M'); |
| 5873 |
var asYears = makeAs('y'); |
| 5874 |
|
| 5875 |
function get$2 (units) { |
| 5876 |
units = normalizeUnits(units); |
| 5877 |
return this.isValid() ? this[units + 's']() : NaN; |
| 5878 |
} |
| 5879 |
|
| 5880 |
function makeGetter(name) { |
| 5881 |
return function () { |
| 5882 |
return this.isValid() ? this._data[name] : NaN; |
| 5883 |
}; |
| 5884 |
} |
| 5885 |
|
| 5886 |
var milliseconds = makeGetter('milliseconds'); |
| 5887 |
var seconds = makeGetter('seconds'); |
| 5888 |
var minutes = makeGetter('minutes'); |
| 5889 |
var hours = makeGetter('hours'); |
| 5890 |
var days = makeGetter('days'); |
| 5891 |
var months = makeGetter('months'); |
| 5892 |
var years = makeGetter('years'); |
| 5893 |
|
| 5894 |
function weeks () { |
| 5895 |
return absFloor(this.days() / 7); |
| 5896 |
} |
| 5897 |
|
| 5898 |
var round = Math.round; |
| 5899 |
var thresholds = { |
| 5900 |
ss: 44, // a few seconds to seconds |
| 5901 |
s : 45, // seconds to minute |
| 5902 |
m : 45, // minutes to hour |
| 5903 |
h : 22, // hours to day |
| 5904 |
d : 26, // days to month |
| 5905 |
M : 11 // months to year |
| 5906 |
}; |
| 5907 |
|
| 5908 |
// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize |
| 5909 |
function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { |
| 5910 |
return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); |
| 5911 |
} |
| 5912 |
|
| 5913 |
function relativeTime$1 (posNegDuration, withoutSuffix, locale) { |
| 5914 |
var duration = createDuration(posNegDuration).abs(); |
| 5915 |
var seconds = round(duration.as('s')); |
| 5916 |
var minutes = round(duration.as('m')); |
| 5917 |
var hours = round(duration.as('h')); |
| 5918 |
var days = round(duration.as('d')); |
| 5919 |
var months = round(duration.as('M')); |
| 5920 |
var years = round(duration.as('y')); |
| 5921 |
|
| 5922 |
var a = seconds <= thresholds.ss && ['s', seconds] || |
| 5923 |
seconds < thresholds.s && ['ss', seconds] || |
| 5924 |
minutes <= 1 && ['m'] || |
| 5925 |
minutes < thresholds.m && ['mm', minutes] || |
| 5926 |
hours <= 1 && ['h'] || |
| 5927 |
hours < thresholds.h && ['hh', hours] || |
| 5928 |
days <= 1 && ['d'] || |
| 5929 |
days < thresholds.d && ['dd', days] || |
| 5930 |
months <= 1 && ['M'] || |
| 5931 |
months < thresholds.M && ['MM', months] || |
| 5932 |
years <= 1 && ['y'] || ['yy', years]; |
| 5933 |
|
| 5934 |
a[2] = withoutSuffix; |
| 5935 |
a[3] = +posNegDuration > 0; |
| 5936 |
a[4] = locale; |
| 5937 |
return substituteTimeAgo.apply(null, a); |
| 5938 |
} |
| 5939 |
|
| 5940 |
// This function allows you to set the rounding function for relative time strings |
| 5941 |
function getSetRelativeTimeRounding (roundingFunction) { |
| 5942 |
if (roundingFunction === undefined) { |
| 5943 |
return round; |
| 5944 |
} |
| 5945 |
if (typeof(roundingFunction) === 'function') { |
| 5946 |
round = roundingFunction; |
| 5947 |
return true; |
| 5948 |
} |
| 5949 |
return false; |
| 5950 |
} |
| 5951 |
|
| 5952 |
// This function allows you to set a threshold for relative time strings |
| 5953 |
function getSetRelativeTimeThreshold (threshold, limit) { |
| 5954 |
if (thresholds[threshold] === undefined) { |
| 5955 |
return false; |
| 5956 |
} |
| 5957 |
if (limit === undefined) { |
| 5958 |
return thresholds[threshold]; |
| 5959 |
} |
| 5960 |
thresholds[threshold] = limit; |
| 5961 |
if (threshold === 's') { |
| 5962 |
thresholds.ss = limit - 1; |
| 5963 |
} |
| 5964 |
return true; |
| 5965 |
} |
| 5966 |
|
| 5967 |
function humanize (withSuffix) { |
| 5968 |
if (!this.isValid()) { |
| 5969 |
return this.localeData().invalidDate(); |
| 5970 |
} |
| 5971 |
|
| 5972 |
var locale = this.localeData(); |
| 5973 |
var output = relativeTime$1(this, !withSuffix, locale); |
| 5974 |
|
| 5975 |
if (withSuffix) { |
| 5976 |
output = locale.pastFuture(+this, output); |
| 5977 |
} |
| 5978 |
|
| 5979 |
return locale.postformat(output); |
| 5980 |
} |
| 5981 |
|
| 5982 |
var abs$1 = Math.abs; |
| 5983 |
|
| 5984 |
function toISOString$1() { |
| 5985 |
// for ISO strings we do not use the normal bubbling rules: |
| 5986 |
// * milliseconds bubble up until they become hours |
| 5987 |
// * days do not bubble at all |
| 5988 |
// * months bubble up until they become years |
| 5989 |
// This is because there is no context-free conversion between hours and days |
| 5990 |
// (think of clock changes) |
| 5991 |
// and also not between days and months (28-31 days per month) |
| 5992 |
if (!this.isValid()) { |
| 5993 |
return this.localeData().invalidDate(); |
| 5994 |
} |
| 5995 |
|
| 5996 |
var seconds = abs$1(this._milliseconds) / 1000; |
| 5997 |
var days = abs$1(this._days); |
| 5998 |
var months = abs$1(this._months); |
| 5999 |
var minutes, hours, years; |
| 6000 |
|
| 6001 |
// 3600 seconds -> 60 minutes -> 1 hour |
| 6002 |
minutes = absFloor(seconds / 60); |
| 6003 |
hours = absFloor(minutes / 60); |
| 6004 |
seconds %= 60; |
| 6005 |
minutes %= 60; |
| 6006 |
|
| 6007 |
// 12 months -> 1 year |
| 6008 |
years = absFloor(months / 12); |
| 6009 |
months %= 12; |
| 6010 |
|
| 6011 |
|
| 6012 |
// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js |
| 6013 |
var Y = years; |
| 6014 |
var M = months; |
| 6015 |
var D = days; |
| 6016 |
var h = hours; |
| 6017 |
var m = minutes; |
| 6018 |
var s = seconds; |
| 6019 |
var total = this.asSeconds(); |
| 6020 |
|
| 6021 |
if (!total) { |
| 6022 |
// this is the same as C#'s (Noda) and python (isodate)... |
| 6023 |
// but not other JS (goog.date) |
| 6024 |
return 'P0D'; |
| 6025 |
} |
| 6026 |
|
| 6027 |
return (total < 0 ? '-' : '') + |
| 6028 |
'P' + |
| 6029 |
(Y ? Y + 'Y' : '') + |
| 6030 |
(M ? M + 'M' : '') + |
| 6031 |
(D ? D + 'D' : '') + |
| 6032 |
((h || m || s) ? 'T' : '') + |
| 6033 |
(h ? h + 'H' : '') + |
| 6034 |
(m ? m + 'M' : '') + |
| 6035 |
(s ? s + 'S' : ''); |
| 6036 |
} |
| 6037 |
|
| 6038 |
var proto$2 = Duration.prototype; |
| 6039 |
|
| 6040 |
proto$2.isValid = isValid$1; |
| 6041 |
proto$2.abs = abs; |
| 6042 |
proto$2.add = add$1; |
| 6043 |
proto$2.subtract = subtract$1; |
| 6044 |
proto$2.as = as; |
| 6045 |
proto$2.asMilliseconds = asMilliseconds; |
| 6046 |
proto$2.asSeconds = asSeconds; |
| 6047 |
proto$2.asMinutes = asMinutes; |
| 6048 |
proto$2.asHours = asHours; |
| 6049 |
proto$2.asDays = asDays; |
| 6050 |
proto$2.asWeeks = asWeeks; |
| 6051 |
proto$2.asMonths = asMonths; |
| 6052 |
proto$2.asYears = asYears; |
| 6053 |
proto$2.valueOf = valueOf$1; |
| 6054 |
proto$2._bubble = bubble; |
| 6055 |
proto$2.get = get$2; |
| 6056 |
proto$2.milliseconds = milliseconds; |
| 6057 |
proto$2.seconds = seconds; |
| 6058 |
proto$2.minutes = minutes; |
| 6059 |
proto$2.hours = hours; |
| 6060 |
proto$2.days = days; |
| 6061 |
proto$2.weeks = weeks; |
| 6062 |
proto$2.months = months; |
| 6063 |
proto$2.years = years; |
| 6064 |
proto$2.humanize = humanize; |
| 6065 |
proto$2.toISOString = toISOString$1; |
| 6066 |
proto$2.toString = toISOString$1; |
| 6067 |
proto$2.toJSON = toISOString$1; |
| 6068 |
proto$2.locale = locale; |
| 6069 |
proto$2.localeData = localeData; |
| 6070 |
|
| 6071 |
// Deprecations |
| 6072 |
proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); |
| 6073 |
proto$2.lang = lang; |
| 6074 |
|
| 6075 |
// Side effect imports |
| 6076 |
|
| 6077 |
// FORMATTING |
| 6078 |
|
| 6079 |
addFormatToken('X', 0, 0, 'unix'); |
| 6080 |
addFormatToken('x', 0, 0, 'valueOf'); |
| 6081 |
|
| 6082 |
// PARSING |
| 6083 |
|
| 6084 |
addRegexToken('x', matchSigned); |
| 6085 |
addRegexToken('X', matchTimestamp); |
| 6086 |
addParseToken('X', function (input, array, config) { |
| 6087 |
config._d = new Date(parseFloat(input, 10) * 1000); |
| 6088 |
}); |
| 6089 |
addParseToken('x', function (input, array, config) { |
| 6090 |
config._d = new Date(toInt(input)); |
| 6091 |
}); |
| 6092 |
|
| 6093 |
// Side effect imports |
| 6094 |
|
| 6095 |
|
| 6096 |
hooks.version = '2.18.1'; |
| 6097 |
|
| 6098 |
setHookCallback(createLocal); |
| 6099 |
|
| 6100 |
hooks.fn = proto; |
| 6101 |
hooks.min = min; |
| 6102 |
hooks.max = max; |
| 6103 |
hooks.now = now; |
| 6104 |
hooks.utc = createUTC; |
| 6105 |
hooks.unix = createUnix; |
| 6106 |
hooks.months = listMonths; |
| 6107 |
hooks.isDate = isDate; |
| 6108 |
hooks.locale = getSetGlobalLocale; |
| 6109 |
hooks.invalid = createInvalid; |
| 6110 |
hooks.duration = createDuration; |
| 6111 |
hooks.isMoment = isMoment; |
| 6112 |
hooks.weekdays = listWeekdays; |
| 6113 |
hooks.parseZone = createInZone; |
| 6114 |
hooks.localeData = getLocale; |
| 6115 |
hooks.isDuration = isDuration; |
| 6116 |
hooks.monthsShort = listMonthsShort; |
| 6117 |
hooks.weekdaysMin = listWeekdaysMin; |
| 6118 |
hooks.defineLocale = defineLocale; |
| 6119 |
hooks.updateLocale = updateLocale; |
| 6120 |
hooks.locales = listLocales; |
| 6121 |
hooks.weekdaysShort = listWeekdaysShort; |
| 6122 |
hooks.normalizeUnits = normalizeUnits; |
| 6123 |
hooks.relativeTimeRounding = getSetRelativeTimeRounding; |
| 6124 |
hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; |
| 6125 |
hooks.calendarFormat = getCalendarFormat; |
| 6126 |
hooks.prototype = proto; |
| 6127 |
|
| 6128 |
return hooks; |
| 6129 |
|
| 6130 |
}))); |
| 6131 |
|
| 6132 |
},{}],7:[function(require,module,exports){ |
| 6133 |
/** |
| 6134 |
* @namespace Chart |
| 6135 |
*/ |
| 6136 |
var Chart = require(29)(); |
| 6137 |
|
| 6138 |
Chart.helpers = require(45); |
| 6139 |
|
| 6140 |
// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! |
| 6141 |
require(27)(Chart); |
| 6142 |
|
| 6143 |
Chart.defaults = require(25); |
| 6144 |
Chart.Element = require(26); |
| 6145 |
Chart.elements = require(40); |
| 6146 |
Chart.Interaction = require(28); |
| 6147 |
Chart.platform = require(48); |
| 6148 |
|
| 6149 |
require(31)(Chart); |
| 6150 |
require(22)(Chart); |
| 6151 |
require(23)(Chart); |
| 6152 |
require(24)(Chart); |
| 6153 |
require(30)(Chart); |
| 6154 |
require(33)(Chart); |
| 6155 |
require(32)(Chart); |
| 6156 |
require(35)(Chart); |
| 6157 |
|
| 6158 |
require(54)(Chart); |
| 6159 |
require(52)(Chart); |
| 6160 |
require(53)(Chart); |
| 6161 |
require(55)(Chart); |
| 6162 |
require(56)(Chart); |
| 6163 |
require(57)(Chart); |
| 6164 |
|
| 6165 |
// Controllers must be loaded after elements |
| 6166 |
// See Chart.core.datasetController.dataElementType |
| 6167 |
require(15)(Chart); |
| 6168 |
require(16)(Chart); |
| 6169 |
require(17)(Chart); |
| 6170 |
require(18)(Chart); |
| 6171 |
require(19)(Chart); |
| 6172 |
require(20)(Chart); |
| 6173 |
require(21)(Chart); |
| 6174 |
|
| 6175 |
require(8)(Chart); |
| 6176 |
require(9)(Chart); |
| 6177 |
require(10)(Chart); |
| 6178 |
require(11)(Chart); |
| 6179 |
require(12)(Chart); |
| 6180 |
require(13)(Chart); |
| 6181 |
require(14)(Chart); |
| 6182 |
|
| 6183 |
// Loading built-it plugins |
| 6184 |
var plugins = []; |
| 6185 |
|
| 6186 |
plugins.push( |
| 6187 |
require(49)(Chart), |
| 6188 |
require(50)(Chart), |
| 6189 |
require(51)(Chart) |
| 6190 |
); |
| 6191 |
|
| 6192 |
Chart.plugins.register(plugins); |
| 6193 |
|
| 6194 |
Chart.platform.initialize(); |
| 6195 |
|
| 6196 |
module.exports = Chart; |
| 6197 |
if (typeof window !== 'undefined') { |
| 6198 |
window.Chart = Chart; |
| 6199 |
} |
| 6200 |
|
| 6201 |
// DEPRECATIONS |
| 6202 |
|
| 6203 |
/** |
| 6204 |
* Provided for backward compatibility, use Chart.helpers.canvas instead. |
| 6205 |
* @namespace Chart.canvasHelpers |
| 6206 |
* @deprecated since version 2.6.0 |
| 6207 |
* @todo remove at version 3 |
| 6208 |
* @private |
| 6209 |
*/ |
| 6210 |
Chart.canvasHelpers = Chart.helpers.canvas; |
| 6211 |
|
| 6212 |
},{"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,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){ |
| 6213 |
'use strict'; |
| 6214 |
|
| 6215 |
module.exports = function(Chart) { |
| 6216 |
|
| 6217 |
Chart.Bar = function(context, config) { |
| 6218 |
config.type = 'bar'; |
| 6219 |
|
| 6220 |
return new Chart(context, config); |
| 6221 |
}; |
| 6222 |
|
| 6223 |
}; |
| 6224 |
|
| 6225 |
},{}],9:[function(require,module,exports){ |
| 6226 |
'use strict'; |
| 6227 |
|
| 6228 |
module.exports = function(Chart) { |
| 6229 |
|
| 6230 |
Chart.Bubble = function(context, config) { |
| 6231 |
config.type = 'bubble'; |
| 6232 |
return new Chart(context, config); |
| 6233 |
}; |
| 6234 |
|
| 6235 |
}; |
| 6236 |
|
| 6237 |
},{}],10:[function(require,module,exports){ |
| 6238 |
'use strict'; |
| 6239 |
|
| 6240 |
module.exports = function(Chart) { |
| 6241 |
|
| 6242 |
Chart.Doughnut = function(context, config) { |
| 6243 |
config.type = 'doughnut'; |
| 6244 |
|
| 6245 |
return new Chart(context, config); |
| 6246 |
}; |
| 6247 |
|
| 6248 |
}; |
| 6249 |
|
| 6250 |
},{}],11:[function(require,module,exports){ |
| 6251 |
'use strict'; |
| 6252 |
|
| 6253 |
module.exports = function(Chart) { |
| 6254 |
|
| 6255 |
Chart.Line = function(context, config) { |
| 6256 |
config.type = 'line'; |
| 6257 |
|
| 6258 |
return new Chart(context, config); |
| 6259 |
}; |
| 6260 |
|
| 6261 |
}; |
| 6262 |
|
| 6263 |
},{}],12:[function(require,module,exports){ |
| 6264 |
'use strict'; |
| 6265 |
|
| 6266 |
module.exports = function(Chart) { |
| 6267 |
|
| 6268 |
Chart.PolarArea = function(context, config) { |
| 6269 |
config.type = 'polarArea'; |
| 6270 |
|
| 6271 |
return new Chart(context, config); |
| 6272 |
}; |
| 6273 |
|
| 6274 |
}; |
| 6275 |
|
| 6276 |
},{}],13:[function(require,module,exports){ |
| 6277 |
'use strict'; |
| 6278 |
|
| 6279 |
module.exports = function(Chart) { |
| 6280 |
|
| 6281 |
Chart.Radar = function(context, config) { |
| 6282 |
config.type = 'radar'; |
| 6283 |
|
| 6284 |
return new Chart(context, config); |
| 6285 |
}; |
| 6286 |
|
| 6287 |
}; |
| 6288 |
|
| 6289 |
},{}],14:[function(require,module,exports){ |
| 6290 |
'use strict'; |
| 6291 |
|
| 6292 |
module.exports = function(Chart) { |
| 6293 |
Chart.Scatter = function(context, config) { |
| 6294 |
config.type = 'scatter'; |
| 6295 |
return new Chart(context, config); |
| 6296 |
}; |
| 6297 |
}; |
| 6298 |
|
| 6299 |
},{}],15:[function(require,module,exports){ |
| 6300 |
'use strict'; |
| 6301 |
|
| 6302 |
var defaults = require(25); |
| 6303 |
var elements = require(40); |
| 6304 |
var helpers = require(45); |
| 6305 |
|
| 6306 |
defaults._set('bar', { |
| 6307 |
hover: { |
| 6308 |
mode: 'label' |
| 6309 |
}, |
| 6310 |
|
| 6311 |
scales: { |
| 6312 |
xAxes: [{ |
| 6313 |
type: 'category', |
| 6314 |
|
| 6315 |
// Specific to Bar Controller |
| 6316 |
categoryPercentage: 0.8, |
| 6317 |
barPercentage: 0.9, |
| 6318 |
|
| 6319 |
// offset settings |
| 6320 |
offset: true, |
| 6321 |
|
| 6322 |
// grid line settings |
| 6323 |
gridLines: { |
| 6324 |
offsetGridLines: true |
| 6325 |
} |
| 6326 |
}], |
| 6327 |
|
| 6328 |
yAxes: [{ |
| 6329 |
type: 'linear' |
| 6330 |
}] |
| 6331 |
} |
| 6332 |
}); |
| 6333 |
|
| 6334 |
defaults._set('horizontalBar', { |
| 6335 |
hover: { |
| 6336 |
mode: 'index', |
| 6337 |
axis: 'y' |
| 6338 |
}, |
| 6339 |
|
| 6340 |
scales: { |
| 6341 |
xAxes: [{ |
| 6342 |
type: 'linear', |
| 6343 |
position: 'bottom' |
| 6344 |
}], |
| 6345 |
|
| 6346 |
yAxes: [{ |
| 6347 |
position: 'left', |
| 6348 |
type: 'category', |
| 6349 |
|
| 6350 |
// Specific to Horizontal Bar Controller |
| 6351 |
categoryPercentage: 0.8, |
| 6352 |
barPercentage: 0.9, |
| 6353 |
|
| 6354 |
// offset settings |
| 6355 |
offset: true, |
| 6356 |
|
| 6357 |
// grid line settings |
| 6358 |
gridLines: { |
| 6359 |
offsetGridLines: true |
| 6360 |
} |
| 6361 |
}] |
| 6362 |
}, |
| 6363 |
|
| 6364 |
elements: { |
| 6365 |
rectangle: { |
| 6366 |
borderSkipped: 'left' |
| 6367 |
} |
| 6368 |
}, |
| 6369 |
|
| 6370 |
tooltips: { |
| 6371 |
callbacks: { |
| 6372 |
title: function(item, data) { |
| 6373 |
// Pick first xLabel for now |
| 6374 |
var title = ''; |
| 6375 |
|
| 6376 |
if (item.length > 0) { |
| 6377 |
if (item[0].yLabel) { |
| 6378 |
title = item[0].yLabel; |
| 6379 |
} else if (data.labels.length > 0 && item[0].index < data.labels.length) { |
| 6380 |
title = data.labels[item[0].index]; |
| 6381 |
} |
| 6382 |
} |
| 6383 |
|
| 6384 |
return title; |
| 6385 |
}, |
| 6386 |
|
| 6387 |
label: function(item, data) { |
| 6388 |
var datasetLabel = data.datasets[item.datasetIndex].label || ''; |
| 6389 |
return datasetLabel + ': ' + item.xLabel; |
| 6390 |
} |
| 6391 |
}, |
| 6392 |
mode: 'index', |
| 6393 |
axis: 'y' |
| 6394 |
} |
| 6395 |
}); |
| 6396 |
|
| 6397 |
module.exports = function(Chart) { |
| 6398 |
|
| 6399 |
Chart.controllers.bar = Chart.DatasetController.extend({ |
| 6400 |
|
| 6401 |
dataElementType: elements.Rectangle, |
| 6402 |
|
| 6403 |
initialize: function() { |
| 6404 |
var me = this; |
| 6405 |
var meta; |
| 6406 |
|
| 6407 |
Chart.DatasetController.prototype.initialize.apply(me, arguments); |
| 6408 |
|
| 6409 |
meta = me.getMeta(); |
| 6410 |
meta.stack = me.getDataset().stack; |
| 6411 |
meta.bar = true; |
| 6412 |
}, |
| 6413 |
|
| 6414 |
update: function(reset) { |
| 6415 |
var me = this; |
| 6416 |
var rects = me.getMeta().data; |
| 6417 |
var i, ilen; |
| 6418 |
|
| 6419 |
me._ruler = me.getRuler(); |
| 6420 |
|
| 6421 |
for (i = 0, ilen = rects.length; i < ilen; ++i) { |
| 6422 |
me.updateElement(rects[i], i, reset); |
| 6423 |
} |
| 6424 |
}, |
| 6425 |
|
| 6426 |
updateElement: function(rectangle, index, reset) { |
| 6427 |
var me = this; |
| 6428 |
var chart = me.chart; |
| 6429 |
var meta = me.getMeta(); |
| 6430 |
var dataset = me.getDataset(); |
| 6431 |
var custom = rectangle.custom || {}; |
| 6432 |
var rectangleOptions = chart.options.elements.rectangle; |
| 6433 |
|
| 6434 |
rectangle._xScale = me.getScaleForId(meta.xAxisID); |
| 6435 |
rectangle._yScale = me.getScaleForId(meta.yAxisID); |
| 6436 |
rectangle._datasetIndex = me.index; |
| 6437 |
rectangle._index = index; |
| 6438 |
|
| 6439 |
rectangle._model = { |
| 6440 |
datasetLabel: dataset.label, |
| 6441 |
label: chart.data.labels[index], |
| 6442 |
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, |
| 6443 |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), |
| 6444 |
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), |
| 6445 |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) |
| 6446 |
}; |
| 6447 |
|
| 6448 |
me.updateElementGeometry(rectangle, index, reset); |
| 6449 |
|
| 6450 |
rectangle.pivot(); |
| 6451 |
}, |
| 6452 |
|
| 6453 |
/** |
| 6454 |
* @private |
| 6455 |
*/ |
| 6456 |
updateElementGeometry: function(rectangle, index, reset) { |
| 6457 |
var me = this; |
| 6458 |
var model = rectangle._model; |
| 6459 |
var vscale = me.getValueScale(); |
| 6460 |
var base = vscale.getBasePixel(); |
| 6461 |
var horizontal = vscale.isHorizontal(); |
| 6462 |
var ruler = me._ruler || me.getRuler(); |
| 6463 |
var vpixels = me.calculateBarValuePixels(me.index, index); |
| 6464 |
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); |
| 6465 |
|
| 6466 |
model.horizontal = horizontal; |
| 6467 |
model.base = reset ? base : vpixels.base; |
| 6468 |
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; |
| 6469 |
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; |
| 6470 |
model.height = horizontal ? ipixels.size : undefined; |
| 6471 |
model.width = horizontal ? undefined : ipixels.size; |
| 6472 |
}, |
| 6473 |
|
| 6474 |
/** |
| 6475 |
* @private |
| 6476 |
*/ |
| 6477 |
getValueScaleId: function() { |
| 6478 |
return this.getMeta().yAxisID; |
| 6479 |
}, |
| 6480 |
|
| 6481 |
/** |
| 6482 |
* @private |
| 6483 |
*/ |
| 6484 |
getIndexScaleId: function() { |
| 6485 |
return this.getMeta().xAxisID; |
| 6486 |
}, |
| 6487 |
|
| 6488 |
/** |
| 6489 |
* @private |
| 6490 |
*/ |
| 6491 |
getValueScale: function() { |
| 6492 |
return this.getScaleForId(this.getValueScaleId()); |
| 6493 |
}, |
| 6494 |
|
| 6495 |
/** |
| 6496 |
* @private |
| 6497 |
*/ |
| 6498 |
getIndexScale: function() { |
| 6499 |
return this.getScaleForId(this.getIndexScaleId()); |
| 6500 |
}, |
| 6501 |
|
| 6502 |
/** |
| 6503 |
* Returns the effective number of stacks based on groups and bar visibility. |
| 6504 |
* @private |
| 6505 |
*/ |
| 6506 |
getStackCount: function(last) { |
| 6507 |
var me = this; |
| 6508 |
var chart = me.chart; |
| 6509 |
var scale = me.getIndexScale(); |
| 6510 |
var stacked = scale.options.stacked; |
| 6511 |
var ilen = last === undefined ? chart.data.datasets.length : last + 1; |
| 6512 |
var stacks = []; |
| 6513 |
var i, meta; |
| 6514 |
|
| 6515 |
for (i = 0; i < ilen; ++i) { |
| 6516 |
meta = chart.getDatasetMeta(i); |
| 6517 |
if (meta.bar && chart.isDatasetVisible(i) && |
| 6518 |
(stacked === false || |
| 6519 |
(stacked === true && stacks.indexOf(meta.stack) === -1) || |
| 6520 |
(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { |
| 6521 |
stacks.push(meta.stack); |
| 6522 |
} |
| 6523 |
} |
| 6524 |
|
| 6525 |
return stacks.length; |
| 6526 |
}, |
| 6527 |
|
| 6528 |
/** |
| 6529 |
* Returns the stack index for the given dataset based on groups and bar visibility. |
| 6530 |
* @private |
| 6531 |
*/ |
| 6532 |
getStackIndex: function(datasetIndex) { |
| 6533 |
return this.getStackCount(datasetIndex) - 1; |
| 6534 |
}, |
| 6535 |
|
| 6536 |
/** |
| 6537 |
* @private |
| 6538 |
*/ |
| 6539 |
getRuler: function() { |
| 6540 |
var me = this; |
| 6541 |
var scale = me.getIndexScale(); |
| 6542 |
var stackCount = me.getStackCount(); |
| 6543 |
var datasetIndex = me.index; |
| 6544 |
var pixels = []; |
| 6545 |
var isHorizontal = scale.isHorizontal(); |
| 6546 |
var start = isHorizontal ? scale.left : scale.top; |
| 6547 |
var end = start + (isHorizontal ? scale.width : scale.height); |
| 6548 |
var i, ilen; |
| 6549 |
|
| 6550 |
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { |
| 6551 |
pixels.push(scale.getPixelForValue(null, i, datasetIndex)); |
| 6552 |
} |
| 6553 |
|
| 6554 |
return { |
| 6555 |
pixels: pixels, |
| 6556 |
start: start, |
| 6557 |
end: end, |
| 6558 |
stackCount: stackCount, |
| 6559 |
scale: scale |
| 6560 |
}; |
| 6561 |
}, |
| 6562 |
|
| 6563 |
/** |
| 6564 |
* Note: pixel values are not clamped to the scale area. |
| 6565 |
* @private |
| 6566 |
*/ |
| 6567 |
calculateBarValuePixels: function(datasetIndex, index) { |
| 6568 |
var me = this; |
| 6569 |
var chart = me.chart; |
| 6570 |
var meta = me.getMeta(); |
| 6571 |
var scale = me.getValueScale(); |
| 6572 |
var datasets = chart.data.datasets; |
| 6573 |
var value = scale.getRightValue(datasets[datasetIndex].data[index]); |
| 6574 |
var stacked = scale.options.stacked; |
| 6575 |
var stack = meta.stack; |
| 6576 |
var start = 0; |
| 6577 |
var i, imeta, ivalue, base, head, size; |
| 6578 |
|
| 6579 |
if (stacked || (stacked === undefined && stack !== undefined)) { |
| 6580 |
for (i = 0; i < datasetIndex; ++i) { |
| 6581 |
imeta = chart.getDatasetMeta(i); |
| 6582 |
|
| 6583 |
if (imeta.bar && |
| 6584 |
imeta.stack === stack && |
| 6585 |
imeta.controller.getValueScaleId() === scale.id && |
| 6586 |
chart.isDatasetVisible(i)) { |
| 6587 |
|
| 6588 |
ivalue = scale.getRightValue(datasets[i].data[index]); |
| 6589 |
if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { |
| 6590 |
start += ivalue; |
| 6591 |
} |
| 6592 |
} |
| 6593 |
} |
| 6594 |
} |
| 6595 |
|
| 6596 |
base = scale.getPixelForValue(start); |
| 6597 |
head = scale.getPixelForValue(start + value); |
| 6598 |
size = (head - base) / 2; |
| 6599 |
|
| 6600 |
return { |
| 6601 |
size: size, |
| 6602 |
base: base, |
| 6603 |
head: head, |
| 6604 |
center: head + size / 2 |
| 6605 |
}; |
| 6606 |
}, |
| 6607 |
|
| 6608 |
/** |
| 6609 |
* @private |
| 6610 |
*/ |
| 6611 |
calculateBarIndexPixels: function(datasetIndex, index, ruler) { |
| 6612 |
var me = this; |
| 6613 |
var options = ruler.scale.options; |
| 6614 |
var stackIndex = me.getStackIndex(datasetIndex); |
| 6615 |
var pixels = ruler.pixels; |
| 6616 |
var base = pixels[index]; |
| 6617 |
var length = pixels.length; |
| 6618 |
var start = ruler.start; |
| 6619 |
var end = ruler.end; |
| 6620 |
var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size; |
| 6621 |
|
| 6622 |
if (length === 1) { |
| 6623 |
leftSampleSize = base > start ? base - start : end - base; |
| 6624 |
rightSampleSize = base < end ? end - base : base - start; |
| 6625 |
} else { |
| 6626 |
if (index > 0) { |
| 6627 |
leftSampleSize = (base - pixels[index - 1]) / 2; |
| 6628 |
if (index === length - 1) { |
| 6629 |
rightSampleSize = leftSampleSize; |
| 6630 |
} |
| 6631 |
} |
| 6632 |
if (index < length - 1) { |
| 6633 |
rightSampleSize = (pixels[index + 1] - base) / 2; |
| 6634 |
if (index === 0) { |
| 6635 |
leftSampleSize = rightSampleSize; |
| 6636 |
} |
| 6637 |
} |
| 6638 |
} |
| 6639 |
|
| 6640 |
leftCategorySize = leftSampleSize * options.categoryPercentage; |
| 6641 |
rightCategorySize = rightSampleSize * options.categoryPercentage; |
| 6642 |
fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount; |
| 6643 |
size = fullBarSize * options.barPercentage; |
| 6644 |
|
| 6645 |
size = Math.min( |
| 6646 |
helpers.valueOrDefault(options.barThickness, size), |
| 6647 |
helpers.valueOrDefault(options.maxBarThickness, Infinity)); |
| 6648 |
|
| 6649 |
base -= leftCategorySize; |
| 6650 |
base += fullBarSize * stackIndex; |
| 6651 |
base += (fullBarSize - size) / 2; |
| 6652 |
|
| 6653 |
return { |
| 6654 |
size: size, |
| 6655 |
base: base, |
| 6656 |
head: base + size, |
| 6657 |
center: base + size / 2 |
| 6658 |
}; |
| 6659 |
}, |
| 6660 |
|
| 6661 |
draw: function() { |
| 6662 |
var me = this; |
| 6663 |
var chart = me.chart; |
| 6664 |
var scale = me.getValueScale(); |
| 6665 |
var rects = me.getMeta().data; |
| 6666 |
var dataset = me.getDataset(); |
| 6667 |
var ilen = rects.length; |
| 6668 |
var i = 0; |
| 6669 |
|
| 6670 |
helpers.canvas.clipArea(chart.ctx, chart.chartArea); |
| 6671 |
|
| 6672 |
for (; i < ilen; ++i) { |
| 6673 |
if (!isNaN(scale.getRightValue(dataset.data[i]))) { |
| 6674 |
rects[i].draw(); |
| 6675 |
} |
| 6676 |
} |
| 6677 |
|
| 6678 |
helpers.canvas.unclipArea(chart.ctx); |
| 6679 |
}, |
| 6680 |
|
| 6681 |
setHoverStyle: function(rectangle) { |
| 6682 |
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; |
| 6683 |
var index = rectangle._index; |
| 6684 |
var custom = rectangle.custom || {}; |
| 6685 |
var model = rectangle._model; |
| 6686 |
|
| 6687 |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
| 6688 |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
| 6689 |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); |
| 6690 |
}, |
| 6691 |
|
| 6692 |
removeHoverStyle: function(rectangle) { |
| 6693 |
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; |
| 6694 |
var index = rectangle._index; |
| 6695 |
var custom = rectangle.custom || {}; |
| 6696 |
var model = rectangle._model; |
| 6697 |
var rectangleElementOptions = this.chart.options.elements.rectangle; |
| 6698 |
|
| 6699 |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); |
| 6700 |
model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); |
| 6701 |
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); |
| 6702 |
} |
| 6703 |
}); |
| 6704 |
|
| 6705 |
Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ |
| 6706 |
/** |
| 6707 |
* @private |
| 6708 |
*/ |
| 6709 |
getValueScaleId: function() { |
| 6710 |
return this.getMeta().xAxisID; |
| 6711 |
}, |
| 6712 |
|
| 6713 |
/** |
| 6714 |
* @private |
| 6715 |
*/ |
| 6716 |
getIndexScaleId: function() { |
| 6717 |
return this.getMeta().yAxisID; |
| 6718 |
} |
| 6719 |
}); |
| 6720 |
}; |
| 6721 |
|
| 6722 |
},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ |
| 6723 |
'use strict'; |
| 6724 |
|
| 6725 |
var defaults = require(25); |
| 6726 |
var elements = require(40); |
| 6727 |
var helpers = require(45); |
| 6728 |
|
| 6729 |
defaults._set('bubble', { |
| 6730 |
hover: { |
| 6731 |
mode: 'single' |
| 6732 |
}, |
| 6733 |
|
| 6734 |
scales: { |
| 6735 |
xAxes: [{ |
| 6736 |
type: 'linear', // bubble should probably use a linear scale by default |
| 6737 |
position: 'bottom', |
| 6738 |
id: 'x-axis-0' // need an ID so datasets can reference the scale |
| 6739 |
}], |
| 6740 |
yAxes: [{ |
| 6741 |
type: 'linear', |
| 6742 |
position: 'left', |
| 6743 |
id: 'y-axis-0' |
| 6744 |
}] |
| 6745 |
}, |
| 6746 |
|
| 6747 |
tooltips: { |
| 6748 |
callbacks: { |
| 6749 |
title: function() { |
| 6750 |
// Title doesn't make sense for scatter since we format the data as a point |
| 6751 |
return ''; |
| 6752 |
}, |
| 6753 |
label: function(item, data) { |
| 6754 |
var datasetLabel = data.datasets[item.datasetIndex].label || ''; |
| 6755 |
var dataPoint = data.datasets[item.datasetIndex].data[item.index]; |
| 6756 |
return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; |
| 6757 |
} |
| 6758 |
} |
| 6759 |
} |
| 6760 |
}); |
| 6761 |
|
| 6762 |
|
| 6763 |
module.exports = function(Chart) { |
| 6764 |
|
| 6765 |
Chart.controllers.bubble = Chart.DatasetController.extend({ |
| 6766 |
/** |
| 6767 |
* @protected |
| 6768 |
*/ |
| 6769 |
dataElementType: elements.Point, |
| 6770 |
|
| 6771 |
/** |
| 6772 |
* @protected |
| 6773 |
*/ |
| 6774 |
update: function(reset) { |
| 6775 |
var me = this; |
| 6776 |
var meta = me.getMeta(); |
| 6777 |
var points = meta.data; |
| 6778 |
|
| 6779 |
// Update Points |
| 6780 |
helpers.each(points, function(point, index) { |
| 6781 |
me.updateElement(point, index, reset); |
| 6782 |
}); |
| 6783 |
}, |
| 6784 |
|
| 6785 |
/** |
| 6786 |
* @protected |
| 6787 |
*/ |
| 6788 |
updateElement: function(point, index, reset) { |
| 6789 |
var me = this; |
| 6790 |
var meta = me.getMeta(); |
| 6791 |
var custom = point.custom || {}; |
| 6792 |
var xScale = me.getScaleForId(meta.xAxisID); |
| 6793 |
var yScale = me.getScaleForId(meta.yAxisID); |
| 6794 |
var options = me._resolveElementOptions(point, index); |
| 6795 |
var data = me.getDataset().data[index]; |
| 6796 |
var dsIndex = me.index; |
| 6797 |
|
| 6798 |
var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); |
| 6799 |
var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); |
| 6800 |
|
| 6801 |
point._xScale = xScale; |
| 6802 |
point._yScale = yScale; |
| 6803 |
point._options = options; |
| 6804 |
point._datasetIndex = dsIndex; |
| 6805 |
point._index = index; |
| 6806 |
point._model = { |
| 6807 |
backgroundColor: options.backgroundColor, |
| 6808 |
borderColor: options.borderColor, |
| 6809 |
borderWidth: options.borderWidth, |
| 6810 |
hitRadius: options.hitRadius, |
| 6811 |
pointStyle: options.pointStyle, |
| 6812 |
radius: reset ? 0 : options.radius, |
| 6813 |
skip: custom.skip || isNaN(x) || isNaN(y), |
| 6814 |
x: x, |
| 6815 |
y: y, |
| 6816 |
}; |
| 6817 |
|
| 6818 |
point.pivot(); |
| 6819 |
}, |
| 6820 |
|
| 6821 |
/** |
| 6822 |
* @protected |
| 6823 |
*/ |
| 6824 |
setHoverStyle: function(point) { |
| 6825 |
var model = point._model; |
| 6826 |
var options = point._options; |
| 6827 |
|
| 6828 |
model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); |
| 6829 |
model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); |
| 6830 |
model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); |
| 6831 |
model.radius = options.radius + options.hoverRadius; |
| 6832 |
}, |
| 6833 |
|
| 6834 |
/** |
| 6835 |
* @protected |
| 6836 |
*/ |
| 6837 |
removeHoverStyle: function(point) { |
| 6838 |
var model = point._model; |
| 6839 |
var options = point._options; |
| 6840 |
|
| 6841 |
model.backgroundColor = options.backgroundColor; |
| 6842 |
model.borderColor = options.borderColor; |
| 6843 |
model.borderWidth = options.borderWidth; |
| 6844 |
model.radius = options.radius; |
| 6845 |
}, |
| 6846 |
|
| 6847 |
/** |
| 6848 |
* @private |
| 6849 |
*/ |
| 6850 |
_resolveElementOptions: function(point, index) { |
| 6851 |
var me = this; |
| 6852 |
var chart = me.chart; |
| 6853 |
var datasets = chart.data.datasets; |
| 6854 |
var dataset = datasets[me.index]; |
| 6855 |
var custom = point.custom || {}; |
| 6856 |
var options = chart.options.elements.point; |
| 6857 |
var resolve = helpers.options.resolve; |
| 6858 |
var data = dataset.data[index]; |
| 6859 |
var values = {}; |
| 6860 |
var i, ilen, key; |
| 6861 |
|
| 6862 |
// Scriptable options |
| 6863 |
var context = { |
| 6864 |
chart: chart, |
| 6865 |
dataIndex: index, |
| 6866 |
dataset: dataset, |
| 6867 |
datasetIndex: me.index |
| 6868 |
}; |
| 6869 |
|
| 6870 |
var keys = [ |
| 6871 |
'backgroundColor', |
| 6872 |
'borderColor', |
| 6873 |
'borderWidth', |
| 6874 |
'hoverBackgroundColor', |
| 6875 |
'hoverBorderColor', |
| 6876 |
'hoverBorderWidth', |
| 6877 |
'hoverRadius', |
| 6878 |
'hitRadius', |
| 6879 |
'pointStyle' |
| 6880 |
]; |
| 6881 |
|
| 6882 |
for (i = 0, ilen = keys.length; i < ilen; ++i) { |
| 6883 |
key = keys[i]; |
| 6884 |
values[key] = resolve([ |
| 6885 |
custom[key], |
| 6886 |
dataset[key], |
| 6887 |
options[key] |
| 6888 |
], context, index); |
| 6889 |
} |
| 6890 |
|
| 6891 |
// Custom radius resolution |
| 6892 |
values.radius = resolve([ |
| 6893 |
custom.radius, |
| 6894 |
data ? data.r : undefined, |
| 6895 |
dataset.radius, |
| 6896 |
options.radius |
| 6897 |
], context, index); |
| 6898 |
|
| 6899 |
return values; |
| 6900 |
} |
| 6901 |
}); |
| 6902 |
}; |
| 6903 |
|
| 6904 |
},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ |
| 6905 |
'use strict'; |
| 6906 |
|
| 6907 |
var defaults = require(25); |
| 6908 |
var elements = require(40); |
| 6909 |
var helpers = require(45); |
| 6910 |
|
| 6911 |
defaults._set('doughnut', { |
| 6912 |
animation: { |
| 6913 |
// Boolean - Whether we animate the rotation of the Doughnut |
| 6914 |
animateRotate: true, |
| 6915 |
// Boolean - Whether we animate scaling the Doughnut from the centre |
| 6916 |
animateScale: false |
| 6917 |
}, |
| 6918 |
hover: { |
| 6919 |
mode: 'single' |
| 6920 |
}, |
| 6921 |
legendCallback: function(chart) { |
| 6922 |
var text = []; |
| 6923 |
text.push('<ul class="' + chart.id + '-legend">'); |
| 6924 |
|
| 6925 |
var data = chart.data; |
| 6926 |
var datasets = data.datasets; |
| 6927 |
var labels = data.labels; |
| 6928 |
|
| 6929 |
if (datasets.length) { |
| 6930 |
for (var i = 0; i < datasets[0].data.length; ++i) { |
| 6931 |
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); |
| 6932 |
if (labels[i]) { |
| 6933 |
text.push(labels[i]); |
| 6934 |
} |
| 6935 |
text.push('</li>'); |
| 6936 |
} |
| 6937 |
} |
| 6938 |
|
| 6939 |
text.push('</ul>'); |
| 6940 |
return text.join(''); |
| 6941 |
}, |
| 6942 |
legend: { |
| 6943 |
labels: { |
| 6944 |
generateLabels: function(chart) { |
| 6945 |
var data = chart.data; |
| 6946 |
if (data.labels.length && data.datasets.length) { |
| 6947 |
return data.labels.map(function(label, i) { |
| 6948 |
var meta = chart.getDatasetMeta(0); |
| 6949 |
var ds = data.datasets[0]; |
| 6950 |
var arc = meta.data[i]; |
| 6951 |
var custom = arc && arc.custom || {}; |
| 6952 |
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; |
| 6953 |
var arcOpts = chart.options.elements.arc; |
| 6954 |
var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); |
| 6955 |
var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); |
| 6956 |
var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); |
| 6957 |
|
| 6958 |
return { |
| 6959 |
text: label, |
| 6960 |
fillStyle: fill, |
| 6961 |
strokeStyle: stroke, |
| 6962 |
lineWidth: bw, |
| 6963 |
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, |
| 6964 |
|
| 6965 |
// Extra data used for toggling the correct item |
| 6966 |
index: i |
| 6967 |
}; |
| 6968 |
}); |
| 6969 |
} |
| 6970 |
return []; |
| 6971 |
} |
| 6972 |
}, |
| 6973 |
|
| 6974 |
onClick: function(e, legendItem) { |
| 6975 |
var index = legendItem.index; |
| 6976 |
var chart = this.chart; |
| 6977 |
var i, ilen, meta; |
| 6978 |
|
| 6979 |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
| 6980 |
meta = chart.getDatasetMeta(i); |
| 6981 |
// toggle visibility of index if exists |
| 6982 |
if (meta.data[index]) { |
| 6983 |
meta.data[index].hidden = !meta.data[index].hidden; |
| 6984 |
} |
| 6985 |
} |
| 6986 |
|
| 6987 |
chart.update(); |
| 6988 |
} |
| 6989 |
}, |
| 6990 |
|
| 6991 |
// The percentage of the chart that we cut out of the middle. |
| 6992 |
cutoutPercentage: 50, |
| 6993 |
|
| 6994 |
// The rotation of the chart, where the first data arc begins. |
| 6995 |
rotation: Math.PI * -0.5, |
| 6996 |
|
| 6997 |
// The total circumference of the chart. |
| 6998 |
circumference: Math.PI * 2.0, |
| 6999 |
|
| 7000 |
// Need to override these to give a nice default |
| 7001 |
tooltips: { |
| 7002 |
callbacks: { |
| 7003 |
title: function() { |
| 7004 |
return ''; |
| 7005 |
}, |
| 7006 |
label: function(tooltipItem, data) { |
| 7007 |
var dataLabel = data.labels[tooltipItem.index]; |
| 7008 |
var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; |
| 7009 |
|
| 7010 |
if (helpers.isArray(dataLabel)) { |
| 7011 |
// show value on first line of multiline label |
| 7012 |
// need to clone because we are changing the value |
| 7013 |
dataLabel = dataLabel.slice(); |
| 7014 |
dataLabel[0] += value; |
| 7015 |
} else { |
| 7016 |
dataLabel += value; |
| 7017 |
} |
| 7018 |
|
| 7019 |
return dataLabel; |
| 7020 |
} |
| 7021 |
} |
| 7022 |
} |
| 7023 |
}); |
| 7024 |
|
| 7025 |
defaults._set('pie', helpers.clone(defaults.doughnut)); |
| 7026 |
defaults._set('pie', { |
| 7027 |
cutoutPercentage: 0 |
| 7028 |
}); |
| 7029 |
|
| 7030 |
module.exports = function(Chart) { |
| 7031 |
|
| 7032 |
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ |
| 7033 |
|
| 7034 |
dataElementType: elements.Arc, |
| 7035 |
|
| 7036 |
linkScales: helpers.noop, |
| 7037 |
|
| 7038 |
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly |
| 7039 |
getRingIndex: function(datasetIndex) { |
| 7040 |
var ringIndex = 0; |
| 7041 |
|
| 7042 |
for (var j = 0; j < datasetIndex; ++j) { |
| 7043 |
if (this.chart.isDatasetVisible(j)) { |
| 7044 |
++ringIndex; |
| 7045 |
} |
| 7046 |
} |
| 7047 |
|
| 7048 |
return ringIndex; |
| 7049 |
}, |
| 7050 |
|
| 7051 |
update: function(reset) { |
| 7052 |
var me = this; |
| 7053 |
var chart = me.chart; |
| 7054 |
var chartArea = chart.chartArea; |
| 7055 |
var opts = chart.options; |
| 7056 |
var arcOpts = opts.elements.arc; |
| 7057 |
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; |
| 7058 |
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; |
| 7059 |
var minSize = Math.min(availableWidth, availableHeight); |
| 7060 |
var offset = {x: 0, y: 0}; |
| 7061 |
var meta = me.getMeta(); |
| 7062 |
var cutoutPercentage = opts.cutoutPercentage; |
| 7063 |
var circumference = opts.circumference; |
| 7064 |
|
| 7065 |
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc |
| 7066 |
if (circumference < Math.PI * 2.0) { |
| 7067 |
var startAngle = opts.rotation % (Math.PI * 2.0); |
| 7068 |
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); |
| 7069 |
var endAngle = startAngle + circumference; |
| 7070 |
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; |
| 7071 |
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; |
| 7072 |
var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); |
| 7073 |
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); |
| 7074 |
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); |
| 7075 |
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); |
| 7076 |
var cutout = cutoutPercentage / 100.0; |
| 7077 |
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))}; |
| 7078 |
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))}; |
| 7079 |
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; |
| 7080 |
minSize = Math.min(availableWidth / size.width, availableHeight / size.height); |
| 7081 |
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; |
| 7082 |
} |
| 7083 |
|
| 7084 |
chart.borderWidth = me.getMaxBorderWidth(meta.data); |
| 7085 |
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); |
| 7086 |
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); |
| 7087 |
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); |
| 7088 |
chart.offsetX = offset.x * chart.outerRadius; |
| 7089 |
chart.offsetY = offset.y * chart.outerRadius; |
| 7090 |
|
| 7091 |
meta.total = me.calculateTotal(); |
| 7092 |
|
| 7093 |
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); |
| 7094 |
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); |
| 7095 |
|
| 7096 |
helpers.each(meta.data, function(arc, index) { |
| 7097 |
me.updateElement(arc, index, reset); |
| 7098 |
}); |
| 7099 |
}, |
| 7100 |
|
| 7101 |
updateElement: function(arc, index, reset) { |
| 7102 |
var me = this; |
| 7103 |
var chart = me.chart; |
| 7104 |
var chartArea = chart.chartArea; |
| 7105 |
var opts = chart.options; |
| 7106 |
var animationOpts = opts.animation; |
| 7107 |
var centerX = (chartArea.left + chartArea.right) / 2; |
| 7108 |
var centerY = (chartArea.top + chartArea.bottom) / 2; |
| 7109 |
var startAngle = opts.rotation; // non reset case handled later |
| 7110 |
var endAngle = opts.rotation; // non reset case handled later |
| 7111 |
var dataset = me.getDataset(); |
| 7112 |
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); |
| 7113 |
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; |
| 7114 |
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; |
| 7115 |
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; |
| 7116 |
|
| 7117 |
helpers.extend(arc, { |
| 7118 |
// Utility |
| 7119 |
_datasetIndex: me.index, |
| 7120 |
_index: index, |
| 7121 |
|
| 7122 |
// Desired view properties |
| 7123 |
_model: { |
| 7124 |
x: centerX + chart.offsetX, |
| 7125 |
y: centerY + chart.offsetY, |
| 7126 |
startAngle: startAngle, |
| 7127 |
endAngle: endAngle, |
| 7128 |
circumference: circumference, |
| 7129 |
outerRadius: outerRadius, |
| 7130 |
innerRadius: innerRadius, |
| 7131 |
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) |
| 7132 |
} |
| 7133 |
}); |
| 7134 |
|
| 7135 |
var model = arc._model; |
| 7136 |
// Resets the visual styles |
| 7137 |
this.removeHoverStyle(arc); |
| 7138 |
|
| 7139 |
// Set correct angles if not resetting |
| 7140 |
if (!reset || !animationOpts.animateRotate) { |
| 7141 |
if (index === 0) { |
| 7142 |
model.startAngle = opts.rotation; |
| 7143 |
} else { |
| 7144 |
model.startAngle = me.getMeta().data[index - 1]._model.endAngle; |
| 7145 |
} |
| 7146 |
|
| 7147 |
model.endAngle = model.startAngle + model.circumference; |
| 7148 |
} |
| 7149 |
|
| 7150 |
arc.pivot(); |
| 7151 |
}, |
| 7152 |
|
| 7153 |
removeHoverStyle: function(arc) { |
| 7154 |
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); |
| 7155 |
}, |
| 7156 |
|
| 7157 |
calculateTotal: function() { |
| 7158 |
var dataset = this.getDataset(); |
| 7159 |
var meta = this.getMeta(); |
| 7160 |
var total = 0; |
| 7161 |
var value; |
| 7162 |
|
| 7163 |
helpers.each(meta.data, function(element, index) { |
| 7164 |
value = dataset.data[index]; |
| 7165 |
if (!isNaN(value) && !element.hidden) { |
| 7166 |
total += Math.abs(value); |
| 7167 |
} |
| 7168 |
}); |
| 7169 |
|
| 7170 |
/* if (total === 0) { |
| 7171 |
total = NaN; |
| 7172 |
}*/ |
| 7173 |
|
| 7174 |
return total; |
| 7175 |
}, |
| 7176 |
|
| 7177 |
calculateCircumference: function(value) { |
| 7178 |
var total = this.getMeta().total; |
| 7179 |
if (total > 0 && !isNaN(value)) { |
| 7180 |
return (Math.PI * 2.0) * (value / total); |
| 7181 |
} |
| 7182 |
return 0; |
| 7183 |
}, |
| 7184 |
|
| 7185 |
// gets the max border or hover width to properly scale pie charts |
| 7186 |
getMaxBorderWidth: function(arcs) { |
| 7187 |
var max = 0; |
| 7188 |
var index = this.index; |
| 7189 |
var length = arcs.length; |
| 7190 |
var borderWidth; |
| 7191 |
var hoverWidth; |
| 7192 |
|
| 7193 |
for (var i = 0; i < length; i++) { |
| 7194 |
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; |
| 7195 |
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; |
| 7196 |
|
| 7197 |
max = borderWidth > max ? borderWidth : max; |
| 7198 |
max = hoverWidth > max ? hoverWidth : max; |
| 7199 |
} |
| 7200 |
return max; |
| 7201 |
} |
| 7202 |
}); |
| 7203 |
}; |
| 7204 |
|
| 7205 |
},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ |
| 7206 |
'use strict'; |
| 7207 |
|
| 7208 |
var defaults = require(25); |
| 7209 |
var elements = require(40); |
| 7210 |
var helpers = require(45); |
| 7211 |
|
| 7212 |
defaults._set('line', { |
| 7213 |
showLines: true, |
| 7214 |
spanGaps: false, |
| 7215 |
|
| 7216 |
hover: { |
| 7217 |
mode: 'label' |
| 7218 |
}, |
| 7219 |
|
| 7220 |
scales: { |
| 7221 |
xAxes: [{ |
| 7222 |
type: 'category', |
| 7223 |
id: 'x-axis-0' |
| 7224 |
}], |
| 7225 |
yAxes: [{ |
| 7226 |
type: 'linear', |
| 7227 |
id: 'y-axis-0' |
| 7228 |
}] |
| 7229 |
} |
| 7230 |
}); |
| 7231 |
|
| 7232 |
module.exports = function(Chart) { |
| 7233 |
|
| 7234 |
function lineEnabled(dataset, options) { |
| 7235 |
return helpers.valueOrDefault(dataset.showLine, options.showLines); |
| 7236 |
} |
| 7237 |
|
| 7238 |
Chart.controllers.line = Chart.DatasetController.extend({ |
| 7239 |
|
| 7240 |
datasetElementType: elements.Line, |
| 7241 |
|
| 7242 |
dataElementType: elements.Point, |
| 7243 |
|
| 7244 |
update: function(reset) { |
| 7245 |
var me = this; |
| 7246 |
var meta = me.getMeta(); |
| 7247 |
var line = meta.dataset; |
| 7248 |
var points = meta.data || []; |
| 7249 |
var options = me.chart.options; |
| 7250 |
var lineElementOptions = options.elements.line; |
| 7251 |
var scale = me.getScaleForId(meta.yAxisID); |
| 7252 |
var i, ilen, custom; |
| 7253 |
var dataset = me.getDataset(); |
| 7254 |
var showLine = lineEnabled(dataset, options); |
| 7255 |
|
| 7256 |
// Update Line |
| 7257 |
if (showLine) { |
| 7258 |
custom = line.custom || {}; |
| 7259 |
|
| 7260 |
// Compatibility: If the properties are defined with only the old name, use those values |
| 7261 |
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { |
| 7262 |
dataset.lineTension = dataset.tension; |
| 7263 |
} |
| 7264 |
|
| 7265 |
// Utility |
| 7266 |
line._scale = scale; |
| 7267 |
line._datasetIndex = me.index; |
| 7268 |
// Data |
| 7269 |
line._children = points; |
| 7270 |
// Model |
| 7271 |
line._model = { |
| 7272 |
// Appearance |
| 7273 |
// The default behavior of lines is to break at null values, according |
| 7274 |
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 |
| 7275 |
// This option gives lines the ability to span gaps |
| 7276 |
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, |
| 7277 |
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), |
| 7278 |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), |
| 7279 |
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), |
| 7280 |
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), |
| 7281 |
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), |
| 7282 |
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), |
| 7283 |
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), |
| 7284 |
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), |
| 7285 |
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), |
| 7286 |
steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), |
| 7287 |
cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), |
| 7288 |
}; |
| 7289 |
|
| 7290 |
line.pivot(); |
| 7291 |
} |
| 7292 |
|
| 7293 |
// Update Points |
| 7294 |
for (i = 0, ilen = points.length; i < ilen; ++i) { |
| 7295 |
me.updateElement(points[i], i, reset); |
| 7296 |
} |
| 7297 |
|
| 7298 |
if (showLine && line._model.tension !== 0) { |
| 7299 |
me.updateBezierControlPoints(); |
| 7300 |
} |
| 7301 |
|
| 7302 |
// Now pivot the point for animation |
| 7303 |
for (i = 0, ilen = points.length; i < ilen; ++i) { |
| 7304 |
points[i].pivot(); |
| 7305 |
} |
| 7306 |
}, |
| 7307 |
|
| 7308 |
getPointBackgroundColor: function(point, index) { |
| 7309 |
var backgroundColor = this.chart.options.elements.point.backgroundColor; |
| 7310 |
var dataset = this.getDataset(); |
| 7311 |
var custom = point.custom || {}; |
| 7312 |
|
| 7313 |
if (custom.backgroundColor) { |
| 7314 |
backgroundColor = custom.backgroundColor; |
| 7315 |
} else if (dataset.pointBackgroundColor) { |
| 7316 |
backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); |
| 7317 |
} else if (dataset.backgroundColor) { |
| 7318 |
backgroundColor = dataset.backgroundColor; |
| 7319 |
} |
| 7320 |
|
| 7321 |
return backgroundColor; |
| 7322 |
}, |
| 7323 |
|
| 7324 |
getPointBorderColor: function(point, index) { |
| 7325 |
var borderColor = this.chart.options.elements.point.borderColor; |
| 7326 |
var dataset = this.getDataset(); |
| 7327 |
var custom = point.custom || {}; |
| 7328 |
|
| 7329 |
if (custom.borderColor) { |
| 7330 |
borderColor = custom.borderColor; |
| 7331 |
} else if (dataset.pointBorderColor) { |
| 7332 |
borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); |
| 7333 |
} else if (dataset.borderColor) { |
| 7334 |
borderColor = dataset.borderColor; |
| 7335 |
} |
| 7336 |
|
| 7337 |
return borderColor; |
| 7338 |
}, |
| 7339 |
|
| 7340 |
getPointBorderWidth: function(point, index) { |
| 7341 |
var borderWidth = this.chart.options.elements.point.borderWidth; |
| 7342 |
var dataset = this.getDataset(); |
| 7343 |
var custom = point.custom || {}; |
| 7344 |
|
| 7345 |
if (!isNaN(custom.borderWidth)) { |
| 7346 |
borderWidth = custom.borderWidth; |
| 7347 |
} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { |
| 7348 |
borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); |
| 7349 |
} else if (!isNaN(dataset.borderWidth)) { |
| 7350 |
borderWidth = dataset.borderWidth; |
| 7351 |
} |
| 7352 |
|
| 7353 |
return borderWidth; |
| 7354 |
}, |
| 7355 |
|
| 7356 |
updateElement: function(point, index, reset) { |
| 7357 |
var me = this; |
| 7358 |
var meta = me.getMeta(); |
| 7359 |
var custom = point.custom || {}; |
| 7360 |
var dataset = me.getDataset(); |
| 7361 |
var datasetIndex = me.index; |
| 7362 |
var value = dataset.data[index]; |
| 7363 |
var yScale = me.getScaleForId(meta.yAxisID); |
| 7364 |
var xScale = me.getScaleForId(meta.xAxisID); |
| 7365 |
var pointOptions = me.chart.options.elements.point; |
| 7366 |
var x, y; |
| 7367 |
|
| 7368 |
// Compatibility: If the properties are defined with only the old name, use those values |
| 7369 |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
| 7370 |
dataset.pointRadius = dataset.radius; |
| 7371 |
} |
| 7372 |
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { |
| 7373 |
dataset.pointHitRadius = dataset.hitRadius; |
| 7374 |
} |
| 7375 |
|
| 7376 |
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); |
| 7377 |
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); |
| 7378 |
|
| 7379 |
// Utility |
| 7380 |
point._xScale = xScale; |
| 7381 |
point._yScale = yScale; |
| 7382 |
point._datasetIndex = datasetIndex; |
| 7383 |
point._index = index; |
| 7384 |
|
| 7385 |
// Desired view properties |
| 7386 |
point._model = { |
| 7387 |
x: x, |
| 7388 |
y: y, |
| 7389 |
skip: custom.skip || isNaN(x) || isNaN(y), |
| 7390 |
// Appearance |
| 7391 |
radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), |
| 7392 |
pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), |
| 7393 |
backgroundColor: me.getPointBackgroundColor(point, index), |
| 7394 |
borderColor: me.getPointBorderColor(point, index), |
| 7395 |
borderWidth: me.getPointBorderWidth(point, index), |
| 7396 |
tension: meta.dataset._model ? meta.dataset._model.tension : 0, |
| 7397 |
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, |
| 7398 |
// Tooltip |
| 7399 |
hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) |
| 7400 |
}; |
| 7401 |
}, |
| 7402 |
|
| 7403 |
calculatePointY: function(value, index, datasetIndex) { |
| 7404 |
var me = this; |
| 7405 |
var chart = me.chart; |
| 7406 |
var meta = me.getMeta(); |
| 7407 |
var yScale = me.getScaleForId(meta.yAxisID); |
| 7408 |
var sumPos = 0; |
| 7409 |
var sumNeg = 0; |
| 7410 |
var i, ds, dsMeta; |
| 7411 |
|
| 7412 |
if (yScale.options.stacked) { |
| 7413 |
for (i = 0; i < datasetIndex; i++) { |
| 7414 |
ds = chart.data.datasets[i]; |
| 7415 |
dsMeta = chart.getDatasetMeta(i); |
| 7416 |
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { |
| 7417 |
var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); |
| 7418 |
if (stackedRightValue < 0) { |
| 7419 |
sumNeg += stackedRightValue || 0; |
| 7420 |
} else { |
| 7421 |
sumPos += stackedRightValue || 0; |
| 7422 |
} |
| 7423 |
} |
| 7424 |
} |
| 7425 |
|
| 7426 |
var rightValue = Number(yScale.getRightValue(value)); |
| 7427 |
if (rightValue < 0) { |
| 7428 |
return yScale.getPixelForValue(sumNeg + rightValue); |
| 7429 |
} |
| 7430 |
return yScale.getPixelForValue(sumPos + rightValue); |
| 7431 |
} |
| 7432 |
|
| 7433 |
return yScale.getPixelForValue(value); |
| 7434 |
}, |
| 7435 |
|
| 7436 |
updateBezierControlPoints: function() { |
| 7437 |
var me = this; |
| 7438 |
var meta = me.getMeta(); |
| 7439 |
var area = me.chart.chartArea; |
| 7440 |
var points = (meta.data || []); |
| 7441 |
var i, ilen, point, model, controlPoints; |
| 7442 |
|
| 7443 |
// Only consider points that are drawn in case the spanGaps option is used |
| 7444 |
if (meta.dataset._model.spanGaps) { |
| 7445 |
points = points.filter(function(pt) { |
| 7446 |
return !pt._model.skip; |
| 7447 |
}); |
| 7448 |
} |
| 7449 |
|
| 7450 |
function capControlPoint(pt, min, max) { |
| 7451 |
return Math.max(Math.min(pt, max), min); |
| 7452 |
} |
| 7453 |
|
| 7454 |
if (meta.dataset._model.cubicInterpolationMode === 'monotone') { |
| 7455 |
helpers.splineCurveMonotone(points); |
| 7456 |
} else { |
| 7457 |
for (i = 0, ilen = points.length; i < ilen; ++i) { |
| 7458 |
point = points[i]; |
| 7459 |
model = point._model; |
| 7460 |
controlPoints = helpers.splineCurve( |
| 7461 |
helpers.previousItem(points, i)._model, |
| 7462 |
model, |
| 7463 |
helpers.nextItem(points, i)._model, |
| 7464 |
meta.dataset._model.tension |
| 7465 |
); |
| 7466 |
model.controlPointPreviousX = controlPoints.previous.x; |
| 7467 |
model.controlPointPreviousY = controlPoints.previous.y; |
| 7468 |
model.controlPointNextX = controlPoints.next.x; |
| 7469 |
model.controlPointNextY = controlPoints.next.y; |
| 7470 |
} |
| 7471 |
} |
| 7472 |
|
| 7473 |
if (me.chart.options.elements.line.capBezierPoints) { |
| 7474 |
for (i = 0, ilen = points.length; i < ilen; ++i) { |
| 7475 |
model = points[i]._model; |
| 7476 |
model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); |
| 7477 |
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); |
| 7478 |
model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); |
| 7479 |
model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); |
| 7480 |
} |
| 7481 |
} |
| 7482 |
}, |
| 7483 |
|
| 7484 |
draw: function() { |
| 7485 |
var me = this; |
| 7486 |
var chart = me.chart; |
| 7487 |
var meta = me.getMeta(); |
| 7488 |
var points = meta.data || []; |
| 7489 |
var area = chart.chartArea; |
| 7490 |
var ilen = points.length; |
| 7491 |
var i = 0; |
| 7492 |
|
| 7493 |
helpers.canvas.clipArea(chart.ctx, area); |
| 7494 |
|
| 7495 |
if (lineEnabled(me.getDataset(), chart.options)) { |
| 7496 |
meta.dataset.draw(); |
| 7497 |
} |
| 7498 |
|
| 7499 |
helpers.canvas.unclipArea(chart.ctx); |
| 7500 |
|
| 7501 |
// Draw the points |
| 7502 |
for (; i < ilen; ++i) { |
| 7503 |
points[i].draw(area); |
| 7504 |
} |
| 7505 |
}, |
| 7506 |
|
| 7507 |
setHoverStyle: function(point) { |
| 7508 |
// Point |
| 7509 |
var dataset = this.chart.data.datasets[point._datasetIndex]; |
| 7510 |
var index = point._index; |
| 7511 |
var custom = point.custom || {}; |
| 7512 |
var model = point._model; |
| 7513 |
|
| 7514 |
model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); |
| 7515 |
model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
| 7516 |
model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
| 7517 |
model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); |
| 7518 |
}, |
| 7519 |
|
| 7520 |
removeHoverStyle: function(point) { |
| 7521 |
var me = this; |
| 7522 |
var dataset = me.chart.data.datasets[point._datasetIndex]; |
| 7523 |
var index = point._index; |
| 7524 |
var custom = point.custom || {}; |
| 7525 |
var model = point._model; |
| 7526 |
|
| 7527 |
// Compatibility: If the properties are defined with only the old name, use those values |
| 7528 |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
| 7529 |
dataset.pointRadius = dataset.radius; |
| 7530 |
} |
| 7531 |
|
| 7532 |
model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); |
| 7533 |
model.backgroundColor = me.getPointBackgroundColor(point, index); |
| 7534 |
model.borderColor = me.getPointBorderColor(point, index); |
| 7535 |
model.borderWidth = me.getPointBorderWidth(point, index); |
| 7536 |
} |
| 7537 |
}); |
| 7538 |
}; |
| 7539 |
|
| 7540 |
},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ |
| 7541 |
'use strict'; |
| 7542 |
|
| 7543 |
var defaults = require(25); |
| 7544 |
var elements = require(40); |
| 7545 |
var helpers = require(45); |
| 7546 |
|
| 7547 |
defaults._set('polarArea', { |
| 7548 |
scale: { |
| 7549 |
type: 'radialLinear', |
| 7550 |
angleLines: { |
| 7551 |
display: false |
| 7552 |
}, |
| 7553 |
gridLines: { |
| 7554 |
circular: true |
| 7555 |
}, |
| 7556 |
pointLabels: { |
| 7557 |
display: false |
| 7558 |
}, |
| 7559 |
ticks: { |
| 7560 |
beginAtZero: true |
| 7561 |
} |
| 7562 |
}, |
| 7563 |
|
| 7564 |
// Boolean - Whether to animate the rotation of the chart |
| 7565 |
animation: { |
| 7566 |
animateRotate: true, |
| 7567 |
animateScale: true |
| 7568 |
}, |
| 7569 |
|
| 7570 |
startAngle: -0.5 * Math.PI, |
| 7571 |
legendCallback: function(chart) { |
| 7572 |
var text = []; |
| 7573 |
text.push('<ul class="' + chart.id + '-legend">'); |
| 7574 |
|
| 7575 |
var data = chart.data; |
| 7576 |
var datasets = data.datasets; |
| 7577 |
var labels = data.labels; |
| 7578 |
|
| 7579 |
if (datasets.length) { |
| 7580 |
for (var i = 0; i < datasets[0].data.length; ++i) { |
| 7581 |
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); |
| 7582 |
if (labels[i]) { |
| 7583 |
text.push(labels[i]); |
| 7584 |
} |
| 7585 |
text.push('</li>'); |
| 7586 |
} |
| 7587 |
} |
| 7588 |
|
| 7589 |
text.push('</ul>'); |
| 7590 |
return text.join(''); |
| 7591 |
}, |
| 7592 |
legend: { |
| 7593 |
labels: { |
| 7594 |
generateLabels: function(chart) { |
| 7595 |
var data = chart.data; |
| 7596 |
if (data.labels.length && data.datasets.length) { |
| 7597 |
return data.labels.map(function(label, i) { |
| 7598 |
var meta = chart.getDatasetMeta(0); |
| 7599 |
var ds = data.datasets[0]; |
| 7600 |
var arc = meta.data[i]; |
| 7601 |
var custom = arc.custom || {}; |
| 7602 |
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; |
| 7603 |
var arcOpts = chart.options.elements.arc; |
| 7604 |
var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); |
| 7605 |
var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); |
| 7606 |
var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); |
| 7607 |
|
| 7608 |
return { |
| 7609 |
text: label, |
| 7610 |
fillStyle: fill, |
| 7611 |
strokeStyle: stroke, |
| 7612 |
lineWidth: bw, |
| 7613 |
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, |
| 7614 |
|
| 7615 |
// Extra data used for toggling the correct item |
| 7616 |
index: i |
| 7617 |
}; |
| 7618 |
}); |
| 7619 |
} |
| 7620 |
return []; |
| 7621 |
} |
| 7622 |
}, |
| 7623 |
|
| 7624 |
onClick: function(e, legendItem) { |
| 7625 |
var index = legendItem.index; |
| 7626 |
var chart = this.chart; |
| 7627 |
var i, ilen, meta; |
| 7628 |
|
| 7629 |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
| 7630 |
meta = chart.getDatasetMeta(i); |
| 7631 |
meta.data[index].hidden = !meta.data[index].hidden; |
| 7632 |
} |
| 7633 |
|
| 7634 |
chart.update(); |
| 7635 |
} |
| 7636 |
}, |
| 7637 |
|
| 7638 |
// Need to override these to give a nice default |
| 7639 |
tooltips: { |
| 7640 |
callbacks: { |
| 7641 |
title: function() { |
| 7642 |
return ''; |
| 7643 |
}, |
| 7644 |
label: function(item, data) { |
| 7645 |
return data.labels[item.index] + ': ' + item.yLabel; |
| 7646 |
} |
| 7647 |
} |
| 7648 |
} |
| 7649 |
}); |
| 7650 |
|
| 7651 |
module.exports = function(Chart) { |
| 7652 |
|
| 7653 |
Chart.controllers.polarArea = Chart.DatasetController.extend({ |
| 7654 |
|
| 7655 |
dataElementType: elements.Arc, |
| 7656 |
|
| 7657 |
linkScales: helpers.noop, |
| 7658 |
|
| 7659 |
update: function(reset) { |
| 7660 |
var me = this; |
| 7661 |
var chart = me.chart; |
| 7662 |
var chartArea = chart.chartArea; |
| 7663 |
var meta = me.getMeta(); |
| 7664 |
var opts = chart.options; |
| 7665 |
var arcOpts = opts.elements.arc; |
| 7666 |
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); |
| 7667 |
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); |
| 7668 |
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); |
| 7669 |
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); |
| 7670 |
|
| 7671 |
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); |
| 7672 |
me.innerRadius = me.outerRadius - chart.radiusLength; |
| 7673 |
|
| 7674 |
meta.count = me.countVisibleElements(); |
| 7675 |
|
| 7676 |
helpers.each(meta.data, function(arc, index) { |
| 7677 |
me.updateElement(arc, index, reset); |
| 7678 |
}); |
| 7679 |
}, |
| 7680 |
|
| 7681 |
updateElement: function(arc, index, reset) { |
| 7682 |
var me = this; |
| 7683 |
var chart = me.chart; |
| 7684 |
var dataset = me.getDataset(); |
| 7685 |
var opts = chart.options; |
| 7686 |
var animationOpts = opts.animation; |
| 7687 |
var scale = chart.scale; |
| 7688 |
var labels = chart.data.labels; |
| 7689 |
|
| 7690 |
var circumference = me.calculateCircumference(dataset.data[index]); |
| 7691 |
var centerX = scale.xCenter; |
| 7692 |
var centerY = scale.yCenter; |
| 7693 |
|
| 7694 |
// If there is NaN data before us, we need to calculate the starting angle correctly. |
| 7695 |
// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data |
| 7696 |
var visibleCount = 0; |
| 7697 |
var meta = me.getMeta(); |
| 7698 |
for (var i = 0; i < index; ++i) { |
| 7699 |
if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { |
| 7700 |
++visibleCount; |
| 7701 |
} |
| 7702 |
} |
| 7703 |
|
| 7704 |
// var negHalfPI = -0.5 * Math.PI; |
| 7705 |
var datasetStartAngle = opts.startAngle; |
| 7706 |
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); |
| 7707 |
var startAngle = datasetStartAngle + (circumference * visibleCount); |
| 7708 |
var endAngle = startAngle + (arc.hidden ? 0 : circumference); |
| 7709 |
|
| 7710 |
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); |
| 7711 |
|
| 7712 |
helpers.extend(arc, { |
| 7713 |
// Utility |
| 7714 |
_datasetIndex: me.index, |
| 7715 |
_index: index, |
| 7716 |
_scale: scale, |
| 7717 |
|
| 7718 |
// Desired view properties |
| 7719 |
_model: { |
| 7720 |
x: centerX, |
| 7721 |
y: centerY, |
| 7722 |
innerRadius: 0, |
| 7723 |
outerRadius: reset ? resetRadius : distance, |
| 7724 |
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, |
| 7725 |
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, |
| 7726 |
label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) |
| 7727 |
} |
| 7728 |
}); |
| 7729 |
|
| 7730 |
// Apply border and fill style |
| 7731 |
me.removeHoverStyle(arc); |
| 7732 |
|
| 7733 |
arc.pivot(); |
| 7734 |
}, |
| 7735 |
|
| 7736 |
removeHoverStyle: function(arc) { |
| 7737 |
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); |
| 7738 |
}, |
| 7739 |
|
| 7740 |
countVisibleElements: function() { |
| 7741 |
var dataset = this.getDataset(); |
| 7742 |
var meta = this.getMeta(); |
| 7743 |
var count = 0; |
| 7744 |
|
| 7745 |
helpers.each(meta.data, function(element, index) { |
| 7746 |
if (!isNaN(dataset.data[index]) && !element.hidden) { |
| 7747 |
count++; |
| 7748 |
} |
| 7749 |
}); |
| 7750 |
|
| 7751 |
return count; |
| 7752 |
}, |
| 7753 |
|
| 7754 |
calculateCircumference: function(value) { |
| 7755 |
var count = this.getMeta().count; |
| 7756 |
if (count > 0 && !isNaN(value)) { |
| 7757 |
return (2 * Math.PI) / count; |
| 7758 |
} |
| 7759 |
return 0; |
| 7760 |
} |
| 7761 |
}); |
| 7762 |
}; |
| 7763 |
|
| 7764 |
},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ |
| 7765 |
'use strict'; |
| 7766 |
|
| 7767 |
var defaults = require(25); |
| 7768 |
var elements = require(40); |
| 7769 |
var helpers = require(45); |
| 7770 |
|
| 7771 |
defaults._set('radar', { |
| 7772 |
scale: { |
| 7773 |
type: 'radialLinear' |
| 7774 |
}, |
| 7775 |
elements: { |
| 7776 |
line: { |
| 7777 |
tension: 0 // no bezier in radar |
| 7778 |
} |
| 7779 |
} |
| 7780 |
}); |
| 7781 |
|
| 7782 |
module.exports = function(Chart) { |
| 7783 |
|
| 7784 |
Chart.controllers.radar = Chart.DatasetController.extend({ |
| 7785 |
|
| 7786 |
datasetElementType: elements.Line, |
| 7787 |
|
| 7788 |
dataElementType: elements.Point, |
| 7789 |
|
| 7790 |
linkScales: helpers.noop, |
| 7791 |
|
| 7792 |
update: function(reset) { |
| 7793 |
var me = this; |
| 7794 |
var meta = me.getMeta(); |
| 7795 |
var line = meta.dataset; |
| 7796 |
var points = meta.data; |
| 7797 |
var custom = line.custom || {}; |
| 7798 |
var dataset = me.getDataset(); |
| 7799 |
var lineElementOptions = me.chart.options.elements.line; |
| 7800 |
var scale = me.chart.scale; |
| 7801 |
|
| 7802 |
// Compatibility: If the properties are defined with only the old name, use those values |
| 7803 |
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { |
| 7804 |
dataset.lineTension = dataset.tension; |
| 7805 |
} |
| 7806 |
|
| 7807 |
helpers.extend(meta.dataset, { |
| 7808 |
// Utility |
| 7809 |
_datasetIndex: me.index, |
| 7810 |
_scale: scale, |
| 7811 |
// Data |
| 7812 |
_children: points, |
| 7813 |
_loop: true, |
| 7814 |
// Model |
| 7815 |
_model: { |
| 7816 |
// Appearance |
| 7817 |
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), |
| 7818 |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), |
| 7819 |
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), |
| 7820 |
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), |
| 7821 |
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), |
| 7822 |
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), |
| 7823 |
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), |
| 7824 |
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), |
| 7825 |
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), |
| 7826 |
} |
| 7827 |
}); |
| 7828 |
|
| 7829 |
meta.dataset.pivot(); |
| 7830 |
|
| 7831 |
// Update Points |
| 7832 |
helpers.each(points, function(point, index) { |
| 7833 |
me.updateElement(point, index, reset); |
| 7834 |
}, me); |
| 7835 |
|
| 7836 |
// Update bezier control points |
| 7837 |
me.updateBezierControlPoints(); |
| 7838 |
}, |
| 7839 |
updateElement: function(point, index, reset) { |
| 7840 |
var me = this; |
| 7841 |
var custom = point.custom || {}; |
| 7842 |
var dataset = me.getDataset(); |
| 7843 |
var scale = me.chart.scale; |
| 7844 |
var pointElementOptions = me.chart.options.elements.point; |
| 7845 |
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); |
| 7846 |
|
| 7847 |
// Compatibility: If the properties are defined with only the old name, use those values |
| 7848 |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
| 7849 |
dataset.pointRadius = dataset.radius; |
| 7850 |
} |
| 7851 |
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { |
| 7852 |
dataset.pointHitRadius = dataset.hitRadius; |
| 7853 |
} |
| 7854 |
|
| 7855 |
helpers.extend(point, { |
| 7856 |
// Utility |
| 7857 |
_datasetIndex: me.index, |
| 7858 |
_index: index, |
| 7859 |
_scale: scale, |
| 7860 |
|
| 7861 |
// Desired view properties |
| 7862 |
_model: { |
| 7863 |
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales |
| 7864 |
y: reset ? scale.yCenter : pointPosition.y, |
| 7865 |
|
| 7866 |
// Appearance |
| 7867 |
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), |
| 7868 |
radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), |
| 7869 |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), |
| 7870 |
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), |
| 7871 |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), |
| 7872 |
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), |
| 7873 |
|
| 7874 |
// Tooltip |
| 7875 |
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) |
| 7876 |
} |
| 7877 |
}); |
| 7878 |
|
| 7879 |
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); |
| 7880 |
}, |
| 7881 |
updateBezierControlPoints: function() { |
| 7882 |
var chartArea = this.chart.chartArea; |
| 7883 |
var meta = this.getMeta(); |
| 7884 |
|
| 7885 |
helpers.each(meta.data, function(point, index) { |
| 7886 |
var model = point._model; |
| 7887 |
var controlPoints = helpers.splineCurve( |
| 7888 |
helpers.previousItem(meta.data, index, true)._model, |
| 7889 |
model, |
| 7890 |
helpers.nextItem(meta.data, index, true)._model, |
| 7891 |
model.tension |
| 7892 |
); |
| 7893 |
|
| 7894 |
// Prevent the bezier going outside of the bounds of the graph |
| 7895 |
model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); |
| 7896 |
model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); |
| 7897 |
|
| 7898 |
model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); |
| 7899 |
model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); |
| 7900 |
|
| 7901 |
// Now pivot the point for animation |
| 7902 |
point.pivot(); |
| 7903 |
}); |
| 7904 |
}, |
| 7905 |
|
| 7906 |
setHoverStyle: function(point) { |
| 7907 |
// Point |
| 7908 |
var dataset = this.chart.data.datasets[point._datasetIndex]; |
| 7909 |
var custom = point.custom || {}; |
| 7910 |
var index = point._index; |
| 7911 |
var model = point._model; |
| 7912 |
|
| 7913 |
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); |
| 7914 |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
| 7915 |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
| 7916 |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); |
| 7917 |
}, |
| 7918 |
|
| 7919 |
removeHoverStyle: function(point) { |
| 7920 |
var dataset = this.chart.data.datasets[point._datasetIndex]; |
| 7921 |
var custom = point.custom || {}; |
| 7922 |
var index = point._index; |
| 7923 |
var model = point._model; |
| 7924 |
var pointElementOptions = this.chart.options.elements.point; |
| 7925 |
|
| 7926 |
model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); |
| 7927 |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); |
| 7928 |
model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); |
| 7929 |
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); |
| 7930 |
} |
| 7931 |
}); |
| 7932 |
}; |
| 7933 |
|
| 7934 |
},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ |
| 7935 |
'use strict'; |
| 7936 |
|
| 7937 |
var defaults = require(25); |
| 7938 |
|
| 7939 |
defaults._set('scatter', { |
| 7940 |
hover: { |
| 7941 |
mode: 'single' |
| 7942 |
}, |
| 7943 |
|
| 7944 |
scales: { |
| 7945 |
xAxes: [{ |
| 7946 |
id: 'x-axis-1', // need an ID so datasets can reference the scale |
| 7947 |
type: 'linear', // scatter should not use a category axis |
| 7948 |
position: 'bottom' |
| 7949 |
}], |
| 7950 |
yAxes: [{ |
| 7951 |
id: 'y-axis-1', |
| 7952 |
type: 'linear', |
| 7953 |
position: 'left' |
| 7954 |
}] |
| 7955 |
}, |
| 7956 |
|
| 7957 |
showLines: false, |
| 7958 |
|
| 7959 |
tooltips: { |
| 7960 |
callbacks: { |
| 7961 |
title: function() { |
| 7962 |
return ''; // doesn't make sense for scatter since data are formatted as a point |
| 7963 |
}, |
| 7964 |
label: function(item) { |
| 7965 |
return '(' + item.xLabel + ', ' + item.yLabel + ')'; |
| 7966 |
} |
| 7967 |
} |
| 7968 |
} |
| 7969 |
}); |
| 7970 |
|
| 7971 |
module.exports = function(Chart) { |
| 7972 |
|
| 7973 |
// Scatter charts use line controllers |
| 7974 |
Chart.controllers.scatter = Chart.controllers.line; |
| 7975 |
|
| 7976 |
}; |
| 7977 |
|
| 7978 |
},{"25":25}],22:[function(require,module,exports){ |
| 7979 |
/* global window: false */ |
| 7980 |
'use strict'; |
| 7981 |
|
| 7982 |
var defaults = require(25); |
| 7983 |
var Element = require(26); |
| 7984 |
var helpers = require(45); |
| 7985 |
|
| 7986 |
defaults._set('global', { |
| 7987 |
animation: { |
| 7988 |
duration: 1000, |
| 7989 |
easing: 'easeOutQuart', |
| 7990 |
onProgress: helpers.noop, |
| 7991 |
onComplete: helpers.noop |
| 7992 |
} |
| 7993 |
}); |
| 7994 |
|
| 7995 |
module.exports = function(Chart) { |
| 7996 |
|
| 7997 |
Chart.Animation = Element.extend({ |
| 7998 |
chart: null, // the animation associated chart instance |
| 7999 |
currentStep: 0, // the current animation step |
| 8000 |
numSteps: 60, // default number of steps |
| 8001 |
easing: '', // the easing to use for this animation |
| 8002 |
render: null, // render function used by the animation service |
| 8003 |
|
| 8004 |
onAnimationProgress: null, // user specified callback to fire on each step of the animation |
| 8005 |
onAnimationComplete: null, // user specified callback to fire when the animation finishes |
| 8006 |
}); |
| 8007 |
|
| 8008 |
Chart.animationService = { |
| 8009 |
frameDuration: 17, |
| 8010 |
animations: [], |
| 8011 |
dropFrames: 0, |
| 8012 |
request: null, |
| 8013 |
|
| 8014 |
/** |
| 8015 |
* @param {Chart} chart - The chart to animate. |
| 8016 |
* @param {Chart.Animation} animation - The animation that we will animate. |
| 8017 |
* @param {Number} duration - The animation duration in ms. |
| 8018 |
* @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions |
| 8019 |
*/ |
| 8020 |
addAnimation: function(chart, animation, duration, lazy) { |
| 8021 |
var animations = this.animations; |
| 8022 |
var i, ilen; |
| 8023 |
|
| 8024 |
animation.chart = chart; |
| 8025 |
|
| 8026 |
if (!lazy) { |
| 8027 |
chart.animating = true; |
| 8028 |
} |
| 8029 |
|
| 8030 |
for (i = 0, ilen = animations.length; i < ilen; ++i) { |
| 8031 |
if (animations[i].chart === chart) { |
| 8032 |
animations[i] = animation; |
| 8033 |
return; |
| 8034 |
} |
| 8035 |
} |
| 8036 |
|
| 8037 |
animations.push(animation); |
| 8038 |
|
| 8039 |
// If there are no animations queued, manually kickstart a digest, for lack of a better word |
| 8040 |
if (animations.length === 1) { |
| 8041 |
this.requestAnimationFrame(); |
| 8042 |
} |
| 8043 |
}, |
| 8044 |
|
| 8045 |
cancelAnimation: function(chart) { |
| 8046 |
var index = helpers.findIndex(this.animations, function(animation) { |
| 8047 |
return animation.chart === chart; |
| 8048 |
}); |
| 8049 |
|
| 8050 |
if (index !== -1) { |
| 8051 |
this.animations.splice(index, 1); |
| 8052 |
chart.animating = false; |
| 8053 |
} |
| 8054 |
}, |
| 8055 |
|
| 8056 |
requestAnimationFrame: function() { |
| 8057 |
var me = this; |
| 8058 |
if (me.request === null) { |
| 8059 |
// Skip animation frame requests until the active one is executed. |
| 8060 |
// This can happen when processing mouse events, e.g. 'mousemove' |
| 8061 |
// and 'mouseout' events will trigger multiple renders. |
| 8062 |
me.request = helpers.requestAnimFrame.call(window, function() { |
| 8063 |
me.request = null; |
| 8064 |
me.startDigest(); |
| 8065 |
}); |
| 8066 |
} |
| 8067 |
}, |
| 8068 |
|
| 8069 |
/** |
| 8070 |
* @private |
| 8071 |
*/ |
| 8072 |
startDigest: function() { |
| 8073 |
var me = this; |
| 8074 |
var startTime = Date.now(); |
| 8075 |
var framesToDrop = 0; |
| 8076 |
|
| 8077 |
if (me.dropFrames > 1) { |
| 8078 |
framesToDrop = Math.floor(me.dropFrames); |
| 8079 |
me.dropFrames = me.dropFrames % 1; |
| 8080 |
} |
| 8081 |
|
| 8082 |
me.advance(1 + framesToDrop); |
| 8083 |
|
| 8084 |
var endTime = Date.now(); |
| 8085 |
|
| 8086 |
me.dropFrames += (endTime - startTime) / me.frameDuration; |
| 8087 |
|
| 8088 |
// Do we have more stuff to animate? |
| 8089 |
if (me.animations.length > 0) { |
| 8090 |
me.requestAnimationFrame(); |
| 8091 |
} |
| 8092 |
}, |
| 8093 |
|
| 8094 |
/** |
| 8095 |
* @private |
| 8096 |
*/ |
| 8097 |
advance: function(count) { |
| 8098 |
var animations = this.animations; |
| 8099 |
var animation, chart; |
| 8100 |
var i = 0; |
| 8101 |
|
| 8102 |
while (i < animations.length) { |
| 8103 |
animation = animations[i]; |
| 8104 |
chart = animation.chart; |
| 8105 |
|
| 8106 |
animation.currentStep = (animation.currentStep || 0) + count; |
| 8107 |
animation.currentStep = Math.min(animation.currentStep, animation.numSteps); |
| 8108 |
|
| 8109 |
helpers.callback(animation.render, [chart, animation], chart); |
| 8110 |
helpers.callback(animation.onAnimationProgress, [animation], chart); |
| 8111 |
|
| 8112 |
if (animation.currentStep >= animation.numSteps) { |
| 8113 |
helpers.callback(animation.onAnimationComplete, [animation], chart); |
| 8114 |
chart.animating = false; |
| 8115 |
animations.splice(i, 1); |
| 8116 |
} else { |
| 8117 |
++i; |
| 8118 |
} |
| 8119 |
} |
| 8120 |
} |
| 8121 |
}; |
| 8122 |
|
| 8123 |
/** |
| 8124 |
* Provided for backward compatibility, use Chart.Animation instead |
| 8125 |
* @prop Chart.Animation#animationObject |
| 8126 |
* @deprecated since version 2.6.0 |
| 8127 |
* @todo remove at version 3 |
| 8128 |
*/ |
| 8129 |
Object.defineProperty(Chart.Animation.prototype, 'animationObject', { |
| 8130 |
get: function() { |
| 8131 |
return this; |
| 8132 |
} |
| 8133 |
}); |
| 8134 |
|
| 8135 |
/** |
| 8136 |
* Provided for backward compatibility, use Chart.Animation#chart instead |
| 8137 |
* @prop Chart.Animation#chartInstance |
| 8138 |
* @deprecated since version 2.6.0 |
| 8139 |
* @todo remove at version 3 |
| 8140 |
*/ |
| 8141 |
Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { |
| 8142 |
get: function() { |
| 8143 |
return this.chart; |
| 8144 |
}, |
| 8145 |
set: function(value) { |
| 8146 |
this.chart = value; |
| 8147 |
} |
| 8148 |
}); |
| 8149 |
|
| 8150 |
}; |
| 8151 |
|
| 8152 |
},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ |
| 8153 |
'use strict'; |
| 8154 |
|
| 8155 |
var defaults = require(25); |
| 8156 |
var helpers = require(45); |
| 8157 |
var Interaction = require(28); |
| 8158 |
var platform = require(48); |
| 8159 |
|
| 8160 |
module.exports = function(Chart) { |
| 8161 |
var plugins = Chart.plugins; |
| 8162 |
|
| 8163 |
// Create a dictionary of chart types, to allow for extension of existing types |
| 8164 |
Chart.types = {}; |
| 8165 |
|
| 8166 |
// Store a reference to each instance - allowing us to globally resize chart instances on window resize. |
| 8167 |
// Destroy method on the chart will remove the instance of the chart from this reference. |
| 8168 |
Chart.instances = {}; |
| 8169 |
|
| 8170 |
// Controllers available for dataset visualization eg. bar, line, slice, etc. |
| 8171 |
Chart.controllers = {}; |
| 8172 |
|
| 8173 |
/** |
| 8174 |
* Initializes the given config with global and chart default values. |
| 8175 |
*/ |
| 8176 |
function initConfig(config) { |
| 8177 |
config = config || {}; |
| 8178 |
|
| 8179 |
// Do NOT use configMerge() for the data object because this method merges arrays |
| 8180 |
// and so would change references to labels and datasets, preventing data updates. |
| 8181 |
var data = config.data = config.data || {}; |
| 8182 |
data.datasets = data.datasets || []; |
| 8183 |
data.labels = data.labels || []; |
| 8184 |
|
| 8185 |
config.options = helpers.configMerge( |
| 8186 |
defaults.global, |
| 8187 |
defaults[config.type], |
| 8188 |
config.options || {}); |
| 8189 |
|
| 8190 |
return config; |
| 8191 |
} |
| 8192 |
|
| 8193 |
/** |
| 8194 |
* Updates the config of the chart |
| 8195 |
* @param chart {Chart} chart to update the options for |
| 8196 |
*/ |
| 8197 |
function updateConfig(chart) { |
| 8198 |
var newOptions = chart.options; |
| 8199 |
|
| 8200 |
// Update Scale(s) with options |
| 8201 |
if (newOptions.scale) { |
| 8202 |
chart.scale.options = newOptions.scale; |
| 8203 |
} else if (newOptions.scales) { |
| 8204 |
newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { |
| 8205 |
chart.scales[scaleOptions.id].options = scaleOptions; |
| 8206 |
}); |
| 8207 |
} |
| 8208 |
|
| 8209 |
// Tooltip |
| 8210 |
chart.tooltip._options = newOptions.tooltips; |
| 8211 |
} |
| 8212 |
|
| 8213 |
function positionIsHorizontal(position) { |
| 8214 |
return position === 'top' || position === 'bottom'; |
| 8215 |
} |
| 8216 |
|
| 8217 |
helpers.extend(Chart.prototype, /** @lends Chart */ { |
| 8218 |
/** |
| 8219 |
* @private |
| 8220 |
*/ |
| 8221 |
construct: function(item, config) { |
| 8222 |
var me = this; |
| 8223 |
|
| 8224 |
config = initConfig(config); |
| 8225 |
|
| 8226 |
var context = platform.acquireContext(item, config); |
| 8227 |
var canvas = context && context.canvas; |
| 8228 |
var height = canvas && canvas.height; |
| 8229 |
var width = canvas && canvas.width; |
| 8230 |
|
| 8231 |
me.id = helpers.uid(); |
| 8232 |
me.ctx = context; |
| 8233 |
me.canvas = canvas; |
| 8234 |
me.config = config; |
| 8235 |
me.width = width; |
| 8236 |
me.height = height; |
| 8237 |
me.aspectRatio = height ? width / height : null; |
| 8238 |
me.options = config.options; |
| 8239 |
me._bufferedRender = false; |
| 8240 |
|
| 8241 |
/** |
| 8242 |
* Provided for backward compatibility, Chart and Chart.Controller have been merged, |
| 8243 |
* the "instance" still need to be defined since it might be called from plugins. |
| 8244 |
* @prop Chart#chart |
| 8245 |
* @deprecated since version 2.6.0 |
| 8246 |
* @todo remove at version 3 |
| 8247 |
* @private |
| 8248 |
*/ |
| 8249 |
me.chart = me; |
| 8250 |
me.controller = me; // chart.chart.controller #inception |
| 8251 |
|
| 8252 |
// Add the chart instance to the global namespace |
| 8253 |
Chart.instances[me.id] = me; |
| 8254 |
|
| 8255 |
// Define alias to the config data: `chart.data === chart.config.data` |
| 8256 |
Object.defineProperty(me, 'data', { |
| 8257 |
get: function() { |
| 8258 |
return me.config.data; |
| 8259 |
}, |
| 8260 |
set: function(value) { |
| 8261 |
me.config.data = value; |
| 8262 |
} |
| 8263 |
}); |
| 8264 |
|
| 8265 |
if (!context || !canvas) { |
| 8266 |
// The given item is not a compatible context2d element, let's return before finalizing |
| 8267 |
// the chart initialization but after setting basic chart / controller properties that |
| 8268 |
// can help to figure out that the chart is not valid (e.g chart.canvas !== null); |
| 8269 |
// https://github.com/chartjs/Chart.js/issues/2807 |
| 8270 |
console.error("Failed to create chart: can't acquire context from the given item"); |
| 8271 |
return; |
| 8272 |
} |
| 8273 |
|
| 8274 |
me.initialize(); |
| 8275 |
me.update(); |
| 8276 |
}, |
| 8277 |
|
| 8278 |
/** |
| 8279 |
* @private |
| 8280 |
*/ |
| 8281 |
initialize: function() { |
| 8282 |
var me = this; |
| 8283 |
|
| 8284 |
// Before init plugin notification |
| 8285 |
plugins.notify(me, 'beforeInit'); |
| 8286 |
|
| 8287 |
helpers.retinaScale(me, me.options.devicePixelRatio); |
| 8288 |
|
| 8289 |
me.bindEvents(); |
| 8290 |
|
| 8291 |
if (me.options.responsive) { |
| 8292 |
// Initial resize before chart draws (must be silent to preserve initial animations). |
| 8293 |
me.resize(true); |
| 8294 |
} |
| 8295 |
|
| 8296 |
// Make sure scales have IDs and are built before we build any controllers. |
| 8297 |
me.ensureScalesHaveIDs(); |
| 8298 |
me.buildScales(); |
| 8299 |
me.initToolTip(); |
| 8300 |
|
| 8301 |
// After init plugin notification |
| 8302 |
plugins.notify(me, 'afterInit'); |
| 8303 |
|
| 8304 |
return me; |
| 8305 |
}, |
| 8306 |
|
| 8307 |
clear: function() { |
| 8308 |
helpers.canvas.clear(this); |
| 8309 |
return this; |
| 8310 |
}, |
| 8311 |
|
| 8312 |
stop: function() { |
| 8313 |
// Stops any current animation loop occurring |
| 8314 |
Chart.animationService.cancelAnimation(this); |
| 8315 |
return this; |
| 8316 |
}, |
| 8317 |
|
| 8318 |
resize: function(silent) { |
| 8319 |
var me = this; |
| 8320 |
var options = me.options; |
| 8321 |
var canvas = me.canvas; |
| 8322 |
var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; |
| 8323 |
|
| 8324 |
// the canvas render width and height will be casted to integers so make sure that |
| 8325 |
// the canvas display style uses the same integer values to avoid blurring effect. |
| 8326 |
|
| 8327 |
// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased |
| 8328 |
var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); |
| 8329 |
var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); |
| 8330 |
|
| 8331 |
if (me.width === newWidth && me.height === newHeight) { |
| 8332 |
return; |
| 8333 |
} |
| 8334 |
|
| 8335 |
canvas.width = me.width = newWidth; |
| 8336 |
canvas.height = me.height = newHeight; |
| 8337 |
canvas.style.width = newWidth + 'px'; |
| 8338 |
canvas.style.height = newHeight + 'px'; |
| 8339 |
|
| 8340 |
helpers.retinaScale(me, options.devicePixelRatio); |
| 8341 |
|
| 8342 |
if (!silent) { |
| 8343 |
// Notify any plugins about the resize |
| 8344 |
var newSize = {width: newWidth, height: newHeight}; |
| 8345 |
plugins.notify(me, 'resize', [newSize]); |
| 8346 |
|
| 8347 |
// Notify of resize |
| 8348 |
if (me.options.onResize) { |
| 8349 |
me.options.onResize(me, newSize); |
| 8350 |
} |
| 8351 |
|
| 8352 |
me.stop(); |
| 8353 |
me.update(me.options.responsiveAnimationDuration); |
| 8354 |
} |
| 8355 |
}, |
| 8356 |
|
| 8357 |
ensureScalesHaveIDs: function() { |
| 8358 |
var options = this.options; |
| 8359 |
var scalesOptions = options.scales || {}; |
| 8360 |
var scaleOptions = options.scale; |
| 8361 |
|
| 8362 |
helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { |
| 8363 |
xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); |
| 8364 |
}); |
| 8365 |
|
| 8366 |
helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { |
| 8367 |
yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); |
| 8368 |
}); |
| 8369 |
|
| 8370 |
if (scaleOptions) { |
| 8371 |
scaleOptions.id = scaleOptions.id || 'scale'; |
| 8372 |
} |
| 8373 |
}, |
| 8374 |
|
| 8375 |
/** |
| 8376 |
* Builds a map of scale ID to scale object for future lookup. |
| 8377 |
*/ |
| 8378 |
buildScales: function() { |
| 8379 |
var me = this; |
| 8380 |
var options = me.options; |
| 8381 |
var scales = me.scales = {}; |
| 8382 |
var items = []; |
| 8383 |
|
| 8384 |
if (options.scales) { |
| 8385 |
items = items.concat( |
| 8386 |
(options.scales.xAxes || []).map(function(xAxisOptions) { |
| 8387 |
return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; |
| 8388 |
}), |
| 8389 |
(options.scales.yAxes || []).map(function(yAxisOptions) { |
| 8390 |
return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; |
| 8391 |
}) |
| 8392 |
); |
| 8393 |
} |
| 8394 |
|
| 8395 |
if (options.scale) { |
| 8396 |
items.push({ |
| 8397 |
options: options.scale, |
| 8398 |
dtype: 'radialLinear', |
| 8399 |
isDefault: true, |
| 8400 |
dposition: 'chartArea' |
| 8401 |
}); |
| 8402 |
} |
| 8403 |
|
| 8404 |
helpers.each(items, function(item) { |
| 8405 |
var scaleOptions = item.options; |
| 8406 |
var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); |
| 8407 |
var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); |
| 8408 |
if (!scaleClass) { |
| 8409 |
return; |
| 8410 |
} |
| 8411 |
|
| 8412 |
if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { |
| 8413 |
scaleOptions.position = item.dposition; |
| 8414 |
} |
| 8415 |
|
| 8416 |
var scale = new scaleClass({ |
| 8417 |
id: scaleOptions.id, |
| 8418 |
options: scaleOptions, |
| 8419 |
ctx: me.ctx, |
| 8420 |
chart: me |
| 8421 |
}); |
| 8422 |
|
| 8423 |
scales[scale.id] = scale; |
| 8424 |
scale.mergeTicksOptions(); |
| 8425 |
|
| 8426 |
// TODO(SB): I think we should be able to remove this custom case (options.scale) |
| 8427 |
// and consider it as a regular scale part of the "scales"" map only! This would |
| 8428 |
// make the logic easier and remove some useless? custom code. |
| 8429 |
if (item.isDefault) { |
| 8430 |
me.scale = scale; |
| 8431 |
} |
| 8432 |
}); |
| 8433 |
|
| 8434 |
Chart.scaleService.addScalesToLayout(this); |
| 8435 |
}, |
| 8436 |
|
| 8437 |
buildOrUpdateControllers: function() { |
| 8438 |
var me = this; |
| 8439 |
var types = []; |
| 8440 |
var newControllers = []; |
| 8441 |
|
| 8442 |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
| 8443 |
var meta = me.getDatasetMeta(datasetIndex); |
| 8444 |
var type = dataset.type || me.config.type; |
| 8445 |
|
| 8446 |
if (meta.type && meta.type !== type) { |
| 8447 |
me.destroyDatasetMeta(datasetIndex); |
| 8448 |
meta = me.getDatasetMeta(datasetIndex); |
| 8449 |
} |
| 8450 |
meta.type = type; |
| 8451 |
|
| 8452 |
types.push(meta.type); |
| 8453 |
|
| 8454 |
if (meta.controller) { |
| 8455 |
meta.controller.updateIndex(datasetIndex); |
| 8456 |
} else { |
| 8457 |
var ControllerClass = Chart.controllers[meta.type]; |
| 8458 |
if (ControllerClass === undefined) { |
| 8459 |
throw new Error('"' + meta.type + '" is not a chart type.'); |
| 8460 |
} |
| 8461 |
|
| 8462 |
meta.controller = new ControllerClass(me, datasetIndex); |
| 8463 |
newControllers.push(meta.controller); |
| 8464 |
} |
| 8465 |
}, me); |
| 8466 |
|
| 8467 |
return newControllers; |
| 8468 |
}, |
| 8469 |
|
| 8470 |
/** |
| 8471 |
* Reset the elements of all datasets |
| 8472 |
* @private |
| 8473 |
*/ |
| 8474 |
resetElements: function() { |
| 8475 |
var me = this; |
| 8476 |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
| 8477 |
me.getDatasetMeta(datasetIndex).controller.reset(); |
| 8478 |
}, me); |
| 8479 |
}, |
| 8480 |
|
| 8481 |
/** |
| 8482 |
* Resets the chart back to it's state before the initial animation |
| 8483 |
*/ |
| 8484 |
reset: function() { |
| 8485 |
this.resetElements(); |
| 8486 |
this.tooltip.initialize(); |
| 8487 |
}, |
| 8488 |
|
| 8489 |
update: function(config) { |
| 8490 |
var me = this; |
| 8491 |
|
| 8492 |
if (!config || typeof config !== 'object') { |
| 8493 |
// backwards compatibility |
| 8494 |
config = { |
| 8495 |
duration: config, |
| 8496 |
lazy: arguments[1] |
| 8497 |
}; |
| 8498 |
} |
| 8499 |
|
| 8500 |
updateConfig(me); |
| 8501 |
|
| 8502 |
if (plugins.notify(me, 'beforeUpdate') === false) { |
| 8503 |
return; |
| 8504 |
} |
| 8505 |
|
| 8506 |
// In case the entire data object changed |
| 8507 |
me.tooltip._data = me.data; |
| 8508 |
|
| 8509 |
// Make sure dataset controllers are updated and new controllers are reset |
| 8510 |
var newControllers = me.buildOrUpdateControllers(); |
| 8511 |
|
| 8512 |
// Make sure all dataset controllers have correct meta data counts |
| 8513 |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
| 8514 |
me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); |
| 8515 |
}, me); |
| 8516 |
|
| 8517 |
me.updateLayout(); |
| 8518 |
|
| 8519 |
// Can only reset the new controllers after the scales have been updated |
| 8520 |
helpers.each(newControllers, function(controller) { |
| 8521 |
controller.reset(); |
| 8522 |
}); |
| 8523 |
|
| 8524 |
me.updateDatasets(); |
| 8525 |
|
| 8526 |
// Do this before render so that any plugins that need final scale updates can use it |
| 8527 |
plugins.notify(me, 'afterUpdate'); |
| 8528 |
|
| 8529 |
if (me._bufferedRender) { |
| 8530 |
me._bufferedRequest = { |
| 8531 |
duration: config.duration, |
| 8532 |
easing: config.easing, |
| 8533 |
lazy: config.lazy |
| 8534 |
}; |
| 8535 |
} else { |
| 8536 |
me.render(config); |
| 8537 |
} |
| 8538 |
}, |
| 8539 |
|
| 8540 |
/** |
| 8541 |
* Updates the chart layout unless a plugin returns `false` to the `beforeLayout` |
| 8542 |
* hook, in which case, plugins will not be called on `afterLayout`. |
| 8543 |
* @private |
| 8544 |
*/ |
| 8545 |
updateLayout: function() { |
| 8546 |
var me = this; |
| 8547 |
|
| 8548 |
if (plugins.notify(me, 'beforeLayout') === false) { |
| 8549 |
return; |
| 8550 |
} |
| 8551 |
|
| 8552 |
Chart.layoutService.update(this, this.width, this.height); |
| 8553 |
|
| 8554 |
/** |
| 8555 |
* Provided for backward compatibility, use `afterLayout` instead. |
| 8556 |
* @method IPlugin#afterScaleUpdate |
| 8557 |
* @deprecated since version 2.5.0 |
| 8558 |
* @todo remove at version 3 |
| 8559 |
* @private |
| 8560 |
*/ |
| 8561 |
plugins.notify(me, 'afterScaleUpdate'); |
| 8562 |
plugins.notify(me, 'afterLayout'); |
| 8563 |
}, |
| 8564 |
|
| 8565 |
/** |
| 8566 |
* Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` |
| 8567 |
* hook, in which case, plugins will not be called on `afterDatasetsUpdate`. |
| 8568 |
* @private |
| 8569 |
*/ |
| 8570 |
updateDatasets: function() { |
| 8571 |
var me = this; |
| 8572 |
|
| 8573 |
if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { |
| 8574 |
return; |
| 8575 |
} |
| 8576 |
|
| 8577 |
for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { |
| 8578 |
me.updateDataset(i); |
| 8579 |
} |
| 8580 |
|
| 8581 |
plugins.notify(me, 'afterDatasetsUpdate'); |
| 8582 |
}, |
| 8583 |
|
| 8584 |
/** |
| 8585 |
* Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` |
| 8586 |
* hook, in which case, plugins will not be called on `afterDatasetUpdate`. |
| 8587 |
* @private |
| 8588 |
*/ |
| 8589 |
updateDataset: function(index) { |
| 8590 |
var me = this; |
| 8591 |
var meta = me.getDatasetMeta(index); |
| 8592 |
var args = { |
| 8593 |
meta: meta, |
| 8594 |
index: index |
| 8595 |
}; |
| 8596 |
|
| 8597 |
if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { |
| 8598 |
return; |
| 8599 |
} |
| 8600 |
|
| 8601 |
meta.controller.update(); |
| 8602 |
|
| 8603 |
plugins.notify(me, 'afterDatasetUpdate', [args]); |
| 8604 |
}, |
| 8605 |
|
| 8606 |
render: function(config) { |
| 8607 |
var me = this; |
| 8608 |
|
| 8609 |
if (!config || typeof config !== 'object') { |
| 8610 |
// backwards compatibility |
| 8611 |
config = { |
| 8612 |
duration: config, |
| 8613 |
lazy: arguments[1] |
| 8614 |
}; |
| 8615 |
} |
| 8616 |
|
| 8617 |
var duration = config.duration; |
| 8618 |
var lazy = config.lazy; |
| 8619 |
|
| 8620 |
if (plugins.notify(me, 'beforeRender') === false) { |
| 8621 |
return; |
| 8622 |
} |
| 8623 |
|
| 8624 |
var animationOptions = me.options.animation; |
| 8625 |
var onComplete = function(animation) { |
| 8626 |
plugins.notify(me, 'afterRender'); |
| 8627 |
helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); |
| 8628 |
}; |
| 8629 |
|
| 8630 |
if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { |
| 8631 |
var animation = new Chart.Animation({ |
| 8632 |
numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps |
| 8633 |
easing: config.easing || animationOptions.easing, |
| 8634 |
|
| 8635 |
render: function(chart, animationObject) { |
| 8636 |
var easingFunction = helpers.easing.effects[animationObject.easing]; |
| 8637 |
var currentStep = animationObject.currentStep; |
| 8638 |
var stepDecimal = currentStep / animationObject.numSteps; |
| 8639 |
|
| 8640 |
chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); |
| 8641 |
}, |
| 8642 |
|
| 8643 |
onAnimationProgress: animationOptions.onProgress, |
| 8644 |
onAnimationComplete: onComplete |
| 8645 |
}); |
| 8646 |
|
| 8647 |
Chart.animationService.addAnimation(me, animation, duration, lazy); |
| 8648 |
} else { |
| 8649 |
me.draw(); |
| 8650 |
|
| 8651 |
// See https://github.com/chartjs/Chart.js/issues/3781 |
| 8652 |
onComplete(new Chart.Animation({numSteps: 0, chart: me})); |
| 8653 |
} |
| 8654 |
|
| 8655 |
return me; |
| 8656 |
}, |
| 8657 |
|
| 8658 |
draw: function(easingValue) { |
| 8659 |
var me = this; |
| 8660 |
|
| 8661 |
me.clear(); |
| 8662 |
|
| 8663 |
if (helpers.isNullOrUndef(easingValue)) { |
| 8664 |
easingValue = 1; |
| 8665 |
} |
| 8666 |
|
| 8667 |
me.transition(easingValue); |
| 8668 |
|
| 8669 |
if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { |
| 8670 |
return; |
| 8671 |
} |
| 8672 |
|
| 8673 |
// Draw all the scales |
| 8674 |
helpers.each(me.boxes, function(box) { |
| 8675 |
box.draw(me.chartArea); |
| 8676 |
}, me); |
| 8677 |
|
| 8678 |
if (me.scale) { |
| 8679 |
me.scale.draw(); |
| 8680 |
} |
| 8681 |
|
| 8682 |
me.drawDatasets(easingValue); |
| 8683 |
|
| 8684 |
// Finally draw the tooltip |
| 8685 |
me.tooltip.draw(); |
| 8686 |
|
| 8687 |
plugins.notify(me, 'afterDraw', [easingValue]); |
| 8688 |
}, |
| 8689 |
|
| 8690 |
/** |
| 8691 |
* @private |
| 8692 |
*/ |
| 8693 |
transition: function(easingValue) { |
| 8694 |
var me = this; |
| 8695 |
|
| 8696 |
for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { |
| 8697 |
if (me.isDatasetVisible(i)) { |
| 8698 |
me.getDatasetMeta(i).controller.transition(easingValue); |
| 8699 |
} |
| 8700 |
} |
| 8701 |
|
| 8702 |
me.tooltip.transition(easingValue); |
| 8703 |
}, |
| 8704 |
|
| 8705 |
/** |
| 8706 |
* Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` |
| 8707 |
* hook, in which case, plugins will not be called on `afterDatasetsDraw`. |
| 8708 |
* @private |
| 8709 |
*/ |
| 8710 |
drawDatasets: function(easingValue) { |
| 8711 |
var me = this; |
| 8712 |
|
| 8713 |
if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { |
| 8714 |
return; |
| 8715 |
} |
| 8716 |
|
| 8717 |
// Draw datasets reversed to support proper line stacking |
| 8718 |
for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { |
| 8719 |
if (me.isDatasetVisible(i)) { |
| 8720 |
me.drawDataset(i, easingValue); |
| 8721 |
} |
| 8722 |
} |
| 8723 |
|
| 8724 |
plugins.notify(me, 'afterDatasetsDraw', [easingValue]); |
| 8725 |
}, |
| 8726 |
|
| 8727 |
/** |
| 8728 |
* Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` |
| 8729 |
* hook, in which case, plugins will not be called on `afterDatasetDraw`. |
| 8730 |
* @private |
| 8731 |
*/ |
| 8732 |
drawDataset: function(index, easingValue) { |
| 8733 |
var me = this; |
| 8734 |
var meta = me.getDatasetMeta(index); |
| 8735 |
var args = { |
| 8736 |
meta: meta, |
| 8737 |
index: index, |
| 8738 |
easingValue: easingValue |
| 8739 |
}; |
| 8740 |
|
| 8741 |
if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { |
| 8742 |
return; |
| 8743 |
} |
| 8744 |
|
| 8745 |
meta.controller.draw(easingValue); |
| 8746 |
|
| 8747 |
plugins.notify(me, 'afterDatasetDraw', [args]); |
| 8748 |
}, |
| 8749 |
|
| 8750 |
// Get the single element that was clicked on |
| 8751 |
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw |
| 8752 |
getElementAtEvent: function(e) { |
| 8753 |
return Interaction.modes.single(this, e); |
| 8754 |
}, |
| 8755 |
|
| 8756 |
getElementsAtEvent: function(e) { |
| 8757 |
return Interaction.modes.label(this, e, {intersect: true}); |
| 8758 |
}, |
| 8759 |
|
| 8760 |
getElementsAtXAxis: function(e) { |
| 8761 |
return Interaction.modes['x-axis'](this, e, {intersect: true}); |
| 8762 |
}, |
| 8763 |
|
| 8764 |
getElementsAtEventForMode: function(e, mode, options) { |
| 8765 |
var method = Interaction.modes[mode]; |
| 8766 |
if (typeof method === 'function') { |
| 8767 |
return method(this, e, options); |
| 8768 |
} |
| 8769 |
|
| 8770 |
return []; |
| 8771 |
}, |
| 8772 |
|
| 8773 |
getDatasetAtEvent: function(e) { |
| 8774 |
return Interaction.modes.dataset(this, e, {intersect: true}); |
| 8775 |
}, |
| 8776 |
|
| 8777 |
getDatasetMeta: function(datasetIndex) { |
| 8778 |
var me = this; |
| 8779 |
var dataset = me.data.datasets[datasetIndex]; |
| 8780 |
if (!dataset._meta) { |
| 8781 |
dataset._meta = {}; |
| 8782 |
} |
| 8783 |
|
| 8784 |
var meta = dataset._meta[me.id]; |
| 8785 |
if (!meta) { |
| 8786 |
meta = dataset._meta[me.id] = { |
| 8787 |
type: null, |
| 8788 |
data: [], |
| 8789 |
dataset: null, |
| 8790 |
controller: null, |
| 8791 |
hidden: null, // See isDatasetVisible() comment |
| 8792 |
xAxisID: null, |
| 8793 |
yAxisID: null |
| 8794 |
}; |
| 8795 |
} |
| 8796 |
|
| 8797 |
return meta; |
| 8798 |
}, |
| 8799 |
|
| 8800 |
getVisibleDatasetCount: function() { |
| 8801 |
var count = 0; |
| 8802 |
for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { |
| 8803 |
if (this.isDatasetVisible(i)) { |
| 8804 |
count++; |
| 8805 |
} |
| 8806 |
} |
| 8807 |
return count; |
| 8808 |
}, |
| 8809 |
|
| 8810 |
isDatasetVisible: function(datasetIndex) { |
| 8811 |
var meta = this.getDatasetMeta(datasetIndex); |
| 8812 |
|
| 8813 |
// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, |
| 8814 |
// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. |
| 8815 |
return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; |
| 8816 |
}, |
| 8817 |
|
| 8818 |
generateLegend: function() { |
| 8819 |
return this.options.legendCallback(this); |
| 8820 |
}, |
| 8821 |
|
| 8822 |
/** |
| 8823 |
* @private |
| 8824 |
*/ |
| 8825 |
destroyDatasetMeta: function(datasetIndex) { |
| 8826 |
var id = this.id; |
| 8827 |
var dataset = this.data.datasets[datasetIndex]; |
| 8828 |
var meta = dataset._meta && dataset._meta[id]; |
| 8829 |
|
| 8830 |
if (meta) { |
| 8831 |
meta.controller.destroy(); |
| 8832 |
delete dataset._meta[id]; |
| 8833 |
} |
| 8834 |
}, |
| 8835 |
|
| 8836 |
destroy: function() { |
| 8837 |
var me = this; |
| 8838 |
var canvas = me.canvas; |
| 8839 |
var i, ilen; |
| 8840 |
|
| 8841 |
me.stop(); |
| 8842 |
|
| 8843 |
// dataset controllers need to cleanup associated data |
| 8844 |
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { |
| 8845 |
me.destroyDatasetMeta(i); |
| 8846 |
} |
| 8847 |
|
| 8848 |
if (canvas) { |
| 8849 |
me.unbindEvents(); |
| 8850 |
helpers.canvas.clear(me); |
| 8851 |
platform.releaseContext(me.ctx); |
| 8852 |
me.canvas = null; |
| 8853 |
me.ctx = null; |
| 8854 |
} |
| 8855 |
|
| 8856 |
plugins.notify(me, 'destroy'); |
| 8857 |
|
| 8858 |
delete Chart.instances[me.id]; |
| 8859 |
}, |
| 8860 |
|
| 8861 |
toBase64Image: function() { |
| 8862 |
return this.canvas.toDataURL.apply(this.canvas, arguments); |
| 8863 |
}, |
| 8864 |
|
| 8865 |
initToolTip: function() { |
| 8866 |
var me = this; |
| 8867 |
me.tooltip = new Chart.Tooltip({ |
| 8868 |
_chart: me, |
| 8869 |
_chartInstance: me, // deprecated, backward compatibility |
| 8870 |
_data: me.data, |
| 8871 |
_options: me.options.tooltips |
| 8872 |
}, me); |
| 8873 |
}, |
| 8874 |
|
| 8875 |
/** |
| 8876 |
* @private |
| 8877 |
*/ |
| 8878 |
bindEvents: function() { |
| 8879 |
var me = this; |
| 8880 |
var listeners = me._listeners = {}; |
| 8881 |
var listener = function() { |
| 8882 |
me.eventHandler.apply(me, arguments); |
| 8883 |
}; |
| 8884 |
|
| 8885 |
helpers.each(me.options.events, function(type) { |
| 8886 |
platform.addEventListener(me, type, listener); |
| 8887 |
listeners[type] = listener; |
| 8888 |
}); |
| 8889 |
|
| 8890 |
// Elements used to detect size change should not be injected for non responsive charts. |
| 8891 |
// See https://github.com/chartjs/Chart.js/issues/2210 |
| 8892 |
if (me.options.responsive) { |
| 8893 |
listener = function() { |
| 8894 |
me.resize(); |
| 8895 |
}; |
| 8896 |
|
| 8897 |
platform.addEventListener(me, 'resize', listener); |
| 8898 |
listeners.resize = listener; |
| 8899 |
} |
| 8900 |
}, |
| 8901 |
|
| 8902 |
/** |
| 8903 |
* @private |
| 8904 |
*/ |
| 8905 |
unbindEvents: function() { |
| 8906 |
var me = this; |
| 8907 |
var listeners = me._listeners; |
| 8908 |
if (!listeners) { |
| 8909 |
return; |
| 8910 |
} |
| 8911 |
|
| 8912 |
delete me._listeners; |
| 8913 |
helpers.each(listeners, function(listener, type) { |
| 8914 |
platform.removeEventListener(me, type, listener); |
| 8915 |
}); |
| 8916 |
}, |
| 8917 |
|
| 8918 |
updateHoverStyle: function(elements, mode, enabled) { |
| 8919 |
var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; |
| 8920 |
var element, i, ilen; |
| 8921 |
|
| 8922 |
for (i = 0, ilen = elements.length; i < ilen; ++i) { |
| 8923 |
element = elements[i]; |
| 8924 |
if (element) { |
| 8925 |
this.getDatasetMeta(element._datasetIndex).controller[method](element); |
| 8926 |
} |
| 8927 |
} |
| 8928 |
}, |
| 8929 |
|
| 8930 |
/** |
| 8931 |
* @private |
| 8932 |
*/ |
| 8933 |
eventHandler: function(e) { |
| 8934 |
var me = this; |
| 8935 |
var tooltip = me.tooltip; |
| 8936 |
|
| 8937 |
if (plugins.notify(me, 'beforeEvent', [e]) === false) { |
| 8938 |
return; |
| 8939 |
} |
| 8940 |
|
| 8941 |
// Buffer any update calls so that renders do not occur |
| 8942 |
me._bufferedRender = true; |
| 8943 |
me._bufferedRequest = null; |
| 8944 |
|
| 8945 |
var changed = me.handleEvent(e); |
| 8946 |
changed |= tooltip && tooltip.handleEvent(e); |
| 8947 |
|
| 8948 |
plugins.notify(me, 'afterEvent', [e]); |
| 8949 |
|
| 8950 |
var bufferedRequest = me._bufferedRequest; |
| 8951 |
if (bufferedRequest) { |
| 8952 |
// If we have an update that was triggered, we need to do a normal render |
| 8953 |
me.render(bufferedRequest); |
| 8954 |
} else if (changed && !me.animating) { |
| 8955 |
// If entering, leaving, or changing elements, animate the change via pivot |
| 8956 |
me.stop(); |
| 8957 |
|
| 8958 |
// We only need to render at this point. Updating will cause scales to be |
| 8959 |
// recomputed generating flicker & using more memory than necessary. |
| 8960 |
me.render(me.options.hover.animationDuration, true); |
| 8961 |
} |
| 8962 |
|
| 8963 |
me._bufferedRender = false; |
| 8964 |
me._bufferedRequest = null; |
| 8965 |
|
| 8966 |
return me; |
| 8967 |
}, |
| 8968 |
|
| 8969 |
/** |
| 8970 |
* Handle an event |
| 8971 |
* @private |
| 8972 |
* @param {IEvent} event the event to handle |
| 8973 |
* @return {Boolean} true if the chart needs to re-render |
| 8974 |
*/ |
| 8975 |
handleEvent: function(e) { |
| 8976 |
var me = this; |
| 8977 |
var options = me.options || {}; |
| 8978 |
var hoverOptions = options.hover; |
| 8979 |
var changed = false; |
| 8980 |
|
| 8981 |
me.lastActive = me.lastActive || []; |
| 8982 |
|
| 8983 |
// Find Active Elements for hover and tooltips |
| 8984 |
if (e.type === 'mouseout') { |
| 8985 |
me.active = []; |
| 8986 |
} else { |
| 8987 |
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); |
| 8988 |
} |
| 8989 |
|
| 8990 |
// Invoke onHover hook |
| 8991 |
// Need to call with native event here to not break backwards compatibility |
| 8992 |
helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); |
| 8993 |
|
| 8994 |
if (e.type === 'mouseup' || e.type === 'click') { |
| 8995 |
if (options.onClick) { |
| 8996 |
// Use e.native here for backwards compatibility |
| 8997 |
options.onClick.call(me, e.native, me.active); |
| 8998 |
} |
| 8999 |
} |
| 9000 |
|
| 9001 |
// Remove styling for last active (even if it may still be active) |
| 9002 |
if (me.lastActive.length) { |
| 9003 |
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); |
| 9004 |
} |
| 9005 |
|
| 9006 |
// Built in hover styling |
| 9007 |
if (me.active.length && hoverOptions.mode) { |
| 9008 |
me.updateHoverStyle(me.active, hoverOptions.mode, true); |
| 9009 |
} |
| 9010 |
|
| 9011 |
changed = !helpers.arrayEquals(me.active, me.lastActive); |
| 9012 |
|
| 9013 |
// Remember Last Actives |
| 9014 |
me.lastActive = me.active; |
| 9015 |
|
| 9016 |
return changed; |
| 9017 |
} |
| 9018 |
}); |
| 9019 |
|
| 9020 |
/** |
| 9021 |
* Provided for backward compatibility, use Chart instead. |
| 9022 |
* @class Chart.Controller |
| 9023 |
* @deprecated since version 2.6.0 |
| 9024 |
* @todo remove at version 3 |
| 9025 |
* @private |
| 9026 |
*/ |
| 9027 |
Chart.Controller = Chart; |
| 9028 |
}; |
| 9029 |
|
| 9030 |
},{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){ |
| 9031 |
'use strict'; |
| 9032 |
|
| 9033 |
var helpers = require(45); |
| 9034 |
|
| 9035 |
module.exports = function(Chart) { |
| 9036 |
|
| 9037 |
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; |
| 9038 |
|
| 9039 |
/** |
| 9040 |
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', |
| 9041 |
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are |
| 9042 |
* called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. |
| 9043 |
*/ |
| 9044 |
function listenArrayEvents(array, listener) { |
| 9045 |
if (array._chartjs) { |
| 9046 |
array._chartjs.listeners.push(listener); |
| 9047 |
return; |
| 9048 |
} |
| 9049 |
|
| 9050 |
Object.defineProperty(array, '_chartjs', { |
| 9051 |
configurable: true, |
| 9052 |
enumerable: false, |
| 9053 |
value: { |
| 9054 |
listeners: [listener] |
| 9055 |
} |
| 9056 |
}); |
| 9057 |
|
| 9058 |
arrayEvents.forEach(function(key) { |
| 9059 |
var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); |
| 9060 |
var base = array[key]; |
| 9061 |
|
| 9062 |
Object.defineProperty(array, key, { |
| 9063 |
configurable: true, |
| 9064 |
enumerable: false, |
| 9065 |
value: function() { |
| 9066 |
var args = Array.prototype.slice.call(arguments); |
| 9067 |
var res = base.apply(this, args); |
| 9068 |
|
| 9069 |
helpers.each(array._chartjs.listeners, function(object) { |
| 9070 |
if (typeof object[method] === 'function') { |
| 9071 |
object[method].apply(object, args); |
| 9072 |
} |
| 9073 |
}); |
| 9074 |
|
| 9075 |
return res; |
| 9076 |
} |
| 9077 |
}); |
| 9078 |
}); |
| 9079 |
} |
| 9080 |
|
| 9081 |
/** |
| 9082 |
* Removes the given array event listener and cleanup extra attached properties (such as |
| 9083 |
* the _chartjs stub and overridden methods) if array doesn't have any more listeners. |
| 9084 |
*/ |
| 9085 |
function unlistenArrayEvents(array, listener) { |
| 9086 |
var stub = array._chartjs; |
| 9087 |
if (!stub) { |
| 9088 |
return; |
| 9089 |
} |
| 9090 |
|
| 9091 |
var listeners = stub.listeners; |
| 9092 |
var index = listeners.indexOf(listener); |
| 9093 |
if (index !== -1) { |
| 9094 |
listeners.splice(index, 1); |
| 9095 |
} |
| 9096 |
|
| 9097 |
if (listeners.length > 0) { |
| 9098 |
return; |
| 9099 |
} |
| 9100 |
|
| 9101 |
arrayEvents.forEach(function(key) { |
| 9102 |
delete array[key]; |
| 9103 |
}); |
| 9104 |
|
| 9105 |
delete array._chartjs; |
| 9106 |
} |
| 9107 |
|
| 9108 |
// Base class for all dataset controllers (line, bar, etc) |
| 9109 |
Chart.DatasetController = function(chart, datasetIndex) { |
| 9110 |
this.initialize(chart, datasetIndex); |
| 9111 |
}; |
| 9112 |
|
| 9113 |
helpers.extend(Chart.DatasetController.prototype, { |
| 9114 |
|
| 9115 |
/** |
| 9116 |
* Element type used to generate a meta dataset (e.g. Chart.element.Line). |
| 9117 |
* @type {Chart.core.element} |
| 9118 |
*/ |
| 9119 |
datasetElementType: null, |
| 9120 |
|
| 9121 |
/** |
| 9122 |
* Element type used to generate a meta data (e.g. Chart.element.Point). |
| 9123 |
* @type {Chart.core.element} |
| 9124 |
*/ |
| 9125 |
dataElementType: null, |
| 9126 |
|
| 9127 |
initialize: function(chart, datasetIndex) { |
| 9128 |
var me = this; |
| 9129 |
me.chart = chart; |
| 9130 |
me.index = datasetIndex; |
| 9131 |
me.linkScales(); |
| 9132 |
me.addElements(); |
| 9133 |
}, |
| 9134 |
|
| 9135 |
updateIndex: function(datasetIndex) { |
| 9136 |
this.index = datasetIndex; |
| 9137 |
}, |
| 9138 |
|
| 9139 |
linkScales: function() { |
| 9140 |
var me = this; |
| 9141 |
var meta = me.getMeta(); |
| 9142 |
var dataset = me.getDataset(); |
| 9143 |
|
| 9144 |
if (meta.xAxisID === null) { |
| 9145 |
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; |
| 9146 |
} |
| 9147 |
if (meta.yAxisID === null) { |
| 9148 |
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; |
| 9149 |
} |
| 9150 |
}, |
| 9151 |
|
| 9152 |
getDataset: function() { |
| 9153 |
return this.chart.data.datasets[this.index]; |
| 9154 |
}, |
| 9155 |
|
| 9156 |
getMeta: function() { |
| 9157 |
return this.chart.getDatasetMeta(this.index); |
| 9158 |
}, |
| 9159 |
|
| 9160 |
getScaleForId: function(scaleID) { |
| 9161 |
return this.chart.scales[scaleID]; |
| 9162 |
}, |
| 9163 |
|
| 9164 |
reset: function() { |
| 9165 |
this.update(true); |
| 9166 |
}, |
| 9167 |
|
| 9168 |
/** |
| 9169 |
* @private |
| 9170 |
*/ |
| 9171 |
destroy: function() { |
| 9172 |
if (this._data) { |
| 9173 |
unlistenArrayEvents(this._data, this); |
| 9174 |
} |
| 9175 |
}, |
| 9176 |
|
| 9177 |
createMetaDataset: function() { |
| 9178 |
var me = this; |
| 9179 |
var type = me.datasetElementType; |
| 9180 |
return type && new type({ |
| 9181 |
_chart: me.chart, |
| 9182 |
_datasetIndex: me.index |
| 9183 |
}); |
| 9184 |
}, |
| 9185 |
|
| 9186 |
createMetaData: function(index) { |
| 9187 |
var me = this; |
| 9188 |
var type = me.dataElementType; |
| 9189 |
return type && new type({ |
| 9190 |
_chart: me.chart, |
| 9191 |
_datasetIndex: me.index, |
| 9192 |
_index: index |
| 9193 |
}); |
| 9194 |
}, |
| 9195 |
|
| 9196 |
addElements: function() { |
| 9197 |
var me = this; |
| 9198 |
var meta = me.getMeta(); |
| 9199 |
var data = me.getDataset().data || []; |
| 9200 |
var metaData = meta.data; |
| 9201 |
var i, ilen; |
| 9202 |
|
| 9203 |
for (i = 0, ilen = data.length; i < ilen; ++i) { |
| 9204 |
metaData[i] = metaData[i] || me.createMetaData(i); |
| 9205 |
} |
| 9206 |
|
| 9207 |
meta.dataset = meta.dataset || me.createMetaDataset(); |
| 9208 |
}, |
| 9209 |
|
| 9210 |
addElementAndReset: function(index) { |
| 9211 |
var element = this.createMetaData(index); |
| 9212 |
this.getMeta().data.splice(index, 0, element); |
| 9213 |
this.updateElement(element, index, true); |
| 9214 |
}, |
| 9215 |
|
| 9216 |
buildOrUpdateElements: function() { |
| 9217 |
var me = this; |
| 9218 |
var dataset = me.getDataset(); |
| 9219 |
var data = dataset.data || (dataset.data = []); |
| 9220 |
|
| 9221 |
// In order to correctly handle data addition/deletion animation (an thus simulate |
| 9222 |
// real-time charts), we need to monitor these data modifications and synchronize |
| 9223 |
// the internal meta data accordingly. |
| 9224 |
if (me._data !== data) { |
| 9225 |
if (me._data) { |
| 9226 |
// This case happens when the user replaced the data array instance. |
| 9227 |
unlistenArrayEvents(me._data, me); |
| 9228 |
} |
| 9229 |
|
| 9230 |
listenArrayEvents(data, me); |
| 9231 |
me._data = data; |
| 9232 |
} |
| 9233 |
|
| 9234 |
// Re-sync meta data in case the user replaced the data array or if we missed |
| 9235 |
// any updates and so make sure that we handle number of datapoints changing. |
| 9236 |
me.resyncElements(); |
| 9237 |
}, |
| 9238 |
|
| 9239 |
update: helpers.noop, |
| 9240 |
|
| 9241 |
transition: function(easingValue) { |
| 9242 |
var meta = this.getMeta(); |
| 9243 |
var elements = meta.data || []; |
| 9244 |
var ilen = elements.length; |
| 9245 |
var i = 0; |
| 9246 |
|
| 9247 |
for (; i < ilen; ++i) { |
| 9248 |
elements[i].transition(easingValue); |
| 9249 |
} |
| 9250 |
|
| 9251 |
if (meta.dataset) { |
| 9252 |
meta.dataset.transition(easingValue); |
| 9253 |
} |
| 9254 |
}, |
| 9255 |
|
| 9256 |
draw: function() { |
| 9257 |
var meta = this.getMeta(); |
| 9258 |
var elements = meta.data || []; |
| 9259 |
var ilen = elements.length; |
| 9260 |
var i = 0; |
| 9261 |
|
| 9262 |
if (meta.dataset) { |
| 9263 |
meta.dataset.draw(); |
| 9264 |
} |
| 9265 |
|
| 9266 |
for (; i < ilen; ++i) { |
| 9267 |
elements[i].draw(); |
| 9268 |
} |
| 9269 |
}, |
| 9270 |
|
| 9271 |
removeHoverStyle: function(element, elementOpts) { |
| 9272 |
var dataset = this.chart.data.datasets[element._datasetIndex]; |
| 9273 |
var index = element._index; |
| 9274 |
var custom = element.custom || {}; |
| 9275 |
var valueOrDefault = helpers.valueAtIndexOrDefault; |
| 9276 |
var model = element._model; |
| 9277 |
|
| 9278 |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); |
| 9279 |
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); |
| 9280 |
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); |
| 9281 |
}, |
| 9282 |
|
| 9283 |
setHoverStyle: function(element) { |
| 9284 |
var dataset = this.chart.data.datasets[element._datasetIndex]; |
| 9285 |
var index = element._index; |
| 9286 |
var custom = element.custom || {}; |
| 9287 |
var valueOrDefault = helpers.valueAtIndexOrDefault; |
| 9288 |
var getHoverColor = helpers.getHoverColor; |
| 9289 |
var model = element._model; |
| 9290 |
|
| 9291 |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); |
| 9292 |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); |
| 9293 |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); |
| 9294 |
}, |
| 9295 |
|
| 9296 |
/** |
| 9297 |
* @private |
| 9298 |
*/ |
| 9299 |
resyncElements: function() { |
| 9300 |
var me = this; |
| 9301 |
var meta = me.getMeta(); |
| 9302 |
var data = me.getDataset().data; |
| 9303 |
var numMeta = meta.data.length; |
| 9304 |
var numData = data.length; |
| 9305 |
|
| 9306 |
if (numData < numMeta) { |
| 9307 |
meta.data.splice(numData, numMeta - numData); |
| 9308 |
} else if (numData > numMeta) { |
| 9309 |
me.insertElements(numMeta, numData - numMeta); |
| 9310 |
} |
| 9311 |
}, |
| 9312 |
|
| 9313 |
/** |
| 9314 |
* @private |
| 9315 |
*/ |
| 9316 |
insertElements: function(start, count) { |
| 9317 |
for (var i = 0; i < count; ++i) { |
| 9318 |
this.addElementAndReset(start + i); |
| 9319 |
} |
| 9320 |
}, |
| 9321 |
|
| 9322 |
/** |
| 9323 |
* @private |
| 9324 |
*/ |
| 9325 |
onDataPush: function() { |
| 9326 |
this.insertElements(this.getDataset().data.length - 1, arguments.length); |
| 9327 |
}, |
| 9328 |
|
| 9329 |
/** |
| 9330 |
* @private |
| 9331 |
*/ |
| 9332 |
onDataPop: function() { |
| 9333 |
this.getMeta().data.pop(); |
| 9334 |
}, |
| 9335 |
|
| 9336 |
/** |
| 9337 |
* @private |
| 9338 |
*/ |
| 9339 |
onDataShift: function() { |
| 9340 |
this.getMeta().data.shift(); |
| 9341 |
}, |
| 9342 |
|
| 9343 |
/** |
| 9344 |
* @private |
| 9345 |
*/ |
| 9346 |
onDataSplice: function(start, count) { |
| 9347 |
this.getMeta().data.splice(start, count); |
| 9348 |
this.insertElements(start, arguments.length - 2); |
| 9349 |
}, |
| 9350 |
|
| 9351 |
/** |
| 9352 |
* @private |
| 9353 |
*/ |
| 9354 |
onDataUnshift: function() { |
| 9355 |
this.insertElements(0, arguments.length); |
| 9356 |
} |
| 9357 |
}); |
| 9358 |
|
| 9359 |
Chart.DatasetController.extend = helpers.inherits; |
| 9360 |
}; |
| 9361 |
|
| 9362 |
},{"45":45}],25:[function(require,module,exports){ |
| 9363 |
'use strict'; |
| 9364 |
|
| 9365 |
var helpers = require(45); |
| 9366 |
|
| 9367 |
module.exports = { |
| 9368 |
/** |
| 9369 |
* @private |
| 9370 |
*/ |
| 9371 |
_set: function(scope, values) { |
| 9372 |
return helpers.merge(this[scope] || (this[scope] = {}), values); |
| 9373 |
} |
| 9374 |
}; |
| 9375 |
|
| 9376 |
},{"45":45}],26:[function(require,module,exports){ |
| 9377 |
'use strict'; |
| 9378 |
|
| 9379 |
var color = require(2); |
| 9380 |
var helpers = require(45); |
| 9381 |
|
| 9382 |
function interpolate(start, view, model, ease) { |
| 9383 |
var keys = Object.keys(model); |
| 9384 |
var i, ilen, key, actual, origin, target, type, c0, c1; |
| 9385 |
|
| 9386 |
for (i = 0, ilen = keys.length; i < ilen; ++i) { |
| 9387 |
key = keys[i]; |
| 9388 |
|
| 9389 |
target = model[key]; |
| 9390 |
|
| 9391 |
// if a value is added to the model after pivot() has been called, the view |
| 9392 |
// doesn't contain it, so let's initialize the view to the target value. |
| 9393 |
if (!view.hasOwnProperty(key)) { |
| 9394 |
view[key] = target; |
| 9395 |
} |
| 9396 |
|
| 9397 |
actual = view[key]; |
| 9398 |
|
| 9399 |
if (actual === target || key[0] === '_') { |
| 9400 |
continue; |
| 9401 |
} |
| 9402 |
|
| 9403 |
if (!start.hasOwnProperty(key)) { |
| 9404 |
start[key] = actual; |
| 9405 |
} |
| 9406 |
|
| 9407 |
origin = start[key]; |
| 9408 |
|
| 9409 |
type = typeof target; |
| 9410 |
|
| 9411 |
if (type === typeof origin) { |
| 9412 |
if (type === 'string') { |
| 9413 |
c0 = color(origin); |
| 9414 |
if (c0.valid) { |
| 9415 |
c1 = color(target); |
| 9416 |
if (c1.valid) { |
| 9417 |
view[key] = c1.mix(c0, ease).rgbString(); |
| 9418 |
continue; |
| 9419 |
} |
| 9420 |
} |
| 9421 |
} else if (type === 'number' && isFinite(origin) && isFinite(target)) { |
| 9422 |
view[key] = origin + (target - origin) * ease; |
| 9423 |
continue; |
| 9424 |
} |
| 9425 |
} |
| 9426 |
|
| 9427 |
view[key] = target; |
| 9428 |
} |
| 9429 |
} |
| 9430 |
|
| 9431 |
var Element = function(configuration) { |
| 9432 |
helpers.extend(this, configuration); |
| 9433 |
this.initialize.apply(this, arguments); |
| 9434 |
}; |
| 9435 |
|
| 9436 |
helpers.extend(Element.prototype, { |
| 9437 |
|
| 9438 |
initialize: function() { |
| 9439 |
this.hidden = false; |
| 9440 |
}, |
| 9441 |
|
| 9442 |
pivot: function() { |
| 9443 |
var me = this; |
| 9444 |
if (!me._view) { |
| 9445 |
me._view = helpers.clone(me._model); |
| 9446 |
} |
| 9447 |
me._start = {}; |
| 9448 |
return me; |
| 9449 |
}, |
| 9450 |
|
| 9451 |
transition: function(ease) { |
| 9452 |
var me = this; |
| 9453 |
var model = me._model; |
| 9454 |
var start = me._start; |
| 9455 |
var view = me._view; |
| 9456 |
|
| 9457 |
// No animation -> No Transition |
| 9458 |
if (!model || ease === 1) { |
| 9459 |
me._view = model; |
| 9460 |
me._start = null; |
| 9461 |
return me; |
| 9462 |
} |
| 9463 |
|
| 9464 |
if (!view) { |
| 9465 |
view = me._view = {}; |
| 9466 |
} |
| 9467 |
|
| 9468 |
if (!start) { |
| 9469 |
start = me._start = {}; |
| 9470 |
} |
| 9471 |
|
| 9472 |
interpolate(start, view, model, ease); |
| 9473 |
|
| 9474 |
return me; |
| 9475 |
}, |
| 9476 |
|
| 9477 |
tooltipPosition: function() { |
| 9478 |
return { |
| 9479 |
x: this._model.x, |
| 9480 |
y: this._model.y |
| 9481 |
}; |
| 9482 |
}, |
| 9483 |
|
| 9484 |
hasValue: function() { |
| 9485 |
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); |
| 9486 |
} |
| 9487 |
}); |
| 9488 |
|
| 9489 |
Element.extend = helpers.inherits; |
| 9490 |
|
| 9491 |
module.exports = Element; |
| 9492 |
|
| 9493 |
},{"2":2,"45":45}],27:[function(require,module,exports){ |
| 9494 |
/* global window: false */ |
| 9495 |
/* global document: false */ |
| 9496 |
'use strict'; |
| 9497 |
|
| 9498 |
var color = require(2); |
| 9499 |
var defaults = require(25); |
| 9500 |
var helpers = require(45); |
| 9501 |
|
| 9502 |
module.exports = function(Chart) { |
| 9503 |
|
| 9504 |
// -- Basic js utility methods |
| 9505 |
|
| 9506 |
helpers.extend = function(base) { |
| 9507 |
var setFn = function(value, key) { |
| 9508 |
base[key] = value; |
| 9509 |
}; |
| 9510 |
for (var i = 1, ilen = arguments.length; i < ilen; i++) { |
| 9511 |
helpers.each(arguments[i], setFn); |
| 9512 |
} |
| 9513 |
return base; |
| 9514 |
}; |
| 9515 |
|
| 9516 |
helpers.configMerge = function(/* objects ... */) { |
| 9517 |
return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { |
| 9518 |
merger: function(key, target, source, options) { |
| 9519 |
var tval = target[key] || {}; |
| 9520 |
var sval = source[key]; |
| 9521 |
|
| 9522 |
if (key === 'scales') { |
| 9523 |
// scale config merging is complex. Add our own function here for that |
| 9524 |
target[key] = helpers.scaleMerge(tval, sval); |
| 9525 |
} else if (key === 'scale') { |
| 9526 |
// used in polar area & radar charts since there is only one scale |
| 9527 |
target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]); |
| 9528 |
} else { |
| 9529 |
helpers._merger(key, target, source, options); |
| 9530 |
} |
| 9531 |
} |
| 9532 |
}); |
| 9533 |
}; |
| 9534 |
|
| 9535 |
helpers.scaleMerge = function(/* objects ... */) { |
| 9536 |
return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { |
| 9537 |
merger: function(key, target, source, options) { |
| 9538 |
if (key === 'xAxes' || key === 'yAxes') { |
| 9539 |
var slen = source[key].length; |
| 9540 |
var i, type, scale; |
| 9541 |
|
| 9542 |
if (!target[key]) { |
| 9543 |
target[key] = []; |
| 9544 |
} |
| 9545 |
|
| 9546 |
for (i = 0; i < slen; ++i) { |
| 9547 |
scale = source[key][i]; |
| 9548 |
type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); |
| 9549 |
|
| 9550 |
if (i >= target[key].length) { |
| 9551 |
target[key].push({}); |
| 9552 |
} |
| 9553 |
|
| 9554 |
if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { |
| 9555 |
// new/untyped scale or type changed: let's apply the new defaults |
| 9556 |
// then merge source scale to correctly overwrite the defaults. |
| 9557 |
helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]); |
| 9558 |
} else { |
| 9559 |
// scales type are the same |
| 9560 |
helpers.merge(target[key][i], scale); |
| 9561 |
} |
| 9562 |
} |
| 9563 |
} else { |
| 9564 |
helpers._merger(key, target, source, options); |
| 9565 |
} |
| 9566 |
} |
| 9567 |
}); |
| 9568 |
}; |
| 9569 |
|
| 9570 |
helpers.where = function(collection, filterCallback) { |
| 9571 |
if (helpers.isArray(collection) && Array.prototype.filter) { |
| 9572 |
return collection.filter(filterCallback); |
| 9573 |
} |
| 9574 |
var filtered = []; |
| 9575 |
|
| 9576 |
helpers.each(collection, function(item) { |
| 9577 |
if (filterCallback(item)) { |
| 9578 |
filtered.push(item); |
| 9579 |
} |
| 9580 |
}); |
| 9581 |
|
| 9582 |
return filtered; |
| 9583 |
}; |
| 9584 |
helpers.findIndex = Array.prototype.findIndex ? |
| 9585 |
function(array, callback, scope) { |
| 9586 |
return array.findIndex(callback, scope); |
| 9587 |
} : |
| 9588 |
function(array, callback, scope) { |
| 9589 |
scope = scope === undefined ? array : scope; |
| 9590 |
for (var i = 0, ilen = array.length; i < ilen; ++i) { |
| 9591 |
if (callback.call(scope, array[i], i, array)) { |
| 9592 |
return i; |
| 9593 |
} |
| 9594 |
} |
| 9595 |
return -1; |
| 9596 |
}; |
| 9597 |
helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { |
| 9598 |
// Default to start of the array |
| 9599 |
if (helpers.isNullOrUndef(startIndex)) { |
| 9600 |
startIndex = -1; |
| 9601 |
} |
| 9602 |
for (var i = startIndex + 1; i < arrayToSearch.length; i++) { |
| 9603 |
var currentItem = arrayToSearch[i]; |
| 9604 |
if (filterCallback(currentItem)) { |
| 9605 |
return currentItem; |
| 9606 |
} |
| 9607 |
} |
| 9608 |
}; |
| 9609 |
helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { |
| 9610 |
// Default to end of the array |
| 9611 |
if (helpers.isNullOrUndef(startIndex)) { |
| 9612 |
startIndex = arrayToSearch.length; |
| 9613 |
} |
| 9614 |
for (var i = startIndex - 1; i >= 0; i--) { |
| 9615 |
var currentItem = arrayToSearch[i]; |
| 9616 |
if (filterCallback(currentItem)) { |
| 9617 |
return currentItem; |
| 9618 |
} |
| 9619 |
} |
| 9620 |
}; |
| 9621 |
helpers.inherits = function(extensions) { |
| 9622 |
// Basic javascript inheritance based on the model created in Backbone.js |
| 9623 |
var me = this; |
| 9624 |
var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { |
| 9625 |
return me.apply(this, arguments); |
| 9626 |
}; |
| 9627 |
|
| 9628 |
var Surrogate = function() { |
| 9629 |
this.constructor = ChartElement; |
| 9630 |
}; |
| 9631 |
Surrogate.prototype = me.prototype; |
| 9632 |
ChartElement.prototype = new Surrogate(); |
| 9633 |
|
| 9634 |
ChartElement.extend = helpers.inherits; |
| 9635 |
|
| 9636 |
if (extensions) { |
| 9637 |
helpers.extend(ChartElement.prototype, extensions); |
| 9638 |
} |
| 9639 |
|
| 9640 |
ChartElement.__super__ = me.prototype; |
| 9641 |
|
| 9642 |
return ChartElement; |
| 9643 |
}; |
| 9644 |
// -- Math methods |
| 9645 |
helpers.isNumber = function(n) { |
| 9646 |
return !isNaN(parseFloat(n)) && isFinite(n); |
| 9647 |
}; |
| 9648 |
helpers.almostEquals = function(x, y, epsilon) { |
| 9649 |
return Math.abs(x - y) < epsilon; |
| 9650 |
}; |
| 9651 |
helpers.almostWhole = function(x, epsilon) { |
| 9652 |
var rounded = Math.round(x); |
| 9653 |
return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); |
| 9654 |
}; |
| 9655 |
helpers.max = function(array) { |
| 9656 |
return array.reduce(function(max, value) { |
| 9657 |
if (!isNaN(value)) { |
| 9658 |
return Math.max(max, value); |
| 9659 |
} |
| 9660 |
return max; |
| 9661 |
}, Number.NEGATIVE_INFINITY); |
| 9662 |
}; |
| 9663 |
helpers.min = function(array) { |
| 9664 |
return array.reduce(function(min, value) { |
| 9665 |
if (!isNaN(value)) { |
| 9666 |
return Math.min(min, value); |
| 9667 |
} |
| 9668 |
return min; |
| 9669 |
}, Number.POSITIVE_INFINITY); |
| 9670 |
}; |
| 9671 |
helpers.sign = Math.sign ? |
| 9672 |
function(x) { |
| 9673 |
return Math.sign(x); |
| 9674 |
} : |
| 9675 |
function(x) { |
| 9676 |
x = +x; // convert to a number |
| 9677 |
if (x === 0 || isNaN(x)) { |
| 9678 |
return x; |
| 9679 |
} |
| 9680 |
return x > 0 ? 1 : -1; |
| 9681 |
}; |
| 9682 |
helpers.log10 = Math.log10 ? |
| 9683 |
function(x) { |
| 9684 |
return Math.log10(x); |
| 9685 |
} : |
| 9686 |
function(x) { |
| 9687 |
return Math.log(x) / Math.LN10; |
| 9688 |
}; |
| 9689 |
helpers.toRadians = function(degrees) { |
| 9690 |
return degrees * (Math.PI / 180); |
| 9691 |
}; |
| 9692 |
helpers.toDegrees = function(radians) { |
| 9693 |
return radians * (180 / Math.PI); |
| 9694 |
}; |
| 9695 |
// Gets the angle from vertical upright to the point about a centre. |
| 9696 |
helpers.getAngleFromPoint = function(centrePoint, anglePoint) { |
| 9697 |
var distanceFromXCenter = anglePoint.x - centrePoint.x; |
| 9698 |
var distanceFromYCenter = anglePoint.y - centrePoint.y; |
| 9699 |
var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); |
| 9700 |
|
| 9701 |
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); |
| 9702 |
|
| 9703 |
if (angle < (-0.5 * Math.PI)) { |
| 9704 |
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] |
| 9705 |
} |
| 9706 |
|
| 9707 |
return { |
| 9708 |
angle: angle, |
| 9709 |
distance: radialDistanceFromCenter |
| 9710 |
}; |
| 9711 |
}; |
| 9712 |
helpers.distanceBetweenPoints = function(pt1, pt2) { |
| 9713 |
return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); |
| 9714 |
}; |
| 9715 |
helpers.aliasPixel = function(pixelWidth) { |
| 9716 |
return (pixelWidth % 2 === 0) ? 0 : 0.5; |
| 9717 |
}; |
| 9718 |
helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { |
| 9719 |
// Props to Rob Spencer at scaled innovation for his post on splining between points |
| 9720 |
// http://scaledinnovation.com/analytics/splines/aboutSplines.html |
| 9721 |
|
| 9722 |
// This function must also respect "skipped" points |
| 9723 |
|
| 9724 |
var previous = firstPoint.skip ? middlePoint : firstPoint; |
| 9725 |
var current = middlePoint; |
| 9726 |
var next = afterPoint.skip ? middlePoint : afterPoint; |
| 9727 |
|
| 9728 |
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); |
| 9729 |
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); |
| 9730 |
|
| 9731 |
var s01 = d01 / (d01 + d12); |
| 9732 |
var s12 = d12 / (d01 + d12); |
| 9733 |
|
| 9734 |
// If all points are the same, s01 & s02 will be inf |
| 9735 |
s01 = isNaN(s01) ? 0 : s01; |
| 9736 |
s12 = isNaN(s12) ? 0 : s12; |
| 9737 |
|
| 9738 |
var fa = t * s01; // scaling factor for triangle Ta |
| 9739 |
var fb = t * s12; |
| 9740 |
|
| 9741 |
return { |
| 9742 |
previous: { |
| 9743 |
x: current.x - fa * (next.x - previous.x), |
| 9744 |
y: current.y - fa * (next.y - previous.y) |
| 9745 |
}, |
| 9746 |
next: { |
| 9747 |
x: current.x + fb * (next.x - previous.x), |
| 9748 |
y: current.y + fb * (next.y - previous.y) |
| 9749 |
} |
| 9750 |
}; |
| 9751 |
}; |
| 9752 |
helpers.EPSILON = Number.EPSILON || 1e-14; |
| 9753 |
helpers.splineCurveMonotone = function(points) { |
| 9754 |
// This function calculates Bézier control points in a similar way than |splineCurve|, |
| 9755 |
// but preserves monotonicity of the provided data and ensures no local extremums are added |
| 9756 |
// between the dataset discrete points due to the interpolation. |
| 9757 |
// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation |
| 9758 |
|
| 9759 |
var pointsWithTangents = (points || []).map(function(point) { |
| 9760 |
return { |
| 9761 |
model: point._model, |
| 9762 |
deltaK: 0, |
| 9763 |
mK: 0 |
| 9764 |
}; |
| 9765 |
}); |
| 9766 |
|
| 9767 |
// Calculate slopes (deltaK) and initialize tangents (mK) |
| 9768 |
var pointsLen = pointsWithTangents.length; |
| 9769 |
var i, pointBefore, pointCurrent, pointAfter; |
| 9770 |
for (i = 0; i < pointsLen; ++i) { |
| 9771 |
pointCurrent = pointsWithTangents[i]; |
| 9772 |
if (pointCurrent.model.skip) { |
| 9773 |
continue; |
| 9774 |
} |
| 9775 |
|
| 9776 |
pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; |
| 9777 |
pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; |
| 9778 |
if (pointAfter && !pointAfter.model.skip) { |
| 9779 |
var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); |
| 9780 |
|
| 9781 |
// In the case of two points that appear at the same x pixel, slopeDeltaX is 0 |
| 9782 |
pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; |
| 9783 |
} |
| 9784 |
|
| 9785 |
if (!pointBefore || pointBefore.model.skip) { |
| 9786 |
pointCurrent.mK = pointCurrent.deltaK; |
| 9787 |
} else if (!pointAfter || pointAfter.model.skip) { |
| 9788 |
pointCurrent.mK = pointBefore.deltaK; |
| 9789 |
} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { |
| 9790 |
pointCurrent.mK = 0; |
| 9791 |
} else { |
| 9792 |
pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; |
| 9793 |
} |
| 9794 |
} |
| 9795 |
|
| 9796 |
// Adjust tangents to ensure monotonic properties |
| 9797 |
var alphaK, betaK, tauK, squaredMagnitude; |
| 9798 |
for (i = 0; i < pointsLen - 1; ++i) { |
| 9799 |
pointCurrent = pointsWithTangents[i]; |
| 9800 |
pointAfter = pointsWithTangents[i + 1]; |
| 9801 |
if (pointCurrent.model.skip || pointAfter.model.skip) { |
| 9802 |
continue; |
| 9803 |
} |
| 9804 |
|
| 9805 |
if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { |
| 9806 |
pointCurrent.mK = pointAfter.mK = 0; |
| 9807 |
continue; |
| 9808 |
} |
| 9809 |
|
| 9810 |
alphaK = pointCurrent.mK / pointCurrent.deltaK; |
| 9811 |
betaK = pointAfter.mK / pointCurrent.deltaK; |
| 9812 |
squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); |
| 9813 |
if (squaredMagnitude <= 9) { |
| 9814 |
continue; |
| 9815 |
} |
| 9816 |
|
| 9817 |
tauK = 3 / Math.sqrt(squaredMagnitude); |
| 9818 |
pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; |
| 9819 |
pointAfter.mK = betaK * tauK * pointCurrent.deltaK; |
| 9820 |
} |
| 9821 |
|
| 9822 |
// Compute control points |
| 9823 |
var deltaX; |
| 9824 |
for (i = 0; i < pointsLen; ++i) { |
| 9825 |
pointCurrent = pointsWithTangents[i]; |
| 9826 |
if (pointCurrent.model.skip) { |
| 9827 |
continue; |
| 9828 |
} |
| 9829 |
|
| 9830 |
pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; |
| 9831 |
pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; |
| 9832 |
if (pointBefore && !pointBefore.model.skip) { |
| 9833 |
deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; |
| 9834 |
pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; |
| 9835 |
pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; |
| 9836 |
} |
| 9837 |
if (pointAfter && !pointAfter.model.skip) { |
| 9838 |
deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; |
| 9839 |
pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; |
| 9840 |
pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; |
| 9841 |
} |
| 9842 |
} |
| 9843 |
}; |
| 9844 |
helpers.nextItem = function(collection, index, loop) { |
| 9845 |
if (loop) { |
| 9846 |
return index >= collection.length - 1 ? collection[0] : collection[index + 1]; |
| 9847 |
} |
| 9848 |
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; |
| 9849 |
}; |
| 9850 |
helpers.previousItem = function(collection, index, loop) { |
| 9851 |
if (loop) { |
| 9852 |
return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; |
| 9853 |
} |
| 9854 |
return index <= 0 ? collection[0] : collection[index - 1]; |
| 9855 |
}; |
| 9856 |
// Implementation of the nice number algorithm used in determining where axis labels will go |
| 9857 |
helpers.niceNum = function(range, round) { |
| 9858 |
var exponent = Math.floor(helpers.log10(range)); |
| 9859 |
var fraction = range / Math.pow(10, exponent); |
| 9860 |
var niceFraction; |
| 9861 |
|
| 9862 |
if (round) { |
| 9863 |
if (fraction < 1.5) { |
| 9864 |
niceFraction = 1; |
| 9865 |
} else if (fraction < 3) { |
| 9866 |
niceFraction = 2; |
| 9867 |
} else if (fraction < 7) { |
| 9868 |
niceFraction = 5; |
| 9869 |
} else { |
| 9870 |
niceFraction = 10; |
| 9871 |
} |
| 9872 |
} else if (fraction <= 1.0) { |
| 9873 |
niceFraction = 1; |
| 9874 |
} else if (fraction <= 2) { |
| 9875 |
niceFraction = 2; |
| 9876 |
} else if (fraction <= 5) { |
| 9877 |
niceFraction = 5; |
| 9878 |
} else { |
| 9879 |
niceFraction = 10; |
| 9880 |
} |
| 9881 |
|
| 9882 |
return niceFraction * Math.pow(10, exponent); |
| 9883 |
}; |
| 9884 |
// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ |
| 9885 |
helpers.requestAnimFrame = (function() { |
| 9886 |
if (typeof window === 'undefined') { |
| 9887 |
return function(callback) { |
| 9888 |
callback(); |
| 9889 |
}; |
| 9890 |
} |
| 9891 |
return window.requestAnimationFrame || |
| 9892 |
window.webkitRequestAnimationFrame || |
| 9893 |
window.mozRequestAnimationFrame || |
| 9894 |
window.oRequestAnimationFrame || |
| 9895 |
window.msRequestAnimationFrame || |
| 9896 |
function(callback) { |
| 9897 |
return window.setTimeout(callback, 1000 / 60); |
| 9898 |
}; |
| 9899 |
}()); |
| 9900 |
// -- DOM methods |
| 9901 |
helpers.getRelativePosition = function(evt, chart) { |
| 9902 |
var mouseX, mouseY; |
| 9903 |
var e = evt.originalEvent || evt; |
| 9904 |
var canvas = evt.currentTarget || evt.srcElement; |
| 9905 |
var boundingRect = canvas.getBoundingClientRect(); |
| 9906 |
|
| 9907 |
var touches = e.touches; |
| 9908 |
if (touches && touches.length > 0) { |
| 9909 |
mouseX = touches[0].clientX; |
| 9910 |
mouseY = touches[0].clientY; |
| 9911 |
|
| 9912 |
} else { |
| 9913 |
mouseX = e.clientX; |
| 9914 |
mouseY = e.clientY; |
| 9915 |
} |
| 9916 |
|
| 9917 |
// Scale mouse coordinates into canvas coordinates |
| 9918 |
// by following the pattern laid out by 'jerryj' in the comments of |
| 9919 |
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ |
| 9920 |
var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); |
| 9921 |
var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); |
| 9922 |
var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); |
| 9923 |
var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); |
| 9924 |
var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; |
| 9925 |
var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; |
| 9926 |
|
| 9927 |
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However |
| 9928 |
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here |
| 9929 |
mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); |
| 9930 |
mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); |
| 9931 |
|
| 9932 |
return { |
| 9933 |
x: mouseX, |
| 9934 |
y: mouseY |
| 9935 |
}; |
| 9936 |
|
| 9937 |
}; |
| 9938 |
|
| 9939 |
// Private helper function to convert max-width/max-height values that may be percentages into a number |
| 9940 |
function parseMaxStyle(styleValue, node, parentProperty) { |
| 9941 |
var valueInPixels; |
| 9942 |
if (typeof styleValue === 'string') { |
| 9943 |
valueInPixels = parseInt(styleValue, 10); |
| 9944 |
|
| 9945 |
if (styleValue.indexOf('%') !== -1) { |
| 9946 |
// percentage * size in dimension |
| 9947 |
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; |
| 9948 |
} |
| 9949 |
} else { |
| 9950 |
valueInPixels = styleValue; |
| 9951 |
} |
| 9952 |
|
| 9953 |
return valueInPixels; |
| 9954 |
} |
| 9955 |
|
| 9956 |
/** |
| 9957 |
* Returns if the given value contains an effective constraint. |
| 9958 |
* @private |
| 9959 |
*/ |
| 9960 |
function isConstrainedValue(value) { |
| 9961 |
return value !== undefined && value !== null && value !== 'none'; |
| 9962 |
} |
| 9963 |
|
| 9964 |
// Private helper to get a constraint dimension |
| 9965 |
// @param domNode : the node to check the constraint on |
| 9966 |
// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) |
| 9967 |
// @param percentageProperty : property of parent to use when calculating width as a percentage |
| 9968 |
// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser |
| 9969 |
function getConstraintDimension(domNode, maxStyle, percentageProperty) { |
| 9970 |
var view = document.defaultView; |
| 9971 |
var parentNode = domNode.parentNode; |
| 9972 |
var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; |
| 9973 |
var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; |
| 9974 |
var hasCNode = isConstrainedValue(constrainedNode); |
| 9975 |
var hasCContainer = isConstrainedValue(constrainedContainer); |
| 9976 |
var infinity = Number.POSITIVE_INFINITY; |
| 9977 |
|
| 9978 |
if (hasCNode || hasCContainer) { |
| 9979 |
return Math.min( |
| 9980 |
hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, |
| 9981 |
hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); |
| 9982 |
} |
| 9983 |
|
| 9984 |
return 'none'; |
| 9985 |
} |
| 9986 |
// returns Number or undefined if no constraint |
| 9987 |
helpers.getConstraintWidth = function(domNode) { |
| 9988 |
return getConstraintDimension(domNode, 'max-width', 'clientWidth'); |
| 9989 |
}; |
| 9990 |
// returns Number or undefined if no constraint |
| 9991 |
helpers.getConstraintHeight = function(domNode) { |
| 9992 |
return getConstraintDimension(domNode, 'max-height', 'clientHeight'); |
| 9993 |
}; |
| 9994 |
helpers.getMaximumWidth = function(domNode) { |
| 9995 |
var container = domNode.parentNode; |
| 9996 |
if (!container) { |
| 9997 |
return domNode.clientWidth; |
| 9998 |
} |
| 9999 |
|
| 10000 |
var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); |
| 10001 |
var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); |
| 10002 |
var w = container.clientWidth - paddingLeft - paddingRight; |
| 10003 |
var cw = helpers.getConstraintWidth(domNode); |
| 10004 |
return isNaN(cw) ? w : Math.min(w, cw); |
| 10005 |
}; |
| 10006 |
helpers.getMaximumHeight = function(domNode) { |
| 10007 |
var container = domNode.parentNode; |
| 10008 |
if (!container) { |
| 10009 |
return domNode.clientHeight; |
| 10010 |
} |
| 10011 |
|
| 10012 |
var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); |
| 10013 |
var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); |
| 10014 |
var h = container.clientHeight - paddingTop - paddingBottom; |
| 10015 |
var ch = helpers.getConstraintHeight(domNode); |
| 10016 |
return isNaN(ch) ? h : Math.min(h, ch); |
| 10017 |
}; |
| 10018 |
helpers.getStyle = function(el, property) { |
| 10019 |
return el.currentStyle ? |
| 10020 |
el.currentStyle[property] : |
| 10021 |
document.defaultView.getComputedStyle(el, null).getPropertyValue(property); |
| 10022 |
}; |
| 10023 |
helpers.retinaScale = function(chart, forceRatio) { |
| 10024 |
var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1; |
| 10025 |
if (pixelRatio === 1) { |
| 10026 |
return; |
| 10027 |
} |
| 10028 |
|
| 10029 |
var canvas = chart.canvas; |
| 10030 |
var height = chart.height; |
| 10031 |
var width = chart.width; |
| 10032 |
|
| 10033 |
canvas.height = height * pixelRatio; |
| 10034 |
canvas.width = width * pixelRatio; |
| 10035 |
chart.ctx.scale(pixelRatio, pixelRatio); |
| 10036 |
|
| 10037 |
// If no style has been set on the canvas, the render size is used as display size, |
| 10038 |
// making the chart visually bigger, so let's enforce it to the "correct" values. |
| 10039 |
// See https://github.com/chartjs/Chart.js/issues/3575 |
| 10040 |
canvas.style.height = height + 'px'; |
| 10041 |
canvas.style.width = width + 'px'; |
| 10042 |
}; |
| 10043 |
// -- Canvas methods |
| 10044 |
helpers.fontString = function(pixelSize, fontStyle, fontFamily) { |
| 10045 |
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; |
| 10046 |
}; |
| 10047 |
helpers.longestText = function(ctx, font, arrayOfThings, cache) { |
| 10048 |
cache = cache || {}; |
| 10049 |
var data = cache.data = cache.data || {}; |
| 10050 |
var gc = cache.garbageCollect = cache.garbageCollect || []; |
| 10051 |
|
| 10052 |
if (cache.font !== font) { |
| 10053 |
data = cache.data = {}; |
| 10054 |
gc = cache.garbageCollect = []; |
| 10055 |
cache.font = font; |
| 10056 |
} |
| 10057 |
|
| 10058 |
ctx.font = font; |
| 10059 |
var longest = 0; |
| 10060 |
helpers.each(arrayOfThings, function(thing) { |
| 10061 |
// Undefined strings and arrays should not be measured |
| 10062 |
if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { |
| 10063 |
longest = helpers.measureText(ctx, data, gc, longest, thing); |
| 10064 |
} else if (helpers.isArray(thing)) { |
| 10065 |
// if it is an array lets measure each element |
| 10066 |
// to do maybe simplify this function a bit so we can do this more recursively? |
| 10067 |
helpers.each(thing, function(nestedThing) { |
| 10068 |
// Undefined strings and arrays should not be measured |
| 10069 |
if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { |
| 10070 |
longest = helpers.measureText(ctx, data, gc, longest, nestedThing); |
| 10071 |
} |
| 10072 |
}); |
| 10073 |
} |
| 10074 |
}); |
| 10075 |
|
| 10076 |
var gcLen = gc.length / 2; |
| 10077 |
if (gcLen > arrayOfThings.length) { |
| 10078 |
for (var i = 0; i < gcLen; i++) { |
| 10079 |
delete data[gc[i]]; |
| 10080 |
} |
| 10081 |
gc.splice(0, gcLen); |
| 10082 |
} |
| 10083 |
return longest; |
| 10084 |
}; |
| 10085 |
helpers.measureText = function(ctx, data, gc, longest, string) { |
| 10086 |
var textWidth = data[string]; |
| 10087 |
if (!textWidth) { |
| 10088 |
textWidth = data[string] = ctx.measureText(string).width; |
| 10089 |
gc.push(string); |
| 10090 |
} |
| 10091 |
if (textWidth > longest) { |
| 10092 |
longest = textWidth; |
| 10093 |
} |
| 10094 |
return longest; |
| 10095 |
}; |
| 10096 |
helpers.numberOfLabelLines = function(arrayOfThings) { |
| 10097 |
var numberOfLines = 1; |
| 10098 |
helpers.each(arrayOfThings, function(thing) { |
| 10099 |
if (helpers.isArray(thing)) { |
| 10100 |
if (thing.length > numberOfLines) { |
| 10101 |
numberOfLines = thing.length; |
| 10102 |
} |
| 10103 |
} |
| 10104 |
}); |
| 10105 |
return numberOfLines; |
| 10106 |
}; |
| 10107 |
|
| 10108 |
helpers.color = !color ? |
| 10109 |
function(value) { |
| 10110 |
console.error('Color.js not found!'); |
| 10111 |
return value; |
| 10112 |
} : |
| 10113 |
function(value) { |
| 10114 |
/* global CanvasGradient */ |
| 10115 |
if (value instanceof CanvasGradient) { |
| 10116 |
value = defaults.global.defaultColor; |
| 10117 |
} |
| 10118 |
|
| 10119 |
return color(value); |
| 10120 |
}; |
| 10121 |
|
| 10122 |
helpers.getHoverColor = function(colorValue) { |
| 10123 |
/* global CanvasPattern */ |
| 10124 |
return (colorValue instanceof CanvasPattern) ? |
| 10125 |
colorValue : |
| 10126 |
helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); |
| 10127 |
}; |
| 10128 |
}; |
| 10129 |
|
| 10130 |
},{"2":2,"25":25,"45":45}],28:[function(require,module,exports){ |
| 10131 |
'use strict'; |
| 10132 |
|
| 10133 |
var helpers = require(45); |
| 10134 |
|
| 10135 |
/** |
| 10136 |
* Helper function to get relative position for an event |
| 10137 |
* @param {Event|IEvent} event - The event to get the position for |
| 10138 |
* @param {Chart} chart - The chart |
| 10139 |
* @returns {Point} the event position |
| 10140 |
*/ |
| 10141 |
function getRelativePosition(e, chart) { |
| 10142 |
if (e.native) { |
| 10143 |
return { |
| 10144 |
x: e.x, |
| 10145 |
y: e.y |
| 10146 |
}; |
| 10147 |
} |
| 10148 |
|
| 10149 |
return helpers.getRelativePosition(e, chart); |
| 10150 |
} |
| 10151 |
|
| 10152 |
/** |
| 10153 |
* Helper function to traverse all of the visible elements in the chart |
| 10154 |
* @param chart {chart} the chart |
| 10155 |
* @param handler {Function} the callback to execute for each visible item |
| 10156 |
*/ |
| 10157 |
function parseVisibleItems(chart, handler) { |
| 10158 |
var datasets = chart.data.datasets; |
| 10159 |
var meta, i, j, ilen, jlen; |
| 10160 |
|
| 10161 |
for (i = 0, ilen = datasets.length; i < ilen; ++i) { |
| 10162 |
if (!chart.isDatasetVisible(i)) { |
| 10163 |
continue; |
| 10164 |
} |
| 10165 |
|
| 10166 |
meta = chart.getDatasetMeta(i); |
| 10167 |
for (j = 0, jlen = meta.data.length; j < jlen; ++j) { |
| 10168 |
var element = meta.data[j]; |
| 10169 |
if (!element._view.skip) { |
| 10170 |
handler(element); |
| 10171 |
} |
| 10172 |
} |
| 10173 |
} |
| 10174 |
} |
| 10175 |
|
| 10176 |
/** |
| 10177 |
* Helper function to get the items that intersect the event position |
| 10178 |
* @param items {ChartElement[]} elements to filter |
| 10179 |
* @param position {Point} the point to be nearest to |
| 10180 |
* @return {ChartElement[]} the nearest items |
| 10181 |
*/ |
| 10182 |
function getIntersectItems(chart, position) { |
| 10183 |
var elements = []; |
| 10184 |
|
| 10185 |
parseVisibleItems(chart, function(element) { |
| 10186 |
if (element.inRange(position.x, position.y)) { |
| 10187 |
elements.push(element); |
| 10188 |
} |
| 10189 |
}); |
| 10190 |
|
| 10191 |
return elements; |
| 10192 |
} |
| 10193 |
|
| 10194 |
/** |
| 10195 |
* Helper function to get the items nearest to the event position considering all visible items in teh chart |
| 10196 |
* @param chart {Chart} the chart to look at elements from |
| 10197 |
* @param position {Point} the point to be nearest to |
| 10198 |
* @param intersect {Boolean} if true, only consider items that intersect the position |
| 10199 |
* @param distanceMetric {Function} function to provide the distance between points |
| 10200 |
* @return {ChartElement[]} the nearest items |
| 10201 |
*/ |
| 10202 |
function getNearestItems(chart, position, intersect, distanceMetric) { |
| 10203 |
var minDistance = Number.POSITIVE_INFINITY; |
| 10204 |
var nearestItems = []; |
| 10205 |
|
| 10206 |
parseVisibleItems(chart, function(element) { |
| 10207 |
if (intersect && !element.inRange(position.x, position.y)) { |
| 10208 |
return; |
| 10209 |
} |
| 10210 |
|
| 10211 |
var center = element.getCenterPoint(); |
| 10212 |
var distance = distanceMetric(position, center); |
| 10213 |
|
| 10214 |
if (distance < minDistance) { |
| 10215 |
nearestItems = [element]; |
| 10216 |
minDistance = distance; |
| 10217 |
} else if (distance === minDistance) { |
| 10218 |
// Can have multiple items at the same distance in which case we sort by size |
| 10219 |
nearestItems.push(element); |
| 10220 |
} |
| 10221 |
}); |
| 10222 |
|
| 10223 |
return nearestItems; |
| 10224 |
} |
| 10225 |
|
| 10226 |
/** |
| 10227 |
* Get a distance metric function for two points based on the |
| 10228 |
* axis mode setting |
| 10229 |
* @param {String} axis the axis mode. x|y|xy |
| 10230 |
*/ |
| 10231 |
function getDistanceMetricForAxis(axis) { |
| 10232 |
var useX = axis.indexOf('x') !== -1; |
| 10233 |
var useY = axis.indexOf('y') !== -1; |
| 10234 |
|
| 10235 |
return function(pt1, pt2) { |
| 10236 |
var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; |
| 10237 |
var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; |
| 10238 |
return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); |
| 10239 |
}; |
| 10240 |
} |
| 10241 |
|
| 10242 |
function indexMode(chart, e, options) { |
| 10243 |
var position = getRelativePosition(e, chart); |
| 10244 |
// Default axis for index mode is 'x' to match old behaviour |
| 10245 |
options.axis = options.axis || 'x'; |
| 10246 |
var distanceMetric = getDistanceMetricForAxis(options.axis); |
| 10247 |
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); |
| 10248 |
var elements = []; |
| 10249 |
|
| 10250 |
if (!items.length) { |
| 10251 |
return []; |
| 10252 |
} |
| 10253 |
|
| 10254 |
chart.data.datasets.forEach(function(dataset, datasetIndex) { |
| 10255 |
if (chart.isDatasetVisible(datasetIndex)) { |
| 10256 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 10257 |
var element = meta.data[items[0]._index]; |
| 10258 |
|
| 10259 |
// don't count items that are skipped (null data) |
| 10260 |
if (element && !element._view.skip) { |
| 10261 |
elements.push(element); |
| 10262 |
} |
| 10263 |
} |
| 10264 |
}); |
| 10265 |
|
| 10266 |
return elements; |
| 10267 |
} |
| 10268 |
|
| 10269 |
/** |
| 10270 |
* @interface IInteractionOptions |
| 10271 |
*/ |
| 10272 |
/** |
| 10273 |
* If true, only consider items that intersect the point |
| 10274 |
* @name IInterfaceOptions#boolean |
| 10275 |
* @type Boolean |
| 10276 |
*/ |
| 10277 |
|
| 10278 |
/** |
| 10279 |
* Contains interaction related functions |
| 10280 |
* @namespace Chart.Interaction |
| 10281 |
*/ |
| 10282 |
module.exports = { |
| 10283 |
// Helper function for different modes |
| 10284 |
modes: { |
| 10285 |
single: function(chart, e) { |
| 10286 |
var position = getRelativePosition(e, chart); |
| 10287 |
var elements = []; |
| 10288 |
|
| 10289 |
parseVisibleItems(chart, function(element) { |
| 10290 |
if (element.inRange(position.x, position.y)) { |
| 10291 |
elements.push(element); |
| 10292 |
return elements; |
| 10293 |
} |
| 10294 |
}); |
| 10295 |
|
| 10296 |
return elements.slice(0, 1); |
| 10297 |
}, |
| 10298 |
|
| 10299 |
/** |
| 10300 |
* @function Chart.Interaction.modes.label |
| 10301 |
* @deprecated since version 2.4.0 |
| 10302 |
* @todo remove at version 3 |
| 10303 |
* @private |
| 10304 |
*/ |
| 10305 |
label: indexMode, |
| 10306 |
|
| 10307 |
/** |
| 10308 |
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something |
| 10309 |
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item |
| 10310 |
* @function Chart.Interaction.modes.index |
| 10311 |
* @since v2.4.0 |
| 10312 |
* @param chart {chart} the chart we are returning items from |
| 10313 |
* @param e {Event} the event we are find things at |
| 10314 |
* @param options {IInteractionOptions} options to use during interaction |
| 10315 |
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 10316 |
*/ |
| 10317 |
index: indexMode, |
| 10318 |
|
| 10319 |
/** |
| 10320 |
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something |
| 10321 |
* If the options.intersect is false, we find the nearest item and return the items in that dataset |
| 10322 |
* @function Chart.Interaction.modes.dataset |
| 10323 |
* @param chart {chart} the chart we are returning items from |
| 10324 |
* @param e {Event} the event we are find things at |
| 10325 |
* @param options {IInteractionOptions} options to use during interaction |
| 10326 |
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 10327 |
*/ |
| 10328 |
dataset: function(chart, e, options) { |
| 10329 |
var position = getRelativePosition(e, chart); |
| 10330 |
options.axis = options.axis || 'xy'; |
| 10331 |
var distanceMetric = getDistanceMetricForAxis(options.axis); |
| 10332 |
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); |
| 10333 |
|
| 10334 |
if (items.length > 0) { |
| 10335 |
items = chart.getDatasetMeta(items[0]._datasetIndex).data; |
| 10336 |
} |
| 10337 |
|
| 10338 |
return items; |
| 10339 |
}, |
| 10340 |
|
| 10341 |
/** |
| 10342 |
* @function Chart.Interaction.modes.x-axis |
| 10343 |
* @deprecated since version 2.4.0. Use index mode and intersect == true |
| 10344 |
* @todo remove at version 3 |
| 10345 |
* @private |
| 10346 |
*/ |
| 10347 |
'x-axis': function(chart, e) { |
| 10348 |
return indexMode(chart, e, {intersect: true}); |
| 10349 |
}, |
| 10350 |
|
| 10351 |
/** |
| 10352 |
* Point mode returns all elements that hit test based on the event position |
| 10353 |
* of the event |
| 10354 |
* @function Chart.Interaction.modes.intersect |
| 10355 |
* @param chart {chart} the chart we are returning items from |
| 10356 |
* @param e {Event} the event we are find things at |
| 10357 |
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 10358 |
*/ |
| 10359 |
point: function(chart, e) { |
| 10360 |
var position = getRelativePosition(e, chart); |
| 10361 |
return getIntersectItems(chart, position); |
| 10362 |
}, |
| 10363 |
|
| 10364 |
/** |
| 10365 |
* nearest mode returns the element closest to the point |
| 10366 |
* @function Chart.Interaction.modes.intersect |
| 10367 |
* @param chart {chart} the chart we are returning items from |
| 10368 |
* @param e {Event} the event we are find things at |
| 10369 |
* @param options {IInteractionOptions} options to use |
| 10370 |
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 10371 |
*/ |
| 10372 |
nearest: function(chart, e, options) { |
| 10373 |
var position = getRelativePosition(e, chart); |
| 10374 |
options.axis = options.axis || 'xy'; |
| 10375 |
var distanceMetric = getDistanceMetricForAxis(options.axis); |
| 10376 |
var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); |
| 10377 |
|
| 10378 |
// We have multiple items at the same distance from the event. Now sort by smallest |
| 10379 |
if (nearestItems.length > 1) { |
| 10380 |
nearestItems.sort(function(a, b) { |
| 10381 |
var sizeA = a.getArea(); |
| 10382 |
var sizeB = b.getArea(); |
| 10383 |
var ret = sizeA - sizeB; |
| 10384 |
|
| 10385 |
if (ret === 0) { |
| 10386 |
// if equal sort by dataset index |
| 10387 |
ret = a._datasetIndex - b._datasetIndex; |
| 10388 |
} |
| 10389 |
|
| 10390 |
return ret; |
| 10391 |
}); |
| 10392 |
} |
| 10393 |
|
| 10394 |
// Return only 1 item |
| 10395 |
return nearestItems.slice(0, 1); |
| 10396 |
}, |
| 10397 |
|
| 10398 |
/** |
| 10399 |
* x mode returns the elements that hit-test at the current x coordinate |
| 10400 |
* @function Chart.Interaction.modes.x |
| 10401 |
* @param chart {chart} the chart we are returning items from |
| 10402 |
* @param e {Event} the event we are find things at |
| 10403 |
* @param options {IInteractionOptions} options to use |
| 10404 |
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 10405 |
*/ |
| 10406 |
x: function(chart, e, options) { |
| 10407 |
var position = getRelativePosition(e, chart); |
| 10408 |
var items = []; |
| 10409 |
var intersectsItem = false; |
| 10410 |
|
| 10411 |
parseVisibleItems(chart, function(element) { |
| 10412 |
if (element.inXRange(position.x)) { |
| 10413 |
items.push(element); |
| 10414 |
} |
| 10415 |
|
| 10416 |
if (element.inRange(position.x, position.y)) { |
| 10417 |
intersectsItem = true; |
| 10418 |
} |
| 10419 |
}); |
| 10420 |
|
| 10421 |
// If we want to trigger on an intersect and we don't have any items |
| 10422 |
// that intersect the position, return nothing |
| 10423 |
if (options.intersect && !intersectsItem) { |
| 10424 |
items = []; |
| 10425 |
} |
| 10426 |
return items; |
| 10427 |
}, |
| 10428 |
|
| 10429 |
/** |
| 10430 |
* y mode returns the elements that hit-test at the current y coordinate |
| 10431 |
* @function Chart.Interaction.modes.y |
| 10432 |
* @param chart {chart} the chart we are returning items from |
| 10433 |
* @param e {Event} the event we are find things at |
| 10434 |
* @param options {IInteractionOptions} options to use |
| 10435 |
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 10436 |
*/ |
| 10437 |
y: function(chart, e, options) { |
| 10438 |
var position = getRelativePosition(e, chart); |
| 10439 |
var items = []; |
| 10440 |
var intersectsItem = false; |
| 10441 |
|
| 10442 |
parseVisibleItems(chart, function(element) { |
| 10443 |
if (element.inYRange(position.y)) { |
| 10444 |
items.push(element); |
| 10445 |
} |
| 10446 |
|
| 10447 |
if (element.inRange(position.x, position.y)) { |
| 10448 |
intersectsItem = true; |
| 10449 |
} |
| 10450 |
}); |
| 10451 |
|
| 10452 |
// If we want to trigger on an intersect and we don't have any items |
| 10453 |
// that intersect the position, return nothing |
| 10454 |
if (options.intersect && !intersectsItem) { |
| 10455 |
items = []; |
| 10456 |
} |
| 10457 |
return items; |
| 10458 |
} |
| 10459 |
} |
| 10460 |
}; |
| 10461 |
|
| 10462 |
},{"45":45}],29:[function(require,module,exports){ |
| 10463 |
'use strict'; |
| 10464 |
|
| 10465 |
var defaults = require(25); |
| 10466 |
|
| 10467 |
defaults._set('global', { |
| 10468 |
responsive: true, |
| 10469 |
responsiveAnimationDuration: 0, |
| 10470 |
maintainAspectRatio: true, |
| 10471 |
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], |
| 10472 |
hover: { |
| 10473 |
onHover: null, |
| 10474 |
mode: 'nearest', |
| 10475 |
intersect: true, |
| 10476 |
animationDuration: 400 |
| 10477 |
}, |
| 10478 |
onClick: null, |
| 10479 |
defaultColor: 'rgba(0,0,0,0.1)', |
| 10480 |
defaultFontColor: '#666', |
| 10481 |
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", |
| 10482 |
defaultFontSize: 12, |
| 10483 |
defaultFontStyle: 'normal', |
| 10484 |
showLines: true, |
| 10485 |
|
| 10486 |
// Element defaults defined in element extensions |
| 10487 |
elements: {}, |
| 10488 |
|
| 10489 |
// Layout options such as padding |
| 10490 |
layout: { |
| 10491 |
padding: { |
| 10492 |
top: 0, |
| 10493 |
right: 0, |
| 10494 |
bottom: 0, |
| 10495 |
left: 0 |
| 10496 |
} |
| 10497 |
} |
| 10498 |
}); |
| 10499 |
|
| 10500 |
module.exports = function() { |
| 10501 |
|
| 10502 |
// Occupy the global variable of Chart, and create a simple base class |
| 10503 |
var Chart = function(item, config) { |
| 10504 |
this.construct(item, config); |
| 10505 |
return this; |
| 10506 |
}; |
| 10507 |
|
| 10508 |
Chart.Chart = Chart; |
| 10509 |
|
| 10510 |
return Chart; |
| 10511 |
}; |
| 10512 |
|
| 10513 |
},{"25":25}],30:[function(require,module,exports){ |
| 10514 |
'use strict'; |
| 10515 |
|
| 10516 |
var helpers = require(45); |
| 10517 |
|
| 10518 |
module.exports = function(Chart) { |
| 10519 |
|
| 10520 |
function filterByPosition(array, position) { |
| 10521 |
return helpers.where(array, function(v) { |
| 10522 |
return v.position === position; |
| 10523 |
}); |
| 10524 |
} |
| 10525 |
|
| 10526 |
function sortByWeight(array, reverse) { |
| 10527 |
array.forEach(function(v, i) { |
| 10528 |
v._tmpIndex_ = i; |
| 10529 |
return v; |
| 10530 |
}); |
| 10531 |
array.sort(function(a, b) { |
| 10532 |
var v0 = reverse ? b : a; |
| 10533 |
var v1 = reverse ? a : b; |
| 10534 |
return v0.weight === v1.weight ? |
| 10535 |
v0._tmpIndex_ - v1._tmpIndex_ : |
| 10536 |
v0.weight - v1.weight; |
| 10537 |
}); |
| 10538 |
array.forEach(function(v) { |
| 10539 |
delete v._tmpIndex_; |
| 10540 |
}); |
| 10541 |
} |
| 10542 |
|
| 10543 |
/** |
| 10544 |
* @interface ILayoutItem |
| 10545 |
* @prop {String} position - The position of the item in the chart layout. Possible values are |
| 10546 |
* 'left', 'top', 'right', 'bottom', and 'chartArea' |
| 10547 |
* @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area |
| 10548 |
* @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down |
| 10549 |
* @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) |
| 10550 |
* @prop {Function} update - Takes two parameters: width and height. Returns size of item |
| 10551 |
* @prop {Function} getPadding - Returns an object with padding on the edges |
| 10552 |
* @prop {Number} width - Width of item. Must be valid after update() |
| 10553 |
* @prop {Number} height - Height of item. Must be valid after update() |
| 10554 |
* @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update |
| 10555 |
* @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update |
| 10556 |
* @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update |
| 10557 |
* @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update |
| 10558 |
*/ |
| 10559 |
|
| 10560 |
// The layout service is very self explanatory. It's responsible for the layout within a chart. |
| 10561 |
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need |
| 10562 |
// It is this service's responsibility of carrying out that layout. |
| 10563 |
Chart.layoutService = { |
| 10564 |
defaults: {}, |
| 10565 |
|
| 10566 |
/** |
| 10567 |
* Register a box to a chart. |
| 10568 |
* A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. |
| 10569 |
* @param {Chart} chart - the chart to use |
| 10570 |
* @param {ILayoutItem} item - the item to add to be layed out |
| 10571 |
*/ |
| 10572 |
addBox: function(chart, item) { |
| 10573 |
if (!chart.boxes) { |
| 10574 |
chart.boxes = []; |
| 10575 |
} |
| 10576 |
|
| 10577 |
// initialize item with default values |
| 10578 |
item.fullWidth = item.fullWidth || false; |
| 10579 |
item.position = item.position || 'top'; |
| 10580 |
item.weight = item.weight || 0; |
| 10581 |
|
| 10582 |
chart.boxes.push(item); |
| 10583 |
}, |
| 10584 |
|
| 10585 |
/** |
| 10586 |
* Remove a layoutItem from a chart |
| 10587 |
* @param {Chart} chart - the chart to remove the box from |
| 10588 |
* @param {Object} layoutItem - the item to remove from the layout |
| 10589 |
*/ |
| 10590 |
removeBox: function(chart, layoutItem) { |
| 10591 |
var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; |
| 10592 |
if (index !== -1) { |
| 10593 |
chart.boxes.splice(index, 1); |
| 10594 |
} |
| 10595 |
}, |
| 10596 |
|
| 10597 |
/** |
| 10598 |
* Sets (or updates) options on the given `item`. |
| 10599 |
* @param {Chart} chart - the chart in which the item lives (or will be added to) |
| 10600 |
* @param {Object} item - the item to configure with the given options |
| 10601 |
* @param {Object} options - the new item options. |
| 10602 |
*/ |
| 10603 |
configure: function(chart, item, options) { |
| 10604 |
var props = ['fullWidth', 'position', 'weight']; |
| 10605 |
var ilen = props.length; |
| 10606 |
var i = 0; |
| 10607 |
var prop; |
| 10608 |
|
| 10609 |
for (; i < ilen; ++i) { |
| 10610 |
prop = props[i]; |
| 10611 |
if (options.hasOwnProperty(prop)) { |
| 10612 |
item[prop] = options[prop]; |
| 10613 |
} |
| 10614 |
} |
| 10615 |
}, |
| 10616 |
|
| 10617 |
/** |
| 10618 |
* Fits boxes of the given chart into the given size by having each box measure itself |
| 10619 |
* then running a fitting algorithm |
| 10620 |
* @param {Chart} chart - the chart |
| 10621 |
* @param {Number} width - the width to fit into |
| 10622 |
* @param {Number} height - the height to fit into |
| 10623 |
*/ |
| 10624 |
update: function(chart, width, height) { |
| 10625 |
if (!chart) { |
| 10626 |
return; |
| 10627 |
} |
| 10628 |
|
| 10629 |
var layoutOptions = chart.options.layout || {}; |
| 10630 |
var padding = helpers.options.toPadding(layoutOptions.padding); |
| 10631 |
var leftPadding = padding.left; |
| 10632 |
var rightPadding = padding.right; |
| 10633 |
var topPadding = padding.top; |
| 10634 |
var bottomPadding = padding.bottom; |
| 10635 |
|
| 10636 |
var leftBoxes = filterByPosition(chart.boxes, 'left'); |
| 10637 |
var rightBoxes = filterByPosition(chart.boxes, 'right'); |
| 10638 |
var topBoxes = filterByPosition(chart.boxes, 'top'); |
| 10639 |
var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); |
| 10640 |
var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); |
| 10641 |
|
| 10642 |
// Sort boxes by weight. A higher weight is further away from the chart area |
| 10643 |
sortByWeight(leftBoxes, true); |
| 10644 |
sortByWeight(rightBoxes, false); |
| 10645 |
sortByWeight(topBoxes, true); |
| 10646 |
sortByWeight(bottomBoxes, false); |
| 10647 |
|
| 10648 |
// Essentially we now have any number of boxes on each of the 4 sides. |
| 10649 |
// Our canvas looks like the following. |
| 10650 |
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and |
| 10651 |
// B1 is the bottom axis |
| 10652 |
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays |
| 10653 |
// These locations are single-box locations only, when trying to register a chartArea location that is already taken, |
| 10654 |
// an error will be thrown. |
| 10655 |
// |
| 10656 |
// |----------------------------------------------------| |
| 10657 |
// | T1 (Full Width) | |
| 10658 |
// |----------------------------------------------------| |
| 10659 |
// | | | T2 | | |
| 10660 |
// | |----|-------------------------------------|----| |
| 10661 |
// | | | C1 | | C2 | | |
| 10662 |
// | | |----| |----| | |
| 10663 |
// | | | | | |
| 10664 |
// | L1 | L2 | ChartArea (C0) | R1 | |
| 10665 |
// | | | | | |
| 10666 |
// | | |----| |----| | |
| 10667 |
// | | | C3 | | C4 | | |
| 10668 |
// | |----|-------------------------------------|----| |
| 10669 |
// | | | B1 | | |
| 10670 |
// |----------------------------------------------------| |
| 10671 |
// | B2 (Full Width) | |
| 10672 |
// |----------------------------------------------------| |
| 10673 |
// |
| 10674 |
// What we do to find the best sizing, we do the following |
| 10675 |
// 1. Determine the minimum size of the chart area. |
| 10676 |
// 2. Split the remaining width equally between each vertical axis |
| 10677 |
// 3. Split the remaining height equally between each horizontal axis |
| 10678 |
// 4. Give each layout the maximum size it can be. The layout will return it's minimum size |
| 10679 |
// 5. Adjust the sizes of each axis based on it's minimum reported size. |
| 10680 |
// 6. Refit each axis |
| 10681 |
// 7. Position each axis in the final location |
| 10682 |
// 8. Tell the chart the final location of the chart area |
| 10683 |
// 9. Tell any axes that overlay the chart area the positions of the chart area |
| 10684 |
|
| 10685 |
// Step 1 |
| 10686 |
var chartWidth = width - leftPadding - rightPadding; |
| 10687 |
var chartHeight = height - topPadding - bottomPadding; |
| 10688 |
var chartAreaWidth = chartWidth / 2; // min 50% |
| 10689 |
var chartAreaHeight = chartHeight / 2; // min 50% |
| 10690 |
|
| 10691 |
// Step 2 |
| 10692 |
var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); |
| 10693 |
|
| 10694 |
// Step 3 |
| 10695 |
var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); |
| 10696 |
|
| 10697 |
// Step 4 |
| 10698 |
var maxChartAreaWidth = chartWidth; |
| 10699 |
var maxChartAreaHeight = chartHeight; |
| 10700 |
var minBoxSizes = []; |
| 10701 |
|
| 10702 |
function getMinimumBoxSize(box) { |
| 10703 |
var minSize; |
| 10704 |
var isHorizontal = box.isHorizontal(); |
| 10705 |
|
| 10706 |
if (isHorizontal) { |
| 10707 |
minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); |
| 10708 |
maxChartAreaHeight -= minSize.height; |
| 10709 |
} else { |
| 10710 |
minSize = box.update(verticalBoxWidth, chartAreaHeight); |
| 10711 |
maxChartAreaWidth -= minSize.width; |
| 10712 |
} |
| 10713 |
|
| 10714 |
minBoxSizes.push({ |
| 10715 |
horizontal: isHorizontal, |
| 10716 |
minSize: minSize, |
| 10717 |
box: box, |
| 10718 |
}); |
| 10719 |
} |
| 10720 |
|
| 10721 |
helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); |
| 10722 |
|
| 10723 |
// If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) |
| 10724 |
var maxHorizontalLeftPadding = 0; |
| 10725 |
var maxHorizontalRightPadding = 0; |
| 10726 |
var maxVerticalTopPadding = 0; |
| 10727 |
var maxVerticalBottomPadding = 0; |
| 10728 |
|
| 10729 |
helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { |
| 10730 |
if (horizontalBox.getPadding) { |
| 10731 |
var boxPadding = horizontalBox.getPadding(); |
| 10732 |
maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); |
| 10733 |
maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); |
| 10734 |
} |
| 10735 |
}); |
| 10736 |
|
| 10737 |
helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { |
| 10738 |
if (verticalBox.getPadding) { |
| 10739 |
var boxPadding = verticalBox.getPadding(); |
| 10740 |
maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); |
| 10741 |
maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); |
| 10742 |
} |
| 10743 |
}); |
| 10744 |
|
| 10745 |
// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could |
| 10746 |
// be if the axes are drawn at their minimum sizes. |
| 10747 |
// Steps 5 & 6 |
| 10748 |
var totalLeftBoxesWidth = leftPadding; |
| 10749 |
var totalRightBoxesWidth = rightPadding; |
| 10750 |
var totalTopBoxesHeight = topPadding; |
| 10751 |
var totalBottomBoxesHeight = bottomPadding; |
| 10752 |
|
| 10753 |
// Function to fit a box |
| 10754 |
function fitBox(box) { |
| 10755 |
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { |
| 10756 |
return minBox.box === box; |
| 10757 |
}); |
| 10758 |
|
| 10759 |
if (minBoxSize) { |
| 10760 |
if (box.isHorizontal()) { |
| 10761 |
var scaleMargin = { |
| 10762 |
left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), |
| 10763 |
right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), |
| 10764 |
top: 0, |
| 10765 |
bottom: 0 |
| 10766 |
}; |
| 10767 |
|
| 10768 |
// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends |
| 10769 |
// on the margin. Sometimes they need to increase in size slightly |
| 10770 |
box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); |
| 10771 |
} else { |
| 10772 |
box.update(minBoxSize.minSize.width, maxChartAreaHeight); |
| 10773 |
} |
| 10774 |
} |
| 10775 |
} |
| 10776 |
|
| 10777 |
// Update, and calculate the left and right margins for the horizontal boxes |
| 10778 |
helpers.each(leftBoxes.concat(rightBoxes), fitBox); |
| 10779 |
|
| 10780 |
helpers.each(leftBoxes, function(box) { |
| 10781 |
totalLeftBoxesWidth += box.width; |
| 10782 |
}); |
| 10783 |
|
| 10784 |
helpers.each(rightBoxes, function(box) { |
| 10785 |
totalRightBoxesWidth += box.width; |
| 10786 |
}); |
| 10787 |
|
| 10788 |
// Set the Left and Right margins for the horizontal boxes |
| 10789 |
helpers.each(topBoxes.concat(bottomBoxes), fitBox); |
| 10790 |
|
| 10791 |
// Figure out how much margin is on the top and bottom of the vertical boxes |
| 10792 |
helpers.each(topBoxes, function(box) { |
| 10793 |
totalTopBoxesHeight += box.height; |
| 10794 |
}); |
| 10795 |
|
| 10796 |
helpers.each(bottomBoxes, function(box) { |
| 10797 |
totalBottomBoxesHeight += box.height; |
| 10798 |
}); |
| 10799 |
|
| 10800 |
function finalFitVerticalBox(box) { |
| 10801 |
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { |
| 10802 |
return minSize.box === box; |
| 10803 |
}); |
| 10804 |
|
| 10805 |
var scaleMargin = { |
| 10806 |
left: 0, |
| 10807 |
right: 0, |
| 10808 |
top: totalTopBoxesHeight, |
| 10809 |
bottom: totalBottomBoxesHeight |
| 10810 |
}; |
| 10811 |
|
| 10812 |
if (minBoxSize) { |
| 10813 |
box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); |
| 10814 |
} |
| 10815 |
} |
| 10816 |
|
| 10817 |
// Let the left layout know the final margin |
| 10818 |
helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); |
| 10819 |
|
| 10820 |
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) |
| 10821 |
totalLeftBoxesWidth = leftPadding; |
| 10822 |
totalRightBoxesWidth = rightPadding; |
| 10823 |
totalTopBoxesHeight = topPadding; |
| 10824 |
totalBottomBoxesHeight = bottomPadding; |
| 10825 |
|
| 10826 |
helpers.each(leftBoxes, function(box) { |
| 10827 |
totalLeftBoxesWidth += box.width; |
| 10828 |
}); |
| 10829 |
|
| 10830 |
helpers.each(rightBoxes, function(box) { |
| 10831 |
totalRightBoxesWidth += box.width; |
| 10832 |
}); |
| 10833 |
|
| 10834 |
helpers.each(topBoxes, function(box) { |
| 10835 |
totalTopBoxesHeight += box.height; |
| 10836 |
}); |
| 10837 |
helpers.each(bottomBoxes, function(box) { |
| 10838 |
totalBottomBoxesHeight += box.height; |
| 10839 |
}); |
| 10840 |
|
| 10841 |
// We may be adding some padding to account for rotated x axis labels |
| 10842 |
var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); |
| 10843 |
totalLeftBoxesWidth += leftPaddingAddition; |
| 10844 |
totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); |
| 10845 |
|
| 10846 |
var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); |
| 10847 |
totalTopBoxesHeight += topPaddingAddition; |
| 10848 |
totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); |
| 10849 |
|
| 10850 |
// Figure out if our chart area changed. This would occur if the dataset layout label rotation |
| 10851 |
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do |
| 10852 |
// without calling `fit` again |
| 10853 |
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; |
| 10854 |
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; |
| 10855 |
|
| 10856 |
if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { |
| 10857 |
helpers.each(leftBoxes, function(box) { |
| 10858 |
box.height = newMaxChartAreaHeight; |
| 10859 |
}); |
| 10860 |
|
| 10861 |
helpers.each(rightBoxes, function(box) { |
| 10862 |
box.height = newMaxChartAreaHeight; |
| 10863 |
}); |
| 10864 |
|
| 10865 |
helpers.each(topBoxes, function(box) { |
| 10866 |
if (!box.fullWidth) { |
| 10867 |
box.width = newMaxChartAreaWidth; |
| 10868 |
} |
| 10869 |
}); |
| 10870 |
|
| 10871 |
helpers.each(bottomBoxes, function(box) { |
| 10872 |
if (!box.fullWidth) { |
| 10873 |
box.width = newMaxChartAreaWidth; |
| 10874 |
} |
| 10875 |
}); |
| 10876 |
|
| 10877 |
maxChartAreaHeight = newMaxChartAreaHeight; |
| 10878 |
maxChartAreaWidth = newMaxChartAreaWidth; |
| 10879 |
} |
| 10880 |
|
| 10881 |
// Step 7 - Position the boxes |
| 10882 |
var left = leftPadding + leftPaddingAddition; |
| 10883 |
var top = topPadding + topPaddingAddition; |
| 10884 |
|
| 10885 |
function placeBox(box) { |
| 10886 |
if (box.isHorizontal()) { |
| 10887 |
box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; |
| 10888 |
box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; |
| 10889 |
box.top = top; |
| 10890 |
box.bottom = top + box.height; |
| 10891 |
|
| 10892 |
// Move to next point |
| 10893 |
top = box.bottom; |
| 10894 |
|
| 10895 |
} else { |
| 10896 |
|
| 10897 |
box.left = left; |
| 10898 |
box.right = left + box.width; |
| 10899 |
box.top = totalTopBoxesHeight; |
| 10900 |
box.bottom = totalTopBoxesHeight + maxChartAreaHeight; |
| 10901 |
|
| 10902 |
// Move to next point |
| 10903 |
left = box.right; |
| 10904 |
} |
| 10905 |
} |
| 10906 |
|
| 10907 |
helpers.each(leftBoxes.concat(topBoxes), placeBox); |
| 10908 |
|
| 10909 |
// Account for chart width and height |
| 10910 |
left += maxChartAreaWidth; |
| 10911 |
top += maxChartAreaHeight; |
| 10912 |
|
| 10913 |
helpers.each(rightBoxes, placeBox); |
| 10914 |
helpers.each(bottomBoxes, placeBox); |
| 10915 |
|
| 10916 |
// Step 8 |
| 10917 |
chart.chartArea = { |
| 10918 |
left: totalLeftBoxesWidth, |
| 10919 |
top: totalTopBoxesHeight, |
| 10920 |
right: totalLeftBoxesWidth + maxChartAreaWidth, |
| 10921 |
bottom: totalTopBoxesHeight + maxChartAreaHeight |
| 10922 |
}; |
| 10923 |
|
| 10924 |
// Step 9 |
| 10925 |
helpers.each(chartAreaBoxes, function(box) { |
| 10926 |
box.left = chart.chartArea.left; |
| 10927 |
box.top = chart.chartArea.top; |
| 10928 |
box.right = chart.chartArea.right; |
| 10929 |
box.bottom = chart.chartArea.bottom; |
| 10930 |
|
| 10931 |
box.update(maxChartAreaWidth, maxChartAreaHeight); |
| 10932 |
}); |
| 10933 |
} |
| 10934 |
}; |
| 10935 |
}; |
| 10936 |
|
| 10937 |
},{"45":45}],31:[function(require,module,exports){ |
| 10938 |
'use strict'; |
| 10939 |
|
| 10940 |
var defaults = require(25); |
| 10941 |
var Element = require(26); |
| 10942 |
var helpers = require(45); |
| 10943 |
|
| 10944 |
defaults._set('global', { |
| 10945 |
plugins: {} |
| 10946 |
}); |
| 10947 |
|
| 10948 |
module.exports = function(Chart) { |
| 10949 |
|
| 10950 |
/** |
| 10951 |
* The plugin service singleton |
| 10952 |
* @namespace Chart.plugins |
| 10953 |
* @since 2.1.0 |
| 10954 |
*/ |
| 10955 |
Chart.plugins = { |
| 10956 |
/** |
| 10957 |
* Globally registered plugins. |
| 10958 |
* @private |
| 10959 |
*/ |
| 10960 |
_plugins: [], |
| 10961 |
|
| 10962 |
/** |
| 10963 |
* This identifier is used to invalidate the descriptors cache attached to each chart |
| 10964 |
* when a global plugin is registered or unregistered. In this case, the cache ID is |
| 10965 |
* incremented and descriptors are regenerated during following API calls. |
| 10966 |
* @private |
| 10967 |
*/ |
| 10968 |
_cacheId: 0, |
| 10969 |
|
| 10970 |
/** |
| 10971 |
* Registers the given plugin(s) if not already registered. |
| 10972 |
* @param {Array|Object} plugins plugin instance(s). |
| 10973 |
*/ |
| 10974 |
register: function(plugins) { |
| 10975 |
var p = this._plugins; |
| 10976 |
([]).concat(plugins).forEach(function(plugin) { |
| 10977 |
if (p.indexOf(plugin) === -1) { |
| 10978 |
p.push(plugin); |
| 10979 |
} |
| 10980 |
}); |
| 10981 |
|
| 10982 |
this._cacheId++; |
| 10983 |
}, |
| 10984 |
|
| 10985 |
/** |
| 10986 |
* Unregisters the given plugin(s) only if registered. |
| 10987 |
* @param {Array|Object} plugins plugin instance(s). |
| 10988 |
*/ |
| 10989 |
unregister: function(plugins) { |
| 10990 |
var p = this._plugins; |
| 10991 |
([]).concat(plugins).forEach(function(plugin) { |
| 10992 |
var idx = p.indexOf(plugin); |
| 10993 |
if (idx !== -1) { |
| 10994 |
p.splice(idx, 1); |
| 10995 |
} |
| 10996 |
}); |
| 10997 |
|
| 10998 |
this._cacheId++; |
| 10999 |
}, |
| 11000 |
|
| 11001 |
/** |
| 11002 |
* Remove all registered plugins. |
| 11003 |
* @since 2.1.5 |
| 11004 |
*/ |
| 11005 |
clear: function() { |
| 11006 |
this._plugins = []; |
| 11007 |
this._cacheId++; |
| 11008 |
}, |
| 11009 |
|
| 11010 |
/** |
| 11011 |
* Returns the number of registered plugins? |
| 11012 |
* @returns {Number} |
| 11013 |
* @since 2.1.5 |
| 11014 |
*/ |
| 11015 |
count: function() { |
| 11016 |
return this._plugins.length; |
| 11017 |
}, |
| 11018 |
|
| 11019 |
/** |
| 11020 |
* Returns all registered plugin instances. |
| 11021 |
* @returns {Array} array of plugin objects. |
| 11022 |
* @since 2.1.5 |
| 11023 |
*/ |
| 11024 |
getAll: function() { |
| 11025 |
return this._plugins; |
| 11026 |
}, |
| 11027 |
|
| 11028 |
/** |
| 11029 |
* Calls enabled plugins for `chart` on the specified hook and with the given args. |
| 11030 |
* This method immediately returns as soon as a plugin explicitly returns false. The |
| 11031 |
* returned value can be used, for instance, to interrupt the current action. |
| 11032 |
* @param {Object} chart - The chart instance for which plugins should be called. |
| 11033 |
* @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). |
| 11034 |
* @param {Array} [args] - Extra arguments to apply to the hook call. |
| 11035 |
* @returns {Boolean} false if any of the plugins return false, else returns true. |
| 11036 |
*/ |
| 11037 |
notify: function(chart, hook, args) { |
| 11038 |
var descriptors = this.descriptors(chart); |
| 11039 |
var ilen = descriptors.length; |
| 11040 |
var i, descriptor, plugin, params, method; |
| 11041 |
|
| 11042 |
for (i = 0; i < ilen; ++i) { |
| 11043 |
descriptor = descriptors[i]; |
| 11044 |
plugin = descriptor.plugin; |
| 11045 |
method = plugin[hook]; |
| 11046 |
if (typeof method === 'function') { |
| 11047 |
params = [chart].concat(args || []); |
| 11048 |
params.push(descriptor.options); |
| 11049 |
if (method.apply(plugin, params) === false) { |
| 11050 |
return false; |
| 11051 |
} |
| 11052 |
} |
| 11053 |
} |
| 11054 |
|
| 11055 |
return true; |
| 11056 |
}, |
| 11057 |
|
| 11058 |
/** |
| 11059 |
* Returns descriptors of enabled plugins for the given chart. |
| 11060 |
* @returns {Array} [{ plugin, options }] |
| 11061 |
* @private |
| 11062 |
*/ |
| 11063 |
descriptors: function(chart) { |
| 11064 |
var cache = chart._plugins || (chart._plugins = {}); |
| 11065 |
if (cache.id === this._cacheId) { |
| 11066 |
return cache.descriptors; |
| 11067 |
} |
| 11068 |
|
| 11069 |
var plugins = []; |
| 11070 |
var descriptors = []; |
| 11071 |
var config = (chart && chart.config) || {}; |
| 11072 |
var options = (config.options && config.options.plugins) || {}; |
| 11073 |
|
| 11074 |
this._plugins.concat(config.plugins || []).forEach(function(plugin) { |
| 11075 |
var idx = plugins.indexOf(plugin); |
| 11076 |
if (idx !== -1) { |
| 11077 |
return; |
| 11078 |
} |
| 11079 |
|
| 11080 |
var id = plugin.id; |
| 11081 |
var opts = options[id]; |
| 11082 |
if (opts === false) { |
| 11083 |
return; |
| 11084 |
} |
| 11085 |
|
| 11086 |
if (opts === true) { |
| 11087 |
opts = helpers.clone(defaults.global.plugins[id]); |
| 11088 |
} |
| 11089 |
|
| 11090 |
plugins.push(plugin); |
| 11091 |
descriptors.push({ |
| 11092 |
plugin: plugin, |
| 11093 |
options: opts || {} |
| 11094 |
}); |
| 11095 |
}); |
| 11096 |
|
| 11097 |
cache.descriptors = descriptors; |
| 11098 |
cache.id = this._cacheId; |
| 11099 |
return descriptors; |
| 11100 |
} |
| 11101 |
}; |
| 11102 |
|
| 11103 |
/** |
| 11104 |
* Plugin extension hooks. |
| 11105 |
* @interface IPlugin |
| 11106 |
* @since 2.1.0 |
| 11107 |
*/ |
| 11108 |
/** |
| 11109 |
* @method IPlugin#beforeInit |
| 11110 |
* @desc Called before initializing `chart`. |
| 11111 |
* @param {Chart.Controller} chart - The chart instance. |
| 11112 |
* @param {Object} options - The plugin options. |
| 11113 |
*/ |
| 11114 |
/** |
| 11115 |
* @method IPlugin#afterInit |
| 11116 |
* @desc Called after `chart` has been initialized and before the first update. |
| 11117 |
* @param {Chart.Controller} chart - The chart instance. |
| 11118 |
* @param {Object} options - The plugin options. |
| 11119 |
*/ |
| 11120 |
/** |
| 11121 |
* @method IPlugin#beforeUpdate |
| 11122 |
* @desc Called before updating `chart`. If any plugin returns `false`, the update |
| 11123 |
* is cancelled (and thus subsequent render(s)) until another `update` is triggered. |
| 11124 |
* @param {Chart.Controller} chart - The chart instance. |
| 11125 |
* @param {Object} options - The plugin options. |
| 11126 |
* @returns {Boolean} `false` to cancel the chart update. |
| 11127 |
*/ |
| 11128 |
/** |
| 11129 |
* @method IPlugin#afterUpdate |
| 11130 |
* @desc Called after `chart` has been updated and before rendering. Note that this |
| 11131 |
* hook will not be called if the chart update has been previously cancelled. |
| 11132 |
* @param {Chart.Controller} chart - The chart instance. |
| 11133 |
* @param {Object} options - The plugin options. |
| 11134 |
*/ |
| 11135 |
/** |
| 11136 |
* @method IPlugin#beforeDatasetsUpdate |
| 11137 |
* @desc Called before updating the `chart` datasets. If any plugin returns `false`, |
| 11138 |
* the datasets update is cancelled until another `update` is triggered. |
| 11139 |
* @param {Chart.Controller} chart - The chart instance. |
| 11140 |
* @param {Object} options - The plugin options. |
| 11141 |
* @returns {Boolean} false to cancel the datasets update. |
| 11142 |
* @since version 2.1.5 |
| 11143 |
*/ |
| 11144 |
/** |
| 11145 |
* @method IPlugin#afterDatasetsUpdate |
| 11146 |
* @desc Called after the `chart` datasets have been updated. Note that this hook |
| 11147 |
* will not be called if the datasets update has been previously cancelled. |
| 11148 |
* @param {Chart.Controller} chart - The chart instance. |
| 11149 |
* @param {Object} options - The plugin options. |
| 11150 |
* @since version 2.1.5 |
| 11151 |
*/ |
| 11152 |
/** |
| 11153 |
* @method IPlugin#beforeDatasetUpdate |
| 11154 |
* @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin |
| 11155 |
* returns `false`, the datasets update is cancelled until another `update` is triggered. |
| 11156 |
* @param {Chart} chart - The chart instance. |
| 11157 |
* @param {Object} args - The call arguments. |
| 11158 |
* @param {Number} args.index - The dataset index. |
| 11159 |
* @param {Object} args.meta - The dataset metadata. |
| 11160 |
* @param {Object} options - The plugin options. |
| 11161 |
* @returns {Boolean} `false` to cancel the chart datasets drawing. |
| 11162 |
*/ |
| 11163 |
/** |
| 11164 |
* @method IPlugin#afterDatasetUpdate |
| 11165 |
* @desc Called after the `chart` datasets at the given `args.index` has been updated. Note |
| 11166 |
* that this hook will not be called if the datasets update has been previously cancelled. |
| 11167 |
* @param {Chart} chart - The chart instance. |
| 11168 |
* @param {Object} args - The call arguments. |
| 11169 |
* @param {Number} args.index - The dataset index. |
| 11170 |
* @param {Object} args.meta - The dataset metadata. |
| 11171 |
* @param {Object} options - The plugin options. |
| 11172 |
*/ |
| 11173 |
/** |
| 11174 |
* @method IPlugin#beforeLayout |
| 11175 |
* @desc Called before laying out `chart`. If any plugin returns `false`, |
| 11176 |
* the layout update is cancelled until another `update` is triggered. |
| 11177 |
* @param {Chart.Controller} chart - The chart instance. |
| 11178 |
* @param {Object} options - The plugin options. |
| 11179 |
* @returns {Boolean} `false` to cancel the chart layout. |
| 11180 |
*/ |
| 11181 |
/** |
| 11182 |
* @method IPlugin#afterLayout |
| 11183 |
* @desc Called after the `chart` has been layed out. Note that this hook will not |
| 11184 |
* be called if the layout update has been previously cancelled. |
| 11185 |
* @param {Chart.Controller} chart - The chart instance. |
| 11186 |
* @param {Object} options - The plugin options. |
| 11187 |
*/ |
| 11188 |
/** |
| 11189 |
* @method IPlugin#beforeRender |
| 11190 |
* @desc Called before rendering `chart`. If any plugin returns `false`, |
| 11191 |
* the rendering is cancelled until another `render` is triggered. |
| 11192 |
* @param {Chart.Controller} chart - The chart instance. |
| 11193 |
* @param {Object} options - The plugin options. |
| 11194 |
* @returns {Boolean} `false` to cancel the chart rendering. |
| 11195 |
*/ |
| 11196 |
/** |
| 11197 |
* @method IPlugin#afterRender |
| 11198 |
* @desc Called after the `chart` has been fully rendered (and animation completed). Note |
| 11199 |
* that this hook will not be called if the rendering has been previously cancelled. |
| 11200 |
* @param {Chart.Controller} chart - The chart instance. |
| 11201 |
* @param {Object} options - The plugin options. |
| 11202 |
*/ |
| 11203 |
/** |
| 11204 |
* @method IPlugin#beforeDraw |
| 11205 |
* @desc Called before drawing `chart` at every animation frame specified by the given |
| 11206 |
* easing value. If any plugin returns `false`, the frame drawing is cancelled until |
| 11207 |
* another `render` is triggered. |
| 11208 |
* @param {Chart.Controller} chart - The chart instance. |
| 11209 |
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 11210 |
* @param {Object} options - The plugin options. |
| 11211 |
* @returns {Boolean} `false` to cancel the chart drawing. |
| 11212 |
*/ |
| 11213 |
/** |
| 11214 |
* @method IPlugin#afterDraw |
| 11215 |
* @desc Called after the `chart` has been drawn for the specific easing value. Note |
| 11216 |
* that this hook will not be called if the drawing has been previously cancelled. |
| 11217 |
* @param {Chart.Controller} chart - The chart instance. |
| 11218 |
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 11219 |
* @param {Object} options - The plugin options. |
| 11220 |
*/ |
| 11221 |
/** |
| 11222 |
* @method IPlugin#beforeDatasetsDraw |
| 11223 |
* @desc Called before drawing the `chart` datasets. If any plugin returns `false`, |
| 11224 |
* the datasets drawing is cancelled until another `render` is triggered. |
| 11225 |
* @param {Chart.Controller} chart - The chart instance. |
| 11226 |
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 11227 |
* @param {Object} options - The plugin options. |
| 11228 |
* @returns {Boolean} `false` to cancel the chart datasets drawing. |
| 11229 |
*/ |
| 11230 |
/** |
| 11231 |
* @method IPlugin#afterDatasetsDraw |
| 11232 |
* @desc Called after the `chart` datasets have been drawn. Note that this hook |
| 11233 |
* will not be called if the datasets drawing has been previously cancelled. |
| 11234 |
* @param {Chart.Controller} chart - The chart instance. |
| 11235 |
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 11236 |
* @param {Object} options - The plugin options. |
| 11237 |
*/ |
| 11238 |
/** |
| 11239 |
* @method IPlugin#beforeDatasetDraw |
| 11240 |
* @desc Called before drawing the `chart` dataset at the given `args.index` (datasets |
| 11241 |
* are drawn in the reverse order). If any plugin returns `false`, the datasets drawing |
| 11242 |
* is cancelled until another `render` is triggered. |
| 11243 |
* @param {Chart} chart - The chart instance. |
| 11244 |
* @param {Object} args - The call arguments. |
| 11245 |
* @param {Number} args.index - The dataset index. |
| 11246 |
* @param {Object} args.meta - The dataset metadata. |
| 11247 |
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. |
| 11248 |
* @param {Object} options - The plugin options. |
| 11249 |
* @returns {Boolean} `false` to cancel the chart datasets drawing. |
| 11250 |
*/ |
| 11251 |
/** |
| 11252 |
* @method IPlugin#afterDatasetDraw |
| 11253 |
* @desc Called after the `chart` datasets at the given `args.index` have been drawn |
| 11254 |
* (datasets are drawn in the reverse order). Note that this hook will not be called |
| 11255 |
* if the datasets drawing has been previously cancelled. |
| 11256 |
* @param {Chart} chart - The chart instance. |
| 11257 |
* @param {Object} args - The call arguments. |
| 11258 |
* @param {Number} args.index - The dataset index. |
| 11259 |
* @param {Object} args.meta - The dataset metadata. |
| 11260 |
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. |
| 11261 |
* @param {Object} options - The plugin options. |
| 11262 |
*/ |
| 11263 |
/** |
| 11264 |
* @method IPlugin#beforeEvent |
| 11265 |
* @desc Called before processing the specified `event`. If any plugin returns `false`, |
| 11266 |
* the event will be discarded. |
| 11267 |
* @param {Chart.Controller} chart - The chart instance. |
| 11268 |
* @param {IEvent} event - The event object. |
| 11269 |
* @param {Object} options - The plugin options. |
| 11270 |
*/ |
| 11271 |
/** |
| 11272 |
* @method IPlugin#afterEvent |
| 11273 |
* @desc Called after the `event` has been consumed. Note that this hook |
| 11274 |
* will not be called if the `event` has been previously discarded. |
| 11275 |
* @param {Chart.Controller} chart - The chart instance. |
| 11276 |
* @param {IEvent} event - The event object. |
| 11277 |
* @param {Object} options - The plugin options. |
| 11278 |
*/ |
| 11279 |
/** |
| 11280 |
* @method IPlugin#resize |
| 11281 |
* @desc Called after the chart as been resized. |
| 11282 |
* @param {Chart.Controller} chart - The chart instance. |
| 11283 |
* @param {Number} size - The new canvas display size (eq. canvas.style width & height). |
| 11284 |
* @param {Object} options - The plugin options. |
| 11285 |
*/ |
| 11286 |
/** |
| 11287 |
* @method IPlugin#destroy |
| 11288 |
* @desc Called after the chart as been destroyed. |
| 11289 |
* @param {Chart.Controller} chart - The chart instance. |
| 11290 |
* @param {Object} options - The plugin options. |
| 11291 |
*/ |
| 11292 |
|
| 11293 |
/** |
| 11294 |
* Provided for backward compatibility, use Chart.plugins instead |
| 11295 |
* @namespace Chart.pluginService |
| 11296 |
* @deprecated since version 2.1.5 |
| 11297 |
* @todo remove at version 3 |
| 11298 |
* @private |
| 11299 |
*/ |
| 11300 |
Chart.pluginService = Chart.plugins; |
| 11301 |
|
| 11302 |
/** |
| 11303 |
* Provided for backward compatibility, inheriting from Chart.PlugingBase has no |
| 11304 |
* effect, instead simply create/register plugins via plain JavaScript objects. |
| 11305 |
* @interface Chart.PluginBase |
| 11306 |
* @deprecated since version 2.5.0 |
| 11307 |
* @todo remove at version 3 |
| 11308 |
* @private |
| 11309 |
*/ |
| 11310 |
Chart.PluginBase = Element.extend({}); |
| 11311 |
}; |
| 11312 |
|
| 11313 |
},{"25":25,"26":26,"45":45}],32:[function(require,module,exports){ |
| 11314 |
'use strict'; |
| 11315 |
|
| 11316 |
var defaults = require(25); |
| 11317 |
var Element = require(26); |
| 11318 |
var helpers = require(45); |
| 11319 |
var Ticks = require(34); |
| 11320 |
|
| 11321 |
defaults._set('scale', { |
| 11322 |
display: true, |
| 11323 |
position: 'left', |
| 11324 |
offset: false, |
| 11325 |
|
| 11326 |
// grid line settings |
| 11327 |
gridLines: { |
| 11328 |
display: true, |
| 11329 |
color: 'rgba(0, 0, 0, 0.1)', |
| 11330 |
lineWidth: 1, |
| 11331 |
drawBorder: true, |
| 11332 |
drawOnChartArea: true, |
| 11333 |
drawTicks: true, |
| 11334 |
tickMarkLength: 10, |
| 11335 |
zeroLineWidth: 1, |
| 11336 |
zeroLineColor: 'rgba(0,0,0,0.25)', |
| 11337 |
zeroLineBorderDash: [], |
| 11338 |
zeroLineBorderDashOffset: 0.0, |
| 11339 |
offsetGridLines: false, |
| 11340 |
borderDash: [], |
| 11341 |
borderDashOffset: 0.0 |
| 11342 |
}, |
| 11343 |
|
| 11344 |
// scale label |
| 11345 |
scaleLabel: { |
| 11346 |
// display property |
| 11347 |
display: false, |
| 11348 |
|
| 11349 |
// actual label |
| 11350 |
labelString: '', |
| 11351 |
|
| 11352 |
// line height |
| 11353 |
lineHeight: 1.2, |
| 11354 |
|
| 11355 |
// top/bottom padding |
| 11356 |
padding: { |
| 11357 |
top: 4, |
| 11358 |
bottom: 4 |
| 11359 |
} |
| 11360 |
}, |
| 11361 |
|
| 11362 |
// label settings |
| 11363 |
ticks: { |
| 11364 |
beginAtZero: false, |
| 11365 |
minRotation: 0, |
| 11366 |
maxRotation: 50, |
| 11367 |
mirror: false, |
| 11368 |
padding: 0, |
| 11369 |
reverse: false, |
| 11370 |
display: true, |
| 11371 |
autoSkip: true, |
| 11372 |
autoSkipPadding: 0, |
| 11373 |
labelOffset: 0, |
| 11374 |
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here. |
| 11375 |
callback: Ticks.formatters.values, |
| 11376 |
minor: {}, |
| 11377 |
major: {} |
| 11378 |
} |
| 11379 |
}); |
| 11380 |
|
| 11381 |
function labelsFromTicks(ticks) { |
| 11382 |
var labels = []; |
| 11383 |
var i, ilen; |
| 11384 |
|
| 11385 |
for (i = 0, ilen = ticks.length; i < ilen; ++i) { |
| 11386 |
labels.push(ticks[i].label); |
| 11387 |
} |
| 11388 |
|
| 11389 |
return labels; |
| 11390 |
} |
| 11391 |
|
| 11392 |
function getLineValue(scale, index, offsetGridLines) { |
| 11393 |
var lineValue = scale.getPixelForTick(index); |
| 11394 |
|
| 11395 |
if (offsetGridLines) { |
| 11396 |
if (index === 0) { |
| 11397 |
lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; |
| 11398 |
} else { |
| 11399 |
lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; |
| 11400 |
} |
| 11401 |
} |
| 11402 |
return lineValue; |
| 11403 |
} |
| 11404 |
|
| 11405 |
module.exports = function(Chart) { |
| 11406 |
|
| 11407 |
function computeTextSize(context, tick, font) { |
| 11408 |
return helpers.isArray(tick) ? |
| 11409 |
helpers.longestText(context, font, tick) : |
| 11410 |
context.measureText(tick).width; |
| 11411 |
} |
| 11412 |
|
| 11413 |
function parseFontOptions(options) { |
| 11414 |
var valueOrDefault = helpers.valueOrDefault; |
| 11415 |
var globalDefaults = defaults.global; |
| 11416 |
var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); |
| 11417 |
var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); |
| 11418 |
var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); |
| 11419 |
|
| 11420 |
return { |
| 11421 |
size: size, |
| 11422 |
style: style, |
| 11423 |
family: family, |
| 11424 |
font: helpers.fontString(size, style, family) |
| 11425 |
}; |
| 11426 |
} |
| 11427 |
|
| 11428 |
function parseLineHeight(options) { |
| 11429 |
return helpers.options.toLineHeight( |
| 11430 |
helpers.valueOrDefault(options.lineHeight, 1.2), |
| 11431 |
helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); |
| 11432 |
} |
| 11433 |
|
| 11434 |
Chart.Scale = Element.extend({ |
| 11435 |
/** |
| 11436 |
* Get the padding needed for the scale |
| 11437 |
* @method getPadding |
| 11438 |
* @private |
| 11439 |
* @returns {Padding} the necessary padding |
| 11440 |
*/ |
| 11441 |
getPadding: function() { |
| 11442 |
var me = this; |
| 11443 |
return { |
| 11444 |
left: me.paddingLeft || 0, |
| 11445 |
top: me.paddingTop || 0, |
| 11446 |
right: me.paddingRight || 0, |
| 11447 |
bottom: me.paddingBottom || 0 |
| 11448 |
}; |
| 11449 |
}, |
| 11450 |
|
| 11451 |
/** |
| 11452 |
* Returns the scale tick objects ({label, major}) |
| 11453 |
* @since 2.7 |
| 11454 |
*/ |
| 11455 |
getTicks: function() { |
| 11456 |
return this._ticks; |
| 11457 |
}, |
| 11458 |
|
| 11459 |
// These methods are ordered by lifecyle. Utilities then follow. |
| 11460 |
// Any function defined here is inherited by all scale types. |
| 11461 |
// Any function can be extended by the scale type |
| 11462 |
|
| 11463 |
mergeTicksOptions: function() { |
| 11464 |
var ticks = this.options.ticks; |
| 11465 |
if (ticks.minor === false) { |
| 11466 |
ticks.minor = { |
| 11467 |
display: false |
| 11468 |
}; |
| 11469 |
} |
| 11470 |
if (ticks.major === false) { |
| 11471 |
ticks.major = { |
| 11472 |
display: false |
| 11473 |
}; |
| 11474 |
} |
| 11475 |
for (var key in ticks) { |
| 11476 |
if (key !== 'major' && key !== 'minor') { |
| 11477 |
if (typeof ticks.minor[key] === 'undefined') { |
| 11478 |
ticks.minor[key] = ticks[key]; |
| 11479 |
} |
| 11480 |
if (typeof ticks.major[key] === 'undefined') { |
| 11481 |
ticks.major[key] = ticks[key]; |
| 11482 |
} |
| 11483 |
} |
| 11484 |
} |
| 11485 |
}, |
| 11486 |
beforeUpdate: function() { |
| 11487 |
helpers.callback(this.options.beforeUpdate, [this]); |
| 11488 |
}, |
| 11489 |
update: function(maxWidth, maxHeight, margins) { |
| 11490 |
var me = this; |
| 11491 |
var i, ilen, labels, label, ticks, tick; |
| 11492 |
|
| 11493 |
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
| 11494 |
me.beforeUpdate(); |
| 11495 |
|
| 11496 |
// Absorb the master measurements |
| 11497 |
me.maxWidth = maxWidth; |
| 11498 |
me.maxHeight = maxHeight; |
| 11499 |
me.margins = helpers.extend({ |
| 11500 |
left: 0, |
| 11501 |
right: 0, |
| 11502 |
top: 0, |
| 11503 |
bottom: 0 |
| 11504 |
}, margins); |
| 11505 |
me.longestTextCache = me.longestTextCache || {}; |
| 11506 |
|
| 11507 |
// Dimensions |
| 11508 |
me.beforeSetDimensions(); |
| 11509 |
me.setDimensions(); |
| 11510 |
me.afterSetDimensions(); |
| 11511 |
|
| 11512 |
// Data min/max |
| 11513 |
me.beforeDataLimits(); |
| 11514 |
me.determineDataLimits(); |
| 11515 |
me.afterDataLimits(); |
| 11516 |
|
| 11517 |
// Ticks - `this.ticks` is now DEPRECATED! |
| 11518 |
// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member |
| 11519 |
// and must not be accessed directly from outside this class. `this.ticks` being |
| 11520 |
// around for long time and not marked as private, we can't change its structure |
| 11521 |
// without unexpected breaking changes. If you need to access the scale ticks, |
| 11522 |
// use scale.getTicks() instead. |
| 11523 |
|
| 11524 |
me.beforeBuildTicks(); |
| 11525 |
|
| 11526 |
// New implementations should return an array of objects but for BACKWARD COMPAT, |
| 11527 |
// we still support no return (`this.ticks` internally set by calling this method). |
| 11528 |
ticks = me.buildTicks() || []; |
| 11529 |
|
| 11530 |
me.afterBuildTicks(); |
| 11531 |
|
| 11532 |
me.beforeTickToLabelConversion(); |
| 11533 |
|
| 11534 |
// New implementations should return the formatted tick labels but for BACKWARD |
| 11535 |
// COMPAT, we still support no return (`this.ticks` internally changed by calling |
| 11536 |
// this method and supposed to contain only string values). |
| 11537 |
labels = me.convertTicksToLabels(ticks) || me.ticks; |
| 11538 |
|
| 11539 |
me.afterTickToLabelConversion(); |
| 11540 |
|
| 11541 |
me.ticks = labels; // BACKWARD COMPATIBILITY |
| 11542 |
|
| 11543 |
// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! |
| 11544 |
|
| 11545 |
// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) |
| 11546 |
for (i = 0, ilen = labels.length; i < ilen; ++i) { |
| 11547 |
label = labels[i]; |
| 11548 |
tick = ticks[i]; |
| 11549 |
if (!tick) { |
| 11550 |
ticks.push(tick = { |
| 11551 |
label: label, |
| 11552 |
major: false |
| 11553 |
}); |
| 11554 |
} else { |
| 11555 |
tick.label = label; |
| 11556 |
} |
| 11557 |
} |
| 11558 |
|
| 11559 |
me._ticks = ticks; |
| 11560 |
|
| 11561 |
// Tick Rotation |
| 11562 |
me.beforeCalculateTickRotation(); |
| 11563 |
me.calculateTickRotation(); |
| 11564 |
me.afterCalculateTickRotation(); |
| 11565 |
// Fit |
| 11566 |
me.beforeFit(); |
| 11567 |
me.fit(); |
| 11568 |
me.afterFit(); |
| 11569 |
// |
| 11570 |
me.afterUpdate(); |
| 11571 |
|
| 11572 |
return me.minSize; |
| 11573 |
|
| 11574 |
}, |
| 11575 |
afterUpdate: function() { |
| 11576 |
helpers.callback(this.options.afterUpdate, [this]); |
| 11577 |
}, |
| 11578 |
|
| 11579 |
// |
| 11580 |
|
| 11581 |
beforeSetDimensions: function() { |
| 11582 |
helpers.callback(this.options.beforeSetDimensions, [this]); |
| 11583 |
}, |
| 11584 |
setDimensions: function() { |
| 11585 |
var me = this; |
| 11586 |
// Set the unconstrained dimension before label rotation |
| 11587 |
if (me.isHorizontal()) { |
| 11588 |
// Reset position before calculating rotation |
| 11589 |
me.width = me.maxWidth; |
| 11590 |
me.left = 0; |
| 11591 |
me.right = me.width; |
| 11592 |
} else { |
| 11593 |
me.height = me.maxHeight; |
| 11594 |
|
| 11595 |
// Reset position before calculating rotation |
| 11596 |
me.top = 0; |
| 11597 |
me.bottom = me.height; |
| 11598 |
} |
| 11599 |
|
| 11600 |
// Reset padding |
| 11601 |
me.paddingLeft = 0; |
| 11602 |
me.paddingTop = 0; |
| 11603 |
me.paddingRight = 0; |
| 11604 |
me.paddingBottom = 0; |
| 11605 |
}, |
| 11606 |
afterSetDimensions: function() { |
| 11607 |
helpers.callback(this.options.afterSetDimensions, [this]); |
| 11608 |
}, |
| 11609 |
|
| 11610 |
// Data limits |
| 11611 |
beforeDataLimits: function() { |
| 11612 |
helpers.callback(this.options.beforeDataLimits, [this]); |
| 11613 |
}, |
| 11614 |
determineDataLimits: helpers.noop, |
| 11615 |
afterDataLimits: function() { |
| 11616 |
helpers.callback(this.options.afterDataLimits, [this]); |
| 11617 |
}, |
| 11618 |
|
| 11619 |
// |
| 11620 |
beforeBuildTicks: function() { |
| 11621 |
helpers.callback(this.options.beforeBuildTicks, [this]); |
| 11622 |
}, |
| 11623 |
buildTicks: helpers.noop, |
| 11624 |
afterBuildTicks: function() { |
| 11625 |
helpers.callback(this.options.afterBuildTicks, [this]); |
| 11626 |
}, |
| 11627 |
|
| 11628 |
beforeTickToLabelConversion: function() { |
| 11629 |
helpers.callback(this.options.beforeTickToLabelConversion, [this]); |
| 11630 |
}, |
| 11631 |
convertTicksToLabels: function() { |
| 11632 |
var me = this; |
| 11633 |
// Convert ticks to strings |
| 11634 |
var tickOpts = me.options.ticks; |
| 11635 |
me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); |
| 11636 |
}, |
| 11637 |
afterTickToLabelConversion: function() { |
| 11638 |
helpers.callback(this.options.afterTickToLabelConversion, [this]); |
| 11639 |
}, |
| 11640 |
|
| 11641 |
// |
| 11642 |
|
| 11643 |
beforeCalculateTickRotation: function() { |
| 11644 |
helpers.callback(this.options.beforeCalculateTickRotation, [this]); |
| 11645 |
}, |
| 11646 |
calculateTickRotation: function() { |
| 11647 |
var me = this; |
| 11648 |
var context = me.ctx; |
| 11649 |
var tickOpts = me.options.ticks; |
| 11650 |
var labels = labelsFromTicks(me._ticks); |
| 11651 |
|
| 11652 |
// Get the width of each grid by calculating the difference |
| 11653 |
// between x offsets between 0 and 1. |
| 11654 |
var tickFont = parseFontOptions(tickOpts); |
| 11655 |
context.font = tickFont.font; |
| 11656 |
|
| 11657 |
var labelRotation = tickOpts.minRotation || 0; |
| 11658 |
|
| 11659 |
if (labels.length && me.options.display && me.isHorizontal()) { |
| 11660 |
var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); |
| 11661 |
var labelWidth = originalLabelWidth; |
| 11662 |
var cosRotation, sinRotation; |
| 11663 |
|
| 11664 |
// Allow 3 pixels x2 padding either side for label readability |
| 11665 |
var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; |
| 11666 |
|
| 11667 |
// Max label rotation can be set or default to 90 - also act as a loop counter |
| 11668 |
while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { |
| 11669 |
var angleRadians = helpers.toRadians(labelRotation); |
| 11670 |
cosRotation = Math.cos(angleRadians); |
| 11671 |
sinRotation = Math.sin(angleRadians); |
| 11672 |
|
| 11673 |
if (sinRotation * originalLabelWidth > me.maxHeight) { |
| 11674 |
// go back one step |
| 11675 |
labelRotation--; |
| 11676 |
break; |
| 11677 |
} |
| 11678 |
|
| 11679 |
labelRotation++; |
| 11680 |
labelWidth = cosRotation * originalLabelWidth; |
| 11681 |
} |
| 11682 |
} |
| 11683 |
|
| 11684 |
me.labelRotation = labelRotation; |
| 11685 |
}, |
| 11686 |
afterCalculateTickRotation: function() { |
| 11687 |
helpers.callback(this.options.afterCalculateTickRotation, [this]); |
| 11688 |
}, |
| 11689 |
|
| 11690 |
// |
| 11691 |
|
| 11692 |
beforeFit: function() { |
| 11693 |
helpers.callback(this.options.beforeFit, [this]); |
| 11694 |
}, |
| 11695 |
fit: function() { |
| 11696 |
var me = this; |
| 11697 |
// Reset |
| 11698 |
var minSize = me.minSize = { |
| 11699 |
width: 0, |
| 11700 |
height: 0 |
| 11701 |
}; |
| 11702 |
|
| 11703 |
var labels = labelsFromTicks(me._ticks); |
| 11704 |
|
| 11705 |
var opts = me.options; |
| 11706 |
var tickOpts = opts.ticks; |
| 11707 |
var scaleLabelOpts = opts.scaleLabel; |
| 11708 |
var gridLineOpts = opts.gridLines; |
| 11709 |
var display = opts.display; |
| 11710 |
var isHorizontal = me.isHorizontal(); |
| 11711 |
|
| 11712 |
var tickFont = parseFontOptions(tickOpts); |
| 11713 |
var tickMarkLength = opts.gridLines.tickMarkLength; |
| 11714 |
|
| 11715 |
// Width |
| 11716 |
if (isHorizontal) { |
| 11717 |
// subtract the margins to line up with the chartArea if we are a full width scale |
| 11718 |
minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; |
| 11719 |
} else { |
| 11720 |
minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; |
| 11721 |
} |
| 11722 |
|
| 11723 |
// height |
| 11724 |
if (isHorizontal) { |
| 11725 |
minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; |
| 11726 |
} else { |
| 11727 |
minSize.height = me.maxHeight; // fill all the height |
| 11728 |
} |
| 11729 |
|
| 11730 |
// Are we showing a title for the scale? |
| 11731 |
if (scaleLabelOpts.display && display) { |
| 11732 |
var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); |
| 11733 |
var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); |
| 11734 |
var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; |
| 11735 |
|
| 11736 |
if (isHorizontal) { |
| 11737 |
minSize.height += deltaHeight; |
| 11738 |
} else { |
| 11739 |
minSize.width += deltaHeight; |
| 11740 |
} |
| 11741 |
} |
| 11742 |
|
| 11743 |
// Don't bother fitting the ticks if we are not showing them |
| 11744 |
if (tickOpts.display && display) { |
| 11745 |
var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); |
| 11746 |
var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); |
| 11747 |
var lineSpace = tickFont.size * 0.5; |
| 11748 |
var tickPadding = me.options.ticks.padding; |
| 11749 |
|
| 11750 |
if (isHorizontal) { |
| 11751 |
// A horizontal axis is more constrained by the height. |
| 11752 |
me.longestLabelWidth = largestTextWidth; |
| 11753 |
|
| 11754 |
var angleRadians = helpers.toRadians(me.labelRotation); |
| 11755 |
var cosRotation = Math.cos(angleRadians); |
| 11756 |
var sinRotation = Math.sin(angleRadians); |
| 11757 |
|
| 11758 |
// TODO - improve this calculation |
| 11759 |
var labelHeight = (sinRotation * largestTextWidth) |
| 11760 |
+ (tickFont.size * tallestLabelHeightInLines) |
| 11761 |
+ (lineSpace * (tallestLabelHeightInLines - 1)) |
| 11762 |
+ lineSpace; // padding |
| 11763 |
|
| 11764 |
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); |
| 11765 |
|
| 11766 |
me.ctx.font = tickFont.font; |
| 11767 |
var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); |
| 11768 |
var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); |
| 11769 |
|
| 11770 |
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned |
| 11771 |
// which means that the right padding is dominated by the font height |
| 11772 |
if (me.labelRotation !== 0) { |
| 11773 |
me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges |
| 11774 |
me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; |
| 11775 |
} else { |
| 11776 |
me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges |
| 11777 |
me.paddingRight = lastLabelWidth / 2 + 3; |
| 11778 |
} |
| 11779 |
} else { |
| 11780 |
// A vertical axis is more constrained by the width. Labels are the |
| 11781 |
// dominant factor here, so get that length first and account for padding |
| 11782 |
if (tickOpts.mirror) { |
| 11783 |
largestTextWidth = 0; |
| 11784 |
} else { |
| 11785 |
// use lineSpace for consistency with horizontal axis |
| 11786 |
// tickPadding is not implemented for horizontal |
| 11787 |
largestTextWidth += tickPadding + lineSpace; |
| 11788 |
} |
| 11789 |
|
| 11790 |
minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); |
| 11791 |
|
| 11792 |
me.paddingTop = tickFont.size / 2; |
| 11793 |
me.paddingBottom = tickFont.size / 2; |
| 11794 |
} |
| 11795 |
} |
| 11796 |
|
| 11797 |
me.handleMargins(); |
| 11798 |
|
| 11799 |
me.width = minSize.width; |
| 11800 |
me.height = minSize.height; |
| 11801 |
}, |
| 11802 |
|
| 11803 |
/** |
| 11804 |
* Handle margins and padding interactions |
| 11805 |
* @private |
| 11806 |
*/ |
| 11807 |
handleMargins: function() { |
| 11808 |
var me = this; |
| 11809 |
if (me.margins) { |
| 11810 |
me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); |
| 11811 |
me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); |
| 11812 |
me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); |
| 11813 |
me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); |
| 11814 |
} |
| 11815 |
}, |
| 11816 |
|
| 11817 |
afterFit: function() { |
| 11818 |
helpers.callback(this.options.afterFit, [this]); |
| 11819 |
}, |
| 11820 |
|
| 11821 |
// Shared Methods |
| 11822 |
isHorizontal: function() { |
| 11823 |
return this.options.position === 'top' || this.options.position === 'bottom'; |
| 11824 |
}, |
| 11825 |
isFullWidth: function() { |
| 11826 |
return (this.options.fullWidth); |
| 11827 |
}, |
| 11828 |
|
| 11829 |
// 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 |
| 11830 |
getRightValue: function(rawValue) { |
| 11831 |
// Null and undefined values first |
| 11832 |
if (helpers.isNullOrUndef(rawValue)) { |
| 11833 |
return NaN; |
| 11834 |
} |
| 11835 |
// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values |
| 11836 |
if (typeof rawValue === 'number' && !isFinite(rawValue)) { |
| 11837 |
return NaN; |
| 11838 |
} |
| 11839 |
// If it is in fact an object, dive in one more level |
| 11840 |
if (rawValue) { |
| 11841 |
if (this.isHorizontal()) { |
| 11842 |
if (rawValue.x !== undefined) { |
| 11843 |
return this.getRightValue(rawValue.x); |
| 11844 |
} |
| 11845 |
} else if (rawValue.y !== undefined) { |
| 11846 |
return this.getRightValue(rawValue.y); |
| 11847 |
} |
| 11848 |
} |
| 11849 |
|
| 11850 |
// Value is good, return it |
| 11851 |
return rawValue; |
| 11852 |
}, |
| 11853 |
|
| 11854 |
// Used to get the value to display in the tooltip for the data at the given index |
| 11855 |
// function getLabelForIndex(index, datasetIndex) |
| 11856 |
getLabelForIndex: helpers.noop, |
| 11857 |
|
| 11858 |
// Used to get data value locations. Value can either be an index or a numerical value |
| 11859 |
getPixelForValue: helpers.noop, |
| 11860 |
|
| 11861 |
// Used to get the data value from a given pixel. This is the inverse of getPixelForValue |
| 11862 |
getValueForPixel: helpers.noop, |
| 11863 |
|
| 11864 |
// Used for tick location, should |
| 11865 |
getPixelForTick: function(index) { |
| 11866 |
var me = this; |
| 11867 |
var offset = me.options.offset; |
| 11868 |
if (me.isHorizontal()) { |
| 11869 |
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
| 11870 |
var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); |
| 11871 |
var pixel = (tickWidth * index) + me.paddingLeft; |
| 11872 |
|
| 11873 |
if (offset) { |
| 11874 |
pixel += tickWidth / 2; |
| 11875 |
} |
| 11876 |
|
| 11877 |
var finalVal = me.left + Math.round(pixel); |
| 11878 |
finalVal += me.isFullWidth() ? me.margins.left : 0; |
| 11879 |
return finalVal; |
| 11880 |
} |
| 11881 |
var innerHeight = me.height - (me.paddingTop + me.paddingBottom); |
| 11882 |
return me.top + (index * (innerHeight / (me._ticks.length - 1))); |
| 11883 |
}, |
| 11884 |
|
| 11885 |
// Utility for getting the pixel location of a percentage of scale |
| 11886 |
getPixelForDecimal: function(decimal) { |
| 11887 |
var me = this; |
| 11888 |
if (me.isHorizontal()) { |
| 11889 |
var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
| 11890 |
var valueOffset = (innerWidth * decimal) + me.paddingLeft; |
| 11891 |
|
| 11892 |
var finalVal = me.left + Math.round(valueOffset); |
| 11893 |
finalVal += me.isFullWidth() ? me.margins.left : 0; |
| 11894 |
return finalVal; |
| 11895 |
} |
| 11896 |
return me.top + (decimal * me.height); |
| 11897 |
}, |
| 11898 |
|
| 11899 |
getBasePixel: function() { |
| 11900 |
return this.getPixelForValue(this.getBaseValue()); |
| 11901 |
}, |
| 11902 |
|
| 11903 |
getBaseValue: function() { |
| 11904 |
var me = this; |
| 11905 |
var min = me.min; |
| 11906 |
var max = me.max; |
| 11907 |
|
| 11908 |
return me.beginAtZero ? 0 : |
| 11909 |
min < 0 && max < 0 ? max : |
| 11910 |
min > 0 && max > 0 ? min : |
| 11911 |
0; |
| 11912 |
}, |
| 11913 |
|
| 11914 |
/** |
| 11915 |
* Returns a subset of ticks to be plotted to avoid overlapping labels. |
| 11916 |
* @private |
| 11917 |
*/ |
| 11918 |
_autoSkip: function(ticks) { |
| 11919 |
var skipRatio; |
| 11920 |
var me = this; |
| 11921 |
var isHorizontal = me.isHorizontal(); |
| 11922 |
var optionTicks = me.options.ticks.minor; |
| 11923 |
var tickCount = ticks.length; |
| 11924 |
var labelRotationRadians = helpers.toRadians(me.labelRotation); |
| 11925 |
var cosRotation = Math.cos(labelRotationRadians); |
| 11926 |
var longestRotatedLabel = me.longestLabelWidth * cosRotation; |
| 11927 |
var result = []; |
| 11928 |
var i, tick, shouldSkip; |
| 11929 |
|
| 11930 |
// figure out the maximum number of gridlines to show |
| 11931 |
var maxTicks; |
| 11932 |
if (optionTicks.maxTicksLimit) { |
| 11933 |
maxTicks = optionTicks.maxTicksLimit; |
| 11934 |
} |
| 11935 |
|
| 11936 |
if (isHorizontal) { |
| 11937 |
skipRatio = false; |
| 11938 |
|
| 11939 |
if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { |
| 11940 |
skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); |
| 11941 |
} |
| 11942 |
|
| 11943 |
// if they defined a max number of optionTicks, |
| 11944 |
// increase skipRatio until that number is met |
| 11945 |
if (maxTicks && tickCount > maxTicks) { |
| 11946 |
skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); |
| 11947 |
} |
| 11948 |
} |
| 11949 |
|
| 11950 |
for (i = 0; i < tickCount; i++) { |
| 11951 |
tick = ticks[i]; |
| 11952 |
|
| 11953 |
// Since we always show the last tick,we need may need to hide the last shown one before |
| 11954 |
shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); |
| 11955 |
if (shouldSkip && i !== tickCount - 1 || helpers.isNullOrUndef(tick.label)) { |
| 11956 |
// leave tick in place but make sure it's not displayed (#4635) |
| 11957 |
delete tick.label; |
| 11958 |
} |
| 11959 |
result.push(tick); |
| 11960 |
} |
| 11961 |
return result; |
| 11962 |
}, |
| 11963 |
|
| 11964 |
// Actually draw the scale on the canvas |
| 11965 |
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on |
| 11966 |
draw: function(chartArea) { |
| 11967 |
var me = this; |
| 11968 |
var options = me.options; |
| 11969 |
if (!options.display) { |
| 11970 |
return; |
| 11971 |
} |
| 11972 |
|
| 11973 |
var context = me.ctx; |
| 11974 |
var globalDefaults = defaults.global; |
| 11975 |
var optionTicks = options.ticks.minor; |
| 11976 |
var optionMajorTicks = options.ticks.major || optionTicks; |
| 11977 |
var gridLines = options.gridLines; |
| 11978 |
var scaleLabel = options.scaleLabel; |
| 11979 |
|
| 11980 |
var isRotated = me.labelRotation !== 0; |
| 11981 |
var isHorizontal = me.isHorizontal(); |
| 11982 |
|
| 11983 |
var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); |
| 11984 |
var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); |
| 11985 |
var tickFont = parseFontOptions(optionTicks); |
| 11986 |
var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); |
| 11987 |
var majorTickFont = parseFontOptions(optionMajorTicks); |
| 11988 |
|
| 11989 |
var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; |
| 11990 |
|
| 11991 |
var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); |
| 11992 |
var scaleLabelFont = parseFontOptions(scaleLabel); |
| 11993 |
var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); |
| 11994 |
var labelRotationRadians = helpers.toRadians(me.labelRotation); |
| 11995 |
|
| 11996 |
var itemsToDraw = []; |
| 11997 |
|
| 11998 |
var xTickStart = options.position === 'right' ? me.left : me.right - tl; |
| 11999 |
var xTickEnd = options.position === 'right' ? me.left + tl : me.right; |
| 12000 |
var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl; |
| 12001 |
var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom; |
| 12002 |
|
| 12003 |
helpers.each(ticks, function(tick, index) { |
| 12004 |
// autoskipper skipped this tick (#4635) |
| 12005 |
if (tick.label === undefined) { |
| 12006 |
return; |
| 12007 |
} |
| 12008 |
|
| 12009 |
var label = tick.label; |
| 12010 |
var lineWidth, lineColor, borderDash, borderDashOffset; |
| 12011 |
if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { |
| 12012 |
// Draw the first index specially |
| 12013 |
lineWidth = gridLines.zeroLineWidth; |
| 12014 |
lineColor = gridLines.zeroLineColor; |
| 12015 |
borderDash = gridLines.zeroLineBorderDash; |
| 12016 |
borderDashOffset = gridLines.zeroLineBorderDashOffset; |
| 12017 |
} else { |
| 12018 |
lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); |
| 12019 |
lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); |
| 12020 |
borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); |
| 12021 |
borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); |
| 12022 |
} |
| 12023 |
|
| 12024 |
// Common properties |
| 12025 |
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; |
| 12026 |
var textAlign = 'middle'; |
| 12027 |
var textBaseline = 'middle'; |
| 12028 |
var tickPadding = optionTicks.padding; |
| 12029 |
|
| 12030 |
if (isHorizontal) { |
| 12031 |
var labelYOffset = tl + tickPadding; |
| 12032 |
|
| 12033 |
if (options.position === 'bottom') { |
| 12034 |
// bottom |
| 12035 |
textBaseline = !isRotated ? 'top' : 'middle'; |
| 12036 |
textAlign = !isRotated ? 'center' : 'right'; |
| 12037 |
labelY = me.top + labelYOffset; |
| 12038 |
} else { |
| 12039 |
// top |
| 12040 |
textBaseline = !isRotated ? 'bottom' : 'middle'; |
| 12041 |
textAlign = !isRotated ? 'center' : 'left'; |
| 12042 |
labelY = me.bottom - labelYOffset; |
| 12043 |
} |
| 12044 |
|
| 12045 |
var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); |
| 12046 |
if (xLineValue < me.left) { |
| 12047 |
lineColor = 'rgba(0,0,0,0)'; |
| 12048 |
} |
| 12049 |
xLineValue += helpers.aliasPixel(lineWidth); |
| 12050 |
|
| 12051 |
labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) |
| 12052 |
|
| 12053 |
tx1 = tx2 = x1 = x2 = xLineValue; |
| 12054 |
ty1 = yTickStart; |
| 12055 |
ty2 = yTickEnd; |
| 12056 |
y1 = chartArea.top; |
| 12057 |
y2 = chartArea.bottom; |
| 12058 |
} else { |
| 12059 |
var isLeft = options.position === 'left'; |
| 12060 |
var labelXOffset; |
| 12061 |
|
| 12062 |
if (optionTicks.mirror) { |
| 12063 |
textAlign = isLeft ? 'left' : 'right'; |
| 12064 |
labelXOffset = tickPadding; |
| 12065 |
} else { |
| 12066 |
textAlign = isLeft ? 'right' : 'left'; |
| 12067 |
labelXOffset = tl + tickPadding; |
| 12068 |
} |
| 12069 |
|
| 12070 |
labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; |
| 12071 |
|
| 12072 |
var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); |
| 12073 |
if (yLineValue < me.top) { |
| 12074 |
lineColor = 'rgba(0,0,0,0)'; |
| 12075 |
} |
| 12076 |
yLineValue += helpers.aliasPixel(lineWidth); |
| 12077 |
|
| 12078 |
labelY = me.getPixelForTick(index) + optionTicks.labelOffset; |
| 12079 |
|
| 12080 |
tx1 = xTickStart; |
| 12081 |
tx2 = xTickEnd; |
| 12082 |
x1 = chartArea.left; |
| 12083 |
x2 = chartArea.right; |
| 12084 |
ty1 = ty2 = y1 = y2 = yLineValue; |
| 12085 |
} |
| 12086 |
|
| 12087 |
itemsToDraw.push({ |
| 12088 |
tx1: tx1, |
| 12089 |
ty1: ty1, |
| 12090 |
tx2: tx2, |
| 12091 |
ty2: ty2, |
| 12092 |
x1: x1, |
| 12093 |
y1: y1, |
| 12094 |
x2: x2, |
| 12095 |
y2: y2, |
| 12096 |
labelX: labelX, |
| 12097 |
labelY: labelY, |
| 12098 |
glWidth: lineWidth, |
| 12099 |
glColor: lineColor, |
| 12100 |
glBorderDash: borderDash, |
| 12101 |
glBorderDashOffset: borderDashOffset, |
| 12102 |
rotation: -1 * labelRotationRadians, |
| 12103 |
label: label, |
| 12104 |
major: tick.major, |
| 12105 |
textBaseline: textBaseline, |
| 12106 |
textAlign: textAlign |
| 12107 |
}); |
| 12108 |
}); |
| 12109 |
|
| 12110 |
// Draw all of the tick labels, tick marks, and grid lines at the correct places |
| 12111 |
helpers.each(itemsToDraw, function(itemToDraw) { |
| 12112 |
if (gridLines.display) { |
| 12113 |
context.save(); |
| 12114 |
context.lineWidth = itemToDraw.glWidth; |
| 12115 |
context.strokeStyle = itemToDraw.glColor; |
| 12116 |
if (context.setLineDash) { |
| 12117 |
context.setLineDash(itemToDraw.glBorderDash); |
| 12118 |
context.lineDashOffset = itemToDraw.glBorderDashOffset; |
| 12119 |
} |
| 12120 |
|
| 12121 |
context.beginPath(); |
| 12122 |
|
| 12123 |
if (gridLines.drawTicks) { |
| 12124 |
context.moveTo(itemToDraw.tx1, itemToDraw.ty1); |
| 12125 |
context.lineTo(itemToDraw.tx2, itemToDraw.ty2); |
| 12126 |
} |
| 12127 |
|
| 12128 |
if (gridLines.drawOnChartArea) { |
| 12129 |
context.moveTo(itemToDraw.x1, itemToDraw.y1); |
| 12130 |
context.lineTo(itemToDraw.x2, itemToDraw.y2); |
| 12131 |
} |
| 12132 |
|
| 12133 |
context.stroke(); |
| 12134 |
context.restore(); |
| 12135 |
} |
| 12136 |
|
| 12137 |
if (optionTicks.display) { |
| 12138 |
// Make sure we draw text in the correct color and font |
| 12139 |
context.save(); |
| 12140 |
context.translate(itemToDraw.labelX, itemToDraw.labelY); |
| 12141 |
context.rotate(itemToDraw.rotation); |
| 12142 |
context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; |
| 12143 |
context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; |
| 12144 |
context.textBaseline = itemToDraw.textBaseline; |
| 12145 |
context.textAlign = itemToDraw.textAlign; |
| 12146 |
|
| 12147 |
var label = itemToDraw.label; |
| 12148 |
if (helpers.isArray(label)) { |
| 12149 |
for (var i = 0, y = 0; i < label.length; ++i) { |
| 12150 |
// We just make sure the multiline element is a string here.. |
| 12151 |
context.fillText('' + label[i], 0, y); |
| 12152 |
// apply same lineSpacing as calculated @ L#320 |
| 12153 |
y += (tickFont.size * 1.5); |
| 12154 |
} |
| 12155 |
} else { |
| 12156 |
context.fillText(label, 0, 0); |
| 12157 |
} |
| 12158 |
context.restore(); |
| 12159 |
} |
| 12160 |
}); |
| 12161 |
|
| 12162 |
if (scaleLabel.display) { |
| 12163 |
// Draw the scale label |
| 12164 |
var scaleLabelX; |
| 12165 |
var scaleLabelY; |
| 12166 |
var rotation = 0; |
| 12167 |
var halfLineHeight = parseLineHeight(scaleLabel) / 2; |
| 12168 |
|
| 12169 |
if (isHorizontal) { |
| 12170 |
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width |
| 12171 |
scaleLabelY = options.position === 'bottom' |
| 12172 |
? me.bottom - halfLineHeight - scaleLabelPadding.bottom |
| 12173 |
: me.top + halfLineHeight + scaleLabelPadding.top; |
| 12174 |
} else { |
| 12175 |
var isLeft = options.position === 'left'; |
| 12176 |
scaleLabelX = isLeft |
| 12177 |
? me.left + halfLineHeight + scaleLabelPadding.top |
| 12178 |
: me.right - halfLineHeight - scaleLabelPadding.top; |
| 12179 |
scaleLabelY = me.top + ((me.bottom - me.top) / 2); |
| 12180 |
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; |
| 12181 |
} |
| 12182 |
|
| 12183 |
context.save(); |
| 12184 |
context.translate(scaleLabelX, scaleLabelY); |
| 12185 |
context.rotate(rotation); |
| 12186 |
context.textAlign = 'center'; |
| 12187 |
context.textBaseline = 'middle'; |
| 12188 |
context.fillStyle = scaleLabelFontColor; // render in correct colour |
| 12189 |
context.font = scaleLabelFont.font; |
| 12190 |
context.fillText(scaleLabel.labelString, 0, 0); |
| 12191 |
context.restore(); |
| 12192 |
} |
| 12193 |
|
| 12194 |
if (gridLines.drawBorder) { |
| 12195 |
// Draw the line at the edge of the axis |
| 12196 |
context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); |
| 12197 |
context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); |
| 12198 |
var x1 = me.left; |
| 12199 |
var x2 = me.right; |
| 12200 |
var y1 = me.top; |
| 12201 |
var y2 = me.bottom; |
| 12202 |
|
| 12203 |
var aliasPixel = helpers.aliasPixel(context.lineWidth); |
| 12204 |
if (isHorizontal) { |
| 12205 |
y1 = y2 = options.position === 'top' ? me.bottom : me.top; |
| 12206 |
y1 += aliasPixel; |
| 12207 |
y2 += aliasPixel; |
| 12208 |
} else { |
| 12209 |
x1 = x2 = options.position === 'left' ? me.right : me.left; |
| 12210 |
x1 += aliasPixel; |
| 12211 |
x2 += aliasPixel; |
| 12212 |
} |
| 12213 |
|
| 12214 |
context.beginPath(); |
| 12215 |
context.moveTo(x1, y1); |
| 12216 |
context.lineTo(x2, y2); |
| 12217 |
context.stroke(); |
| 12218 |
} |
| 12219 |
} |
| 12220 |
}); |
| 12221 |
}; |
| 12222 |
|
| 12223 |
},{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){ |
| 12224 |
'use strict'; |
| 12225 |
|
| 12226 |
var defaults = require(25); |
| 12227 |
var helpers = require(45); |
| 12228 |
|
| 12229 |
module.exports = function(Chart) { |
| 12230 |
|
| 12231 |
Chart.scaleService = { |
| 12232 |
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then |
| 12233 |
// use the new chart options to grab the correct scale |
| 12234 |
constructors: {}, |
| 12235 |
// Use a registration function so that we can move to an ES6 map when we no longer need to support |
| 12236 |
// old browsers |
| 12237 |
|
| 12238 |
// Scale config defaults |
| 12239 |
defaults: {}, |
| 12240 |
registerScaleType: function(type, scaleConstructor, scaleDefaults) { |
| 12241 |
this.constructors[type] = scaleConstructor; |
| 12242 |
this.defaults[type] = helpers.clone(scaleDefaults); |
| 12243 |
}, |
| 12244 |
getScaleConstructor: function(type) { |
| 12245 |
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; |
| 12246 |
}, |
| 12247 |
getScaleDefaults: function(type) { |
| 12248 |
// Return the scale defaults merged with the global settings so that we always use the latest ones |
| 12249 |
return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; |
| 12250 |
}, |
| 12251 |
updateScaleDefaults: function(type, additions) { |
| 12252 |
var me = this; |
| 12253 |
if (me.defaults.hasOwnProperty(type)) { |
| 12254 |
me.defaults[type] = helpers.extend(me.defaults[type], additions); |
| 12255 |
} |
| 12256 |
}, |
| 12257 |
addScalesToLayout: function(chart) { |
| 12258 |
// Adds each scale to the chart.boxes array to be sized accordingly |
| 12259 |
helpers.each(chart.scales, function(scale) { |
| 12260 |
// Set ILayoutItem parameters for backwards compatibility |
| 12261 |
scale.fullWidth = scale.options.fullWidth; |
| 12262 |
scale.position = scale.options.position; |
| 12263 |
scale.weight = scale.options.weight; |
| 12264 |
Chart.layoutService.addBox(chart, scale); |
| 12265 |
}); |
| 12266 |
} |
| 12267 |
}; |
| 12268 |
}; |
| 12269 |
|
| 12270 |
},{"25":25,"45":45}],34:[function(require,module,exports){ |
| 12271 |
'use strict'; |
| 12272 |
|
| 12273 |
var helpers = require(45); |
| 12274 |
|
| 12275 |
/** |
| 12276 |
* Namespace to hold static tick generation functions |
| 12277 |
* @namespace Chart.Ticks |
| 12278 |
*/ |
| 12279 |
module.exports = { |
| 12280 |
/** |
| 12281 |
* Namespace to hold generators for different types of ticks |
| 12282 |
* @namespace Chart.Ticks.generators |
| 12283 |
*/ |
| 12284 |
generators: { |
| 12285 |
/** |
| 12286 |
* Interface for the options provided to the numeric tick generator |
| 12287 |
* @interface INumericTickGenerationOptions |
| 12288 |
*/ |
| 12289 |
/** |
| 12290 |
* The maximum number of ticks to display |
| 12291 |
* @name INumericTickGenerationOptions#maxTicks |
| 12292 |
* @type Number |
| 12293 |
*/ |
| 12294 |
/** |
| 12295 |
* The distance between each tick. |
| 12296 |
* @name INumericTickGenerationOptions#stepSize |
| 12297 |
* @type Number |
| 12298 |
* @optional |
| 12299 |
*/ |
| 12300 |
/** |
| 12301 |
* Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum |
| 12302 |
* @name INumericTickGenerationOptions#min |
| 12303 |
* @type Number |
| 12304 |
* @optional |
| 12305 |
*/ |
| 12306 |
/** |
| 12307 |
* The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum |
| 12308 |
* @name INumericTickGenerationOptions#max |
| 12309 |
* @type Number |
| 12310 |
* @optional |
| 12311 |
*/ |
| 12312 |
|
| 12313 |
/** |
| 12314 |
* Generate a set of linear ticks |
| 12315 |
* @method Chart.Ticks.generators.linear |
| 12316 |
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks |
| 12317 |
* @param dataRange {IRange} the range of the data |
| 12318 |
* @returns {Array<Number>} array of tick values |
| 12319 |
*/ |
| 12320 |
linear: function(generationOptions, dataRange) { |
| 12321 |
var ticks = []; |
| 12322 |
// To get a "nice" value for the tick spacing, we will use the appropriately named |
| 12323 |
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks |
| 12324 |
// for details. |
| 12325 |
|
| 12326 |
var spacing; |
| 12327 |
if (generationOptions.stepSize && generationOptions.stepSize > 0) { |
| 12328 |
spacing = generationOptions.stepSize; |
| 12329 |
} else { |
| 12330 |
var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); |
| 12331 |
spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); |
| 12332 |
} |
| 12333 |
var niceMin = Math.floor(dataRange.min / spacing) * spacing; |
| 12334 |
var niceMax = Math.ceil(dataRange.max / spacing) * spacing; |
| 12335 |
|
| 12336 |
// If min, max and stepSize is set and they make an evenly spaced scale use it. |
| 12337 |
if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { |
| 12338 |
// If very close to our whole number, use it. |
| 12339 |
if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { |
| 12340 |
niceMin = generationOptions.min; |
| 12341 |
niceMax = generationOptions.max; |
| 12342 |
} |
| 12343 |
} |
| 12344 |
|
| 12345 |
var numSpaces = (niceMax - niceMin) / spacing; |
| 12346 |
// If very close to our rounded value, use it. |
| 12347 |
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { |
| 12348 |
numSpaces = Math.round(numSpaces); |
| 12349 |
} else { |
| 12350 |
numSpaces = Math.ceil(numSpaces); |
| 12351 |
} |
| 12352 |
|
| 12353 |
// Put the values into the ticks array |
| 12354 |
ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); |
| 12355 |
for (var j = 1; j < numSpaces; ++j) { |
| 12356 |
ticks.push(niceMin + (j * spacing)); |
| 12357 |
} |
| 12358 |
ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); |
| 12359 |
|
| 12360 |
return ticks; |
| 12361 |
}, |
| 12362 |
|
| 12363 |
/** |
| 12364 |
* Generate a set of logarithmic ticks |
| 12365 |
* @method Chart.Ticks.generators.logarithmic |
| 12366 |
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks |
| 12367 |
* @param dataRange {IRange} the range of the data |
| 12368 |
* @returns {Array<Number>} array of tick values |
| 12369 |
*/ |
| 12370 |
logarithmic: function(generationOptions, dataRange) { |
| 12371 |
var ticks = []; |
| 12372 |
var valueOrDefault = helpers.valueOrDefault; |
| 12373 |
|
| 12374 |
// Figure out what the max number of ticks we can support it is based on the size of |
| 12375 |
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50 |
| 12376 |
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on |
| 12377 |
// the graph |
| 12378 |
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); |
| 12379 |
|
| 12380 |
var endExp = Math.floor(helpers.log10(dataRange.max)); |
| 12381 |
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); |
| 12382 |
var exp, significand; |
| 12383 |
|
| 12384 |
if (tickVal === 0) { |
| 12385 |
exp = Math.floor(helpers.log10(dataRange.minNotZero)); |
| 12386 |
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); |
| 12387 |
|
| 12388 |
ticks.push(tickVal); |
| 12389 |
tickVal = significand * Math.pow(10, exp); |
| 12390 |
} else { |
| 12391 |
exp = Math.floor(helpers.log10(tickVal)); |
| 12392 |
significand = Math.floor(tickVal / Math.pow(10, exp)); |
| 12393 |
} |
| 12394 |
|
| 12395 |
do { |
| 12396 |
ticks.push(tickVal); |
| 12397 |
|
| 12398 |
++significand; |
| 12399 |
if (significand === 10) { |
| 12400 |
significand = 1; |
| 12401 |
++exp; |
| 12402 |
} |
| 12403 |
|
| 12404 |
tickVal = significand * Math.pow(10, exp); |
| 12405 |
} while (exp < endExp || (exp === endExp && significand < endSignificand)); |
| 12406 |
|
| 12407 |
var lastTick = valueOrDefault(generationOptions.max, tickVal); |
| 12408 |
ticks.push(lastTick); |
| 12409 |
|
| 12410 |
return ticks; |
| 12411 |
} |
| 12412 |
}, |
| 12413 |
|
| 12414 |
/** |
| 12415 |
* Namespace to hold formatters for different types of ticks |
| 12416 |
* @namespace Chart.Ticks.formatters |
| 12417 |
*/ |
| 12418 |
formatters: { |
| 12419 |
/** |
| 12420 |
* Formatter for value labels |
| 12421 |
* @method Chart.Ticks.formatters.values |
| 12422 |
* @param value the value to display |
| 12423 |
* @return {String|Array} the label to display |
| 12424 |
*/ |
| 12425 |
values: function(value) { |
| 12426 |
return helpers.isArray(value) ? value : '' + value; |
| 12427 |
}, |
| 12428 |
|
| 12429 |
/** |
| 12430 |
* Formatter for linear numeric ticks |
| 12431 |
* @method Chart.Ticks.formatters.linear |
| 12432 |
* @param tickValue {Number} the value to be formatted |
| 12433 |
* @param index {Number} the position of the tickValue parameter in the ticks array |
| 12434 |
* @param ticks {Array<Number>} the list of ticks being converted |
| 12435 |
* @return {String} string representation of the tickValue parameter |
| 12436 |
*/ |
| 12437 |
linear: function(tickValue, index, ticks) { |
| 12438 |
// If we have lots of ticks, don't use the ones |
| 12439 |
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; |
| 12440 |
|
| 12441 |
// If we have a number like 2.5 as the delta, figure out how many decimal places we need |
| 12442 |
if (Math.abs(delta) > 1) { |
| 12443 |
if (tickValue !== Math.floor(tickValue)) { |
| 12444 |
// not an integer |
| 12445 |
delta = tickValue - Math.floor(tickValue); |
| 12446 |
} |
| 12447 |
} |
| 12448 |
|
| 12449 |
var logDelta = helpers.log10(Math.abs(delta)); |
| 12450 |
var tickString = ''; |
| 12451 |
|
| 12452 |
if (tickValue !== 0) { |
| 12453 |
var numDecimal = -1 * Math.floor(logDelta); |
| 12454 |
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places |
| 12455 |
tickString = tickValue.toFixed(numDecimal); |
| 12456 |
} else { |
| 12457 |
tickString = '0'; // never show decimal places for 0 |
| 12458 |
} |
| 12459 |
|
| 12460 |
return tickString; |
| 12461 |
}, |
| 12462 |
|
| 12463 |
logarithmic: function(tickValue, index, ticks) { |
| 12464 |
var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); |
| 12465 |
|
| 12466 |
if (tickValue === 0) { |
| 12467 |
return '0'; |
| 12468 |
} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { |
| 12469 |
return tickValue.toExponential(); |
| 12470 |
} |
| 12471 |
return ''; |
| 12472 |
} |
| 12473 |
} |
| 12474 |
}; |
| 12475 |
|
| 12476 |
},{"45":45}],35:[function(require,module,exports){ |
| 12477 |
'use strict'; |
| 12478 |
|
| 12479 |
var defaults = require(25); |
| 12480 |
var Element = require(26); |
| 12481 |
var helpers = require(45); |
| 12482 |
|
| 12483 |
defaults._set('global', { |
| 12484 |
tooltips: { |
| 12485 |
enabled: true, |
| 12486 |
custom: null, |
| 12487 |
mode: 'nearest', |
| 12488 |
position: 'average', |
| 12489 |
intersect: true, |
| 12490 |
backgroundColor: 'rgba(0,0,0,0.8)', |
| 12491 |
titleFontStyle: 'bold', |
| 12492 |
titleSpacing: 2, |
| 12493 |
titleMarginBottom: 6, |
| 12494 |
titleFontColor: '#fff', |
| 12495 |
titleAlign: 'left', |
| 12496 |
bodySpacing: 2, |
| 12497 |
bodyFontColor: '#fff', |
| 12498 |
bodyAlign: 'left', |
| 12499 |
footerFontStyle: 'bold', |
| 12500 |
footerSpacing: 2, |
| 12501 |
footerMarginTop: 6, |
| 12502 |
footerFontColor: '#fff', |
| 12503 |
footerAlign: 'left', |
| 12504 |
yPadding: 6, |
| 12505 |
xPadding: 6, |
| 12506 |
caretPadding: 2, |
| 12507 |
caretSize: 5, |
| 12508 |
cornerRadius: 6, |
| 12509 |
multiKeyBackground: '#fff', |
| 12510 |
displayColors: true, |
| 12511 |
borderColor: 'rgba(0,0,0,0)', |
| 12512 |
borderWidth: 0, |
| 12513 |
callbacks: { |
| 12514 |
// Args are: (tooltipItems, data) |
| 12515 |
beforeTitle: helpers.noop, |
| 12516 |
title: function(tooltipItems, data) { |
| 12517 |
// Pick first xLabel for now |
| 12518 |
var title = ''; |
| 12519 |
var labels = data.labels; |
| 12520 |
var labelCount = labels ? labels.length : 0; |
| 12521 |
|
| 12522 |
if (tooltipItems.length > 0) { |
| 12523 |
var item = tooltipItems[0]; |
| 12524 |
|
| 12525 |
if (item.xLabel) { |
| 12526 |
title = item.xLabel; |
| 12527 |
} else if (labelCount > 0 && item.index < labelCount) { |
| 12528 |
title = labels[item.index]; |
| 12529 |
} |
| 12530 |
} |
| 12531 |
|
| 12532 |
return title; |
| 12533 |
}, |
| 12534 |
afterTitle: helpers.noop, |
| 12535 |
|
| 12536 |
// Args are: (tooltipItems, data) |
| 12537 |
beforeBody: helpers.noop, |
| 12538 |
|
| 12539 |
// Args are: (tooltipItem, data) |
| 12540 |
beforeLabel: helpers.noop, |
| 12541 |
label: function(tooltipItem, data) { |
| 12542 |
var label = data.datasets[tooltipItem.datasetIndex].label || ''; |
| 12543 |
|
| 12544 |
if (label) { |
| 12545 |
label += ': '; |
| 12546 |
} |
| 12547 |
label += tooltipItem.yLabel; |
| 12548 |
return label; |
| 12549 |
}, |
| 12550 |
labelColor: function(tooltipItem, chart) { |
| 12551 |
var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); |
| 12552 |
var activeElement = meta.data[tooltipItem.index]; |
| 12553 |
var view = activeElement._view; |
| 12554 |
return { |
| 12555 |
borderColor: view.borderColor, |
| 12556 |
backgroundColor: view.backgroundColor |
| 12557 |
}; |
| 12558 |
}, |
| 12559 |
labelTextColor: function() { |
| 12560 |
return this._options.bodyFontColor; |
| 12561 |
}, |
| 12562 |
afterLabel: helpers.noop, |
| 12563 |
|
| 12564 |
// Args are: (tooltipItems, data) |
| 12565 |
afterBody: helpers.noop, |
| 12566 |
|
| 12567 |
// Args are: (tooltipItems, data) |
| 12568 |
beforeFooter: helpers.noop, |
| 12569 |
footer: helpers.noop, |
| 12570 |
afterFooter: helpers.noop |
| 12571 |
} |
| 12572 |
} |
| 12573 |
}); |
| 12574 |
|
| 12575 |
module.exports = function(Chart) { |
| 12576 |
|
| 12577 |
/** |
| 12578 |
* Helper method to merge the opacity into a color |
| 12579 |
*/ |
| 12580 |
function mergeOpacity(colorString, opacity) { |
| 12581 |
var color = helpers.color(colorString); |
| 12582 |
return color.alpha(opacity * color.alpha()).rgbaString(); |
| 12583 |
} |
| 12584 |
|
| 12585 |
// Helper to push or concat based on if the 2nd parameter is an array or not |
| 12586 |
function pushOrConcat(base, toPush) { |
| 12587 |
if (toPush) { |
| 12588 |
if (helpers.isArray(toPush)) { |
| 12589 |
// base = base.concat(toPush); |
| 12590 |
Array.prototype.push.apply(base, toPush); |
| 12591 |
} else { |
| 12592 |
base.push(toPush); |
| 12593 |
} |
| 12594 |
} |
| 12595 |
|
| 12596 |
return base; |
| 12597 |
} |
| 12598 |
|
| 12599 |
// Private helper to create a tooltip item model |
| 12600 |
// @param element : the chart element (point, arc, bar) to create the tooltip item for |
| 12601 |
// @return : new tooltip item |
| 12602 |
function createTooltipItem(element) { |
| 12603 |
var xScale = element._xScale; |
| 12604 |
var yScale = element._yScale || element._scale; // handle radar || polarArea charts |
| 12605 |
var index = element._index; |
| 12606 |
var datasetIndex = element._datasetIndex; |
| 12607 |
|
| 12608 |
return { |
| 12609 |
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', |
| 12610 |
yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', |
| 12611 |
index: index, |
| 12612 |
datasetIndex: datasetIndex, |
| 12613 |
x: element._model.x, |
| 12614 |
y: element._model.y |
| 12615 |
}; |
| 12616 |
} |
| 12617 |
|
| 12618 |
/** |
| 12619 |
* Helper to get the reset model for the tooltip |
| 12620 |
* @param tooltipOpts {Object} the tooltip options |
| 12621 |
*/ |
| 12622 |
function getBaseModel(tooltipOpts) { |
| 12623 |
var globalDefaults = defaults.global; |
| 12624 |
var valueOrDefault = helpers.valueOrDefault; |
| 12625 |
|
| 12626 |
return { |
| 12627 |
// Positioning |
| 12628 |
xPadding: tooltipOpts.xPadding, |
| 12629 |
yPadding: tooltipOpts.yPadding, |
| 12630 |
xAlign: tooltipOpts.xAlign, |
| 12631 |
yAlign: tooltipOpts.yAlign, |
| 12632 |
|
| 12633 |
// Body |
| 12634 |
bodyFontColor: tooltipOpts.bodyFontColor, |
| 12635 |
_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), |
| 12636 |
_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), |
| 12637 |
_bodyAlign: tooltipOpts.bodyAlign, |
| 12638 |
bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), |
| 12639 |
bodySpacing: tooltipOpts.bodySpacing, |
| 12640 |
|
| 12641 |
// Title |
| 12642 |
titleFontColor: tooltipOpts.titleFontColor, |
| 12643 |
_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), |
| 12644 |
_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), |
| 12645 |
titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), |
| 12646 |
_titleAlign: tooltipOpts.titleAlign, |
| 12647 |
titleSpacing: tooltipOpts.titleSpacing, |
| 12648 |
titleMarginBottom: tooltipOpts.titleMarginBottom, |
| 12649 |
|
| 12650 |
// Footer |
| 12651 |
footerFontColor: tooltipOpts.footerFontColor, |
| 12652 |
_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), |
| 12653 |
_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), |
| 12654 |
footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), |
| 12655 |
_footerAlign: tooltipOpts.footerAlign, |
| 12656 |
footerSpacing: tooltipOpts.footerSpacing, |
| 12657 |
footerMarginTop: tooltipOpts.footerMarginTop, |
| 12658 |
|
| 12659 |
// Appearance |
| 12660 |
caretSize: tooltipOpts.caretSize, |
| 12661 |
cornerRadius: tooltipOpts.cornerRadius, |
| 12662 |
backgroundColor: tooltipOpts.backgroundColor, |
| 12663 |
opacity: 0, |
| 12664 |
legendColorBackground: tooltipOpts.multiKeyBackground, |
| 12665 |
displayColors: tooltipOpts.displayColors, |
| 12666 |
borderColor: tooltipOpts.borderColor, |
| 12667 |
borderWidth: tooltipOpts.borderWidth |
| 12668 |
}; |
| 12669 |
} |
| 12670 |
|
| 12671 |
/** |
| 12672 |
* Get the size of the tooltip |
| 12673 |
*/ |
| 12674 |
function getTooltipSize(tooltip, model) { |
| 12675 |
var ctx = tooltip._chart.ctx; |
| 12676 |
|
| 12677 |
var height = model.yPadding * 2; // Tooltip Padding |
| 12678 |
var width = 0; |
| 12679 |
|
| 12680 |
// Count of all lines in the body |
| 12681 |
var body = model.body; |
| 12682 |
var combinedBodyLength = body.reduce(function(count, bodyItem) { |
| 12683 |
return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; |
| 12684 |
}, 0); |
| 12685 |
combinedBodyLength += model.beforeBody.length + model.afterBody.length; |
| 12686 |
|
| 12687 |
var titleLineCount = model.title.length; |
| 12688 |
var footerLineCount = model.footer.length; |
| 12689 |
var titleFontSize = model.titleFontSize; |
| 12690 |
var bodyFontSize = model.bodyFontSize; |
| 12691 |
var footerFontSize = model.footerFontSize; |
| 12692 |
|
| 12693 |
height += titleLineCount * titleFontSize; // Title Lines |
| 12694 |
height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing |
| 12695 |
height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin |
| 12696 |
height += combinedBodyLength * bodyFontSize; // Body Lines |
| 12697 |
height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing |
| 12698 |
height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin |
| 12699 |
height += footerLineCount * (footerFontSize); // Footer Lines |
| 12700 |
height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing |
| 12701 |
|
| 12702 |
// Title width |
| 12703 |
var widthPadding = 0; |
| 12704 |
var maxLineWidth = function(line) { |
| 12705 |
width = Math.max(width, ctx.measureText(line).width + widthPadding); |
| 12706 |
}; |
| 12707 |
|
| 12708 |
ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); |
| 12709 |
helpers.each(model.title, maxLineWidth); |
| 12710 |
|
| 12711 |
// Body width |
| 12712 |
ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); |
| 12713 |
helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); |
| 12714 |
|
| 12715 |
// Body lines may include some extra width due to the color box |
| 12716 |
widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; |
| 12717 |
helpers.each(body, function(bodyItem) { |
| 12718 |
helpers.each(bodyItem.before, maxLineWidth); |
| 12719 |
helpers.each(bodyItem.lines, maxLineWidth); |
| 12720 |
helpers.each(bodyItem.after, maxLineWidth); |
| 12721 |
}); |
| 12722 |
|
| 12723 |
// Reset back to 0 |
| 12724 |
widthPadding = 0; |
| 12725 |
|
| 12726 |
// Footer width |
| 12727 |
ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); |
| 12728 |
helpers.each(model.footer, maxLineWidth); |
| 12729 |
|
| 12730 |
// Add padding |
| 12731 |
width += 2 * model.xPadding; |
| 12732 |
|
| 12733 |
return { |
| 12734 |
width: width, |
| 12735 |
height: height |
| 12736 |
}; |
| 12737 |
} |
| 12738 |
|
| 12739 |
/** |
| 12740 |
* Helper to get the alignment of a tooltip given the size |
| 12741 |
*/ |
| 12742 |
function determineAlignment(tooltip, size) { |
| 12743 |
var model = tooltip._model; |
| 12744 |
var chart = tooltip._chart; |
| 12745 |
var chartArea = tooltip._chart.chartArea; |
| 12746 |
var xAlign = 'center'; |
| 12747 |
var yAlign = 'center'; |
| 12748 |
|
| 12749 |
if (model.y < size.height) { |
| 12750 |
yAlign = 'top'; |
| 12751 |
} else if (model.y > (chart.height - size.height)) { |
| 12752 |
yAlign = 'bottom'; |
| 12753 |
} |
| 12754 |
|
| 12755 |
var lf, rf; // functions to determine left, right alignment |
| 12756 |
var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart |
| 12757 |
var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges |
| 12758 |
var midX = (chartArea.left + chartArea.right) / 2; |
| 12759 |
var midY = (chartArea.top + chartArea.bottom) / 2; |
| 12760 |
|
| 12761 |
if (yAlign === 'center') { |
| 12762 |
lf = function(x) { |
| 12763 |
return x <= midX; |
| 12764 |
}; |
| 12765 |
rf = function(x) { |
| 12766 |
return x > midX; |
| 12767 |
}; |
| 12768 |
} else { |
| 12769 |
lf = function(x) { |
| 12770 |
return x <= (size.width / 2); |
| 12771 |
}; |
| 12772 |
rf = function(x) { |
| 12773 |
return x >= (chart.width - (size.width / 2)); |
| 12774 |
}; |
| 12775 |
} |
| 12776 |
|
| 12777 |
olf = function(x) { |
| 12778 |
return x + size.width > chart.width; |
| 12779 |
}; |
| 12780 |
orf = function(x) { |
| 12781 |
return x - size.width < 0; |
| 12782 |
}; |
| 12783 |
yf = function(y) { |
| 12784 |
return y <= midY ? 'top' : 'bottom'; |
| 12785 |
}; |
| 12786 |
|
| 12787 |
if (lf(model.x)) { |
| 12788 |
xAlign = 'left'; |
| 12789 |
|
| 12790 |
// Is tooltip too wide and goes over the right side of the chart.? |
| 12791 |
if (olf(model.x)) { |
| 12792 |
xAlign = 'center'; |
| 12793 |
yAlign = yf(model.y); |
| 12794 |
} |
| 12795 |
} else if (rf(model.x)) { |
| 12796 |
xAlign = 'right'; |
| 12797 |
|
| 12798 |
// Is tooltip too wide and goes outside left edge of canvas? |
| 12799 |
if (orf(model.x)) { |
| 12800 |
xAlign = 'center'; |
| 12801 |
yAlign = yf(model.y); |
| 12802 |
} |
| 12803 |
} |
| 12804 |
|
| 12805 |
var opts = tooltip._options; |
| 12806 |
return { |
| 12807 |
xAlign: opts.xAlign ? opts.xAlign : xAlign, |
| 12808 |
yAlign: opts.yAlign ? opts.yAlign : yAlign |
| 12809 |
}; |
| 12810 |
} |
| 12811 |
|
| 12812 |
/** |
| 12813 |
* @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment |
| 12814 |
*/ |
| 12815 |
function getBackgroundPoint(vm, size, alignment) { |
| 12816 |
// Background Position |
| 12817 |
var x = vm.x; |
| 12818 |
var y = vm.y; |
| 12819 |
|
| 12820 |
var caretSize = vm.caretSize; |
| 12821 |
var caretPadding = vm.caretPadding; |
| 12822 |
var cornerRadius = vm.cornerRadius; |
| 12823 |
var xAlign = alignment.xAlign; |
| 12824 |
var yAlign = alignment.yAlign; |
| 12825 |
var paddingAndSize = caretSize + caretPadding; |
| 12826 |
var radiusAndPadding = cornerRadius + caretPadding; |
| 12827 |
|
| 12828 |
if (xAlign === 'right') { |
| 12829 |
x -= size.width; |
| 12830 |
} else if (xAlign === 'center') { |
| 12831 |
x -= (size.width / 2); |
| 12832 |
} |
| 12833 |
|
| 12834 |
if (yAlign === 'top') { |
| 12835 |
y += paddingAndSize; |
| 12836 |
} else if (yAlign === 'bottom') { |
| 12837 |
y -= size.height + paddingAndSize; |
| 12838 |
} else { |
| 12839 |
y -= (size.height / 2); |
| 12840 |
} |
| 12841 |
|
| 12842 |
if (yAlign === 'center') { |
| 12843 |
if (xAlign === 'left') { |
| 12844 |
x += paddingAndSize; |
| 12845 |
} else if (xAlign === 'right') { |
| 12846 |
x -= paddingAndSize; |
| 12847 |
} |
| 12848 |
} else if (xAlign === 'left') { |
| 12849 |
x -= radiusAndPadding; |
| 12850 |
} else if (xAlign === 'right') { |
| 12851 |
x += radiusAndPadding; |
| 12852 |
} |
| 12853 |
|
| 12854 |
return { |
| 12855 |
x: x, |
| 12856 |
y: y |
| 12857 |
}; |
| 12858 |
} |
| 12859 |
|
| 12860 |
Chart.Tooltip = Element.extend({ |
| 12861 |
initialize: function() { |
| 12862 |
this._model = getBaseModel(this._options); |
| 12863 |
}, |
| 12864 |
|
| 12865 |
// Get the title |
| 12866 |
// Args are: (tooltipItem, data) |
| 12867 |
getTitle: function() { |
| 12868 |
var me = this; |
| 12869 |
var opts = me._options; |
| 12870 |
var callbacks = opts.callbacks; |
| 12871 |
|
| 12872 |
var beforeTitle = callbacks.beforeTitle.apply(me, arguments); |
| 12873 |
var title = callbacks.title.apply(me, arguments); |
| 12874 |
var afterTitle = callbacks.afterTitle.apply(me, arguments); |
| 12875 |
|
| 12876 |
var lines = []; |
| 12877 |
lines = pushOrConcat(lines, beforeTitle); |
| 12878 |
lines = pushOrConcat(lines, title); |
| 12879 |
lines = pushOrConcat(lines, afterTitle); |
| 12880 |
|
| 12881 |
return lines; |
| 12882 |
}, |
| 12883 |
|
| 12884 |
// Args are: (tooltipItem, data) |
| 12885 |
getBeforeBody: function() { |
| 12886 |
var lines = this._options.callbacks.beforeBody.apply(this, arguments); |
| 12887 |
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; |
| 12888 |
}, |
| 12889 |
|
| 12890 |
// Args are: (tooltipItem, data) |
| 12891 |
getBody: function(tooltipItems, data) { |
| 12892 |
var me = this; |
| 12893 |
var callbacks = me._options.callbacks; |
| 12894 |
var bodyItems = []; |
| 12895 |
|
| 12896 |
helpers.each(tooltipItems, function(tooltipItem) { |
| 12897 |
var bodyItem = { |
| 12898 |
before: [], |
| 12899 |
lines: [], |
| 12900 |
after: [] |
| 12901 |
}; |
| 12902 |
pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); |
| 12903 |
pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); |
| 12904 |
pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); |
| 12905 |
|
| 12906 |
bodyItems.push(bodyItem); |
| 12907 |
}); |
| 12908 |
|
| 12909 |
return bodyItems; |
| 12910 |
}, |
| 12911 |
|
| 12912 |
// Args are: (tooltipItem, data) |
| 12913 |
getAfterBody: function() { |
| 12914 |
var lines = this._options.callbacks.afterBody.apply(this, arguments); |
| 12915 |
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; |
| 12916 |
}, |
| 12917 |
|
| 12918 |
// Get the footer and beforeFooter and afterFooter lines |
| 12919 |
// Args are: (tooltipItem, data) |
| 12920 |
getFooter: function() { |
| 12921 |
var me = this; |
| 12922 |
var callbacks = me._options.callbacks; |
| 12923 |
|
| 12924 |
var beforeFooter = callbacks.beforeFooter.apply(me, arguments); |
| 12925 |
var footer = callbacks.footer.apply(me, arguments); |
| 12926 |
var afterFooter = callbacks.afterFooter.apply(me, arguments); |
| 12927 |
|
| 12928 |
var lines = []; |
| 12929 |
lines = pushOrConcat(lines, beforeFooter); |
| 12930 |
lines = pushOrConcat(lines, footer); |
| 12931 |
lines = pushOrConcat(lines, afterFooter); |
| 12932 |
|
| 12933 |
return lines; |
| 12934 |
}, |
| 12935 |
|
| 12936 |
update: function(changed) { |
| 12937 |
var me = this; |
| 12938 |
var opts = me._options; |
| 12939 |
|
| 12940 |
// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition |
| 12941 |
// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time |
| 12942 |
// which breaks any animations. |
| 12943 |
var existingModel = me._model; |
| 12944 |
var model = me._model = getBaseModel(opts); |
| 12945 |
var active = me._active; |
| 12946 |
|
| 12947 |
var data = me._data; |
| 12948 |
|
| 12949 |
// In the case where active.length === 0 we need to keep these at existing values for good animations |
| 12950 |
var alignment = { |
| 12951 |
xAlign: existingModel.xAlign, |
| 12952 |
yAlign: existingModel.yAlign |
| 12953 |
}; |
| 12954 |
var backgroundPoint = { |
| 12955 |
x: existingModel.x, |
| 12956 |
y: existingModel.y |
| 12957 |
}; |
| 12958 |
var tooltipSize = { |
| 12959 |
width: existingModel.width, |
| 12960 |
height: existingModel.height |
| 12961 |
}; |
| 12962 |
var tooltipPosition = { |
| 12963 |
x: existingModel.caretX, |
| 12964 |
y: existingModel.caretY |
| 12965 |
}; |
| 12966 |
|
| 12967 |
var i, len; |
| 12968 |
|
| 12969 |
if (active.length) { |
| 12970 |
model.opacity = 1; |
| 12971 |
|
| 12972 |
var labelColors = []; |
| 12973 |
var labelTextColors = []; |
| 12974 |
tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition); |
| 12975 |
|
| 12976 |
var tooltipItems = []; |
| 12977 |
for (i = 0, len = active.length; i < len; ++i) { |
| 12978 |
tooltipItems.push(createTooltipItem(active[i])); |
| 12979 |
} |
| 12980 |
|
| 12981 |
// If the user provided a filter function, use it to modify the tooltip items |
| 12982 |
if (opts.filter) { |
| 12983 |
tooltipItems = tooltipItems.filter(function(a) { |
| 12984 |
return opts.filter(a, data); |
| 12985 |
}); |
| 12986 |
} |
| 12987 |
|
| 12988 |
// If the user provided a sorting function, use it to modify the tooltip items |
| 12989 |
if (opts.itemSort) { |
| 12990 |
tooltipItems = tooltipItems.sort(function(a, b) { |
| 12991 |
return opts.itemSort(a, b, data); |
| 12992 |
}); |
| 12993 |
} |
| 12994 |
|
| 12995 |
// Determine colors for boxes |
| 12996 |
helpers.each(tooltipItems, function(tooltipItem) { |
| 12997 |
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); |
| 12998 |
labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); |
| 12999 |
}); |
| 13000 |
|
| 13001 |
|
| 13002 |
// Build the Text Lines |
| 13003 |
model.title = me.getTitle(tooltipItems, data); |
| 13004 |
model.beforeBody = me.getBeforeBody(tooltipItems, data); |
| 13005 |
model.body = me.getBody(tooltipItems, data); |
| 13006 |
model.afterBody = me.getAfterBody(tooltipItems, data); |
| 13007 |
model.footer = me.getFooter(tooltipItems, data); |
| 13008 |
|
| 13009 |
// Initial positioning and colors |
| 13010 |
model.x = Math.round(tooltipPosition.x); |
| 13011 |
model.y = Math.round(tooltipPosition.y); |
| 13012 |
model.caretPadding = opts.caretPadding; |
| 13013 |
model.labelColors = labelColors; |
| 13014 |
model.labelTextColors = labelTextColors; |
| 13015 |
|
| 13016 |
// data points |
| 13017 |
model.dataPoints = tooltipItems; |
| 13018 |
|
| 13019 |
// We need to determine alignment of the tooltip |
| 13020 |
tooltipSize = getTooltipSize(this, model); |
| 13021 |
alignment = determineAlignment(this, tooltipSize); |
| 13022 |
// Final Size and Position |
| 13023 |
backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment); |
| 13024 |
} else { |
| 13025 |
model.opacity = 0; |
| 13026 |
} |
| 13027 |
|
| 13028 |
model.xAlign = alignment.xAlign; |
| 13029 |
model.yAlign = alignment.yAlign; |
| 13030 |
model.x = backgroundPoint.x; |
| 13031 |
model.y = backgroundPoint.y; |
| 13032 |
model.width = tooltipSize.width; |
| 13033 |
model.height = tooltipSize.height; |
| 13034 |
|
| 13035 |
// Point where the caret on the tooltip points to |
| 13036 |
model.caretX = tooltipPosition.x; |
| 13037 |
model.caretY = tooltipPosition.y; |
| 13038 |
|
| 13039 |
me._model = model; |
| 13040 |
|
| 13041 |
if (changed && opts.custom) { |
| 13042 |
opts.custom.call(me, model); |
| 13043 |
} |
| 13044 |
|
| 13045 |
return me; |
| 13046 |
}, |
| 13047 |
drawCaret: function(tooltipPoint, size) { |
| 13048 |
var ctx = this._chart.ctx; |
| 13049 |
var vm = this._view; |
| 13050 |
var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); |
| 13051 |
|
| 13052 |
ctx.lineTo(caretPosition.x1, caretPosition.y1); |
| 13053 |
ctx.lineTo(caretPosition.x2, caretPosition.y2); |
| 13054 |
ctx.lineTo(caretPosition.x3, caretPosition.y3); |
| 13055 |
}, |
| 13056 |
getCaretPosition: function(tooltipPoint, size, vm) { |
| 13057 |
var x1, x2, x3, y1, y2, y3; |
| 13058 |
var caretSize = vm.caretSize; |
| 13059 |
var cornerRadius = vm.cornerRadius; |
| 13060 |
var xAlign = vm.xAlign; |
| 13061 |
var yAlign = vm.yAlign; |
| 13062 |
var ptX = tooltipPoint.x; |
| 13063 |
var ptY = tooltipPoint.y; |
| 13064 |
var width = size.width; |
| 13065 |
var height = size.height; |
| 13066 |
|
| 13067 |
if (yAlign === 'center') { |
| 13068 |
y2 = ptY + (height / 2); |
| 13069 |
|
| 13070 |
if (xAlign === 'left') { |
| 13071 |
x1 = ptX; |
| 13072 |
x2 = x1 - caretSize; |
| 13073 |
x3 = x1; |
| 13074 |
|
| 13075 |
y1 = y2 + caretSize; |
| 13076 |
y3 = y2 - caretSize; |
| 13077 |
} else { |
| 13078 |
x1 = ptX + width; |
| 13079 |
x2 = x1 + caretSize; |
| 13080 |
x3 = x1; |
| 13081 |
|
| 13082 |
y1 = y2 - caretSize; |
| 13083 |
y3 = y2 + caretSize; |
| 13084 |
} |
| 13085 |
} else { |
| 13086 |
if (xAlign === 'left') { |
| 13087 |
x2 = ptX + cornerRadius + (caretSize); |
| 13088 |
x1 = x2 - caretSize; |
| 13089 |
x3 = x2 + caretSize; |
| 13090 |
} else if (xAlign === 'right') { |
| 13091 |
x2 = ptX + width - cornerRadius - caretSize; |
| 13092 |
x1 = x2 - caretSize; |
| 13093 |
x3 = x2 + caretSize; |
| 13094 |
} else { |
| 13095 |
x2 = ptX + (width / 2); |
| 13096 |
x1 = x2 - caretSize; |
| 13097 |
x3 = x2 + caretSize; |
| 13098 |
} |
| 13099 |
if (yAlign === 'top') { |
| 13100 |
y1 = ptY; |
| 13101 |
y2 = y1 - caretSize; |
| 13102 |
y3 = y1; |
| 13103 |
} else { |
| 13104 |
y1 = ptY + height; |
| 13105 |
y2 = y1 + caretSize; |
| 13106 |
y3 = y1; |
| 13107 |
// invert drawing order |
| 13108 |
var tmp = x3; |
| 13109 |
x3 = x1; |
| 13110 |
x1 = tmp; |
| 13111 |
} |
| 13112 |
} |
| 13113 |
return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; |
| 13114 |
}, |
| 13115 |
drawTitle: function(pt, vm, ctx, opacity) { |
| 13116 |
var title = vm.title; |
| 13117 |
|
| 13118 |
if (title.length) { |
| 13119 |
ctx.textAlign = vm._titleAlign; |
| 13120 |
ctx.textBaseline = 'top'; |
| 13121 |
|
| 13122 |
var titleFontSize = vm.titleFontSize; |
| 13123 |
var titleSpacing = vm.titleSpacing; |
| 13124 |
|
| 13125 |
ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); |
| 13126 |
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); |
| 13127 |
|
| 13128 |
var i, len; |
| 13129 |
for (i = 0, len = title.length; i < len; ++i) { |
| 13130 |
ctx.fillText(title[i], pt.x, pt.y); |
| 13131 |
pt.y += titleFontSize + titleSpacing; // Line Height and spacing |
| 13132 |
|
| 13133 |
if (i + 1 === title.length) { |
| 13134 |
pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing |
| 13135 |
} |
| 13136 |
} |
| 13137 |
} |
| 13138 |
}, |
| 13139 |
drawBody: function(pt, vm, ctx, opacity) { |
| 13140 |
var bodyFontSize = vm.bodyFontSize; |
| 13141 |
var bodySpacing = vm.bodySpacing; |
| 13142 |
var body = vm.body; |
| 13143 |
|
| 13144 |
ctx.textAlign = vm._bodyAlign; |
| 13145 |
ctx.textBaseline = 'top'; |
| 13146 |
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); |
| 13147 |
|
| 13148 |
// Before Body |
| 13149 |
var xLinePadding = 0; |
| 13150 |
var fillLineOfText = function(line) { |
| 13151 |
ctx.fillText(line, pt.x + xLinePadding, pt.y); |
| 13152 |
pt.y += bodyFontSize + bodySpacing; |
| 13153 |
}; |
| 13154 |
|
| 13155 |
// Before body lines |
| 13156 |
helpers.each(vm.beforeBody, fillLineOfText); |
| 13157 |
|
| 13158 |
var drawColorBoxes = vm.displayColors; |
| 13159 |
xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; |
| 13160 |
|
| 13161 |
// Draw body lines now |
| 13162 |
helpers.each(body, function(bodyItem, i) { |
| 13163 |
helpers.each(bodyItem.before, fillLineOfText); |
| 13164 |
|
| 13165 |
helpers.each(bodyItem.lines, function(line) { |
| 13166 |
// Draw Legend-like boxes if needed |
| 13167 |
if (drawColorBoxes) { |
| 13168 |
// Fill a white rect so that colours merge nicely if the opacity is < 1 |
| 13169 |
ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); |
| 13170 |
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); |
| 13171 |
|
| 13172 |
// Border |
| 13173 |
ctx.lineWidth = 1; |
| 13174 |
ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); |
| 13175 |
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); |
| 13176 |
|
| 13177 |
// Inner square |
| 13178 |
ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); |
| 13179 |
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); |
| 13180 |
var textColor = mergeOpacity(vm.labelTextColors[i], opacity); |
| 13181 |
ctx.fillStyle = textColor; |
| 13182 |
} |
| 13183 |
|
| 13184 |
fillLineOfText(line); |
| 13185 |
}); |
| 13186 |
|
| 13187 |
helpers.each(bodyItem.after, fillLineOfText); |
| 13188 |
}); |
| 13189 |
|
| 13190 |
// Reset back to 0 for after body |
| 13191 |
xLinePadding = 0; |
| 13192 |
|
| 13193 |
// After body lines |
| 13194 |
helpers.each(vm.afterBody, fillLineOfText); |
| 13195 |
pt.y -= bodySpacing; // Remove last body spacing |
| 13196 |
}, |
| 13197 |
drawFooter: function(pt, vm, ctx, opacity) { |
| 13198 |
var footer = vm.footer; |
| 13199 |
|
| 13200 |
if (footer.length) { |
| 13201 |
pt.y += vm.footerMarginTop; |
| 13202 |
|
| 13203 |
ctx.textAlign = vm._footerAlign; |
| 13204 |
ctx.textBaseline = 'top'; |
| 13205 |
|
| 13206 |
ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); |
| 13207 |
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); |
| 13208 |
|
| 13209 |
helpers.each(footer, function(line) { |
| 13210 |
ctx.fillText(line, pt.x, pt.y); |
| 13211 |
pt.y += vm.footerFontSize + vm.footerSpacing; |
| 13212 |
}); |
| 13213 |
} |
| 13214 |
}, |
| 13215 |
drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { |
| 13216 |
ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); |
| 13217 |
ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); |
| 13218 |
ctx.lineWidth = vm.borderWidth; |
| 13219 |
var xAlign = vm.xAlign; |
| 13220 |
var yAlign = vm.yAlign; |
| 13221 |
var x = pt.x; |
| 13222 |
var y = pt.y; |
| 13223 |
var width = tooltipSize.width; |
| 13224 |
var height = tooltipSize.height; |
| 13225 |
var radius = vm.cornerRadius; |
| 13226 |
|
| 13227 |
ctx.beginPath(); |
| 13228 |
ctx.moveTo(x + radius, y); |
| 13229 |
if (yAlign === 'top') { |
| 13230 |
this.drawCaret(pt, tooltipSize); |
| 13231 |
} |
| 13232 |
ctx.lineTo(x + width - radius, y); |
| 13233 |
ctx.quadraticCurveTo(x + width, y, x + width, y + radius); |
| 13234 |
if (yAlign === 'center' && xAlign === 'right') { |
| 13235 |
this.drawCaret(pt, tooltipSize); |
| 13236 |
} |
| 13237 |
ctx.lineTo(x + width, y + height - radius); |
| 13238 |
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); |
| 13239 |
if (yAlign === 'bottom') { |
| 13240 |
this.drawCaret(pt, tooltipSize); |
| 13241 |
} |
| 13242 |
ctx.lineTo(x + radius, y + height); |
| 13243 |
ctx.quadraticCurveTo(x, y + height, x, y + height - radius); |
| 13244 |
if (yAlign === 'center' && xAlign === 'left') { |
| 13245 |
this.drawCaret(pt, tooltipSize); |
| 13246 |
} |
| 13247 |
ctx.lineTo(x, y + radius); |
| 13248 |
ctx.quadraticCurveTo(x, y, x + radius, y); |
| 13249 |
ctx.closePath(); |
| 13250 |
|
| 13251 |
ctx.fill(); |
| 13252 |
|
| 13253 |
if (vm.borderWidth > 0) { |
| 13254 |
ctx.stroke(); |
| 13255 |
} |
| 13256 |
}, |
| 13257 |
draw: function() { |
| 13258 |
var ctx = this._chart.ctx; |
| 13259 |
var vm = this._view; |
| 13260 |
|
| 13261 |
if (vm.opacity === 0) { |
| 13262 |
return; |
| 13263 |
} |
| 13264 |
|
| 13265 |
var tooltipSize = { |
| 13266 |
width: vm.width, |
| 13267 |
height: vm.height |
| 13268 |
}; |
| 13269 |
var pt = { |
| 13270 |
x: vm.x, |
| 13271 |
y: vm.y |
| 13272 |
}; |
| 13273 |
|
| 13274 |
// IE11/Edge does not like very small opacities, so snap to 0 |
| 13275 |
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; |
| 13276 |
|
| 13277 |
// Truthy/falsey value for empty tooltip |
| 13278 |
var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; |
| 13279 |
|
| 13280 |
if (this._options.enabled && hasTooltipContent) { |
| 13281 |
// Draw Background |
| 13282 |
this.drawBackground(pt, vm, ctx, tooltipSize, opacity); |
| 13283 |
|
| 13284 |
// Draw Title, Body, and Footer |
| 13285 |
pt.x += vm.xPadding; |
| 13286 |
pt.y += vm.yPadding; |
| 13287 |
|
| 13288 |
// Titles |
| 13289 |
this.drawTitle(pt, vm, ctx, opacity); |
| 13290 |
|
| 13291 |
// Body |
| 13292 |
this.drawBody(pt, vm, ctx, opacity); |
| 13293 |
|
| 13294 |
// Footer |
| 13295 |
this.drawFooter(pt, vm, ctx, opacity); |
| 13296 |
} |
| 13297 |
}, |
| 13298 |
|
| 13299 |
/** |
| 13300 |
* Handle an event |
| 13301 |
* @private |
| 13302 |
* @param {IEvent} event - The event to handle |
| 13303 |
* @returns {Boolean} true if the tooltip changed |
| 13304 |
*/ |
| 13305 |
handleEvent: function(e) { |
| 13306 |
var me = this; |
| 13307 |
var options = me._options; |
| 13308 |
var changed = false; |
| 13309 |
|
| 13310 |
me._lastActive = me._lastActive || []; |
| 13311 |
|
| 13312 |
// Find Active Elements for tooltips |
| 13313 |
if (e.type === 'mouseout') { |
| 13314 |
me._active = []; |
| 13315 |
} else { |
| 13316 |
me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); |
| 13317 |
} |
| 13318 |
|
| 13319 |
// Remember Last Actives |
| 13320 |
changed = !helpers.arrayEquals(me._active, me._lastActive); |
| 13321 |
|
| 13322 |
// If tooltip didn't change, do not handle the target event |
| 13323 |
if (!changed) { |
| 13324 |
return false; |
| 13325 |
} |
| 13326 |
|
| 13327 |
me._lastActive = me._active; |
| 13328 |
|
| 13329 |
if (options.enabled || options.custom) { |
| 13330 |
me._eventPosition = { |
| 13331 |
x: e.x, |
| 13332 |
y: e.y |
| 13333 |
}; |
| 13334 |
|
| 13335 |
var model = me._model; |
| 13336 |
me.update(true); |
| 13337 |
me.pivot(); |
| 13338 |
|
| 13339 |
// See if our tooltip position changed |
| 13340 |
changed |= (model.x !== me._model.x) || (model.y !== me._model.y); |
| 13341 |
} |
| 13342 |
|
| 13343 |
return changed; |
| 13344 |
} |
| 13345 |
}); |
| 13346 |
|
| 13347 |
/** |
| 13348 |
* @namespace Chart.Tooltip.positioners |
| 13349 |
*/ |
| 13350 |
Chart.Tooltip.positioners = { |
| 13351 |
/** |
| 13352 |
* Average mode places the tooltip at the average position of the elements shown |
| 13353 |
* @function Chart.Tooltip.positioners.average |
| 13354 |
* @param elements {ChartElement[]} the elements being displayed in the tooltip |
| 13355 |
* @returns {Point} tooltip position |
| 13356 |
*/ |
| 13357 |
average: function(elements) { |
| 13358 |
if (!elements.length) { |
| 13359 |
return false; |
| 13360 |
} |
| 13361 |
|
| 13362 |
var i, len; |
| 13363 |
var x = 0; |
| 13364 |
var y = 0; |
| 13365 |
var count = 0; |
| 13366 |
|
| 13367 |
for (i = 0, len = elements.length; i < len; ++i) { |
| 13368 |
var el = elements[i]; |
| 13369 |
if (el && el.hasValue()) { |
| 13370 |
var pos = el.tooltipPosition(); |
| 13371 |
x += pos.x; |
| 13372 |
y += pos.y; |
| 13373 |
++count; |
| 13374 |
} |
| 13375 |
} |
| 13376 |
|
| 13377 |
return { |
| 13378 |
x: Math.round(x / count), |
| 13379 |
y: Math.round(y / count) |
| 13380 |
}; |
| 13381 |
}, |
| 13382 |
|
| 13383 |
/** |
| 13384 |
* Gets the tooltip position nearest of the item nearest to the event position |
| 13385 |
* @function Chart.Tooltip.positioners.nearest |
| 13386 |
* @param elements {Chart.Element[]} the tooltip elements |
| 13387 |
* @param eventPosition {Point} the position of the event in canvas coordinates |
| 13388 |
* @returns {Point} the tooltip position |
| 13389 |
*/ |
| 13390 |
nearest: function(elements, eventPosition) { |
| 13391 |
var x = eventPosition.x; |
| 13392 |
var y = eventPosition.y; |
| 13393 |
var minDistance = Number.POSITIVE_INFINITY; |
| 13394 |
var i, len, nearestElement; |
| 13395 |
|
| 13396 |
for (i = 0, len = elements.length; i < len; ++i) { |
| 13397 |
var el = elements[i]; |
| 13398 |
if (el && el.hasValue()) { |
| 13399 |
var center = el.getCenterPoint(); |
| 13400 |
var d = helpers.distanceBetweenPoints(eventPosition, center); |
| 13401 |
|
| 13402 |
if (d < minDistance) { |
| 13403 |
minDistance = d; |
| 13404 |
nearestElement = el; |
| 13405 |
} |
| 13406 |
} |
| 13407 |
} |
| 13408 |
|
| 13409 |
if (nearestElement) { |
| 13410 |
var tp = nearestElement.tooltipPosition(); |
| 13411 |
x = tp.x; |
| 13412 |
y = tp.y; |
| 13413 |
} |
| 13414 |
|
| 13415 |
return { |
| 13416 |
x: x, |
| 13417 |
y: y |
| 13418 |
}; |
| 13419 |
} |
| 13420 |
}; |
| 13421 |
}; |
| 13422 |
|
| 13423 |
},{"25":25,"26":26,"45":45}],36:[function(require,module,exports){ |
| 13424 |
'use strict'; |
| 13425 |
|
| 13426 |
var defaults = require(25); |
| 13427 |
var Element = require(26); |
| 13428 |
var helpers = require(45); |
| 13429 |
|
| 13430 |
defaults._set('global', { |
| 13431 |
elements: { |
| 13432 |
arc: { |
| 13433 |
backgroundColor: defaults.global.defaultColor, |
| 13434 |
borderColor: '#fff', |
| 13435 |
borderWidth: 2 |
| 13436 |
} |
| 13437 |
} |
| 13438 |
}); |
| 13439 |
|
| 13440 |
module.exports = Element.extend({ |
| 13441 |
inLabelRange: function(mouseX) { |
| 13442 |
var vm = this._view; |
| 13443 |
|
| 13444 |
if (vm) { |
| 13445 |
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); |
| 13446 |
} |
| 13447 |
return false; |
| 13448 |
}, |
| 13449 |
|
| 13450 |
inRange: function(chartX, chartY) { |
| 13451 |
var vm = this._view; |
| 13452 |
|
| 13453 |
if (vm) { |
| 13454 |
var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); |
| 13455 |
var angle = pointRelativePosition.angle; |
| 13456 |
var distance = pointRelativePosition.distance; |
| 13457 |
|
| 13458 |
// Sanitise angle range |
| 13459 |
var startAngle = vm.startAngle; |
| 13460 |
var endAngle = vm.endAngle; |
| 13461 |
while (endAngle < startAngle) { |
| 13462 |
endAngle += 2.0 * Math.PI; |
| 13463 |
} |
| 13464 |
while (angle > endAngle) { |
| 13465 |
angle -= 2.0 * Math.PI; |
| 13466 |
} |
| 13467 |
while (angle < startAngle) { |
| 13468 |
angle += 2.0 * Math.PI; |
| 13469 |
} |
| 13470 |
|
| 13471 |
// Check if within the range of the open/close angle |
| 13472 |
var betweenAngles = (angle >= startAngle && angle <= endAngle); |
| 13473 |
var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); |
| 13474 |
|
| 13475 |
return (betweenAngles && withinRadius); |
| 13476 |
} |
| 13477 |
return false; |
| 13478 |
}, |
| 13479 |
|
| 13480 |
getCenterPoint: function() { |
| 13481 |
var vm = this._view; |
| 13482 |
var halfAngle = (vm.startAngle + vm.endAngle) / 2; |
| 13483 |
var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; |
| 13484 |
return { |
| 13485 |
x: vm.x + Math.cos(halfAngle) * halfRadius, |
| 13486 |
y: vm.y + Math.sin(halfAngle) * halfRadius |
| 13487 |
}; |
| 13488 |
}, |
| 13489 |
|
| 13490 |
getArea: function() { |
| 13491 |
var vm = this._view; |
| 13492 |
return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); |
| 13493 |
}, |
| 13494 |
|
| 13495 |
tooltipPosition: function() { |
| 13496 |
var vm = this._view; |
| 13497 |
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); |
| 13498 |
var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; |
| 13499 |
|
| 13500 |
return { |
| 13501 |
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), |
| 13502 |
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) |
| 13503 |
}; |
| 13504 |
}, |
| 13505 |
|
| 13506 |
draw: function() { |
| 13507 |
var ctx = this._chart.ctx; |
| 13508 |
var vm = this._view; |
| 13509 |
var sA = vm.startAngle; |
| 13510 |
var eA = vm.endAngle; |
| 13511 |
|
| 13512 |
ctx.beginPath(); |
| 13513 |
|
| 13514 |
ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); |
| 13515 |
ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); |
| 13516 |
|
| 13517 |
ctx.closePath(); |
| 13518 |
ctx.strokeStyle = vm.borderColor; |
| 13519 |
ctx.lineWidth = vm.borderWidth; |
| 13520 |
|
| 13521 |
ctx.fillStyle = vm.backgroundColor; |
| 13522 |
|
| 13523 |
ctx.fill(); |
| 13524 |
ctx.lineJoin = 'bevel'; |
| 13525 |
|
| 13526 |
if (vm.borderWidth) { |
| 13527 |
ctx.stroke(); |
| 13528 |
} |
| 13529 |
} |
| 13530 |
}); |
| 13531 |
|
| 13532 |
},{"25":25,"26":26,"45":45}],37:[function(require,module,exports){ |
| 13533 |
'use strict'; |
| 13534 |
|
| 13535 |
var defaults = require(25); |
| 13536 |
var Element = require(26); |
| 13537 |
var helpers = require(45); |
| 13538 |
|
| 13539 |
var globalDefaults = defaults.global; |
| 13540 |
|
| 13541 |
defaults._set('global', { |
| 13542 |
elements: { |
| 13543 |
line: { |
| 13544 |
tension: 0.4, |
| 13545 |
backgroundColor: globalDefaults.defaultColor, |
| 13546 |
borderWidth: 3, |
| 13547 |
borderColor: globalDefaults.defaultColor, |
| 13548 |
borderCapStyle: 'butt', |
| 13549 |
borderDash: [], |
| 13550 |
borderDashOffset: 0.0, |
| 13551 |
borderJoinStyle: 'miter', |
| 13552 |
capBezierPoints: true, |
| 13553 |
fill: true, // do we fill in the area between the line and its base axis |
| 13554 |
} |
| 13555 |
} |
| 13556 |
}); |
| 13557 |
|
| 13558 |
module.exports = Element.extend({ |
| 13559 |
draw: function() { |
| 13560 |
var me = this; |
| 13561 |
var vm = me._view; |
| 13562 |
var ctx = me._chart.ctx; |
| 13563 |
var spanGaps = vm.spanGaps; |
| 13564 |
var points = me._children.slice(); // clone array |
| 13565 |
var globalOptionLineElements = globalDefaults.elements.line; |
| 13566 |
var lastDrawnIndex = -1; |
| 13567 |
var index, current, previous, currentVM; |
| 13568 |
|
| 13569 |
// If we are looping, adding the first point again |
| 13570 |
if (me._loop && points.length) { |
| 13571 |
points.push(points[0]); |
| 13572 |
} |
| 13573 |
|
| 13574 |
ctx.save(); |
| 13575 |
|
| 13576 |
// Stroke Line Options |
| 13577 |
ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; |
| 13578 |
|
| 13579 |
// IE 9 and 10 do not support line dash |
| 13580 |
if (ctx.setLineDash) { |
| 13581 |
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); |
| 13582 |
} |
| 13583 |
|
| 13584 |
ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; |
| 13585 |
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; |
| 13586 |
ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; |
| 13587 |
ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; |
| 13588 |
|
| 13589 |
// Stroke Line |
| 13590 |
ctx.beginPath(); |
| 13591 |
lastDrawnIndex = -1; |
| 13592 |
|
| 13593 |
for (index = 0; index < points.length; ++index) { |
| 13594 |
current = points[index]; |
| 13595 |
previous = helpers.previousItem(points, index); |
| 13596 |
currentVM = current._view; |
| 13597 |
|
| 13598 |
// First point moves to it's starting position no matter what |
| 13599 |
if (index === 0) { |
| 13600 |
if (!currentVM.skip) { |
| 13601 |
ctx.moveTo(currentVM.x, currentVM.y); |
| 13602 |
lastDrawnIndex = index; |
| 13603 |
} |
| 13604 |
} else { |
| 13605 |
previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; |
| 13606 |
|
| 13607 |
if (!currentVM.skip) { |
| 13608 |
if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { |
| 13609 |
// There was a gap and this is the first point after the gap |
| 13610 |
ctx.moveTo(currentVM.x, currentVM.y); |
| 13611 |
} else { |
| 13612 |
// Line to next point |
| 13613 |
helpers.canvas.lineTo(ctx, previous._view, current._view); |
| 13614 |
} |
| 13615 |
lastDrawnIndex = index; |
| 13616 |
} |
| 13617 |
} |
| 13618 |
} |
| 13619 |
|
| 13620 |
ctx.stroke(); |
| 13621 |
ctx.restore(); |
| 13622 |
} |
| 13623 |
}); |
| 13624 |
|
| 13625 |
},{"25":25,"26":26,"45":45}],38:[function(require,module,exports){ |
| 13626 |
'use strict'; |
| 13627 |
|
| 13628 |
var defaults = require(25); |
| 13629 |
var Element = require(26); |
| 13630 |
var helpers = require(45); |
| 13631 |
|
| 13632 |
var defaultColor = defaults.global.defaultColor; |
| 13633 |
|
| 13634 |
defaults._set('global', { |
| 13635 |
elements: { |
| 13636 |
point: { |
| 13637 |
radius: 3, |
| 13638 |
pointStyle: 'circle', |
| 13639 |
backgroundColor: defaultColor, |
| 13640 |
borderColor: defaultColor, |
| 13641 |
borderWidth: 1, |
| 13642 |
// Hover |
| 13643 |
hitRadius: 1, |
| 13644 |
hoverRadius: 4, |
| 13645 |
hoverBorderWidth: 1 |
| 13646 |
} |
| 13647 |
} |
| 13648 |
}); |
| 13649 |
|
| 13650 |
function xRange(mouseX) { |
| 13651 |
var vm = this._view; |
| 13652 |
return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; |
| 13653 |
} |
| 13654 |
|
| 13655 |
function yRange(mouseY) { |
| 13656 |
var vm = this._view; |
| 13657 |
return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; |
| 13658 |
} |
| 13659 |
|
| 13660 |
module.exports = Element.extend({ |
| 13661 |
inRange: function(mouseX, mouseY) { |
| 13662 |
var vm = this._view; |
| 13663 |
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; |
| 13664 |
}, |
| 13665 |
|
| 13666 |
inLabelRange: xRange, |
| 13667 |
inXRange: xRange, |
| 13668 |
inYRange: yRange, |
| 13669 |
|
| 13670 |
getCenterPoint: function() { |
| 13671 |
var vm = this._view; |
| 13672 |
return { |
| 13673 |
x: vm.x, |
| 13674 |
y: vm.y |
| 13675 |
}; |
| 13676 |
}, |
| 13677 |
|
| 13678 |
getArea: function() { |
| 13679 |
return Math.PI * Math.pow(this._view.radius, 2); |
| 13680 |
}, |
| 13681 |
|
| 13682 |
tooltipPosition: function() { |
| 13683 |
var vm = this._view; |
| 13684 |
return { |
| 13685 |
x: vm.x, |
| 13686 |
y: vm.y, |
| 13687 |
padding: vm.radius + vm.borderWidth |
| 13688 |
}; |
| 13689 |
}, |
| 13690 |
|
| 13691 |
draw: function(chartArea) { |
| 13692 |
var vm = this._view; |
| 13693 |
var model = this._model; |
| 13694 |
var ctx = this._chart.ctx; |
| 13695 |
var pointStyle = vm.pointStyle; |
| 13696 |
var radius = vm.radius; |
| 13697 |
var x = vm.x; |
| 13698 |
var y = vm.y; |
| 13699 |
var color = helpers.color; |
| 13700 |
var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) |
| 13701 |
var ratio = 0; |
| 13702 |
|
| 13703 |
if (vm.skip) { |
| 13704 |
return; |
| 13705 |
} |
| 13706 |
|
| 13707 |
ctx.strokeStyle = vm.borderColor || defaultColor; |
| 13708 |
ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); |
| 13709 |
ctx.fillStyle = vm.backgroundColor || defaultColor; |
| 13710 |
|
| 13711 |
// Cliping for Points. |
| 13712 |
// going out from inner charArea? |
| 13713 |
if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) { |
| 13714 |
// Point fade out |
| 13715 |
if (model.x < chartArea.left) { |
| 13716 |
ratio = (x - model.x) / (chartArea.left - model.x); |
| 13717 |
} else if (chartArea.right * errMargin < model.x) { |
| 13718 |
ratio = (model.x - x) / (model.x - chartArea.right); |
| 13719 |
} else if (model.y < chartArea.top) { |
| 13720 |
ratio = (y - model.y) / (chartArea.top - model.y); |
| 13721 |
} else if (chartArea.bottom * errMargin < model.y) { |
| 13722 |
ratio = (model.y - y) / (model.y - chartArea.bottom); |
| 13723 |
} |
| 13724 |
ratio = Math.round(ratio * 100) / 100; |
| 13725 |
ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); |
| 13726 |
ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); |
| 13727 |
} |
| 13728 |
|
| 13729 |
helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y); |
| 13730 |
} |
| 13731 |
}); |
| 13732 |
|
| 13733 |
},{"25":25,"26":26,"45":45}],39:[function(require,module,exports){ |
| 13734 |
'use strict'; |
| 13735 |
|
| 13736 |
var defaults = require(25); |
| 13737 |
var Element = require(26); |
| 13738 |
|
| 13739 |
defaults._set('global', { |
| 13740 |
elements: { |
| 13741 |
rectangle: { |
| 13742 |
backgroundColor: defaults.global.defaultColor, |
| 13743 |
borderColor: defaults.global.defaultColor, |
| 13744 |
borderSkipped: 'bottom', |
| 13745 |
borderWidth: 0 |
| 13746 |
} |
| 13747 |
} |
| 13748 |
}); |
| 13749 |
|
| 13750 |
function isVertical(bar) { |
| 13751 |
return bar._view.width !== undefined; |
| 13752 |
} |
| 13753 |
|
| 13754 |
/** |
| 13755 |
* Helper function to get the bounds of the bar regardless of the orientation |
| 13756 |
* @param bar {Chart.Element.Rectangle} the bar |
| 13757 |
* @return {Bounds} bounds of the bar |
| 13758 |
* @private |
| 13759 |
*/ |
| 13760 |
function getBarBounds(bar) { |
| 13761 |
var vm = bar._view; |
| 13762 |
var x1, x2, y1, y2; |
| 13763 |
|
| 13764 |
if (isVertical(bar)) { |
| 13765 |
// vertical |
| 13766 |
var halfWidth = vm.width / 2; |
| 13767 |
x1 = vm.x - halfWidth; |
| 13768 |
x2 = vm.x + halfWidth; |
| 13769 |
y1 = Math.min(vm.y, vm.base); |
| 13770 |
y2 = Math.max(vm.y, vm.base); |
| 13771 |
} else { |
| 13772 |
// horizontal bar |
| 13773 |
var halfHeight = vm.height / 2; |
| 13774 |
x1 = Math.min(vm.x, vm.base); |
| 13775 |
x2 = Math.max(vm.x, vm.base); |
| 13776 |
y1 = vm.y - halfHeight; |
| 13777 |
y2 = vm.y + halfHeight; |
| 13778 |
} |
| 13779 |
|
| 13780 |
return { |
| 13781 |
left: x1, |
| 13782 |
top: y1, |
| 13783 |
right: x2, |
| 13784 |
bottom: y2 |
| 13785 |
}; |
| 13786 |
} |
| 13787 |
|
| 13788 |
module.exports = Element.extend({ |
| 13789 |
draw: function() { |
| 13790 |
var ctx = this._chart.ctx; |
| 13791 |
var vm = this._view; |
| 13792 |
var left, right, top, bottom, signX, signY, borderSkipped; |
| 13793 |
var borderWidth = vm.borderWidth; |
| 13794 |
|
| 13795 |
if (!vm.horizontal) { |
| 13796 |
// bar |
| 13797 |
left = vm.x - vm.width / 2; |
| 13798 |
right = vm.x + vm.width / 2; |
| 13799 |
top = vm.y; |
| 13800 |
bottom = vm.base; |
| 13801 |
signX = 1; |
| 13802 |
signY = bottom > top ? 1 : -1; |
| 13803 |
borderSkipped = vm.borderSkipped || 'bottom'; |
| 13804 |
} else { |
| 13805 |
// horizontal bar |
| 13806 |
left = vm.base; |
| 13807 |
right = vm.x; |
| 13808 |
top = vm.y - vm.height / 2; |
| 13809 |
bottom = vm.y + vm.height / 2; |
| 13810 |
signX = right > left ? 1 : -1; |
| 13811 |
signY = 1; |
| 13812 |
borderSkipped = vm.borderSkipped || 'left'; |
| 13813 |
} |
| 13814 |
|
| 13815 |
// Canvas doesn't allow us to stroke inside the width so we can |
| 13816 |
// adjust the sizes to fit if we're setting a stroke on the line |
| 13817 |
if (borderWidth) { |
| 13818 |
// borderWidth shold be less than bar width and bar height. |
| 13819 |
var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); |
| 13820 |
borderWidth = borderWidth > barSize ? barSize : borderWidth; |
| 13821 |
var halfStroke = borderWidth / 2; |
| 13822 |
// Adjust borderWidth when bar top position is near vm.base(zero). |
| 13823 |
var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); |
| 13824 |
var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); |
| 13825 |
var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); |
| 13826 |
var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); |
| 13827 |
// not become a vertical line? |
| 13828 |
if (borderLeft !== borderRight) { |
| 13829 |
top = borderTop; |
| 13830 |
bottom = borderBottom; |
| 13831 |
} |
| 13832 |
// not become a horizontal line? |
| 13833 |
if (borderTop !== borderBottom) { |
| 13834 |
left = borderLeft; |
| 13835 |
right = borderRight; |
| 13836 |
} |
| 13837 |
} |
| 13838 |
|
| 13839 |
ctx.beginPath(); |
| 13840 |
ctx.fillStyle = vm.backgroundColor; |
| 13841 |
ctx.strokeStyle = vm.borderColor; |
| 13842 |
ctx.lineWidth = borderWidth; |
| 13843 |
|
| 13844 |
// Corner points, from bottom-left to bottom-right clockwise |
| 13845 |
// | 1 2 | |
| 13846 |
// | 0 3 | |
| 13847 |
var corners = [ |
| 13848 |
[left, bottom], |
| 13849 |
[left, top], |
| 13850 |
[right, top], |
| 13851 |
[right, bottom] |
| 13852 |
]; |
| 13853 |
|
| 13854 |
// Find first (starting) corner with fallback to 'bottom' |
| 13855 |
var borders = ['bottom', 'left', 'top', 'right']; |
| 13856 |
var startCorner = borders.indexOf(borderSkipped, 0); |
| 13857 |
if (startCorner === -1) { |
| 13858 |
startCorner = 0; |
| 13859 |
} |
| 13860 |
|
| 13861 |
function cornerAt(index) { |
| 13862 |
return corners[(startCorner + index) % 4]; |
| 13863 |
} |
| 13864 |
|
| 13865 |
// Draw rectangle from 'startCorner' |
| 13866 |
var corner = cornerAt(0); |
| 13867 |
ctx.moveTo(corner[0], corner[1]); |
| 13868 |
|
| 13869 |
for (var i = 1; i < 4; i++) { |
| 13870 |
corner = cornerAt(i); |
| 13871 |
ctx.lineTo(corner[0], corner[1]); |
| 13872 |
} |
| 13873 |
|
| 13874 |
ctx.fill(); |
| 13875 |
if (borderWidth) { |
| 13876 |
ctx.stroke(); |
| 13877 |
} |
| 13878 |
}, |
| 13879 |
|
| 13880 |
height: function() { |
| 13881 |
var vm = this._view; |
| 13882 |
return vm.base - vm.y; |
| 13883 |
}, |
| 13884 |
|
| 13885 |
inRange: function(mouseX, mouseY) { |
| 13886 |
var inRange = false; |
| 13887 |
|
| 13888 |
if (this._view) { |
| 13889 |
var bounds = getBarBounds(this); |
| 13890 |
inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; |
| 13891 |
} |
| 13892 |
|
| 13893 |
return inRange; |
| 13894 |
}, |
| 13895 |
|
| 13896 |
inLabelRange: function(mouseX, mouseY) { |
| 13897 |
var me = this; |
| 13898 |
if (!me._view) { |
| 13899 |
return false; |
| 13900 |
} |
| 13901 |
|
| 13902 |
var inRange = false; |
| 13903 |
var bounds = getBarBounds(me); |
| 13904 |
|
| 13905 |
if (isVertical(me)) { |
| 13906 |
inRange = mouseX >= bounds.left && mouseX <= bounds.right; |
| 13907 |
} else { |
| 13908 |
inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; |
| 13909 |
} |
| 13910 |
|
| 13911 |
return inRange; |
| 13912 |
}, |
| 13913 |
|
| 13914 |
inXRange: function(mouseX) { |
| 13915 |
var bounds = getBarBounds(this); |
| 13916 |
return mouseX >= bounds.left && mouseX <= bounds.right; |
| 13917 |
}, |
| 13918 |
|
| 13919 |
inYRange: function(mouseY) { |
| 13920 |
var bounds = getBarBounds(this); |
| 13921 |
return mouseY >= bounds.top && mouseY <= bounds.bottom; |
| 13922 |
}, |
| 13923 |
|
| 13924 |
getCenterPoint: function() { |
| 13925 |
var vm = this._view; |
| 13926 |
var x, y; |
| 13927 |
if (isVertical(this)) { |
| 13928 |
x = vm.x; |
| 13929 |
y = (vm.y + vm.base) / 2; |
| 13930 |
} else { |
| 13931 |
x = (vm.x + vm.base) / 2; |
| 13932 |
y = vm.y; |
| 13933 |
} |
| 13934 |
|
| 13935 |
return {x: x, y: y}; |
| 13936 |
}, |
| 13937 |
|
| 13938 |
getArea: function() { |
| 13939 |
var vm = this._view; |
| 13940 |
return vm.width * Math.abs(vm.y - vm.base); |
| 13941 |
}, |
| 13942 |
|
| 13943 |
tooltipPosition: function() { |
| 13944 |
var vm = this._view; |
| 13945 |
return { |
| 13946 |
x: vm.x, |
| 13947 |
y: vm.y |
| 13948 |
}; |
| 13949 |
} |
| 13950 |
}); |
| 13951 |
|
| 13952 |
},{"25":25,"26":26}],40:[function(require,module,exports){ |
| 13953 |
'use strict'; |
| 13954 |
|
| 13955 |
module.exports = {}; |
| 13956 |
module.exports.Arc = require(36); |
| 13957 |
module.exports.Line = require(37); |
| 13958 |
module.exports.Point = require(38); |
| 13959 |
module.exports.Rectangle = require(39); |
| 13960 |
|
| 13961 |
},{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){ |
| 13962 |
'use strict'; |
| 13963 |
|
| 13964 |
var helpers = require(42); |
| 13965 |
|
| 13966 |
/** |
| 13967 |
* @namespace Chart.helpers.canvas |
| 13968 |
*/ |
| 13969 |
var exports = module.exports = { |
| 13970 |
/** |
| 13971 |
* Clears the entire canvas associated to the given `chart`. |
| 13972 |
* @param {Chart} chart - The chart for which to clear the canvas. |
| 13973 |
*/ |
| 13974 |
clear: function(chart) { |
| 13975 |
chart.ctx.clearRect(0, 0, chart.width, chart.height); |
| 13976 |
}, |
| 13977 |
|
| 13978 |
/** |
| 13979 |
* Creates a "path" for a rectangle with rounded corners at position (x, y) with a |
| 13980 |
* given size (width, height) and the same `radius` for all corners. |
| 13981 |
* @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. |
| 13982 |
* @param {Number} x - The x axis of the coordinate for the rectangle starting point. |
| 13983 |
* @param {Number} y - The y axis of the coordinate for the rectangle starting point. |
| 13984 |
* @param {Number} width - The rectangle's width. |
| 13985 |
* @param {Number} height - The rectangle's height. |
| 13986 |
* @param {Number} radius - The rounded amount (in pixels) for the four corners. |
| 13987 |
* @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? |
| 13988 |
*/ |
| 13989 |
roundedRect: function(ctx, x, y, width, height, radius) { |
| 13990 |
if (radius) { |
| 13991 |
var rx = Math.min(radius, width / 2); |
| 13992 |
var ry = Math.min(radius, height / 2); |
| 13993 |
|
| 13994 |
ctx.moveTo(x + rx, y); |
| 13995 |
ctx.lineTo(x + width - rx, y); |
| 13996 |
ctx.quadraticCurveTo(x + width, y, x + width, y + ry); |
| 13997 |
ctx.lineTo(x + width, y + height - ry); |
| 13998 |
ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); |
| 13999 |
ctx.lineTo(x + rx, y + height); |
| 14000 |
ctx.quadraticCurveTo(x, y + height, x, y + height - ry); |
| 14001 |
ctx.lineTo(x, y + ry); |
| 14002 |
ctx.quadraticCurveTo(x, y, x + rx, y); |
| 14003 |
} else { |
| 14004 |
ctx.rect(x, y, width, height); |
| 14005 |
} |
| 14006 |
}, |
| 14007 |
|
| 14008 |
drawPoint: function(ctx, style, radius, x, y) { |
| 14009 |
var type, edgeLength, xOffset, yOffset, height, size; |
| 14010 |
|
| 14011 |
if (typeof style === 'object') { |
| 14012 |
type = style.toString(); |
| 14013 |
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { |
| 14014 |
ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); |
| 14015 |
return; |
| 14016 |
} |
| 14017 |
} |
| 14018 |
|
| 14019 |
if (isNaN(radius) || radius <= 0) { |
| 14020 |
return; |
| 14021 |
} |
| 14022 |
|
| 14023 |
switch (style) { |
| 14024 |
// Default includes circle |
| 14025 |
default: |
| 14026 |
ctx.beginPath(); |
| 14027 |
ctx.arc(x, y, radius, 0, Math.PI * 2); |
| 14028 |
ctx.closePath(); |
| 14029 |
ctx.fill(); |
| 14030 |
break; |
| 14031 |
case 'triangle': |
| 14032 |
ctx.beginPath(); |
| 14033 |
edgeLength = 3 * radius / Math.sqrt(3); |
| 14034 |
height = edgeLength * Math.sqrt(3) / 2; |
| 14035 |
ctx.moveTo(x - edgeLength / 2, y + height / 3); |
| 14036 |
ctx.lineTo(x + edgeLength / 2, y + height / 3); |
| 14037 |
ctx.lineTo(x, y - 2 * height / 3); |
| 14038 |
ctx.closePath(); |
| 14039 |
ctx.fill(); |
| 14040 |
break; |
| 14041 |
case 'rect': |
| 14042 |
size = 1 / Math.SQRT2 * radius; |
| 14043 |
ctx.beginPath(); |
| 14044 |
ctx.fillRect(x - size, y - size, 2 * size, 2 * size); |
| 14045 |
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); |
| 14046 |
break; |
| 14047 |
case 'rectRounded': |
| 14048 |
var offset = radius / Math.SQRT2; |
| 14049 |
var leftX = x - offset; |
| 14050 |
var topY = y - offset; |
| 14051 |
var sideSize = Math.SQRT2 * radius; |
| 14052 |
ctx.beginPath(); |
| 14053 |
this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2); |
| 14054 |
ctx.closePath(); |
| 14055 |
ctx.fill(); |
| 14056 |
break; |
| 14057 |
case 'rectRot': |
| 14058 |
size = 1 / Math.SQRT2 * radius; |
| 14059 |
ctx.beginPath(); |
| 14060 |
ctx.moveTo(x - size, y); |
| 14061 |
ctx.lineTo(x, y + size); |
| 14062 |
ctx.lineTo(x + size, y); |
| 14063 |
ctx.lineTo(x, y - size); |
| 14064 |
ctx.closePath(); |
| 14065 |
ctx.fill(); |
| 14066 |
break; |
| 14067 |
case 'cross': |
| 14068 |
ctx.beginPath(); |
| 14069 |
ctx.moveTo(x, y + radius); |
| 14070 |
ctx.lineTo(x, y - radius); |
| 14071 |
ctx.moveTo(x - radius, y); |
| 14072 |
ctx.lineTo(x + radius, y); |
| 14073 |
ctx.closePath(); |
| 14074 |
break; |
| 14075 |
case 'crossRot': |
| 14076 |
ctx.beginPath(); |
| 14077 |
xOffset = Math.cos(Math.PI / 4) * radius; |
| 14078 |
yOffset = Math.sin(Math.PI / 4) * radius; |
| 14079 |
ctx.moveTo(x - xOffset, y - yOffset); |
| 14080 |
ctx.lineTo(x + xOffset, y + yOffset); |
| 14081 |
ctx.moveTo(x - xOffset, y + yOffset); |
| 14082 |
ctx.lineTo(x + xOffset, y - yOffset); |
| 14083 |
ctx.closePath(); |
| 14084 |
break; |
| 14085 |
case 'star': |
| 14086 |
ctx.beginPath(); |
| 14087 |
ctx.moveTo(x, y + radius); |
| 14088 |
ctx.lineTo(x, y - radius); |
| 14089 |
ctx.moveTo(x - radius, y); |
| 14090 |
ctx.lineTo(x + radius, y); |
| 14091 |
xOffset = Math.cos(Math.PI / 4) * radius; |
| 14092 |
yOffset = Math.sin(Math.PI / 4) * radius; |
| 14093 |
ctx.moveTo(x - xOffset, y - yOffset); |
| 14094 |
ctx.lineTo(x + xOffset, y + yOffset); |
| 14095 |
ctx.moveTo(x - xOffset, y + yOffset); |
| 14096 |
ctx.lineTo(x + xOffset, y - yOffset); |
| 14097 |
ctx.closePath(); |
| 14098 |
break; |
| 14099 |
case 'line': |
| 14100 |
ctx.beginPath(); |
| 14101 |
ctx.moveTo(x - radius, y); |
| 14102 |
ctx.lineTo(x + radius, y); |
| 14103 |
ctx.closePath(); |
| 14104 |
break; |
| 14105 |
case 'dash': |
| 14106 |
ctx.beginPath(); |
| 14107 |
ctx.moveTo(x, y); |
| 14108 |
ctx.lineTo(x + radius, y); |
| 14109 |
ctx.closePath(); |
| 14110 |
break; |
| 14111 |
} |
| 14112 |
|
| 14113 |
ctx.stroke(); |
| 14114 |
}, |
| 14115 |
|
| 14116 |
clipArea: function(ctx, area) { |
| 14117 |
ctx.save(); |
| 14118 |
ctx.beginPath(); |
| 14119 |
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); |
| 14120 |
ctx.clip(); |
| 14121 |
}, |
| 14122 |
|
| 14123 |
unclipArea: function(ctx) { |
| 14124 |
ctx.restore(); |
| 14125 |
}, |
| 14126 |
|
| 14127 |
lineTo: function(ctx, previous, target, flip) { |
| 14128 |
if (target.steppedLine) { |
| 14129 |
if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { |
| 14130 |
ctx.lineTo(previous.x, target.y); |
| 14131 |
} else { |
| 14132 |
ctx.lineTo(target.x, previous.y); |
| 14133 |
} |
| 14134 |
ctx.lineTo(target.x, target.y); |
| 14135 |
return; |
| 14136 |
} |
| 14137 |
|
| 14138 |
if (!target.tension) { |
| 14139 |
ctx.lineTo(target.x, target.y); |
| 14140 |
return; |
| 14141 |
} |
| 14142 |
|
| 14143 |
ctx.bezierCurveTo( |
| 14144 |
flip ? previous.controlPointPreviousX : previous.controlPointNextX, |
| 14145 |
flip ? previous.controlPointPreviousY : previous.controlPointNextY, |
| 14146 |
flip ? target.controlPointNextX : target.controlPointPreviousX, |
| 14147 |
flip ? target.controlPointNextY : target.controlPointPreviousY, |
| 14148 |
target.x, |
| 14149 |
target.y); |
| 14150 |
} |
| 14151 |
}; |
| 14152 |
|
| 14153 |
// DEPRECATIONS |
| 14154 |
|
| 14155 |
/** |
| 14156 |
* Provided for backward compatibility, use Chart.helpers.canvas.clear instead. |
| 14157 |
* @namespace Chart.helpers.clear |
| 14158 |
* @deprecated since version 2.7.0 |
| 14159 |
* @todo remove at version 3 |
| 14160 |
* @private |
| 14161 |
*/ |
| 14162 |
helpers.clear = exports.clear; |
| 14163 |
|
| 14164 |
/** |
| 14165 |
* Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. |
| 14166 |
* @namespace Chart.helpers.drawRoundedRectangle |
| 14167 |
* @deprecated since version 2.7.0 |
| 14168 |
* @todo remove at version 3 |
| 14169 |
* @private |
| 14170 |
*/ |
| 14171 |
helpers.drawRoundedRectangle = function(ctx) { |
| 14172 |
ctx.beginPath(); |
| 14173 |
exports.roundedRect.apply(exports, arguments); |
| 14174 |
ctx.closePath(); |
| 14175 |
}; |
| 14176 |
|
| 14177 |
},{"42":42}],42:[function(require,module,exports){ |
| 14178 |
'use strict'; |
| 14179 |
|
| 14180 |
/** |
| 14181 |
* @namespace Chart.helpers |
| 14182 |
*/ |
| 14183 |
var helpers = { |
| 14184 |
/** |
| 14185 |
* An empty function that can be used, for example, for optional callback. |
| 14186 |
*/ |
| 14187 |
noop: function() {}, |
| 14188 |
|
| 14189 |
/** |
| 14190 |
* Returns a unique id, sequentially generated from a global variable. |
| 14191 |
* @returns {Number} |
| 14192 |
* @function |
| 14193 |
*/ |
| 14194 |
uid: (function() { |
| 14195 |
var id = 0; |
| 14196 |
return function() { |
| 14197 |
return id++; |
| 14198 |
}; |
| 14199 |
}()), |
| 14200 |
|
| 14201 |
/** |
| 14202 |
* Returns true if `value` is neither null nor undefined, else returns false. |
| 14203 |
* @param {*} value - The value to test. |
| 14204 |
* @returns {Boolean} |
| 14205 |
* @since 2.7.0 |
| 14206 |
*/ |
| 14207 |
isNullOrUndef: function(value) { |
| 14208 |
return value === null || typeof value === 'undefined'; |
| 14209 |
}, |
| 14210 |
|
| 14211 |
/** |
| 14212 |
* Returns true if `value` is an array, else returns false. |
| 14213 |
* @param {*} value - The value to test. |
| 14214 |
* @returns {Boolean} |
| 14215 |
* @function |
| 14216 |
*/ |
| 14217 |
isArray: Array.isArray ? Array.isArray : function(value) { |
| 14218 |
return Object.prototype.toString.call(value) === '[object Array]'; |
| 14219 |
}, |
| 14220 |
|
| 14221 |
/** |
| 14222 |
* Returns true if `value` is an object (excluding null), else returns false. |
| 14223 |
* @param {*} value - The value to test. |
| 14224 |
* @returns {Boolean} |
| 14225 |
* @since 2.7.0 |
| 14226 |
*/ |
| 14227 |
isObject: function(value) { |
| 14228 |
return value !== null && Object.prototype.toString.call(value) === '[object Object]'; |
| 14229 |
}, |
| 14230 |
|
| 14231 |
/** |
| 14232 |
* Returns `value` if defined, else returns `defaultValue`. |
| 14233 |
* @param {*} value - The value to return if defined. |
| 14234 |
* @param {*} defaultValue - The value to return if `value` is undefined. |
| 14235 |
* @returns {*} |
| 14236 |
*/ |
| 14237 |
valueOrDefault: function(value, defaultValue) { |
| 14238 |
return typeof value === 'undefined' ? defaultValue : value; |
| 14239 |
}, |
| 14240 |
|
| 14241 |
/** |
| 14242 |
* Returns value at the given `index` in array if defined, else returns `defaultValue`. |
| 14243 |
* @param {Array} value - The array to lookup for value at `index`. |
| 14244 |
* @param {Number} index - The index in `value` to lookup for value. |
| 14245 |
* @param {*} defaultValue - The value to return if `value[index]` is undefined. |
| 14246 |
* @returns {*} |
| 14247 |
*/ |
| 14248 |
valueAtIndexOrDefault: function(value, index, defaultValue) { |
| 14249 |
return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); |
| 14250 |
}, |
| 14251 |
|
| 14252 |
/** |
| 14253 |
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the |
| 14254 |
* value returned by `fn`. If `fn` is not a function, this method returns undefined. |
| 14255 |
* @param {Function} fn - The function to call. |
| 14256 |
* @param {Array|undefined|null} args - The arguments with which `fn` should be called. |
| 14257 |
* @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. |
| 14258 |
* @returns {*} |
| 14259 |
*/ |
| 14260 |
callback: function(fn, args, thisArg) { |
| 14261 |
if (fn && typeof fn.call === 'function') { |
| 14262 |
return fn.apply(thisArg, args); |
| 14263 |
} |
| 14264 |
}, |
| 14265 |
|
| 14266 |
/** |
| 14267 |
* Note(SB) for performance sake, this method should only be used when loopable type |
| 14268 |
* is unknown or in none intensive code (not called often and small loopable). Else |
| 14269 |
* it's preferable to use a regular for() loop and save extra function calls. |
| 14270 |
* @param {Object|Array} loopable - The object or array to be iterated. |
| 14271 |
* @param {Function} fn - The function to call for each item. |
| 14272 |
* @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. |
| 14273 |
* @param {Boolean} [reverse] - If true, iterates backward on the loopable. |
| 14274 |
*/ |
| 14275 |
each: function(loopable, fn, thisArg, reverse) { |
| 14276 |
var i, len, keys; |
| 14277 |
if (helpers.isArray(loopable)) { |
| 14278 |
len = loopable.length; |
| 14279 |
if (reverse) { |
| 14280 |
for (i = len - 1; i >= 0; i--) { |
| 14281 |
fn.call(thisArg, loopable[i], i); |
| 14282 |
} |
| 14283 |
} else { |
| 14284 |
for (i = 0; i < len; i++) { |
| 14285 |
fn.call(thisArg, loopable[i], i); |
| 14286 |
} |
| 14287 |
} |
| 14288 |
} else if (helpers.isObject(loopable)) { |
| 14289 |
keys = Object.keys(loopable); |
| 14290 |
len = keys.length; |
| 14291 |
for (i = 0; i < len; i++) { |
| 14292 |
fn.call(thisArg, loopable[keys[i]], keys[i]); |
| 14293 |
} |
| 14294 |
} |
| 14295 |
}, |
| 14296 |
|
| 14297 |
/** |
| 14298 |
* Returns true if the `a0` and `a1` arrays have the same content, else returns false. |
| 14299 |
* @see http://stackoverflow.com/a/14853974 |
| 14300 |
* @param {Array} a0 - The array to compare |
| 14301 |
* @param {Array} a1 - The array to compare |
| 14302 |
* @returns {Boolean} |
| 14303 |
*/ |
| 14304 |
arrayEquals: function(a0, a1) { |
| 14305 |
var i, ilen, v0, v1; |
| 14306 |
|
| 14307 |
if (!a0 || !a1 || a0.length !== a1.length) { |
| 14308 |
return false; |
| 14309 |
} |
| 14310 |
|
| 14311 |
for (i = 0, ilen = a0.length; i < ilen; ++i) { |
| 14312 |
v0 = a0[i]; |
| 14313 |
v1 = a1[i]; |
| 14314 |
|
| 14315 |
if (v0 instanceof Array && v1 instanceof Array) { |
| 14316 |
if (!helpers.arrayEquals(v0, v1)) { |
| 14317 |
return false; |
| 14318 |
} |
| 14319 |
} else if (v0 !== v1) { |
| 14320 |
// NOTE: two different object instances will never be equal: {x:20} != {x:20} |
| 14321 |
return false; |
| 14322 |
} |
| 14323 |
} |
| 14324 |
|
| 14325 |
return true; |
| 14326 |
}, |
| 14327 |
|
| 14328 |
/** |
| 14329 |
* Returns a deep copy of `source` without keeping references on objects and arrays. |
| 14330 |
* @param {*} source - The value to clone. |
| 14331 |
* @returns {*} |
| 14332 |
*/ |
| 14333 |
clone: function(source) { |
| 14334 |
if (helpers.isArray(source)) { |
| 14335 |
return source.map(helpers.clone); |
| 14336 |
} |
| 14337 |
|
| 14338 |
if (helpers.isObject(source)) { |
| 14339 |
var target = {}; |
| 14340 |
var keys = Object.keys(source); |
| 14341 |
var klen = keys.length; |
| 14342 |
var k = 0; |
| 14343 |
|
| 14344 |
for (; k < klen; ++k) { |
| 14345 |
target[keys[k]] = helpers.clone(source[keys[k]]); |
| 14346 |
} |
| 14347 |
|
| 14348 |
return target; |
| 14349 |
} |
| 14350 |
|
| 14351 |
return source; |
| 14352 |
}, |
| 14353 |
|
| 14354 |
/** |
| 14355 |
* The default merger when Chart.helpers.merge is called without merger option. |
| 14356 |
* Note(SB): this method is also used by configMerge and scaleMerge as fallback. |
| 14357 |
* @private |
| 14358 |
*/ |
| 14359 |
_merger: function(key, target, source, options) { |
| 14360 |
var tval = target[key]; |
| 14361 |
var sval = source[key]; |
| 14362 |
|
| 14363 |
if (helpers.isObject(tval) && helpers.isObject(sval)) { |
| 14364 |
helpers.merge(tval, sval, options); |
| 14365 |
} else { |
| 14366 |
target[key] = helpers.clone(sval); |
| 14367 |
} |
| 14368 |
}, |
| 14369 |
|
| 14370 |
/** |
| 14371 |
* Merges source[key] in target[key] only if target[key] is undefined. |
| 14372 |
* @private |
| 14373 |
*/ |
| 14374 |
_mergerIf: function(key, target, source) { |
| 14375 |
var tval = target[key]; |
| 14376 |
var sval = source[key]; |
| 14377 |
|
| 14378 |
if (helpers.isObject(tval) && helpers.isObject(sval)) { |
| 14379 |
helpers.mergeIf(tval, sval); |
| 14380 |
} else if (!target.hasOwnProperty(key)) { |
| 14381 |
target[key] = helpers.clone(sval); |
| 14382 |
} |
| 14383 |
}, |
| 14384 |
|
| 14385 |
/** |
| 14386 |
* Recursively deep copies `source` properties into `target` with the given `options`. |
| 14387 |
* IMPORTANT: `target` is not cloned and will be updated with `source` properties. |
| 14388 |
* @param {Object} target - The target object in which all sources are merged into. |
| 14389 |
* @param {Object|Array(Object)} source - Object(s) to merge into `target`. |
| 14390 |
* @param {Object} [options] - Merging options: |
| 14391 |
* @param {Function} [options.merger] - The merge method (key, target, source, options) |
| 14392 |
* @returns {Object} The `target` object. |
| 14393 |
*/ |
| 14394 |
merge: function(target, source, options) { |
| 14395 |
var sources = helpers.isArray(source) ? source : [source]; |
| 14396 |
var ilen = sources.length; |
| 14397 |
var merge, i, keys, klen, k; |
| 14398 |
|
| 14399 |
if (!helpers.isObject(target)) { |
| 14400 |
return target; |
| 14401 |
} |
| 14402 |
|
| 14403 |
options = options || {}; |
| 14404 |
merge = options.merger || helpers._merger; |
| 14405 |
|
| 14406 |
for (i = 0; i < ilen; ++i) { |
| 14407 |
source = sources[i]; |
| 14408 |
if (!helpers.isObject(source)) { |
| 14409 |
continue; |
| 14410 |
} |
| 14411 |
|
| 14412 |
keys = Object.keys(source); |
| 14413 |
for (k = 0, klen = keys.length; k < klen; ++k) { |
| 14414 |
merge(keys[k], target, source, options); |
| 14415 |
} |
| 14416 |
} |
| 14417 |
|
| 14418 |
return target; |
| 14419 |
}, |
| 14420 |
|
| 14421 |
/** |
| 14422 |
* Recursively deep copies `source` properties into `target` *only* if not defined in target. |
| 14423 |
* IMPORTANT: `target` is not cloned and will be updated with `source` properties. |
| 14424 |
* @param {Object} target - The target object in which all sources are merged into. |
| 14425 |
* @param {Object|Array(Object)} source - Object(s) to merge into `target`. |
| 14426 |
* @returns {Object} The `target` object. |
| 14427 |
*/ |
| 14428 |
mergeIf: function(target, source) { |
| 14429 |
return helpers.merge(target, source, {merger: helpers._mergerIf}); |
| 14430 |
} |
| 14431 |
}; |
| 14432 |
|
| 14433 |
module.exports = helpers; |
| 14434 |
|
| 14435 |
// DEPRECATIONS |
| 14436 |
|
| 14437 |
/** |
| 14438 |
* Provided for backward compatibility, use Chart.helpers.callback instead. |
| 14439 |
* @function Chart.helpers.callCallback |
| 14440 |
* @deprecated since version 2.6.0 |
| 14441 |
* @todo remove at version 3 |
| 14442 |
* @private |
| 14443 |
*/ |
| 14444 |
helpers.callCallback = helpers.callback; |
| 14445 |
|
| 14446 |
/** |
| 14447 |
* Provided for backward compatibility, use Array.prototype.indexOf instead. |
| 14448 |
* Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ |
| 14449 |
* @function Chart.helpers.indexOf |
| 14450 |
* @deprecated since version 2.7.0 |
| 14451 |
* @todo remove at version 3 |
| 14452 |
* @private |
| 14453 |
*/ |
| 14454 |
helpers.indexOf = function(array, item, fromIndex) { |
| 14455 |
return Array.prototype.indexOf.call(array, item, fromIndex); |
| 14456 |
}; |
| 14457 |
|
| 14458 |
/** |
| 14459 |
* Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. |
| 14460 |
* @function Chart.helpers.getValueOrDefault |
| 14461 |
* @deprecated since version 2.7.0 |
| 14462 |
* @todo remove at version 3 |
| 14463 |
* @private |
| 14464 |
*/ |
| 14465 |
helpers.getValueOrDefault = helpers.valueOrDefault; |
| 14466 |
|
| 14467 |
/** |
| 14468 |
* Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. |
| 14469 |
* @function Chart.helpers.getValueAtIndexOrDefault |
| 14470 |
* @deprecated since version 2.7.0 |
| 14471 |
* @todo remove at version 3 |
| 14472 |
* @private |
| 14473 |
*/ |
| 14474 |
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; |
| 14475 |
|
| 14476 |
},{}],43:[function(require,module,exports){ |
| 14477 |
'use strict'; |
| 14478 |
|
| 14479 |
var helpers = require(42); |
| 14480 |
|
| 14481 |
/** |
| 14482 |
* Easing functions adapted from Robert Penner's easing equations. |
| 14483 |
* @namespace Chart.helpers.easingEffects |
| 14484 |
* @see http://www.robertpenner.com/easing/ |
| 14485 |
*/ |
| 14486 |
var effects = { |
| 14487 |
linear: function(t) { |
| 14488 |
return t; |
| 14489 |
}, |
| 14490 |
|
| 14491 |
easeInQuad: function(t) { |
| 14492 |
return t * t; |
| 14493 |
}, |
| 14494 |
|
| 14495 |
easeOutQuad: function(t) { |
| 14496 |
return -t * (t - 2); |
| 14497 |
}, |
| 14498 |
|
| 14499 |
easeInOutQuad: function(t) { |
| 14500 |
if ((t /= 0.5) < 1) { |
| 14501 |
return 0.5 * t * t; |
| 14502 |
} |
| 14503 |
return -0.5 * ((--t) * (t - 2) - 1); |
| 14504 |
}, |
| 14505 |
|
| 14506 |
easeInCubic: function(t) { |
| 14507 |
return t * t * t; |
| 14508 |
}, |
| 14509 |
|
| 14510 |
easeOutCubic: function(t) { |
| 14511 |
return (t = t - 1) * t * t + 1; |
| 14512 |
}, |
| 14513 |
|
| 14514 |
easeInOutCubic: function(t) { |
| 14515 |
if ((t /= 0.5) < 1) { |
| 14516 |
return 0.5 * t * t * t; |
| 14517 |
} |
| 14518 |
return 0.5 * ((t -= 2) * t * t + 2); |
| 14519 |
}, |
| 14520 |
|
| 14521 |
easeInQuart: function(t) { |
| 14522 |
return t * t * t * t; |
| 14523 |
}, |
| 14524 |
|
| 14525 |
easeOutQuart: function(t) { |
| 14526 |
return -((t = t - 1) * t * t * t - 1); |
| 14527 |
}, |
| 14528 |
|
| 14529 |
easeInOutQuart: function(t) { |
| 14530 |
if ((t /= 0.5) < 1) { |
| 14531 |
return 0.5 * t * t * t * t; |
| 14532 |
} |
| 14533 |
return -0.5 * ((t -= 2) * t * t * t - 2); |
| 14534 |
}, |
| 14535 |
|
| 14536 |
easeInQuint: function(t) { |
| 14537 |
return t * t * t * t * t; |
| 14538 |
}, |
| 14539 |
|
| 14540 |
easeOutQuint: function(t) { |
| 14541 |
return (t = t - 1) * t * t * t * t + 1; |
| 14542 |
}, |
| 14543 |
|
| 14544 |
easeInOutQuint: function(t) { |
| 14545 |
if ((t /= 0.5) < 1) { |
| 14546 |
return 0.5 * t * t * t * t * t; |
| 14547 |
} |
| 14548 |
return 0.5 * ((t -= 2) * t * t * t * t + 2); |
| 14549 |
}, |
| 14550 |
|
| 14551 |
easeInSine: function(t) { |
| 14552 |
return -Math.cos(t * (Math.PI / 2)) + 1; |
| 14553 |
}, |
| 14554 |
|
| 14555 |
easeOutSine: function(t) { |
| 14556 |
return Math.sin(t * (Math.PI / 2)); |
| 14557 |
}, |
| 14558 |
|
| 14559 |
easeInOutSine: function(t) { |
| 14560 |
return -0.5 * (Math.cos(Math.PI * t) - 1); |
| 14561 |
}, |
| 14562 |
|
| 14563 |
easeInExpo: function(t) { |
| 14564 |
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); |
| 14565 |
}, |
| 14566 |
|
| 14567 |
easeOutExpo: function(t) { |
| 14568 |
return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; |
| 14569 |
}, |
| 14570 |
|
| 14571 |
easeInOutExpo: function(t) { |
| 14572 |
if (t === 0) { |
| 14573 |
return 0; |
| 14574 |
} |
| 14575 |
if (t === 1) { |
| 14576 |
return 1; |
| 14577 |
} |
| 14578 |
if ((t /= 0.5) < 1) { |
| 14579 |
return 0.5 * Math.pow(2, 10 * (t - 1)); |
| 14580 |
} |
| 14581 |
return 0.5 * (-Math.pow(2, -10 * --t) + 2); |
| 14582 |
}, |
| 14583 |
|
| 14584 |
easeInCirc: function(t) { |
| 14585 |
if (t >= 1) { |
| 14586 |
return t; |
| 14587 |
} |
| 14588 |
return -(Math.sqrt(1 - t * t) - 1); |
| 14589 |
}, |
| 14590 |
|
| 14591 |
easeOutCirc: function(t) { |
| 14592 |
return Math.sqrt(1 - (t = t - 1) * t); |
| 14593 |
}, |
| 14594 |
|
| 14595 |
easeInOutCirc: function(t) { |
| 14596 |
if ((t /= 0.5) < 1) { |
| 14597 |
return -0.5 * (Math.sqrt(1 - t * t) - 1); |
| 14598 |
} |
| 14599 |
return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); |
| 14600 |
}, |
| 14601 |
|
| 14602 |
easeInElastic: function(t) { |
| 14603 |
var s = 1.70158; |
| 14604 |
var p = 0; |
| 14605 |
var a = 1; |
| 14606 |
if (t === 0) { |
| 14607 |
return 0; |
| 14608 |
} |
| 14609 |
if (t === 1) { |
| 14610 |
return 1; |
| 14611 |
} |
| 14612 |
if (!p) { |
| 14613 |
p = 0.3; |
| 14614 |
} |
| 14615 |
if (a < 1) { |
| 14616 |
a = 1; |
| 14617 |
s = p / 4; |
| 14618 |
} else { |
| 14619 |
s = p / (2 * Math.PI) * Math.asin(1 / a); |
| 14620 |
} |
| 14621 |
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); |
| 14622 |
}, |
| 14623 |
|
| 14624 |
easeOutElastic: function(t) { |
| 14625 |
var s = 1.70158; |
| 14626 |
var p = 0; |
| 14627 |
var a = 1; |
| 14628 |
if (t === 0) { |
| 14629 |
return 0; |
| 14630 |
} |
| 14631 |
if (t === 1) { |
| 14632 |
return 1; |
| 14633 |
} |
| 14634 |
if (!p) { |
| 14635 |
p = 0.3; |
| 14636 |
} |
| 14637 |
if (a < 1) { |
| 14638 |
a = 1; |
| 14639 |
s = p / 4; |
| 14640 |
} else { |
| 14641 |
s = p / (2 * Math.PI) * Math.asin(1 / a); |
| 14642 |
} |
| 14643 |
return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; |
| 14644 |
}, |
| 14645 |
|
| 14646 |
easeInOutElastic: function(t) { |
| 14647 |
var s = 1.70158; |
| 14648 |
var p = 0; |
| 14649 |
var a = 1; |
| 14650 |
if (t === 0) { |
| 14651 |
return 0; |
| 14652 |
} |
| 14653 |
if ((t /= 0.5) === 2) { |
| 14654 |
return 1; |
| 14655 |
} |
| 14656 |
if (!p) { |
| 14657 |
p = 0.45; |
| 14658 |
} |
| 14659 |
if (a < 1) { |
| 14660 |
a = 1; |
| 14661 |
s = p / 4; |
| 14662 |
} else { |
| 14663 |
s = p / (2 * Math.PI) * Math.asin(1 / a); |
| 14664 |
} |
| 14665 |
if (t < 1) { |
| 14666 |
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); |
| 14667 |
} |
| 14668 |
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; |
| 14669 |
}, |
| 14670 |
easeInBack: function(t) { |
| 14671 |
var s = 1.70158; |
| 14672 |
return t * t * ((s + 1) * t - s); |
| 14673 |
}, |
| 14674 |
|
| 14675 |
easeOutBack: function(t) { |
| 14676 |
var s = 1.70158; |
| 14677 |
return (t = t - 1) * t * ((s + 1) * t + s) + 1; |
| 14678 |
}, |
| 14679 |
|
| 14680 |
easeInOutBack: function(t) { |
| 14681 |
var s = 1.70158; |
| 14682 |
if ((t /= 0.5) < 1) { |
| 14683 |
return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); |
| 14684 |
} |
| 14685 |
return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); |
| 14686 |
}, |
| 14687 |
|
| 14688 |
easeInBounce: function(t) { |
| 14689 |
return 1 - effects.easeOutBounce(1 - t); |
| 14690 |
}, |
| 14691 |
|
| 14692 |
easeOutBounce: function(t) { |
| 14693 |
if (t < (1 / 2.75)) { |
| 14694 |
return 7.5625 * t * t; |
| 14695 |
} |
| 14696 |
if (t < (2 / 2.75)) { |
| 14697 |
return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; |
| 14698 |
} |
| 14699 |
if (t < (2.5 / 2.75)) { |
| 14700 |
return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; |
| 14701 |
} |
| 14702 |
return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; |
| 14703 |
}, |
| 14704 |
|
| 14705 |
easeInOutBounce: function(t) { |
| 14706 |
if (t < 0.5) { |
| 14707 |
return effects.easeInBounce(t * 2) * 0.5; |
| 14708 |
} |
| 14709 |
return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; |
| 14710 |
} |
| 14711 |
}; |
| 14712 |
|
| 14713 |
module.exports = { |
| 14714 |
effects: effects |
| 14715 |
}; |
| 14716 |
|
| 14717 |
// DEPRECATIONS |
| 14718 |
|
| 14719 |
/** |
| 14720 |
* Provided for backward compatibility, use Chart.helpers.easing.effects instead. |
| 14721 |
* @function Chart.helpers.easingEffects |
| 14722 |
* @deprecated since version 2.7.0 |
| 14723 |
* @todo remove at version 3 |
| 14724 |
* @private |
| 14725 |
*/ |
| 14726 |
helpers.easingEffects = effects; |
| 14727 |
|
| 14728 |
},{"42":42}],44:[function(require,module,exports){ |
| 14729 |
'use strict'; |
| 14730 |
|
| 14731 |
var helpers = require(42); |
| 14732 |
|
| 14733 |
/** |
| 14734 |
* @alias Chart.helpers.options |
| 14735 |
* @namespace |
| 14736 |
*/ |
| 14737 |
module.exports = { |
| 14738 |
/** |
| 14739 |
* Converts the given line height `value` in pixels for a specific font `size`. |
| 14740 |
* @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). |
| 14741 |
* @param {Number} size - The font size (in pixels) used to resolve relative `value`. |
| 14742 |
* @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). |
| 14743 |
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height |
| 14744 |
* @since 2.7.0 |
| 14745 |
*/ |
| 14746 |
toLineHeight: function(value, size) { |
| 14747 |
var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); |
| 14748 |
if (!matches || matches[1] === 'normal') { |
| 14749 |
return size * 1.2; |
| 14750 |
} |
| 14751 |
|
| 14752 |
value = +matches[2]; |
| 14753 |
|
| 14754 |
switch (matches[3]) { |
| 14755 |
case 'px': |
| 14756 |
return value; |
| 14757 |
case '%': |
| 14758 |
value /= 100; |
| 14759 |
break; |
| 14760 |
default: |
| 14761 |
break; |
| 14762 |
} |
| 14763 |
|
| 14764 |
return size * value; |
| 14765 |
}, |
| 14766 |
|
| 14767 |
/** |
| 14768 |
* Converts the given value into a padding object with pre-computed width/height. |
| 14769 |
* @param {Number|Object} value - If a number, set the value to all TRBL component, |
| 14770 |
* else, if and object, use defined properties and sets undefined ones to 0. |
| 14771 |
* @returns {Object} The padding values (top, right, bottom, left, width, height) |
| 14772 |
* @since 2.7.0 |
| 14773 |
*/ |
| 14774 |
toPadding: function(value) { |
| 14775 |
var t, r, b, l; |
| 14776 |
|
| 14777 |
if (helpers.isObject(value)) { |
| 14778 |
t = +value.top || 0; |
| 14779 |
r = +value.right || 0; |
| 14780 |
b = +value.bottom || 0; |
| 14781 |
l = +value.left || 0; |
| 14782 |
} else { |
| 14783 |
t = r = b = l = +value || 0; |
| 14784 |
} |
| 14785 |
|
| 14786 |
return { |
| 14787 |
top: t, |
| 14788 |
right: r, |
| 14789 |
bottom: b, |
| 14790 |
left: l, |
| 14791 |
height: t + b, |
| 14792 |
width: l + r |
| 14793 |
}; |
| 14794 |
}, |
| 14795 |
|
| 14796 |
/** |
| 14797 |
* Evaluates the given `inputs` sequentially and returns the first defined value. |
| 14798 |
* @param {Array[]} inputs - An array of values, falling back to the last value. |
| 14799 |
* @param {Object} [context] - If defined and the current value is a function, the value |
| 14800 |
* is called with `context` as first argument and the result becomes the new input. |
| 14801 |
* @param {Number} [index] - If defined and the current value is an array, the value |
| 14802 |
* at `index` become the new input. |
| 14803 |
* @since 2.7.0 |
| 14804 |
*/ |
| 14805 |
resolve: function(inputs, context, index) { |
| 14806 |
var i, ilen, value; |
| 14807 |
|
| 14808 |
for (i = 0, ilen = inputs.length; i < ilen; ++i) { |
| 14809 |
value = inputs[i]; |
| 14810 |
if (value === undefined) { |
| 14811 |
continue; |
| 14812 |
} |
| 14813 |
if (context !== undefined && typeof value === 'function') { |
| 14814 |
value = value(context); |
| 14815 |
} |
| 14816 |
if (index !== undefined && helpers.isArray(value)) { |
| 14817 |
value = value[index]; |
| 14818 |
} |
| 14819 |
if (value !== undefined) { |
| 14820 |
return value; |
| 14821 |
} |
| 14822 |
} |
| 14823 |
} |
| 14824 |
}; |
| 14825 |
|
| 14826 |
},{"42":42}],45:[function(require,module,exports){ |
| 14827 |
'use strict'; |
| 14828 |
|
| 14829 |
module.exports = require(42); |
| 14830 |
module.exports.easing = require(43); |
| 14831 |
module.exports.canvas = require(41); |
| 14832 |
module.exports.options = require(44); |
| 14833 |
|
| 14834 |
},{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){ |
| 14835 |
/** |
| 14836 |
* Platform fallback implementation (minimal). |
| 14837 |
* @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 |
| 14838 |
*/ |
| 14839 |
|
| 14840 |
module.exports = { |
| 14841 |
acquireContext: function(item) { |
| 14842 |
if (item && item.canvas) { |
| 14843 |
// Support for any object associated to a canvas (including a context2d) |
| 14844 |
item = item.canvas; |
| 14845 |
} |
| 14846 |
|
| 14847 |
return item && item.getContext('2d') || null; |
| 14848 |
} |
| 14849 |
}; |
| 14850 |
|
| 14851 |
},{}],47:[function(require,module,exports){ |
| 14852 |
/** |
| 14853 |
* Chart.Platform implementation for targeting a web browser |
| 14854 |
*/ |
| 14855 |
|
| 14856 |
'use strict'; |
| 14857 |
|
| 14858 |
var helpers = require(45); |
| 14859 |
|
| 14860 |
var EXPANDO_KEY = '$chartjs'; |
| 14861 |
var CSS_PREFIX = 'chartjs-'; |
| 14862 |
var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; |
| 14863 |
var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; |
| 14864 |
var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; |
| 14865 |
|
| 14866 |
/** |
| 14867 |
* DOM event types -> Chart.js event types. |
| 14868 |
* Note: only events with different types are mapped. |
| 14869 |
* @see https://developer.mozilla.org/en-US/docs/Web/Events |
| 14870 |
*/ |
| 14871 |
var EVENT_TYPES = { |
| 14872 |
touchstart: 'mousedown', |
| 14873 |
touchmove: 'mousemove', |
| 14874 |
touchend: 'mouseup', |
| 14875 |
pointerenter: 'mouseenter', |
| 14876 |
pointerdown: 'mousedown', |
| 14877 |
pointermove: 'mousemove', |
| 14878 |
pointerup: 'mouseup', |
| 14879 |
pointerleave: 'mouseout', |
| 14880 |
pointerout: 'mouseout' |
| 14881 |
}; |
| 14882 |
|
| 14883 |
/** |
| 14884 |
* The "used" size is the final value of a dimension property after all calculations have |
| 14885 |
* been performed. This method uses the computed style of `element` but returns undefined |
| 14886 |
* if the computed style is not expressed in pixels. That can happen in some cases where |
| 14887 |
* `element` has a size relative to its parent and this last one is not yet displayed, |
| 14888 |
* for example because of `display: none` on a parent node. |
| 14889 |
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value |
| 14890 |
* @returns {Number} Size in pixels or undefined if unknown. |
| 14891 |
*/ |
| 14892 |
function readUsedSize(element, property) { |
| 14893 |
var value = helpers.getStyle(element, property); |
| 14894 |
var matches = value && value.match(/^(\d+)(\.\d+)?px$/); |
| 14895 |
return matches ? Number(matches[1]) : undefined; |
| 14896 |
} |
| 14897 |
|
| 14898 |
/** |
| 14899 |
* Initializes the canvas style and render size without modifying the canvas display size, |
| 14900 |
* since responsiveness is handled by the controller.resize() method. The config is used |
| 14901 |
* to determine the aspect ratio to apply in case no explicit height has been specified. |
| 14902 |
*/ |
| 14903 |
function initCanvas(canvas, config) { |
| 14904 |
var style = canvas.style; |
| 14905 |
|
| 14906 |
// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it |
| 14907 |
// returns null or '' if no explicit value has been set to the canvas attribute. |
| 14908 |
var renderHeight = canvas.getAttribute('height'); |
| 14909 |
var renderWidth = canvas.getAttribute('width'); |
| 14910 |
|
| 14911 |
// Chart.js modifies some canvas values that we want to restore on destroy |
| 14912 |
canvas[EXPANDO_KEY] = { |
| 14913 |
initial: { |
| 14914 |
height: renderHeight, |
| 14915 |
width: renderWidth, |
| 14916 |
style: { |
| 14917 |
display: style.display, |
| 14918 |
height: style.height, |
| 14919 |
width: style.width |
| 14920 |
} |
| 14921 |
} |
| 14922 |
}; |
| 14923 |
|
| 14924 |
// Force canvas to display as block to avoid extra space caused by inline |
| 14925 |
// elements, which would interfere with the responsive resize process. |
| 14926 |
// https://github.com/chartjs/Chart.js/issues/2538 |
| 14927 |
style.display = style.display || 'block'; |
| 14928 |
|
| 14929 |
if (renderWidth === null || renderWidth === '') { |
| 14930 |
var displayWidth = readUsedSize(canvas, 'width'); |
| 14931 |
if (displayWidth !== undefined) { |
| 14932 |
canvas.width = displayWidth; |
| 14933 |
} |
| 14934 |
} |
| 14935 |
|
| 14936 |
if (renderHeight === null || renderHeight === '') { |
| 14937 |
if (canvas.style.height === '') { |
| 14938 |
// If no explicit render height and style height, let's apply the aspect ratio, |
| 14939 |
// which one can be specified by the user but also by charts as default option |
| 14940 |
// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. |
| 14941 |
canvas.height = canvas.width / (config.options.aspectRatio || 2); |
| 14942 |
} else { |
| 14943 |
var displayHeight = readUsedSize(canvas, 'height'); |
| 14944 |
if (displayWidth !== undefined) { |
| 14945 |
canvas.height = displayHeight; |
| 14946 |
} |
| 14947 |
} |
| 14948 |
} |
| 14949 |
|
| 14950 |
return canvas; |
| 14951 |
} |
| 14952 |
|
| 14953 |
/** |
| 14954 |
* Detects support for options object argument in addEventListener. |
| 14955 |
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support |
| 14956 |
* @private |
| 14957 |
*/ |
| 14958 |
var supportsEventListenerOptions = (function() { |
| 14959 |
var supports = false; |
| 14960 |
try { |
| 14961 |
var options = Object.defineProperty({}, 'passive', { |
| 14962 |
get: function() { |
| 14963 |
supports = true; |
| 14964 |
} |
| 14965 |
}); |
| 14966 |
window.addEventListener('e', null, options); |
| 14967 |
} catch (e) { |
| 14968 |
// continue regardless of error |
| 14969 |
} |
| 14970 |
return supports; |
| 14971 |
}()); |
| 14972 |
|
| 14973 |
// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. |
| 14974 |
// https://github.com/chartjs/Chart.js/issues/4287 |
| 14975 |
var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; |
| 14976 |
|
| 14977 |
function addEventListener(node, type, listener) { |
| 14978 |
node.addEventListener(type, listener, eventListenerOptions); |
| 14979 |
} |
| 14980 |
|
| 14981 |
function removeEventListener(node, type, listener) { |
| 14982 |
node.removeEventListener(type, listener, eventListenerOptions); |
| 14983 |
} |
| 14984 |
|
| 14985 |
function createEvent(type, chart, x, y, nativeEvent) { |
| 14986 |
return { |
| 14987 |
type: type, |
| 14988 |
chart: chart, |
| 14989 |
native: nativeEvent || null, |
| 14990 |
x: x !== undefined ? x : null, |
| 14991 |
y: y !== undefined ? y : null, |
| 14992 |
}; |
| 14993 |
} |
| 14994 |
|
| 14995 |
function fromNativeEvent(event, chart) { |
| 14996 |
var type = EVENT_TYPES[event.type] || event.type; |
| 14997 |
var pos = helpers.getRelativePosition(event, chart); |
| 14998 |
return createEvent(type, chart, pos.x, pos.y, event); |
| 14999 |
} |
| 15000 |
|
| 15001 |
function throttled(fn, thisArg) { |
| 15002 |
var ticking = false; |
| 15003 |
var args = []; |
| 15004 |
|
| 15005 |
return function() { |
| 15006 |
args = Array.prototype.slice.call(arguments); |
| 15007 |
thisArg = thisArg || this; |
| 15008 |
|
| 15009 |
if (!ticking) { |
| 15010 |
ticking = true; |
| 15011 |
helpers.requestAnimFrame.call(window, function() { |
| 15012 |
ticking = false; |
| 15013 |
fn.apply(thisArg, args); |
| 15014 |
}); |
| 15015 |
} |
| 15016 |
}; |
| 15017 |
} |
| 15018 |
|
| 15019 |
// Implementation based on https://github.com/marcj/css-element-queries |
| 15020 |
function createResizer(handler) { |
| 15021 |
var resizer = document.createElement('div'); |
| 15022 |
var cls = CSS_PREFIX + 'size-monitor'; |
| 15023 |
var maxSize = 1000000; |
| 15024 |
var style = |
| 15025 |
'position:absolute;' + |
| 15026 |
'left:0;' + |
| 15027 |
'top:0;' + |
| 15028 |
'right:0;' + |
| 15029 |
'bottom:0;' + |
| 15030 |
'overflow:hidden;' + |
| 15031 |
'pointer-events:none;' + |
| 15032 |
'visibility:hidden;' + |
| 15033 |
'z-index:-1;'; |
| 15034 |
|
| 15035 |
resizer.style.cssText = style; |
| 15036 |
resizer.className = cls; |
| 15037 |
resizer.innerHTML = |
| 15038 |
'<div class="' + cls + '-expand" style="' + style + '">' + |
| 15039 |
'<div style="' + |
| 15040 |
'position:absolute;' + |
| 15041 |
'width:' + maxSize + 'px;' + |
| 15042 |
'height:' + maxSize + 'px;' + |
| 15043 |
'left:0;' + |
| 15044 |
'top:0">' + |
| 15045 |
'</div>' + |
| 15046 |
'</div>' + |
| 15047 |
'<div class="' + cls + '-shrink" style="' + style + '">' + |
| 15048 |
'<div style="' + |
| 15049 |
'position:absolute;' + |
| 15050 |
'width:200%;' + |
| 15051 |
'height:200%;' + |
| 15052 |
'left:0; ' + |
| 15053 |
'top:0">' + |
| 15054 |
'</div>' + |
| 15055 |
'</div>'; |
| 15056 |
|
| 15057 |
var expand = resizer.childNodes[0]; |
| 15058 |
var shrink = resizer.childNodes[1]; |
| 15059 |
|
| 15060 |
resizer._reset = function() { |
| 15061 |
expand.scrollLeft = maxSize; |
| 15062 |
expand.scrollTop = maxSize; |
| 15063 |
shrink.scrollLeft = maxSize; |
| 15064 |
shrink.scrollTop = maxSize; |
| 15065 |
}; |
| 15066 |
var onScroll = function() { |
| 15067 |
resizer._reset(); |
| 15068 |
handler(); |
| 15069 |
}; |
| 15070 |
|
| 15071 |
addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); |
| 15072 |
addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); |
| 15073 |
|
| 15074 |
return resizer; |
| 15075 |
} |
| 15076 |
|
| 15077 |
// https://davidwalsh.name/detect-node-insertion |
| 15078 |
function watchForRender(node, handler) { |
| 15079 |
var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); |
| 15080 |
var proxy = expando.renderProxy = function(e) { |
| 15081 |
if (e.animationName === CSS_RENDER_ANIMATION) { |
| 15082 |
handler(); |
| 15083 |
} |
| 15084 |
}; |
| 15085 |
|
| 15086 |
helpers.each(ANIMATION_START_EVENTS, function(type) { |
| 15087 |
addEventListener(node, type, proxy); |
| 15088 |
}); |
| 15089 |
|
| 15090 |
node.classList.add(CSS_RENDER_MONITOR); |
| 15091 |
} |
| 15092 |
|
| 15093 |
function unwatchForRender(node) { |
| 15094 |
var expando = node[EXPANDO_KEY] || {}; |
| 15095 |
var proxy = expando.renderProxy; |
| 15096 |
|
| 15097 |
if (proxy) { |
| 15098 |
helpers.each(ANIMATION_START_EVENTS, function(type) { |
| 15099 |
removeEventListener(node, type, proxy); |
| 15100 |
}); |
| 15101 |
|
| 15102 |
delete expando.renderProxy; |
| 15103 |
} |
| 15104 |
|
| 15105 |
node.classList.remove(CSS_RENDER_MONITOR); |
| 15106 |
} |
| 15107 |
|
| 15108 |
function addResizeListener(node, listener, chart) { |
| 15109 |
var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); |
| 15110 |
|
| 15111 |
// Let's keep track of this added resizer and thus avoid DOM query when removing it. |
| 15112 |
var resizer = expando.resizer = createResizer(throttled(function() { |
| 15113 |
if (expando.resizer) { |
| 15114 |
return listener(createEvent('resize', chart)); |
| 15115 |
} |
| 15116 |
})); |
| 15117 |
|
| 15118 |
// The resizer needs to be attached to the node parent, so we first need to be |
| 15119 |
// sure that `node` is attached to the DOM before injecting the resizer element. |
| 15120 |
watchForRender(node, function() { |
| 15121 |
if (expando.resizer) { |
| 15122 |
var container = node.parentNode; |
| 15123 |
if (container && container !== resizer.parentNode) { |
| 15124 |
container.insertBefore(resizer, container.firstChild); |
| 15125 |
} |
| 15126 |
|
| 15127 |
// The container size might have changed, let's reset the resizer state. |
| 15128 |
resizer._reset(); |
| 15129 |
} |
| 15130 |
}); |
| 15131 |
} |
| 15132 |
|
| 15133 |
function removeResizeListener(node) { |
| 15134 |
var expando = node[EXPANDO_KEY] || {}; |
| 15135 |
var resizer = expando.resizer; |
| 15136 |
|
| 15137 |
delete expando.resizer; |
| 15138 |
unwatchForRender(node); |
| 15139 |
|
| 15140 |
if (resizer && resizer.parentNode) { |
| 15141 |
resizer.parentNode.removeChild(resizer); |
| 15142 |
} |
| 15143 |
} |
| 15144 |
|
| 15145 |
function injectCSS(platform, css) { |
| 15146 |
// http://stackoverflow.com/q/3922139 |
| 15147 |
var style = platform._style || document.createElement('style'); |
| 15148 |
if (!platform._style) { |
| 15149 |
platform._style = style; |
| 15150 |
css = '/* Chart.js */\n' + css; |
| 15151 |
style.setAttribute('type', 'text/css'); |
| 15152 |
document.getElementsByTagName('head')[0].appendChild(style); |
| 15153 |
} |
| 15154 |
|
| 15155 |
style.appendChild(document.createTextNode(css)); |
| 15156 |
} |
| 15157 |
|
| 15158 |
module.exports = { |
| 15159 |
/** |
| 15160 |
* This property holds whether this platform is enabled for the current environment. |
| 15161 |
* Currently used by platform.js to select the proper implementation. |
| 15162 |
* @private |
| 15163 |
*/ |
| 15164 |
_enabled: typeof window !== 'undefined' && typeof document !== 'undefined', |
| 15165 |
|
| 15166 |
initialize: function() { |
| 15167 |
var keyframes = 'from{opacity:0.99}to{opacity:1}'; |
| 15168 |
|
| 15169 |
injectCSS(this, |
| 15170 |
// DOM rendering detection |
| 15171 |
// https://davidwalsh.name/detect-node-insertion |
| 15172 |
'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + |
| 15173 |
'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + |
| 15174 |
'.' + CSS_RENDER_MONITOR + '{' + |
| 15175 |
'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + |
| 15176 |
'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + |
| 15177 |
'}' |
| 15178 |
); |
| 15179 |
}, |
| 15180 |
|
| 15181 |
acquireContext: function(item, config) { |
| 15182 |
if (typeof item === 'string') { |
| 15183 |
item = document.getElementById(item); |
| 15184 |
} else if (item.length) { |
| 15185 |
// Support for array based queries (such as jQuery) |
| 15186 |
item = item[0]; |
| 15187 |
} |
| 15188 |
|
| 15189 |
if (item && item.canvas) { |
| 15190 |
// Support for any object associated to a canvas (including a context2d) |
| 15191 |
item = item.canvas; |
| 15192 |
} |
| 15193 |
|
| 15194 |
// To prevent canvas fingerprinting, some add-ons undefine the getContext |
| 15195 |
// method, for example: https://github.com/kkapsner/CanvasBlocker |
| 15196 |
// https://github.com/chartjs/Chart.js/issues/2807 |
| 15197 |
var context = item && item.getContext && item.getContext('2d'); |
| 15198 |
|
| 15199 |
// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is |
| 15200 |
// inside an iframe or when running in a protected environment. We could guess the |
| 15201 |
// types from their toString() value but let's keep things flexible and assume it's |
| 15202 |
// a sufficient condition if the item has a context2D which has item as `canvas`. |
| 15203 |
// https://github.com/chartjs/Chart.js/issues/3887 |
| 15204 |
// https://github.com/chartjs/Chart.js/issues/4102 |
| 15205 |
// https://github.com/chartjs/Chart.js/issues/4152 |
| 15206 |
if (context && context.canvas === item) { |
| 15207 |
initCanvas(item, config); |
| 15208 |
return context; |
| 15209 |
} |
| 15210 |
|
| 15211 |
return null; |
| 15212 |
}, |
| 15213 |
|
| 15214 |
releaseContext: function(context) { |
| 15215 |
var canvas = context.canvas; |
| 15216 |
if (!canvas[EXPANDO_KEY]) { |
| 15217 |
return; |
| 15218 |
} |
| 15219 |
|
| 15220 |
var initial = canvas[EXPANDO_KEY].initial; |
| 15221 |
['height', 'width'].forEach(function(prop) { |
| 15222 |
var value = initial[prop]; |
| 15223 |
if (helpers.isNullOrUndef(value)) { |
| 15224 |
canvas.removeAttribute(prop); |
| 15225 |
} else { |
| 15226 |
canvas.setAttribute(prop, value); |
| 15227 |
} |
| 15228 |
}); |
| 15229 |
|
| 15230 |
helpers.each(initial.style || {}, function(value, key) { |
| 15231 |
canvas.style[key] = value; |
| 15232 |
}); |
| 15233 |
|
| 15234 |
// The canvas render size might have been changed (and thus the state stack discarded), |
| 15235 |
// we can't use save() and restore() to restore the initial state. So make sure that at |
| 15236 |
// least the canvas context is reset to the default state by setting the canvas width. |
| 15237 |
// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html |
| 15238 |
canvas.width = canvas.width; |
| 15239 |
|
| 15240 |
delete canvas[EXPANDO_KEY]; |
| 15241 |
}, |
| 15242 |
|
| 15243 |
addEventListener: function(chart, type, listener) { |
| 15244 |
var canvas = chart.canvas; |
| 15245 |
if (type === 'resize') { |
| 15246 |
// Note: the resize event is not supported on all browsers. |
| 15247 |
addResizeListener(canvas, listener, chart); |
| 15248 |
return; |
| 15249 |
} |
| 15250 |
|
| 15251 |
var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); |
| 15252 |
var proxies = expando.proxies || (expando.proxies = {}); |
| 15253 |
var proxy = proxies[chart.id + '_' + type] = function(event) { |
| 15254 |
listener(fromNativeEvent(event, chart)); |
| 15255 |
}; |
| 15256 |
|
| 15257 |
addEventListener(canvas, type, proxy); |
| 15258 |
}, |
| 15259 |
|
| 15260 |
removeEventListener: function(chart, type, listener) { |
| 15261 |
var canvas = chart.canvas; |
| 15262 |
if (type === 'resize') { |
| 15263 |
// Note: the resize event is not supported on all browsers. |
| 15264 |
removeResizeListener(canvas, listener); |
| 15265 |
return; |
| 15266 |
} |
| 15267 |
|
| 15268 |
var expando = listener[EXPANDO_KEY] || {}; |
| 15269 |
var proxies = expando.proxies || {}; |
| 15270 |
var proxy = proxies[chart.id + '_' + type]; |
| 15271 |
if (!proxy) { |
| 15272 |
return; |
| 15273 |
} |
| 15274 |
|
| 15275 |
removeEventListener(canvas, type, proxy); |
| 15276 |
} |
| 15277 |
}; |
| 15278 |
|
| 15279 |
// DEPRECATIONS |
| 15280 |
|
| 15281 |
/** |
| 15282 |
* Provided for backward compatibility, use EventTarget.addEventListener instead. |
| 15283 |
* EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ |
| 15284 |
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener |
| 15285 |
* @function Chart.helpers.addEvent |
| 15286 |
* @deprecated since version 2.7.0 |
| 15287 |
* @todo remove at version 3 |
| 15288 |
* @private |
| 15289 |
*/ |
| 15290 |
helpers.addEvent = addEventListener; |
| 15291 |
|
| 15292 |
/** |
| 15293 |
* Provided for backward compatibility, use EventTarget.removeEventListener instead. |
| 15294 |
* EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ |
| 15295 |
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener |
| 15296 |
* @function Chart.helpers.removeEvent |
| 15297 |
* @deprecated since version 2.7.0 |
| 15298 |
* @todo remove at version 3 |
| 15299 |
* @private |
| 15300 |
*/ |
| 15301 |
helpers.removeEvent = removeEventListener; |
| 15302 |
|
| 15303 |
},{"45":45}],48:[function(require,module,exports){ |
| 15304 |
'use strict'; |
| 15305 |
|
| 15306 |
var helpers = require(45); |
| 15307 |
var basic = require(46); |
| 15308 |
var dom = require(47); |
| 15309 |
|
| 15310 |
// @TODO Make possible to select another platform at build time. |
| 15311 |
var implementation = dom._enabled ? dom : basic; |
| 15312 |
|
| 15313 |
/** |
| 15314 |
* @namespace Chart.platform |
| 15315 |
* @see https://chartjs.gitbooks.io/proposals/content/Platform.html |
| 15316 |
* @since 2.4.0 |
| 15317 |
*/ |
| 15318 |
module.exports = helpers.extend({ |
| 15319 |
/** |
| 15320 |
* @since 2.7.0 |
| 15321 |
*/ |
| 15322 |
initialize: function() {}, |
| 15323 |
|
| 15324 |
/** |
| 15325 |
* Called at chart construction time, returns a context2d instance implementing |
| 15326 |
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. |
| 15327 |
* @param {*} item - The native item from which to acquire context (platform specific) |
| 15328 |
* @param {Object} options - The chart options |
| 15329 |
* @returns {CanvasRenderingContext2D} context2d instance |
| 15330 |
*/ |
| 15331 |
acquireContext: function() {}, |
| 15332 |
|
| 15333 |
/** |
| 15334 |
* Called at chart destruction time, releases any resources associated to the context |
| 15335 |
* previously returned by the acquireContext() method. |
| 15336 |
* @param {CanvasRenderingContext2D} context - The context2d instance |
| 15337 |
* @returns {Boolean} true if the method succeeded, else false |
| 15338 |
*/ |
| 15339 |
releaseContext: function() {}, |
| 15340 |
|
| 15341 |
/** |
| 15342 |
* Registers the specified listener on the given chart. |
| 15343 |
* @param {Chart} chart - Chart from which to listen for event |
| 15344 |
* @param {String} type - The ({@link IEvent}) type to listen for |
| 15345 |
* @param {Function} listener - Receives a notification (an object that implements |
| 15346 |
* the {@link IEvent} interface) when an event of the specified type occurs. |
| 15347 |
*/ |
| 15348 |
addEventListener: function() {}, |
| 15349 |
|
| 15350 |
/** |
| 15351 |
* Removes the specified listener previously registered with addEventListener. |
| 15352 |
* @param {Chart} chart -Chart from which to remove the listener |
| 15353 |
* @param {String} type - The ({@link IEvent}) type to remove |
| 15354 |
* @param {Function} listener - The listener function to remove from the event target. |
| 15355 |
*/ |
| 15356 |
removeEventListener: function() {} |
| 15357 |
|
| 15358 |
}, implementation); |
| 15359 |
|
| 15360 |
/** |
| 15361 |
* @interface IPlatform |
| 15362 |
* Allows abstracting platform dependencies away from the chart |
| 15363 |
* @borrows Chart.platform.acquireContext as acquireContext |
| 15364 |
* @borrows Chart.platform.releaseContext as releaseContext |
| 15365 |
* @borrows Chart.platform.addEventListener as addEventListener |
| 15366 |
* @borrows Chart.platform.removeEventListener as removeEventListener |
| 15367 |
*/ |
| 15368 |
|
| 15369 |
/** |
| 15370 |
* @interface IEvent |
| 15371 |
* @prop {String} type - The event type name, possible values are: |
| 15372 |
* 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout', |
| 15373 |
* 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' |
| 15374 |
* @prop {*} native - The original native event (null for emulated events, e.g. 'resize') |
| 15375 |
* @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) |
| 15376 |
* @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) |
| 15377 |
*/ |
| 15378 |
|
| 15379 |
},{"45":45,"46":46,"47":47}],49:[function(require,module,exports){ |
| 15380 |
/** |
| 15381 |
* Plugin based on discussion from the following Chart.js issues: |
| 15382 |
* @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 |
| 15383 |
* @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 |
| 15384 |
*/ |
| 15385 |
|
| 15386 |
'use strict'; |
| 15387 |
|
| 15388 |
var defaults = require(25); |
| 15389 |
var elements = require(40); |
| 15390 |
var helpers = require(45); |
| 15391 |
|
| 15392 |
defaults._set('global', { |
| 15393 |
plugins: { |
| 15394 |
filler: { |
| 15395 |
propagate: true |
| 15396 |
} |
| 15397 |
} |
| 15398 |
}); |
| 15399 |
|
| 15400 |
module.exports = function() { |
| 15401 |
|
| 15402 |
var mappers = { |
| 15403 |
dataset: function(source) { |
| 15404 |
var index = source.fill; |
| 15405 |
var chart = source.chart; |
| 15406 |
var meta = chart.getDatasetMeta(index); |
| 15407 |
var visible = meta && chart.isDatasetVisible(index); |
| 15408 |
var points = (visible && meta.dataset._children) || []; |
| 15409 |
var length = points.length || 0; |
| 15410 |
|
| 15411 |
return !length ? null : function(point, i) { |
| 15412 |
return (i < length && points[i]._view) || null; |
| 15413 |
}; |
| 15414 |
}, |
| 15415 |
|
| 15416 |
boundary: function(source) { |
| 15417 |
var boundary = source.boundary; |
| 15418 |
var x = boundary ? boundary.x : null; |
| 15419 |
var y = boundary ? boundary.y : null; |
| 15420 |
|
| 15421 |
return function(point) { |
| 15422 |
return { |
| 15423 |
x: x === null ? point.x : x, |
| 15424 |
y: y === null ? point.y : y, |
| 15425 |
}; |
| 15426 |
}; |
| 15427 |
} |
| 15428 |
}; |
| 15429 |
|
| 15430 |
// @todo if (fill[0] === '#') |
| 15431 |
function decodeFill(el, index, count) { |
| 15432 |
var model = el._model || {}; |
| 15433 |
var fill = model.fill; |
| 15434 |
var target; |
| 15435 |
|
| 15436 |
if (fill === undefined) { |
| 15437 |
fill = !!model.backgroundColor; |
| 15438 |
} |
| 15439 |
|
| 15440 |
if (fill === false || fill === null) { |
| 15441 |
return false; |
| 15442 |
} |
| 15443 |
|
| 15444 |
if (fill === true) { |
| 15445 |
return 'origin'; |
| 15446 |
} |
| 15447 |
|
| 15448 |
target = parseFloat(fill, 10); |
| 15449 |
if (isFinite(target) && Math.floor(target) === target) { |
| 15450 |
if (fill[0] === '-' || fill[0] === '+') { |
| 15451 |
target = index + target; |
| 15452 |
} |
| 15453 |
|
| 15454 |
if (target === index || target < 0 || target >= count) { |
| 15455 |
return false; |
| 15456 |
} |
| 15457 |
|
| 15458 |
return target; |
| 15459 |
} |
| 15460 |
|
| 15461 |
switch (fill) { |
| 15462 |
// compatibility |
| 15463 |
case 'bottom': |
| 15464 |
return 'start'; |
| 15465 |
case 'top': |
| 15466 |
return 'end'; |
| 15467 |
case 'zero': |
| 15468 |
return 'origin'; |
| 15469 |
// supported boundaries |
| 15470 |
case 'origin': |
| 15471 |
case 'start': |
| 15472 |
case 'end': |
| 15473 |
return fill; |
| 15474 |
// invalid fill values |
| 15475 |
default: |
| 15476 |
return false; |
| 15477 |
} |
| 15478 |
} |
| 15479 |
|
| 15480 |
function computeBoundary(source) { |
| 15481 |
var model = source.el._model || {}; |
| 15482 |
var scale = source.el._scale || {}; |
| 15483 |
var fill = source.fill; |
| 15484 |
var target = null; |
| 15485 |
var horizontal; |
| 15486 |
|
| 15487 |
if (isFinite(fill)) { |
| 15488 |
return null; |
| 15489 |
} |
| 15490 |
|
| 15491 |
// Backward compatibility: until v3, we still need to support boundary values set on |
| 15492 |
// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and |
| 15493 |
// controllers might still use it (e.g. the Smith chart). |
| 15494 |
|
| 15495 |
if (fill === 'start') { |
| 15496 |
target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; |
| 15497 |
} else if (fill === 'end') { |
| 15498 |
target = model.scaleTop === undefined ? scale.top : model.scaleTop; |
| 15499 |
} else if (model.scaleZero !== undefined) { |
| 15500 |
target = model.scaleZero; |
| 15501 |
} else if (scale.getBasePosition) { |
| 15502 |
target = scale.getBasePosition(); |
| 15503 |
} else if (scale.getBasePixel) { |
| 15504 |
target = scale.getBasePixel(); |
| 15505 |
} |
| 15506 |
|
| 15507 |
if (target !== undefined && target !== null) { |
| 15508 |
if (target.x !== undefined && target.y !== undefined) { |
| 15509 |
return target; |
| 15510 |
} |
| 15511 |
|
| 15512 |
if (typeof target === 'number' && isFinite(target)) { |
| 15513 |
horizontal = scale.isHorizontal(); |
| 15514 |
return { |
| 15515 |
x: horizontal ? target : null, |
| 15516 |
y: horizontal ? null : target |
| 15517 |
}; |
| 15518 |
} |
| 15519 |
} |
| 15520 |
|
| 15521 |
return null; |
| 15522 |
} |
| 15523 |
|
| 15524 |
function resolveTarget(sources, index, propagate) { |
| 15525 |
var source = sources[index]; |
| 15526 |
var fill = source.fill; |
| 15527 |
var visited = [index]; |
| 15528 |
var target; |
| 15529 |
|
| 15530 |
if (!propagate) { |
| 15531 |
return fill; |
| 15532 |
} |
| 15533 |
|
| 15534 |
while (fill !== false && visited.indexOf(fill) === -1) { |
| 15535 |
if (!isFinite(fill)) { |
| 15536 |
return fill; |
| 15537 |
} |
| 15538 |
|
| 15539 |
target = sources[fill]; |
| 15540 |
if (!target) { |
| 15541 |
return false; |
| 15542 |
} |
| 15543 |
|
| 15544 |
if (target.visible) { |
| 15545 |
return fill; |
| 15546 |
} |
| 15547 |
|
| 15548 |
visited.push(fill); |
| 15549 |
fill = target.fill; |
| 15550 |
} |
| 15551 |
|
| 15552 |
return false; |
| 15553 |
} |
| 15554 |
|
| 15555 |
function createMapper(source) { |
| 15556 |
var fill = source.fill; |
| 15557 |
var type = 'dataset'; |
| 15558 |
|
| 15559 |
if (fill === false) { |
| 15560 |
return null; |
| 15561 |
} |
| 15562 |
|
| 15563 |
if (!isFinite(fill)) { |
| 15564 |
type = 'boundary'; |
| 15565 |
} |
| 15566 |
|
| 15567 |
return mappers[type](source); |
| 15568 |
} |
| 15569 |
|
| 15570 |
function isDrawable(point) { |
| 15571 |
return point && !point.skip; |
| 15572 |
} |
| 15573 |
|
| 15574 |
function drawArea(ctx, curve0, curve1, len0, len1) { |
| 15575 |
var i; |
| 15576 |
|
| 15577 |
if (!len0 || !len1) { |
| 15578 |
return; |
| 15579 |
} |
| 15580 |
|
| 15581 |
// building first area curve (normal) |
| 15582 |
ctx.moveTo(curve0[0].x, curve0[0].y); |
| 15583 |
for (i = 1; i < len0; ++i) { |
| 15584 |
helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); |
| 15585 |
} |
| 15586 |
|
| 15587 |
// joining the two area curves |
| 15588 |
ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); |
| 15589 |
|
| 15590 |
// building opposite area curve (reverse) |
| 15591 |
for (i = len1 - 1; i > 0; --i) { |
| 15592 |
helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); |
| 15593 |
} |
| 15594 |
} |
| 15595 |
|
| 15596 |
function doFill(ctx, points, mapper, view, color, loop) { |
| 15597 |
var count = points.length; |
| 15598 |
var span = view.spanGaps; |
| 15599 |
var curve0 = []; |
| 15600 |
var curve1 = []; |
| 15601 |
var len0 = 0; |
| 15602 |
var len1 = 0; |
| 15603 |
var i, ilen, index, p0, p1, d0, d1; |
| 15604 |
|
| 15605 |
ctx.beginPath(); |
| 15606 |
|
| 15607 |
for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { |
| 15608 |
index = i % count; |
| 15609 |
p0 = points[index]._view; |
| 15610 |
p1 = mapper(p0, index, view); |
| 15611 |
d0 = isDrawable(p0); |
| 15612 |
d1 = isDrawable(p1); |
| 15613 |
|
| 15614 |
if (d0 && d1) { |
| 15615 |
len0 = curve0.push(p0); |
| 15616 |
len1 = curve1.push(p1); |
| 15617 |
} else if (len0 && len1) { |
| 15618 |
if (!span) { |
| 15619 |
drawArea(ctx, curve0, curve1, len0, len1); |
| 15620 |
len0 = len1 = 0; |
| 15621 |
curve0 = []; |
| 15622 |
curve1 = []; |
| 15623 |
} else { |
| 15624 |
if (d0) { |
| 15625 |
curve0.push(p0); |
| 15626 |
} |
| 15627 |
if (d1) { |
| 15628 |
curve1.push(p1); |
| 15629 |
} |
| 15630 |
} |
| 15631 |
} |
| 15632 |
} |
| 15633 |
|
| 15634 |
drawArea(ctx, curve0, curve1, len0, len1); |
| 15635 |
|
| 15636 |
ctx.closePath(); |
| 15637 |
ctx.fillStyle = color; |
| 15638 |
ctx.fill(); |
| 15639 |
} |
| 15640 |
|
| 15641 |
return { |
| 15642 |
id: 'filler', |
| 15643 |
|
| 15644 |
afterDatasetsUpdate: function(chart, options) { |
| 15645 |
var count = (chart.data.datasets || []).length; |
| 15646 |
var propagate = options.propagate; |
| 15647 |
var sources = []; |
| 15648 |
var meta, i, el, source; |
| 15649 |
|
| 15650 |
for (i = 0; i < count; ++i) { |
| 15651 |
meta = chart.getDatasetMeta(i); |
| 15652 |
el = meta.dataset; |
| 15653 |
source = null; |
| 15654 |
|
| 15655 |
if (el && el._model && el instanceof elements.Line) { |
| 15656 |
source = { |
| 15657 |
visible: chart.isDatasetVisible(i), |
| 15658 |
fill: decodeFill(el, i, count), |
| 15659 |
chart: chart, |
| 15660 |
el: el |
| 15661 |
}; |
| 15662 |
} |
| 15663 |
|
| 15664 |
meta.$filler = source; |
| 15665 |
sources.push(source); |
| 15666 |
} |
| 15667 |
|
| 15668 |
for (i = 0; i < count; ++i) { |
| 15669 |
source = sources[i]; |
| 15670 |
if (!source) { |
| 15671 |
continue; |
| 15672 |
} |
| 15673 |
|
| 15674 |
source.fill = resolveTarget(sources, i, propagate); |
| 15675 |
source.boundary = computeBoundary(source); |
| 15676 |
source.mapper = createMapper(source); |
| 15677 |
} |
| 15678 |
}, |
| 15679 |
|
| 15680 |
beforeDatasetDraw: function(chart, args) { |
| 15681 |
var meta = args.meta.$filler; |
| 15682 |
if (!meta) { |
| 15683 |
return; |
| 15684 |
} |
| 15685 |
|
| 15686 |
var ctx = chart.ctx; |
| 15687 |
var el = meta.el; |
| 15688 |
var view = el._view; |
| 15689 |
var points = el._children || []; |
| 15690 |
var mapper = meta.mapper; |
| 15691 |
var color = view.backgroundColor || defaults.global.defaultColor; |
| 15692 |
|
| 15693 |
if (mapper && color && points.length) { |
| 15694 |
helpers.canvas.clipArea(ctx, chart.chartArea); |
| 15695 |
doFill(ctx, points, mapper, view, color, el._loop); |
| 15696 |
helpers.canvas.unclipArea(ctx); |
| 15697 |
} |
| 15698 |
} |
| 15699 |
}; |
| 15700 |
}; |
| 15701 |
|
| 15702 |
},{"25":25,"40":40,"45":45}],50:[function(require,module,exports){ |
| 15703 |
'use strict'; |
| 15704 |
|
| 15705 |
var defaults = require(25); |
| 15706 |
var Element = require(26); |
| 15707 |
var helpers = require(45); |
| 15708 |
|
| 15709 |
defaults._set('global', { |
| 15710 |
legend: { |
| 15711 |
display: true, |
| 15712 |
position: 'top', |
| 15713 |
fullWidth: true, |
| 15714 |
reverse: false, |
| 15715 |
weight: 1000, |
| 15716 |
|
| 15717 |
// a callback that will handle |
| 15718 |
onClick: function(e, legendItem) { |
| 15719 |
var index = legendItem.datasetIndex; |
| 15720 |
var ci = this.chart; |
| 15721 |
var meta = ci.getDatasetMeta(index); |
| 15722 |
|
| 15723 |
// See controller.isDatasetVisible comment |
| 15724 |
meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; |
| 15725 |
|
| 15726 |
// We hid a dataset ... rerender the chart |
| 15727 |
ci.update(); |
| 15728 |
}, |
| 15729 |
|
| 15730 |
onHover: null, |
| 15731 |
|
| 15732 |
labels: { |
| 15733 |
boxWidth: 40, |
| 15734 |
padding: 10, |
| 15735 |
// Generates labels shown in the legend |
| 15736 |
// Valid properties to return: |
| 15737 |
// text : text to display |
| 15738 |
// fillStyle : fill of coloured box |
| 15739 |
// strokeStyle: stroke of coloured box |
| 15740 |
// hidden : if this legend item refers to a hidden item |
| 15741 |
// lineCap : cap style for line |
| 15742 |
// lineDash |
| 15743 |
// lineDashOffset : |
| 15744 |
// lineJoin : |
| 15745 |
// lineWidth : |
| 15746 |
generateLabels: function(chart) { |
| 15747 |
var data = chart.data; |
| 15748 |
return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { |
| 15749 |
return { |
| 15750 |
text: dataset.label, |
| 15751 |
fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), |
| 15752 |
hidden: !chart.isDatasetVisible(i), |
| 15753 |
lineCap: dataset.borderCapStyle, |
| 15754 |
lineDash: dataset.borderDash, |
| 15755 |
lineDashOffset: dataset.borderDashOffset, |
| 15756 |
lineJoin: dataset.borderJoinStyle, |
| 15757 |
lineWidth: dataset.borderWidth, |
| 15758 |
strokeStyle: dataset.borderColor, |
| 15759 |
pointStyle: dataset.pointStyle, |
| 15760 |
|
| 15761 |
// Below is extra data used for toggling the datasets |
| 15762 |
datasetIndex: i |
| 15763 |
}; |
| 15764 |
}, this) : []; |
| 15765 |
} |
| 15766 |
} |
| 15767 |
}, |
| 15768 |
|
| 15769 |
legendCallback: function(chart) { |
| 15770 |
var text = []; |
| 15771 |
text.push('<ul class="' + chart.id + '-legend">'); |
| 15772 |
for (var i = 0; i < chart.data.datasets.length; i++) { |
| 15773 |
text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); |
| 15774 |
if (chart.data.datasets[i].label) { |
| 15775 |
text.push(chart.data.datasets[i].label); |
| 15776 |
} |
| 15777 |
text.push('</li>'); |
| 15778 |
} |
| 15779 |
text.push('</ul>'); |
| 15780 |
return text.join(''); |
| 15781 |
} |
| 15782 |
}); |
| 15783 |
|
| 15784 |
module.exports = function(Chart) { |
| 15785 |
|
| 15786 |
var layout = Chart.layoutService; |
| 15787 |
var noop = helpers.noop; |
| 15788 |
|
| 15789 |
/** |
| 15790 |
* Helper function to get the box width based on the usePointStyle option |
| 15791 |
* @param labelopts {Object} the label options on the legend |
| 15792 |
* @param fontSize {Number} the label font size |
| 15793 |
* @return {Number} width of the color box area |
| 15794 |
*/ |
| 15795 |
function getBoxWidth(labelOpts, fontSize) { |
| 15796 |
return labelOpts.usePointStyle ? |
| 15797 |
fontSize * Math.SQRT2 : |
| 15798 |
labelOpts.boxWidth; |
| 15799 |
} |
| 15800 |
|
| 15801 |
Chart.Legend = Element.extend({ |
| 15802 |
|
| 15803 |
initialize: function(config) { |
| 15804 |
helpers.extend(this, config); |
| 15805 |
|
| 15806 |
// Contains hit boxes for each dataset (in dataset order) |
| 15807 |
this.legendHitBoxes = []; |
| 15808 |
|
| 15809 |
// Are we in doughnut mode which has a different data type |
| 15810 |
this.doughnutMode = false; |
| 15811 |
}, |
| 15812 |
|
| 15813 |
// These methods are ordered by lifecycle. Utilities then follow. |
| 15814 |
// Any function defined here is inherited by all legend types. |
| 15815 |
// Any function can be extended by the legend type |
| 15816 |
|
| 15817 |
beforeUpdate: noop, |
| 15818 |
update: function(maxWidth, maxHeight, margins) { |
| 15819 |
var me = this; |
| 15820 |
|
| 15821 |
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
| 15822 |
me.beforeUpdate(); |
| 15823 |
|
| 15824 |
// Absorb the master measurements |
| 15825 |
me.maxWidth = maxWidth; |
| 15826 |
me.maxHeight = maxHeight; |
| 15827 |
me.margins = margins; |
| 15828 |
|
| 15829 |
// Dimensions |
| 15830 |
me.beforeSetDimensions(); |
| 15831 |
me.setDimensions(); |
| 15832 |
me.afterSetDimensions(); |
| 15833 |
// Labels |
| 15834 |
me.beforeBuildLabels(); |
| 15835 |
me.buildLabels(); |
| 15836 |
me.afterBuildLabels(); |
| 15837 |
|
| 15838 |
// Fit |
| 15839 |
me.beforeFit(); |
| 15840 |
me.fit(); |
| 15841 |
me.afterFit(); |
| 15842 |
// |
| 15843 |
me.afterUpdate(); |
| 15844 |
|
| 15845 |
return me.minSize; |
| 15846 |
}, |
| 15847 |
afterUpdate: noop, |
| 15848 |
|
| 15849 |
// |
| 15850 |
|
| 15851 |
beforeSetDimensions: noop, |
| 15852 |
setDimensions: function() { |
| 15853 |
var me = this; |
| 15854 |
// Set the unconstrained dimension before label rotation |
| 15855 |
if (me.isHorizontal()) { |
| 15856 |
// Reset position before calculating rotation |
| 15857 |
me.width = me.maxWidth; |
| 15858 |
me.left = 0; |
| 15859 |
me.right = me.width; |
| 15860 |
} else { |
| 15861 |
me.height = me.maxHeight; |
| 15862 |
|
| 15863 |
// Reset position before calculating rotation |
| 15864 |
me.top = 0; |
| 15865 |
me.bottom = me.height; |
| 15866 |
} |
| 15867 |
|
| 15868 |
// Reset padding |
| 15869 |
me.paddingLeft = 0; |
| 15870 |
me.paddingTop = 0; |
| 15871 |
me.paddingRight = 0; |
| 15872 |
me.paddingBottom = 0; |
| 15873 |
|
| 15874 |
// Reset minSize |
| 15875 |
me.minSize = { |
| 15876 |
width: 0, |
| 15877 |
height: 0 |
| 15878 |
}; |
| 15879 |
}, |
| 15880 |
afterSetDimensions: noop, |
| 15881 |
|
| 15882 |
// |
| 15883 |
|
| 15884 |
beforeBuildLabels: noop, |
| 15885 |
buildLabels: function() { |
| 15886 |
var me = this; |
| 15887 |
var labelOpts = me.options.labels || {}; |
| 15888 |
var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; |
| 15889 |
|
| 15890 |
if (labelOpts.filter) { |
| 15891 |
legendItems = legendItems.filter(function(item) { |
| 15892 |
return labelOpts.filter(item, me.chart.data); |
| 15893 |
}); |
| 15894 |
} |
| 15895 |
|
| 15896 |
if (me.options.reverse) { |
| 15897 |
legendItems.reverse(); |
| 15898 |
} |
| 15899 |
|
| 15900 |
me.legendItems = legendItems; |
| 15901 |
}, |
| 15902 |
afterBuildLabels: noop, |
| 15903 |
|
| 15904 |
// |
| 15905 |
|
| 15906 |
beforeFit: noop, |
| 15907 |
fit: function() { |
| 15908 |
var me = this; |
| 15909 |
var opts = me.options; |
| 15910 |
var labelOpts = opts.labels; |
| 15911 |
var display = opts.display; |
| 15912 |
|
| 15913 |
var ctx = me.ctx; |
| 15914 |
|
| 15915 |
var globalDefault = defaults.global; |
| 15916 |
var valueOrDefault = helpers.valueOrDefault; |
| 15917 |
var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); |
| 15918 |
var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); |
| 15919 |
var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); |
| 15920 |
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
| 15921 |
|
| 15922 |
// Reset hit boxes |
| 15923 |
var hitboxes = me.legendHitBoxes = []; |
| 15924 |
|
| 15925 |
var minSize = me.minSize; |
| 15926 |
var isHorizontal = me.isHorizontal(); |
| 15927 |
|
| 15928 |
if (isHorizontal) { |
| 15929 |
minSize.width = me.maxWidth; // fill all the width |
| 15930 |
minSize.height = display ? 10 : 0; |
| 15931 |
} else { |
| 15932 |
minSize.width = display ? 10 : 0; |
| 15933 |
minSize.height = me.maxHeight; // fill all the height |
| 15934 |
} |
| 15935 |
|
| 15936 |
// Increase sizes here |
| 15937 |
if (display) { |
| 15938 |
ctx.font = labelFont; |
| 15939 |
|
| 15940 |
if (isHorizontal) { |
| 15941 |
// Labels |
| 15942 |
|
| 15943 |
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one |
| 15944 |
var lineWidths = me.lineWidths = [0]; |
| 15945 |
var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; |
| 15946 |
|
| 15947 |
ctx.textAlign = 'left'; |
| 15948 |
ctx.textBaseline = 'top'; |
| 15949 |
|
| 15950 |
helpers.each(me.legendItems, function(legendItem, i) { |
| 15951 |
var boxWidth = getBoxWidth(labelOpts, fontSize); |
| 15952 |
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; |
| 15953 |
|
| 15954 |
if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { |
| 15955 |
totalHeight += fontSize + (labelOpts.padding); |
| 15956 |
lineWidths[lineWidths.length] = me.left; |
| 15957 |
} |
| 15958 |
|
| 15959 |
// Store the hitbox width and height here. Final position will be updated in `draw` |
| 15960 |
hitboxes[i] = { |
| 15961 |
left: 0, |
| 15962 |
top: 0, |
| 15963 |
width: width, |
| 15964 |
height: fontSize |
| 15965 |
}; |
| 15966 |
|
| 15967 |
lineWidths[lineWidths.length - 1] += width + labelOpts.padding; |
| 15968 |
}); |
| 15969 |
|
| 15970 |
minSize.height += totalHeight; |
| 15971 |
|
| 15972 |
} else { |
| 15973 |
var vPadding = labelOpts.padding; |
| 15974 |
var columnWidths = me.columnWidths = []; |
| 15975 |
var totalWidth = labelOpts.padding; |
| 15976 |
var currentColWidth = 0; |
| 15977 |
var currentColHeight = 0; |
| 15978 |
var itemHeight = fontSize + vPadding; |
| 15979 |
|
| 15980 |
helpers.each(me.legendItems, function(legendItem, i) { |
| 15981 |
var boxWidth = getBoxWidth(labelOpts, fontSize); |
| 15982 |
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; |
| 15983 |
|
| 15984 |
// If too tall, go to new column |
| 15985 |
if (currentColHeight + itemHeight > minSize.height) { |
| 15986 |
totalWidth += currentColWidth + labelOpts.padding; |
| 15987 |
columnWidths.push(currentColWidth); // previous column width |
| 15988 |
|
| 15989 |
currentColWidth = 0; |
| 15990 |
currentColHeight = 0; |
| 15991 |
} |
| 15992 |
|
| 15993 |
// Get max width |
| 15994 |
currentColWidth = Math.max(currentColWidth, itemWidth); |
| 15995 |
currentColHeight += itemHeight; |
| 15996 |
|
| 15997 |
// Store the hitbox width and height here. Final position will be updated in `draw` |
| 15998 |
hitboxes[i] = { |
| 15999 |
left: 0, |
| 16000 |
top: 0, |
| 16001 |
width: itemWidth, |
| 16002 |
height: fontSize |
| 16003 |
}; |
| 16004 |
}); |
| 16005 |
|
| 16006 |
totalWidth += currentColWidth; |
| 16007 |
columnWidths.push(currentColWidth); |
| 16008 |
minSize.width += totalWidth; |
| 16009 |
} |
| 16010 |
} |
| 16011 |
|
| 16012 |
me.width = minSize.width; |
| 16013 |
me.height = minSize.height; |
| 16014 |
}, |
| 16015 |
afterFit: noop, |
| 16016 |
|
| 16017 |
// Shared Methods |
| 16018 |
isHorizontal: function() { |
| 16019 |
return this.options.position === 'top' || this.options.position === 'bottom'; |
| 16020 |
}, |
| 16021 |
|
| 16022 |
// Actually draw the legend on the canvas |
| 16023 |
draw: function() { |
| 16024 |
var me = this; |
| 16025 |
var opts = me.options; |
| 16026 |
var labelOpts = opts.labels; |
| 16027 |
var globalDefault = defaults.global; |
| 16028 |
var lineDefault = globalDefault.elements.line; |
| 16029 |
var legendWidth = me.width; |
| 16030 |
var lineWidths = me.lineWidths; |
| 16031 |
|
| 16032 |
if (opts.display) { |
| 16033 |
var ctx = me.ctx; |
| 16034 |
var valueOrDefault = helpers.valueOrDefault; |
| 16035 |
var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); |
| 16036 |
var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); |
| 16037 |
var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); |
| 16038 |
var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); |
| 16039 |
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
| 16040 |
var cursor; |
| 16041 |
|
| 16042 |
// Canvas setup |
| 16043 |
ctx.textAlign = 'left'; |
| 16044 |
ctx.textBaseline = 'middle'; |
| 16045 |
ctx.lineWidth = 0.5; |
| 16046 |
ctx.strokeStyle = fontColor; // for strikethrough effect |
| 16047 |
ctx.fillStyle = fontColor; // render in correct colour |
| 16048 |
ctx.font = labelFont; |
| 16049 |
|
| 16050 |
var boxWidth = getBoxWidth(labelOpts, fontSize); |
| 16051 |
var hitboxes = me.legendHitBoxes; |
| 16052 |
|
| 16053 |
// current position |
| 16054 |
var drawLegendBox = function(x, y, legendItem) { |
| 16055 |
if (isNaN(boxWidth) || boxWidth <= 0) { |
| 16056 |
return; |
| 16057 |
} |
| 16058 |
|
| 16059 |
// Set the ctx for the box |
| 16060 |
ctx.save(); |
| 16061 |
|
| 16062 |
ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); |
| 16063 |
ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); |
| 16064 |
ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); |
| 16065 |
ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); |
| 16066 |
ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth); |
| 16067 |
ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); |
| 16068 |
var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); |
| 16069 |
|
| 16070 |
if (ctx.setLineDash) { |
| 16071 |
// IE 9 and 10 do not support line dash |
| 16072 |
ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); |
| 16073 |
} |
| 16074 |
|
| 16075 |
if (opts.labels && opts.labels.usePointStyle) { |
| 16076 |
// Recalculate x and y for drawPoint() because its expecting |
| 16077 |
// x and y to be center of figure (instead of top left) |
| 16078 |
var radius = fontSize * Math.SQRT2 / 2; |
| 16079 |
var offSet = radius / Math.SQRT2; |
| 16080 |
var centerX = x + offSet; |
| 16081 |
var centerY = y + offSet; |
| 16082 |
|
| 16083 |
// Draw pointStyle as legend symbol |
| 16084 |
helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); |
| 16085 |
} else { |
| 16086 |
// Draw box as legend symbol |
| 16087 |
if (!isLineWidthZero) { |
| 16088 |
ctx.strokeRect(x, y, boxWidth, fontSize); |
| 16089 |
} |
| 16090 |
ctx.fillRect(x, y, boxWidth, fontSize); |
| 16091 |
} |
| 16092 |
|
| 16093 |
ctx.restore(); |
| 16094 |
}; |
| 16095 |
var fillText = function(x, y, legendItem, textWidth) { |
| 16096 |
var halfFontSize = fontSize / 2; |
| 16097 |
var xLeft = boxWidth + halfFontSize + x; |
| 16098 |
var yMiddle = y + halfFontSize; |
| 16099 |
|
| 16100 |
ctx.fillText(legendItem.text, xLeft, yMiddle); |
| 16101 |
|
| 16102 |
if (legendItem.hidden) { |
| 16103 |
// Strikethrough the text if hidden |
| 16104 |
ctx.beginPath(); |
| 16105 |
ctx.lineWidth = 2; |
| 16106 |
ctx.moveTo(xLeft, yMiddle); |
| 16107 |
ctx.lineTo(xLeft + textWidth, yMiddle); |
| 16108 |
ctx.stroke(); |
| 16109 |
} |
| 16110 |
}; |
| 16111 |
|
| 16112 |
// Horizontal |
| 16113 |
var isHorizontal = me.isHorizontal(); |
| 16114 |
if (isHorizontal) { |
| 16115 |
cursor = { |
| 16116 |
x: me.left + ((legendWidth - lineWidths[0]) / 2), |
| 16117 |
y: me.top + labelOpts.padding, |
| 16118 |
line: 0 |
| 16119 |
}; |
| 16120 |
} else { |
| 16121 |
cursor = { |
| 16122 |
x: me.left + labelOpts.padding, |
| 16123 |
y: me.top + labelOpts.padding, |
| 16124 |
line: 0 |
| 16125 |
}; |
| 16126 |
} |
| 16127 |
|
| 16128 |
var itemHeight = fontSize + labelOpts.padding; |
| 16129 |
helpers.each(me.legendItems, function(legendItem, i) { |
| 16130 |
var textWidth = ctx.measureText(legendItem.text).width; |
| 16131 |
var width = boxWidth + (fontSize / 2) + textWidth; |
| 16132 |
var x = cursor.x; |
| 16133 |
var y = cursor.y; |
| 16134 |
|
| 16135 |
if (isHorizontal) { |
| 16136 |
if (x + width >= legendWidth) { |
| 16137 |
y = cursor.y += itemHeight; |
| 16138 |
cursor.line++; |
| 16139 |
x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); |
| 16140 |
} |
| 16141 |
} else if (y + itemHeight > me.bottom) { |
| 16142 |
x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; |
| 16143 |
y = cursor.y = me.top + labelOpts.padding; |
| 16144 |
cursor.line++; |
| 16145 |
} |
| 16146 |
|
| 16147 |
drawLegendBox(x, y, legendItem); |
| 16148 |
|
| 16149 |
hitboxes[i].left = x; |
| 16150 |
hitboxes[i].top = y; |
| 16151 |
|
| 16152 |
// Fill the actual label |
| 16153 |
fillText(x, y, legendItem, textWidth); |
| 16154 |
|
| 16155 |
if (isHorizontal) { |
| 16156 |
cursor.x += width + (labelOpts.padding); |
| 16157 |
} else { |
| 16158 |
cursor.y += itemHeight; |
| 16159 |
} |
| 16160 |
|
| 16161 |
}); |
| 16162 |
} |
| 16163 |
}, |
| 16164 |
|
| 16165 |
/** |
| 16166 |
* Handle an event |
| 16167 |
* @private |
| 16168 |
* @param {IEvent} event - The event to handle |
| 16169 |
* @return {Boolean} true if a change occured |
| 16170 |
*/ |
| 16171 |
handleEvent: function(e) { |
| 16172 |
var me = this; |
| 16173 |
var opts = me.options; |
| 16174 |
var type = e.type === 'mouseup' ? 'click' : e.type; |
| 16175 |
var changed = false; |
| 16176 |
|
| 16177 |
if (type === 'mousemove') { |
| 16178 |
if (!opts.onHover) { |
| 16179 |
return; |
| 16180 |
} |
| 16181 |
} else if (type === 'click') { |
| 16182 |
if (!opts.onClick) { |
| 16183 |
return; |
| 16184 |
} |
| 16185 |
} else { |
| 16186 |
return; |
| 16187 |
} |
| 16188 |
|
| 16189 |
// Chart event already has relative position in it |
| 16190 |
var x = e.x; |
| 16191 |
var y = e.y; |
| 16192 |
|
| 16193 |
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { |
| 16194 |
// See if we are touching one of the dataset boxes |
| 16195 |
var lh = me.legendHitBoxes; |
| 16196 |
for (var i = 0; i < lh.length; ++i) { |
| 16197 |
var hitBox = lh[i]; |
| 16198 |
|
| 16199 |
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { |
| 16200 |
// Touching an element |
| 16201 |
if (type === 'click') { |
| 16202 |
// use e.native for backwards compatibility |
| 16203 |
opts.onClick.call(me, e.native, me.legendItems[i]); |
| 16204 |
changed = true; |
| 16205 |
break; |
| 16206 |
} else if (type === 'mousemove') { |
| 16207 |
// use e.native for backwards compatibility |
| 16208 |
opts.onHover.call(me, e.native, me.legendItems[i]); |
| 16209 |
changed = true; |
| 16210 |
break; |
| 16211 |
} |
| 16212 |
} |
| 16213 |
} |
| 16214 |
} |
| 16215 |
|
| 16216 |
return changed; |
| 16217 |
} |
| 16218 |
}); |
| 16219 |
|
| 16220 |
function createNewLegendAndAttach(chart, legendOpts) { |
| 16221 |
var legend = new Chart.Legend({ |
| 16222 |
ctx: chart.ctx, |
| 16223 |
options: legendOpts, |
| 16224 |
chart: chart |
| 16225 |
}); |
| 16226 |
|
| 16227 |
layout.configure(chart, legend, legendOpts); |
| 16228 |
layout.addBox(chart, legend); |
| 16229 |
chart.legend = legend; |
| 16230 |
} |
| 16231 |
|
| 16232 |
return { |
| 16233 |
id: 'legend', |
| 16234 |
|
| 16235 |
beforeInit: function(chart) { |
| 16236 |
var legendOpts = chart.options.legend; |
| 16237 |
|
| 16238 |
if (legendOpts) { |
| 16239 |
createNewLegendAndAttach(chart, legendOpts); |
| 16240 |
} |
| 16241 |
}, |
| 16242 |
|
| 16243 |
beforeUpdate: function(chart) { |
| 16244 |
var legendOpts = chart.options.legend; |
| 16245 |
var legend = chart.legend; |
| 16246 |
|
| 16247 |
if (legendOpts) { |
| 16248 |
helpers.mergeIf(legendOpts, defaults.global.legend); |
| 16249 |
|
| 16250 |
if (legend) { |
| 16251 |
layout.configure(chart, legend, legendOpts); |
| 16252 |
legend.options = legendOpts; |
| 16253 |
} else { |
| 16254 |
createNewLegendAndAttach(chart, legendOpts); |
| 16255 |
} |
| 16256 |
} else if (legend) { |
| 16257 |
layout.removeBox(chart, legend); |
| 16258 |
delete chart.legend; |
| 16259 |
} |
| 16260 |
}, |
| 16261 |
|
| 16262 |
afterEvent: function(chart, e) { |
| 16263 |
var legend = chart.legend; |
| 16264 |
if (legend) { |
| 16265 |
legend.handleEvent(e); |
| 16266 |
} |
| 16267 |
} |
| 16268 |
}; |
| 16269 |
}; |
| 16270 |
|
| 16271 |
},{"25":25,"26":26,"45":45}],51:[function(require,module,exports){ |
| 16272 |
'use strict'; |
| 16273 |
|
| 16274 |
var defaults = require(25); |
| 16275 |
var Element = require(26); |
| 16276 |
var helpers = require(45); |
| 16277 |
|
| 16278 |
defaults._set('global', { |
| 16279 |
title: { |
| 16280 |
display: false, |
| 16281 |
fontStyle: 'bold', |
| 16282 |
fullWidth: true, |
| 16283 |
lineHeight: 1.2, |
| 16284 |
padding: 10, |
| 16285 |
position: 'top', |
| 16286 |
text: '', |
| 16287 |
weight: 2000 // by default greater than legend (1000) to be above |
| 16288 |
} |
| 16289 |
}); |
| 16290 |
|
| 16291 |
module.exports = function(Chart) { |
| 16292 |
|
| 16293 |
var layout = Chart.layoutService; |
| 16294 |
var noop = helpers.noop; |
| 16295 |
|
| 16296 |
Chart.Title = Element.extend({ |
| 16297 |
initialize: function(config) { |
| 16298 |
var me = this; |
| 16299 |
helpers.extend(me, config); |
| 16300 |
|
| 16301 |
// Contains hit boxes for each dataset (in dataset order) |
| 16302 |
me.legendHitBoxes = []; |
| 16303 |
}, |
| 16304 |
|
| 16305 |
// These methods are ordered by lifecycle. Utilities then follow. |
| 16306 |
|
| 16307 |
beforeUpdate: noop, |
| 16308 |
update: function(maxWidth, maxHeight, margins) { |
| 16309 |
var me = this; |
| 16310 |
|
| 16311 |
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
| 16312 |
me.beforeUpdate(); |
| 16313 |
|
| 16314 |
// Absorb the master measurements |
| 16315 |
me.maxWidth = maxWidth; |
| 16316 |
me.maxHeight = maxHeight; |
| 16317 |
me.margins = margins; |
| 16318 |
|
| 16319 |
// Dimensions |
| 16320 |
me.beforeSetDimensions(); |
| 16321 |
me.setDimensions(); |
| 16322 |
me.afterSetDimensions(); |
| 16323 |
// Labels |
| 16324 |
me.beforeBuildLabels(); |
| 16325 |
me.buildLabels(); |
| 16326 |
me.afterBuildLabels(); |
| 16327 |
|
| 16328 |
// Fit |
| 16329 |
me.beforeFit(); |
| 16330 |
me.fit(); |
| 16331 |
me.afterFit(); |
| 16332 |
// |
| 16333 |
me.afterUpdate(); |
| 16334 |
|
| 16335 |
return me.minSize; |
| 16336 |
|
| 16337 |
}, |
| 16338 |
afterUpdate: noop, |
| 16339 |
|
| 16340 |
// |
| 16341 |
|
| 16342 |
beforeSetDimensions: noop, |
| 16343 |
setDimensions: function() { |
| 16344 |
var me = this; |
| 16345 |
// Set the unconstrained dimension before label rotation |
| 16346 |
if (me.isHorizontal()) { |
| 16347 |
// Reset position before calculating rotation |
| 16348 |
me.width = me.maxWidth; |
| 16349 |
me.left = 0; |
| 16350 |
me.right = me.width; |
| 16351 |
} else { |
| 16352 |
me.height = me.maxHeight; |
| 16353 |
|
| 16354 |
// Reset position before calculating rotation |
| 16355 |
me.top = 0; |
| 16356 |
me.bottom = me.height; |
| 16357 |
} |
| 16358 |
|
| 16359 |
// Reset padding |
| 16360 |
me.paddingLeft = 0; |
| 16361 |
me.paddingTop = 0; |
| 16362 |
me.paddingRight = 0; |
| 16363 |
me.paddingBottom = 0; |
| 16364 |
|
| 16365 |
// Reset minSize |
| 16366 |
me.minSize = { |
| 16367 |
width: 0, |
| 16368 |
height: 0 |
| 16369 |
}; |
| 16370 |
}, |
| 16371 |
afterSetDimensions: noop, |
| 16372 |
|
| 16373 |
// |
| 16374 |
|
| 16375 |
beforeBuildLabels: noop, |
| 16376 |
buildLabels: noop, |
| 16377 |
afterBuildLabels: noop, |
| 16378 |
|
| 16379 |
// |
| 16380 |
|
| 16381 |
beforeFit: noop, |
| 16382 |
fit: function() { |
| 16383 |
var me = this; |
| 16384 |
var valueOrDefault = helpers.valueOrDefault; |
| 16385 |
var opts = me.options; |
| 16386 |
var display = opts.display; |
| 16387 |
var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); |
| 16388 |
var minSize = me.minSize; |
| 16389 |
var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; |
| 16390 |
var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); |
| 16391 |
var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; |
| 16392 |
|
| 16393 |
if (me.isHorizontal()) { |
| 16394 |
minSize.width = me.maxWidth; // fill all the width |
| 16395 |
minSize.height = textSize; |
| 16396 |
} else { |
| 16397 |
minSize.width = textSize; |
| 16398 |
minSize.height = me.maxHeight; // fill all the height |
| 16399 |
} |
| 16400 |
|
| 16401 |
me.width = minSize.width; |
| 16402 |
me.height = minSize.height; |
| 16403 |
|
| 16404 |
}, |
| 16405 |
afterFit: noop, |
| 16406 |
|
| 16407 |
// Shared Methods |
| 16408 |
isHorizontal: function() { |
| 16409 |
var pos = this.options.position; |
| 16410 |
return pos === 'top' || pos === 'bottom'; |
| 16411 |
}, |
| 16412 |
|
| 16413 |
// Actually draw the title block on the canvas |
| 16414 |
draw: function() { |
| 16415 |
var me = this; |
| 16416 |
var ctx = me.ctx; |
| 16417 |
var valueOrDefault = helpers.valueOrDefault; |
| 16418 |
var opts = me.options; |
| 16419 |
var globalDefaults = defaults.global; |
| 16420 |
|
| 16421 |
if (opts.display) { |
| 16422 |
var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); |
| 16423 |
var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); |
| 16424 |
var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); |
| 16425 |
var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
| 16426 |
var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); |
| 16427 |
var offset = lineHeight / 2 + opts.padding; |
| 16428 |
var rotation = 0; |
| 16429 |
var top = me.top; |
| 16430 |
var left = me.left; |
| 16431 |
var bottom = me.bottom; |
| 16432 |
var right = me.right; |
| 16433 |
var maxWidth, titleX, titleY; |
| 16434 |
|
| 16435 |
ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour |
| 16436 |
ctx.font = titleFont; |
| 16437 |
|
| 16438 |
// Horizontal |
| 16439 |
if (me.isHorizontal()) { |
| 16440 |
titleX = left + ((right - left) / 2); // midpoint of the width |
| 16441 |
titleY = top + offset; |
| 16442 |
maxWidth = right - left; |
| 16443 |
} else { |
| 16444 |
titleX = opts.position === 'left' ? left + offset : right - offset; |
| 16445 |
titleY = top + ((bottom - top) / 2); |
| 16446 |
maxWidth = bottom - top; |
| 16447 |
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); |
| 16448 |
} |
| 16449 |
|
| 16450 |
ctx.save(); |
| 16451 |
ctx.translate(titleX, titleY); |
| 16452 |
ctx.rotate(rotation); |
| 16453 |
ctx.textAlign = 'center'; |
| 16454 |
ctx.textBaseline = 'middle'; |
| 16455 |
|
| 16456 |
var text = opts.text; |
| 16457 |
if (helpers.isArray(text)) { |
| 16458 |
var y = 0; |
| 16459 |
for (var i = 0; i < text.length; ++i) { |
| 16460 |
ctx.fillText(text[i], 0, y, maxWidth); |
| 16461 |
y += lineHeight; |
| 16462 |
} |
| 16463 |
} else { |
| 16464 |
ctx.fillText(text, 0, 0, maxWidth); |
| 16465 |
} |
| 16466 |
|
| 16467 |
ctx.restore(); |
| 16468 |
} |
| 16469 |
} |
| 16470 |
}); |
| 16471 |
|
| 16472 |
function createNewTitleBlockAndAttach(chart, titleOpts) { |
| 16473 |
var title = new Chart.Title({ |
| 16474 |
ctx: chart.ctx, |
| 16475 |
options: titleOpts, |
| 16476 |
chart: chart |
| 16477 |
}); |
| 16478 |
|
| 16479 |
layout.configure(chart, title, titleOpts); |
| 16480 |
layout.addBox(chart, title); |
| 16481 |
chart.titleBlock = title; |
| 16482 |
} |
| 16483 |
|
| 16484 |
return { |
| 16485 |
id: 'title', |
| 16486 |
|
| 16487 |
beforeInit: function(chart) { |
| 16488 |
var titleOpts = chart.options.title; |
| 16489 |
|
| 16490 |
if (titleOpts) { |
| 16491 |
createNewTitleBlockAndAttach(chart, titleOpts); |
| 16492 |
} |
| 16493 |
}, |
| 16494 |
|
| 16495 |
beforeUpdate: function(chart) { |
| 16496 |
var titleOpts = chart.options.title; |
| 16497 |
var titleBlock = chart.titleBlock; |
| 16498 |
|
| 16499 |
if (titleOpts) { |
| 16500 |
helpers.mergeIf(titleOpts, defaults.global.title); |
| 16501 |
|
| 16502 |
if (titleBlock) { |
| 16503 |
layout.configure(chart, titleBlock, titleOpts); |
| 16504 |
titleBlock.options = titleOpts; |
| 16505 |
} else { |
| 16506 |
createNewTitleBlockAndAttach(chart, titleOpts); |
| 16507 |
} |
| 16508 |
} else if (titleBlock) { |
| 16509 |
Chart.layoutService.removeBox(chart, titleBlock); |
| 16510 |
delete chart.titleBlock; |
| 16511 |
} |
| 16512 |
} |
| 16513 |
}; |
| 16514 |
}; |
| 16515 |
|
| 16516 |
},{"25":25,"26":26,"45":45}],52:[function(require,module,exports){ |
| 16517 |
'use strict'; |
| 16518 |
|
| 16519 |
module.exports = function(Chart) { |
| 16520 |
|
| 16521 |
// Default config for a category scale |
| 16522 |
var defaultConfig = { |
| 16523 |
position: 'bottom' |
| 16524 |
}; |
| 16525 |
|
| 16526 |
var DatasetScale = Chart.Scale.extend({ |
| 16527 |
/** |
| 16528 |
* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those |
| 16529 |
* else fall back to data.labels |
| 16530 |
* @private |
| 16531 |
*/ |
| 16532 |
getLabels: function() { |
| 16533 |
var data = this.chart.data; |
| 16534 |
return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; |
| 16535 |
}, |
| 16536 |
|
| 16537 |
determineDataLimits: function() { |
| 16538 |
var me = this; |
| 16539 |
var labels = me.getLabels(); |
| 16540 |
me.minIndex = 0; |
| 16541 |
me.maxIndex = labels.length - 1; |
| 16542 |
var findIndex; |
| 16543 |
|
| 16544 |
if (me.options.ticks.min !== undefined) { |
| 16545 |
// user specified min value |
| 16546 |
findIndex = labels.indexOf(me.options.ticks.min); |
| 16547 |
me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; |
| 16548 |
} |
| 16549 |
|
| 16550 |
if (me.options.ticks.max !== undefined) { |
| 16551 |
// user specified max value |
| 16552 |
findIndex = labels.indexOf(me.options.ticks.max); |
| 16553 |
me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; |
| 16554 |
} |
| 16555 |
|
| 16556 |
me.min = labels[me.minIndex]; |
| 16557 |
me.max = labels[me.maxIndex]; |
| 16558 |
}, |
| 16559 |
|
| 16560 |
buildTicks: function() { |
| 16561 |
var me = this; |
| 16562 |
var labels = me.getLabels(); |
| 16563 |
// If we are viewing some subset of labels, slice the original array |
| 16564 |
me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); |
| 16565 |
}, |
| 16566 |
|
| 16567 |
getLabelForIndex: function(index, datasetIndex) { |
| 16568 |
var me = this; |
| 16569 |
var data = me.chart.data; |
| 16570 |
var isHorizontal = me.isHorizontal(); |
| 16571 |
|
| 16572 |
if (data.yLabels && !isHorizontal) { |
| 16573 |
return me.getRightValue(data.datasets[datasetIndex].data[index]); |
| 16574 |
} |
| 16575 |
return me.ticks[index - me.minIndex]; |
| 16576 |
}, |
| 16577 |
|
| 16578 |
// Used to get data value locations. Value can either be an index or a numerical value |
| 16579 |
getPixelForValue: function(value, index) { |
| 16580 |
var me = this; |
| 16581 |
var offset = me.options.offset; |
| 16582 |
// 1 is added because we need the length but we have the indexes |
| 16583 |
var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); |
| 16584 |
|
| 16585 |
// If value is a data object, then index is the index in the data array, |
| 16586 |
// not the index of the scale. We need to change that. |
| 16587 |
var valueCategory; |
| 16588 |
if (value !== undefined && value !== null) { |
| 16589 |
valueCategory = me.isHorizontal() ? value.x : value.y; |
| 16590 |
} |
| 16591 |
if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { |
| 16592 |
var labels = me.getLabels(); |
| 16593 |
value = valueCategory || value; |
| 16594 |
var idx = labels.indexOf(value); |
| 16595 |
index = idx !== -1 ? idx : index; |
| 16596 |
} |
| 16597 |
|
| 16598 |
if (me.isHorizontal()) { |
| 16599 |
var valueWidth = me.width / offsetAmt; |
| 16600 |
var widthOffset = (valueWidth * (index - me.minIndex)); |
| 16601 |
|
| 16602 |
if (offset) { |
| 16603 |
widthOffset += (valueWidth / 2); |
| 16604 |
} |
| 16605 |
|
| 16606 |
return me.left + Math.round(widthOffset); |
| 16607 |
} |
| 16608 |
var valueHeight = me.height / offsetAmt; |
| 16609 |
var heightOffset = (valueHeight * (index - me.minIndex)); |
| 16610 |
|
| 16611 |
if (offset) { |
| 16612 |
heightOffset += (valueHeight / 2); |
| 16613 |
} |
| 16614 |
|
| 16615 |
return me.top + Math.round(heightOffset); |
| 16616 |
}, |
| 16617 |
getPixelForTick: function(index) { |
| 16618 |
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); |
| 16619 |
}, |
| 16620 |
getValueForPixel: function(pixel) { |
| 16621 |
var me = this; |
| 16622 |
var offset = me.options.offset; |
| 16623 |
var value; |
| 16624 |
var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); |
| 16625 |
var horz = me.isHorizontal(); |
| 16626 |
var valueDimension = (horz ? me.width : me.height) / offsetAmt; |
| 16627 |
|
| 16628 |
pixel -= horz ? me.left : me.top; |
| 16629 |
|
| 16630 |
if (offset) { |
| 16631 |
pixel -= (valueDimension / 2); |
| 16632 |
} |
| 16633 |
|
| 16634 |
if (pixel <= 0) { |
| 16635 |
value = 0; |
| 16636 |
} else { |
| 16637 |
value = Math.round(pixel / valueDimension); |
| 16638 |
} |
| 16639 |
|
| 16640 |
return value + me.minIndex; |
| 16641 |
}, |
| 16642 |
getBasePixel: function() { |
| 16643 |
return this.bottom; |
| 16644 |
} |
| 16645 |
}); |
| 16646 |
|
| 16647 |
Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); |
| 16648 |
|
| 16649 |
}; |
| 16650 |
|
| 16651 |
},{}],53:[function(require,module,exports){ |
| 16652 |
'use strict'; |
| 16653 |
|
| 16654 |
var defaults = require(25); |
| 16655 |
var helpers = require(45); |
| 16656 |
var Ticks = require(34); |
| 16657 |
|
| 16658 |
module.exports = function(Chart) { |
| 16659 |
|
| 16660 |
var defaultConfig = { |
| 16661 |
position: 'left', |
| 16662 |
ticks: { |
| 16663 |
callback: Ticks.formatters.linear |
| 16664 |
} |
| 16665 |
}; |
| 16666 |
|
| 16667 |
var LinearScale = Chart.LinearScaleBase.extend({ |
| 16668 |
|
| 16669 |
determineDataLimits: function() { |
| 16670 |
var me = this; |
| 16671 |
var opts = me.options; |
| 16672 |
var chart = me.chart; |
| 16673 |
var data = chart.data; |
| 16674 |
var datasets = data.datasets; |
| 16675 |
var isHorizontal = me.isHorizontal(); |
| 16676 |
var DEFAULT_MIN = 0; |
| 16677 |
var DEFAULT_MAX = 1; |
| 16678 |
|
| 16679 |
function IDMatches(meta) { |
| 16680 |
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; |
| 16681 |
} |
| 16682 |
|
| 16683 |
// First Calculate the range |
| 16684 |
me.min = null; |
| 16685 |
me.max = null; |
| 16686 |
|
| 16687 |
var hasStacks = opts.stacked; |
| 16688 |
if (hasStacks === undefined) { |
| 16689 |
helpers.each(datasets, function(dataset, datasetIndex) { |
| 16690 |
if (hasStacks) { |
| 16691 |
return; |
| 16692 |
} |
| 16693 |
|
| 16694 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 16695 |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && |
| 16696 |
meta.stack !== undefined) { |
| 16697 |
hasStacks = true; |
| 16698 |
} |
| 16699 |
}); |
| 16700 |
} |
| 16701 |
|
| 16702 |
if (opts.stacked || hasStacks) { |
| 16703 |
var valuesPerStack = {}; |
| 16704 |
|
| 16705 |
helpers.each(datasets, function(dataset, datasetIndex) { |
| 16706 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 16707 |
var key = [ |
| 16708 |
meta.type, |
| 16709 |
// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined |
| 16710 |
((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), |
| 16711 |
meta.stack |
| 16712 |
].join('.'); |
| 16713 |
|
| 16714 |
if (valuesPerStack[key] === undefined) { |
| 16715 |
valuesPerStack[key] = { |
| 16716 |
positiveValues: [], |
| 16717 |
negativeValues: [] |
| 16718 |
}; |
| 16719 |
} |
| 16720 |
|
| 16721 |
// Store these per type |
| 16722 |
var positiveValues = valuesPerStack[key].positiveValues; |
| 16723 |
var negativeValues = valuesPerStack[key].negativeValues; |
| 16724 |
|
| 16725 |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 16726 |
helpers.each(dataset.data, function(rawValue, index) { |
| 16727 |
var value = +me.getRightValue(rawValue); |
| 16728 |
if (isNaN(value) || meta.data[index].hidden) { |
| 16729 |
return; |
| 16730 |
} |
| 16731 |
|
| 16732 |
positiveValues[index] = positiveValues[index] || 0; |
| 16733 |
negativeValues[index] = negativeValues[index] || 0; |
| 16734 |
|
| 16735 |
if (opts.relativePoints) { |
| 16736 |
positiveValues[index] = 100; |
| 16737 |
} else if (value < 0) { |
| 16738 |
negativeValues[index] += value; |
| 16739 |
} else { |
| 16740 |
positiveValues[index] += value; |
| 16741 |
} |
| 16742 |
}); |
| 16743 |
} |
| 16744 |
}); |
| 16745 |
|
| 16746 |
helpers.each(valuesPerStack, function(valuesForType) { |
| 16747 |
var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); |
| 16748 |
var minVal = helpers.min(values); |
| 16749 |
var maxVal = helpers.max(values); |
| 16750 |
me.min = me.min === null ? minVal : Math.min(me.min, minVal); |
| 16751 |
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); |
| 16752 |
}); |
| 16753 |
|
| 16754 |
} else { |
| 16755 |
helpers.each(datasets, function(dataset, datasetIndex) { |
| 16756 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 16757 |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 16758 |
helpers.each(dataset.data, function(rawValue, index) { |
| 16759 |
var value = +me.getRightValue(rawValue); |
| 16760 |
if (isNaN(value) || meta.data[index].hidden) { |
| 16761 |
return; |
| 16762 |
} |
| 16763 |
|
| 16764 |
if (me.min === null) { |
| 16765 |
me.min = value; |
| 16766 |
} else if (value < me.min) { |
| 16767 |
me.min = value; |
| 16768 |
} |
| 16769 |
|
| 16770 |
if (me.max === null) { |
| 16771 |
me.max = value; |
| 16772 |
} else if (value > me.max) { |
| 16773 |
me.max = value; |
| 16774 |
} |
| 16775 |
}); |
| 16776 |
} |
| 16777 |
}); |
| 16778 |
} |
| 16779 |
|
| 16780 |
me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; |
| 16781 |
me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; |
| 16782 |
|
| 16783 |
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero |
| 16784 |
this.handleTickRangeOptions(); |
| 16785 |
}, |
| 16786 |
getTickLimit: function() { |
| 16787 |
var maxTicks; |
| 16788 |
var me = this; |
| 16789 |
var tickOpts = me.options.ticks; |
| 16790 |
|
| 16791 |
if (me.isHorizontal()) { |
| 16792 |
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); |
| 16793 |
} else { |
| 16794 |
// The factor of 2 used to scale the font size has been experimentally determined. |
| 16795 |
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); |
| 16796 |
maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); |
| 16797 |
} |
| 16798 |
|
| 16799 |
return maxTicks; |
| 16800 |
}, |
| 16801 |
// Called after the ticks are built. We need |
| 16802 |
handleDirectionalChanges: function() { |
| 16803 |
if (!this.isHorizontal()) { |
| 16804 |
// We are in a vertical orientation. The top value is the highest. So reverse the array |
| 16805 |
this.ticks.reverse(); |
| 16806 |
} |
| 16807 |
}, |
| 16808 |
getLabelForIndex: function(index, datasetIndex) { |
| 16809 |
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
| 16810 |
}, |
| 16811 |
// Utils |
| 16812 |
getPixelForValue: function(value) { |
| 16813 |
// This must be called after fit has been run so that |
| 16814 |
// this.left, this.top, this.right, and this.bottom have been defined |
| 16815 |
var me = this; |
| 16816 |
var start = me.start; |
| 16817 |
|
| 16818 |
var rightValue = +me.getRightValue(value); |
| 16819 |
var pixel; |
| 16820 |
var range = me.end - start; |
| 16821 |
|
| 16822 |
if (me.isHorizontal()) { |
| 16823 |
pixel = me.left + (me.width / range * (rightValue - start)); |
| 16824 |
return Math.round(pixel); |
| 16825 |
} |
| 16826 |
|
| 16827 |
pixel = me.bottom - (me.height / range * (rightValue - start)); |
| 16828 |
return Math.round(pixel); |
| 16829 |
}, |
| 16830 |
getValueForPixel: function(pixel) { |
| 16831 |
var me = this; |
| 16832 |
var isHorizontal = me.isHorizontal(); |
| 16833 |
var innerDimension = isHorizontal ? me.width : me.height; |
| 16834 |
var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; |
| 16835 |
return me.start + ((me.end - me.start) * offset); |
| 16836 |
}, |
| 16837 |
getPixelForTick: function(index) { |
| 16838 |
return this.getPixelForValue(this.ticksAsNumbers[index]); |
| 16839 |
} |
| 16840 |
}); |
| 16841 |
Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); |
| 16842 |
|
| 16843 |
}; |
| 16844 |
|
| 16845 |
},{"25":25,"34":34,"45":45}],54:[function(require,module,exports){ |
| 16846 |
'use strict'; |
| 16847 |
|
| 16848 |
var helpers = require(45); |
| 16849 |
var Ticks = require(34); |
| 16850 |
|
| 16851 |
module.exports = function(Chart) { |
| 16852 |
|
| 16853 |
var noop = helpers.noop; |
| 16854 |
|
| 16855 |
Chart.LinearScaleBase = Chart.Scale.extend({ |
| 16856 |
getRightValue: function(value) { |
| 16857 |
if (typeof value === 'string') { |
| 16858 |
return +value; |
| 16859 |
} |
| 16860 |
return Chart.Scale.prototype.getRightValue.call(this, value); |
| 16861 |
}, |
| 16862 |
|
| 16863 |
handleTickRangeOptions: function() { |
| 16864 |
var me = this; |
| 16865 |
var opts = me.options; |
| 16866 |
var tickOpts = opts.ticks; |
| 16867 |
|
| 16868 |
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart, |
| 16869 |
// do nothing since that would make the chart weird. If the user really wants a weird chart |
| 16870 |
// axis, they can manually override it |
| 16871 |
if (tickOpts.beginAtZero) { |
| 16872 |
var minSign = helpers.sign(me.min); |
| 16873 |
var maxSign = helpers.sign(me.max); |
| 16874 |
|
| 16875 |
if (minSign < 0 && maxSign < 0) { |
| 16876 |
// move the top up to 0 |
| 16877 |
me.max = 0; |
| 16878 |
} else if (minSign > 0 && maxSign > 0) { |
| 16879 |
// move the bottom down to 0 |
| 16880 |
me.min = 0; |
| 16881 |
} |
| 16882 |
} |
| 16883 |
|
| 16884 |
var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; |
| 16885 |
var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; |
| 16886 |
|
| 16887 |
if (tickOpts.min !== undefined) { |
| 16888 |
me.min = tickOpts.min; |
| 16889 |
} else if (tickOpts.suggestedMin !== undefined) { |
| 16890 |
if (me.min === null) { |
| 16891 |
me.min = tickOpts.suggestedMin; |
| 16892 |
} else { |
| 16893 |
me.min = Math.min(me.min, tickOpts.suggestedMin); |
| 16894 |
} |
| 16895 |
} |
| 16896 |
|
| 16897 |
if (tickOpts.max !== undefined) { |
| 16898 |
me.max = tickOpts.max; |
| 16899 |
} else if (tickOpts.suggestedMax !== undefined) { |
| 16900 |
if (me.max === null) { |
| 16901 |
me.max = tickOpts.suggestedMax; |
| 16902 |
} else { |
| 16903 |
me.max = Math.max(me.max, tickOpts.suggestedMax); |
| 16904 |
} |
| 16905 |
} |
| 16906 |
|
| 16907 |
if (setMin !== setMax) { |
| 16908 |
// We set the min or the max but not both. |
| 16909 |
// So ensure that our range is good |
| 16910 |
// Inverted or 0 length range can happen when |
| 16911 |
// ticks.min is set, and no datasets are visible |
| 16912 |
if (me.min >= me.max) { |
| 16913 |
if (setMin) { |
| 16914 |
me.max = me.min + 1; |
| 16915 |
} else { |
| 16916 |
me.min = me.max - 1; |
| 16917 |
} |
| 16918 |
} |
| 16919 |
} |
| 16920 |
|
| 16921 |
if (me.min === me.max) { |
| 16922 |
me.max++; |
| 16923 |
|
| 16924 |
if (!tickOpts.beginAtZero) { |
| 16925 |
me.min--; |
| 16926 |
} |
| 16927 |
} |
| 16928 |
}, |
| 16929 |
getTickLimit: noop, |
| 16930 |
handleDirectionalChanges: noop, |
| 16931 |
|
| 16932 |
buildTicks: function() { |
| 16933 |
var me = this; |
| 16934 |
var opts = me.options; |
| 16935 |
var tickOpts = opts.ticks; |
| 16936 |
|
| 16937 |
// Figure out what the max number of ticks we can support it is based on the size of |
| 16938 |
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50 |
| 16939 |
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on |
| 16940 |
// the graph. Make sure we always have at least 2 ticks |
| 16941 |
var maxTicks = me.getTickLimit(); |
| 16942 |
maxTicks = Math.max(2, maxTicks); |
| 16943 |
|
| 16944 |
var numericGeneratorOptions = { |
| 16945 |
maxTicks: maxTicks, |
| 16946 |
min: tickOpts.min, |
| 16947 |
max: tickOpts.max, |
| 16948 |
stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) |
| 16949 |
}; |
| 16950 |
var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me); |
| 16951 |
|
| 16952 |
me.handleDirectionalChanges(); |
| 16953 |
|
| 16954 |
// At this point, we need to update our max and min given the tick values since we have expanded the |
| 16955 |
// range of the scale |
| 16956 |
me.max = helpers.max(ticks); |
| 16957 |
me.min = helpers.min(ticks); |
| 16958 |
|
| 16959 |
if (tickOpts.reverse) { |
| 16960 |
ticks.reverse(); |
| 16961 |
|
| 16962 |
me.start = me.max; |
| 16963 |
me.end = me.min; |
| 16964 |
} else { |
| 16965 |
me.start = me.min; |
| 16966 |
me.end = me.max; |
| 16967 |
} |
| 16968 |
}, |
| 16969 |
convertTicksToLabels: function() { |
| 16970 |
var me = this; |
| 16971 |
me.ticksAsNumbers = me.ticks.slice(); |
| 16972 |
me.zeroLineIndex = me.ticks.indexOf(0); |
| 16973 |
|
| 16974 |
Chart.Scale.prototype.convertTicksToLabels.call(me); |
| 16975 |
} |
| 16976 |
}); |
| 16977 |
}; |
| 16978 |
|
| 16979 |
},{"34":34,"45":45}],55:[function(require,module,exports){ |
| 16980 |
'use strict'; |
| 16981 |
|
| 16982 |
var helpers = require(45); |
| 16983 |
var Ticks = require(34); |
| 16984 |
|
| 16985 |
module.exports = function(Chart) { |
| 16986 |
|
| 16987 |
var defaultConfig = { |
| 16988 |
position: 'left', |
| 16989 |
|
| 16990 |
// label settings |
| 16991 |
ticks: { |
| 16992 |
callback: Ticks.formatters.logarithmic |
| 16993 |
} |
| 16994 |
}; |
| 16995 |
|
| 16996 |
var LogarithmicScale = Chart.Scale.extend({ |
| 16997 |
determineDataLimits: function() { |
| 16998 |
var me = this; |
| 16999 |
var opts = me.options; |
| 17000 |
var tickOpts = opts.ticks; |
| 17001 |
var chart = me.chart; |
| 17002 |
var data = chart.data; |
| 17003 |
var datasets = data.datasets; |
| 17004 |
var valueOrDefault = helpers.valueOrDefault; |
| 17005 |
var isHorizontal = me.isHorizontal(); |
| 17006 |
function IDMatches(meta) { |
| 17007 |
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; |
| 17008 |
} |
| 17009 |
|
| 17010 |
// Calculate Range |
| 17011 |
me.min = null; |
| 17012 |
me.max = null; |
| 17013 |
me.minNotZero = null; |
| 17014 |
|
| 17015 |
var hasStacks = opts.stacked; |
| 17016 |
if (hasStacks === undefined) { |
| 17017 |
helpers.each(datasets, function(dataset, datasetIndex) { |
| 17018 |
if (hasStacks) { |
| 17019 |
return; |
| 17020 |
} |
| 17021 |
|
| 17022 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 17023 |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && |
| 17024 |
meta.stack !== undefined) { |
| 17025 |
hasStacks = true; |
| 17026 |
} |
| 17027 |
}); |
| 17028 |
} |
| 17029 |
|
| 17030 |
if (opts.stacked || hasStacks) { |
| 17031 |
var valuesPerStack = {}; |
| 17032 |
|
| 17033 |
helpers.each(datasets, function(dataset, datasetIndex) { |
| 17034 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 17035 |
var key = [ |
| 17036 |
meta.type, |
| 17037 |
// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined |
| 17038 |
((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), |
| 17039 |
meta.stack |
| 17040 |
].join('.'); |
| 17041 |
|
| 17042 |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 17043 |
if (valuesPerStack[key] === undefined) { |
| 17044 |
valuesPerStack[key] = []; |
| 17045 |
} |
| 17046 |
|
| 17047 |
helpers.each(dataset.data, function(rawValue, index) { |
| 17048 |
var values = valuesPerStack[key]; |
| 17049 |
var value = +me.getRightValue(rawValue); |
| 17050 |
if (isNaN(value) || meta.data[index].hidden) { |
| 17051 |
return; |
| 17052 |
} |
| 17053 |
|
| 17054 |
values[index] = values[index] || 0; |
| 17055 |
|
| 17056 |
if (opts.relativePoints) { |
| 17057 |
values[index] = 100; |
| 17058 |
} else { |
| 17059 |
// Don't need to split positive and negative since the log scale can't handle a 0 crossing |
| 17060 |
values[index] += value; |
| 17061 |
} |
| 17062 |
}); |
| 17063 |
} |
| 17064 |
}); |
| 17065 |
|
| 17066 |
helpers.each(valuesPerStack, function(valuesForType) { |
| 17067 |
var minVal = helpers.min(valuesForType); |
| 17068 |
var maxVal = helpers.max(valuesForType); |
| 17069 |
me.min = me.min === null ? minVal : Math.min(me.min, minVal); |
| 17070 |
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); |
| 17071 |
}); |
| 17072 |
|
| 17073 |
} else { |
| 17074 |
helpers.each(datasets, function(dataset, datasetIndex) { |
| 17075 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 17076 |
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 17077 |
helpers.each(dataset.data, function(rawValue, index) { |
| 17078 |
var value = +me.getRightValue(rawValue); |
| 17079 |
if (isNaN(value) || meta.data[index].hidden) { |
| 17080 |
return; |
| 17081 |
} |
| 17082 |
|
| 17083 |
if (me.min === null) { |
| 17084 |
me.min = value; |
| 17085 |
} else if (value < me.min) { |
| 17086 |
me.min = value; |
| 17087 |
} |
| 17088 |
|
| 17089 |
if (me.max === null) { |
| 17090 |
me.max = value; |
| 17091 |
} else if (value > me.max) { |
| 17092 |
me.max = value; |
| 17093 |
} |
| 17094 |
|
| 17095 |
if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { |
| 17096 |
me.minNotZero = value; |
| 17097 |
} |
| 17098 |
}); |
| 17099 |
} |
| 17100 |
}); |
| 17101 |
} |
| 17102 |
|
| 17103 |
me.min = valueOrDefault(tickOpts.min, me.min); |
| 17104 |
me.max = valueOrDefault(tickOpts.max, me.max); |
| 17105 |
|
| 17106 |
if (me.min === me.max) { |
| 17107 |
if (me.min !== 0 && me.min !== null) { |
| 17108 |
me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); |
| 17109 |
me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); |
| 17110 |
} else { |
| 17111 |
me.min = 1; |
| 17112 |
me.max = 10; |
| 17113 |
} |
| 17114 |
} |
| 17115 |
}, |
| 17116 |
buildTicks: function() { |
| 17117 |
var me = this; |
| 17118 |
var opts = me.options; |
| 17119 |
var tickOpts = opts.ticks; |
| 17120 |
|
| 17121 |
var generationOptions = { |
| 17122 |
min: tickOpts.min, |
| 17123 |
max: tickOpts.max |
| 17124 |
}; |
| 17125 |
var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me); |
| 17126 |
|
| 17127 |
if (!me.isHorizontal()) { |
| 17128 |
// We are in a vertical orientation. The top value is the highest. So reverse the array |
| 17129 |
ticks.reverse(); |
| 17130 |
} |
| 17131 |
|
| 17132 |
// At this point, we need to update our max and min given the tick values since we have expanded the |
| 17133 |
// range of the scale |
| 17134 |
me.max = helpers.max(ticks); |
| 17135 |
me.min = helpers.min(ticks); |
| 17136 |
|
| 17137 |
if (tickOpts.reverse) { |
| 17138 |
ticks.reverse(); |
| 17139 |
|
| 17140 |
me.start = me.max; |
| 17141 |
me.end = me.min; |
| 17142 |
} else { |
| 17143 |
me.start = me.min; |
| 17144 |
me.end = me.max; |
| 17145 |
} |
| 17146 |
}, |
| 17147 |
convertTicksToLabels: function() { |
| 17148 |
this.tickValues = this.ticks.slice(); |
| 17149 |
|
| 17150 |
Chart.Scale.prototype.convertTicksToLabels.call(this); |
| 17151 |
}, |
| 17152 |
// Get the correct tooltip label |
| 17153 |
getLabelForIndex: function(index, datasetIndex) { |
| 17154 |
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
| 17155 |
}, |
| 17156 |
getPixelForTick: function(index) { |
| 17157 |
return this.getPixelForValue(this.tickValues[index]); |
| 17158 |
}, |
| 17159 |
getPixelForValue: function(value) { |
| 17160 |
var me = this; |
| 17161 |
var start = me.start; |
| 17162 |
var newVal = +me.getRightValue(value); |
| 17163 |
var opts = me.options; |
| 17164 |
var tickOpts = opts.ticks; |
| 17165 |
var innerDimension, pixel, range; |
| 17166 |
|
| 17167 |
if (me.isHorizontal()) { |
| 17168 |
range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0 |
| 17169 |
if (newVal === 0) { |
| 17170 |
pixel = me.left; |
| 17171 |
} else { |
| 17172 |
innerDimension = me.width; |
| 17173 |
pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); |
| 17174 |
} |
| 17175 |
} else { |
| 17176 |
// Bottom - top since pixels increase downward on a screen |
| 17177 |
innerDimension = me.height; |
| 17178 |
if (start === 0 && !tickOpts.reverse) { |
| 17179 |
range = helpers.log10(me.end) - helpers.log10(me.minNotZero); |
| 17180 |
if (newVal === start) { |
| 17181 |
pixel = me.bottom; |
| 17182 |
} else if (newVal === me.minNotZero) { |
| 17183 |
pixel = me.bottom - innerDimension * 0.02; |
| 17184 |
} else { |
| 17185 |
pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); |
| 17186 |
} |
| 17187 |
} else if (me.end === 0 && tickOpts.reverse) { |
| 17188 |
range = helpers.log10(me.start) - helpers.log10(me.minNotZero); |
| 17189 |
if (newVal === me.end) { |
| 17190 |
pixel = me.top; |
| 17191 |
} else if (newVal === me.minNotZero) { |
| 17192 |
pixel = me.top + innerDimension * 0.02; |
| 17193 |
} else { |
| 17194 |
pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); |
| 17195 |
} |
| 17196 |
} else if (newVal === 0) { |
| 17197 |
pixel = tickOpts.reverse ? me.top : me.bottom; |
| 17198 |
} else { |
| 17199 |
range = helpers.log10(me.end) - helpers.log10(start); |
| 17200 |
innerDimension = me.height; |
| 17201 |
pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); |
| 17202 |
} |
| 17203 |
} |
| 17204 |
return pixel; |
| 17205 |
}, |
| 17206 |
getValueForPixel: function(pixel) { |
| 17207 |
var me = this; |
| 17208 |
var range = helpers.log10(me.end) - helpers.log10(me.start); |
| 17209 |
var value, innerDimension; |
| 17210 |
|
| 17211 |
if (me.isHorizontal()) { |
| 17212 |
innerDimension = me.width; |
| 17213 |
value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension); |
| 17214 |
} else { // todo: if start === 0 |
| 17215 |
innerDimension = me.height; |
| 17216 |
value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start; |
| 17217 |
} |
| 17218 |
return value; |
| 17219 |
} |
| 17220 |
}); |
| 17221 |
Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); |
| 17222 |
|
| 17223 |
}; |
| 17224 |
|
| 17225 |
},{"34":34,"45":45}],56:[function(require,module,exports){ |
| 17226 |
'use strict'; |
| 17227 |
|
| 17228 |
var defaults = require(25); |
| 17229 |
var helpers = require(45); |
| 17230 |
var Ticks = require(34); |
| 17231 |
|
| 17232 |
module.exports = function(Chart) { |
| 17233 |
|
| 17234 |
var globalDefaults = defaults.global; |
| 17235 |
|
| 17236 |
var defaultConfig = { |
| 17237 |
display: true, |
| 17238 |
|
| 17239 |
// Boolean - Whether to animate scaling the chart from the centre |
| 17240 |
animate: true, |
| 17241 |
position: 'chartArea', |
| 17242 |
|
| 17243 |
angleLines: { |
| 17244 |
display: true, |
| 17245 |
color: 'rgba(0, 0, 0, 0.1)', |
| 17246 |
lineWidth: 1 |
| 17247 |
}, |
| 17248 |
|
| 17249 |
gridLines: { |
| 17250 |
circular: false |
| 17251 |
}, |
| 17252 |
|
| 17253 |
// label settings |
| 17254 |
ticks: { |
| 17255 |
// Boolean - Show a backdrop to the scale label |
| 17256 |
showLabelBackdrop: true, |
| 17257 |
|
| 17258 |
// String - The colour of the label backdrop |
| 17259 |
backdropColor: 'rgba(255,255,255,0.75)', |
| 17260 |
|
| 17261 |
// Number - The backdrop padding above & below the label in pixels |
| 17262 |
backdropPaddingY: 2, |
| 17263 |
|
| 17264 |
// Number - The backdrop padding to the side of the label in pixels |
| 17265 |
backdropPaddingX: 2, |
| 17266 |
|
| 17267 |
callback: Ticks.formatters.linear |
| 17268 |
}, |
| 17269 |
|
| 17270 |
pointLabels: { |
| 17271 |
// Boolean - if true, show point labels |
| 17272 |
display: true, |
| 17273 |
|
| 17274 |
// Number - Point label font size in pixels |
| 17275 |
fontSize: 10, |
| 17276 |
|
| 17277 |
// Function - Used to convert point labels |
| 17278 |
callback: function(label) { |
| 17279 |
return label; |
| 17280 |
} |
| 17281 |
} |
| 17282 |
}; |
| 17283 |
|
| 17284 |
function getValueCount(scale) { |
| 17285 |
var opts = scale.options; |
| 17286 |
return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; |
| 17287 |
} |
| 17288 |
|
| 17289 |
function getPointLabelFontOptions(scale) { |
| 17290 |
var pointLabelOptions = scale.options.pointLabels; |
| 17291 |
var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); |
| 17292 |
var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); |
| 17293 |
var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); |
| 17294 |
var font = helpers.fontString(fontSize, fontStyle, fontFamily); |
| 17295 |
|
| 17296 |
return { |
| 17297 |
size: fontSize, |
| 17298 |
style: fontStyle, |
| 17299 |
family: fontFamily, |
| 17300 |
font: font |
| 17301 |
}; |
| 17302 |
} |
| 17303 |
|
| 17304 |
function measureLabelSize(ctx, fontSize, label) { |
| 17305 |
if (helpers.isArray(label)) { |
| 17306 |
return { |
| 17307 |
w: helpers.longestText(ctx, ctx.font, label), |
| 17308 |
h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) |
| 17309 |
}; |
| 17310 |
} |
| 17311 |
|
| 17312 |
return { |
| 17313 |
w: ctx.measureText(label).width, |
| 17314 |
h: fontSize |
| 17315 |
}; |
| 17316 |
} |
| 17317 |
|
| 17318 |
function determineLimits(angle, pos, size, min, max) { |
| 17319 |
if (angle === min || angle === max) { |
| 17320 |
return { |
| 17321 |
start: pos - (size / 2), |
| 17322 |
end: pos + (size / 2) |
| 17323 |
}; |
| 17324 |
} else if (angle < min || angle > max) { |
| 17325 |
return { |
| 17326 |
start: pos - size - 5, |
| 17327 |
end: pos |
| 17328 |
}; |
| 17329 |
} |
| 17330 |
|
| 17331 |
return { |
| 17332 |
start: pos, |
| 17333 |
end: pos + size + 5 |
| 17334 |
}; |
| 17335 |
} |
| 17336 |
|
| 17337 |
/** |
| 17338 |
* Helper function to fit a radial linear scale with point labels |
| 17339 |
*/ |
| 17340 |
function fitWithPointLabels(scale) { |
| 17341 |
/* |
| 17342 |
* Right, this is really confusing and there is a lot of maths going on here |
| 17343 |
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 |
| 17344 |
* |
| 17345 |
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif |
| 17346 |
* |
| 17347 |
* Solution: |
| 17348 |
* |
| 17349 |
* We assume the radius of the polygon is half the size of the canvas at first |
| 17350 |
* at each index we check if the text overlaps. |
| 17351 |
* |
| 17352 |
* Where it does, we store that angle and that index. |
| 17353 |
* |
| 17354 |
* After finding the largest index and angle we calculate how much we need to remove |
| 17355 |
* from the shape radius to move the point inwards by that x. |
| 17356 |
* |
| 17357 |
* We average the left and right distances to get the maximum shape radius that can fit in the box |
| 17358 |
* along with labels. |
| 17359 |
* |
| 17360 |
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion |
| 17361 |
* on each side, removing that from the size, halving it and adding the left x protrusion width. |
| 17362 |
* |
| 17363 |
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels |
| 17364 |
* and position it in the most space efficient manner |
| 17365 |
* |
| 17366 |
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif |
| 17367 |
*/ |
| 17368 |
|
| 17369 |
var plFont = getPointLabelFontOptions(scale); |
| 17370 |
|
| 17371 |
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. |
| 17372 |
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points |
| 17373 |
var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); |
| 17374 |
var furthestLimits = { |
| 17375 |
r: scale.width, |
| 17376 |
l: 0, |
| 17377 |
t: scale.height, |
| 17378 |
b: 0 |
| 17379 |
}; |
| 17380 |
var furthestAngles = {}; |
| 17381 |
var i, textSize, pointPosition; |
| 17382 |
|
| 17383 |
scale.ctx.font = plFont.font; |
| 17384 |
scale._pointLabelSizes = []; |
| 17385 |
|
| 17386 |
var valueCount = getValueCount(scale); |
| 17387 |
for (i = 0; i < valueCount; i++) { |
| 17388 |
pointPosition = scale.getPointPosition(i, largestPossibleRadius); |
| 17389 |
textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); |
| 17390 |
scale._pointLabelSizes[i] = textSize; |
| 17391 |
|
| 17392 |
// Add quarter circle to make degree 0 mean top of circle |
| 17393 |
var angleRadians = scale.getIndexAngle(i); |
| 17394 |
var angle = helpers.toDegrees(angleRadians) % 360; |
| 17395 |
var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); |
| 17396 |
var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); |
| 17397 |
|
| 17398 |
if (hLimits.start < furthestLimits.l) { |
| 17399 |
furthestLimits.l = hLimits.start; |
| 17400 |
furthestAngles.l = angleRadians; |
| 17401 |
} |
| 17402 |
|
| 17403 |
if (hLimits.end > furthestLimits.r) { |
| 17404 |
furthestLimits.r = hLimits.end; |
| 17405 |
furthestAngles.r = angleRadians; |
| 17406 |
} |
| 17407 |
|
| 17408 |
if (vLimits.start < furthestLimits.t) { |
| 17409 |
furthestLimits.t = vLimits.start; |
| 17410 |
furthestAngles.t = angleRadians; |
| 17411 |
} |
| 17412 |
|
| 17413 |
if (vLimits.end > furthestLimits.b) { |
| 17414 |
furthestLimits.b = vLimits.end; |
| 17415 |
furthestAngles.b = angleRadians; |
| 17416 |
} |
| 17417 |
} |
| 17418 |
|
| 17419 |
scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); |
| 17420 |
} |
| 17421 |
|
| 17422 |
/** |
| 17423 |
* Helper function to fit a radial linear scale with no point labels |
| 17424 |
*/ |
| 17425 |
function fit(scale) { |
| 17426 |
var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); |
| 17427 |
scale.drawingArea = Math.round(largestPossibleRadius); |
| 17428 |
scale.setCenterPoint(0, 0, 0, 0); |
| 17429 |
} |
| 17430 |
|
| 17431 |
function getTextAlignForAngle(angle) { |
| 17432 |
if (angle === 0 || angle === 180) { |
| 17433 |
return 'center'; |
| 17434 |
} else if (angle < 180) { |
| 17435 |
return 'left'; |
| 17436 |
} |
| 17437 |
|
| 17438 |
return 'right'; |
| 17439 |
} |
| 17440 |
|
| 17441 |
function fillText(ctx, text, position, fontSize) { |
| 17442 |
if (helpers.isArray(text)) { |
| 17443 |
var y = position.y; |
| 17444 |
var spacing = 1.5 * fontSize; |
| 17445 |
|
| 17446 |
for (var i = 0; i < text.length; ++i) { |
| 17447 |
ctx.fillText(text[i], position.x, y); |
| 17448 |
y += spacing; |
| 17449 |
} |
| 17450 |
} else { |
| 17451 |
ctx.fillText(text, position.x, position.y); |
| 17452 |
} |
| 17453 |
} |
| 17454 |
|
| 17455 |
function adjustPointPositionForLabelHeight(angle, textSize, position) { |
| 17456 |
if (angle === 90 || angle === 270) { |
| 17457 |
position.y -= (textSize.h / 2); |
| 17458 |
} else if (angle > 270 || angle < 90) { |
| 17459 |
position.y -= textSize.h; |
| 17460 |
} |
| 17461 |
} |
| 17462 |
|
| 17463 |
function drawPointLabels(scale) { |
| 17464 |
var ctx = scale.ctx; |
| 17465 |
var valueOrDefault = helpers.valueOrDefault; |
| 17466 |
var opts = scale.options; |
| 17467 |
var angleLineOpts = opts.angleLines; |
| 17468 |
var pointLabelOpts = opts.pointLabels; |
| 17469 |
|
| 17470 |
ctx.lineWidth = angleLineOpts.lineWidth; |
| 17471 |
ctx.strokeStyle = angleLineOpts.color; |
| 17472 |
|
| 17473 |
var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); |
| 17474 |
|
| 17475 |
// Point Label Font |
| 17476 |
var plFont = getPointLabelFontOptions(scale); |
| 17477 |
|
| 17478 |
ctx.textBaseline = 'top'; |
| 17479 |
|
| 17480 |
for (var i = getValueCount(scale) - 1; i >= 0; i--) { |
| 17481 |
if (angleLineOpts.display) { |
| 17482 |
var outerPosition = scale.getPointPosition(i, outerDistance); |
| 17483 |
ctx.beginPath(); |
| 17484 |
ctx.moveTo(scale.xCenter, scale.yCenter); |
| 17485 |
ctx.lineTo(outerPosition.x, outerPosition.y); |
| 17486 |
ctx.stroke(); |
| 17487 |
ctx.closePath(); |
| 17488 |
} |
| 17489 |
|
| 17490 |
if (pointLabelOpts.display) { |
| 17491 |
// Extra 3px out for some label spacing |
| 17492 |
var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); |
| 17493 |
|
| 17494 |
// Keep this in loop since we may support array properties here |
| 17495 |
var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); |
| 17496 |
ctx.font = plFont.font; |
| 17497 |
ctx.fillStyle = pointLabelFontColor; |
| 17498 |
|
| 17499 |
var angleRadians = scale.getIndexAngle(i); |
| 17500 |
var angle = helpers.toDegrees(angleRadians); |
| 17501 |
ctx.textAlign = getTextAlignForAngle(angle); |
| 17502 |
adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); |
| 17503 |
fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); |
| 17504 |
} |
| 17505 |
} |
| 17506 |
} |
| 17507 |
|
| 17508 |
function drawRadiusLine(scale, gridLineOpts, radius, index) { |
| 17509 |
var ctx = scale.ctx; |
| 17510 |
ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); |
| 17511 |
ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); |
| 17512 |
|
| 17513 |
if (scale.options.gridLines.circular) { |
| 17514 |
// Draw circular arcs between the points |
| 17515 |
ctx.beginPath(); |
| 17516 |
ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); |
| 17517 |
ctx.closePath(); |
| 17518 |
ctx.stroke(); |
| 17519 |
} else { |
| 17520 |
// Draw straight lines connecting each index |
| 17521 |
var valueCount = getValueCount(scale); |
| 17522 |
|
| 17523 |
if (valueCount === 0) { |
| 17524 |
return; |
| 17525 |
} |
| 17526 |
|
| 17527 |
ctx.beginPath(); |
| 17528 |
var pointPosition = scale.getPointPosition(0, radius); |
| 17529 |
ctx.moveTo(pointPosition.x, pointPosition.y); |
| 17530 |
|
| 17531 |
for (var i = 1; i < valueCount; i++) { |
| 17532 |
pointPosition = scale.getPointPosition(i, radius); |
| 17533 |
ctx.lineTo(pointPosition.x, pointPosition.y); |
| 17534 |
} |
| 17535 |
|
| 17536 |
ctx.closePath(); |
| 17537 |
ctx.stroke(); |
| 17538 |
} |
| 17539 |
} |
| 17540 |
|
| 17541 |
function numberOrZero(param) { |
| 17542 |
return helpers.isNumber(param) ? param : 0; |
| 17543 |
} |
| 17544 |
|
| 17545 |
var LinearRadialScale = Chart.LinearScaleBase.extend({ |
| 17546 |
setDimensions: function() { |
| 17547 |
var me = this; |
| 17548 |
var opts = me.options; |
| 17549 |
var tickOpts = opts.ticks; |
| 17550 |
// Set the unconstrained dimension before label rotation |
| 17551 |
me.width = me.maxWidth; |
| 17552 |
me.height = me.maxHeight; |
| 17553 |
me.xCenter = Math.round(me.width / 2); |
| 17554 |
me.yCenter = Math.round(me.height / 2); |
| 17555 |
|
| 17556 |
var minSize = helpers.min([me.height, me.width]); |
| 17557 |
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
| 17558 |
me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); |
| 17559 |
}, |
| 17560 |
determineDataLimits: function() { |
| 17561 |
var me = this; |
| 17562 |
var chart = me.chart; |
| 17563 |
var min = Number.POSITIVE_INFINITY; |
| 17564 |
var max = Number.NEGATIVE_INFINITY; |
| 17565 |
|
| 17566 |
helpers.each(chart.data.datasets, function(dataset, datasetIndex) { |
| 17567 |
if (chart.isDatasetVisible(datasetIndex)) { |
| 17568 |
var meta = chart.getDatasetMeta(datasetIndex); |
| 17569 |
|
| 17570 |
helpers.each(dataset.data, function(rawValue, index) { |
| 17571 |
var value = +me.getRightValue(rawValue); |
| 17572 |
if (isNaN(value) || meta.data[index].hidden) { |
| 17573 |
return; |
| 17574 |
} |
| 17575 |
|
| 17576 |
min = Math.min(value, min); |
| 17577 |
max = Math.max(value, max); |
| 17578 |
}); |
| 17579 |
} |
| 17580 |
}); |
| 17581 |
|
| 17582 |
me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); |
| 17583 |
me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); |
| 17584 |
|
| 17585 |
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero |
| 17586 |
me.handleTickRangeOptions(); |
| 17587 |
}, |
| 17588 |
getTickLimit: function() { |
| 17589 |
var tickOpts = this.options.ticks; |
| 17590 |
var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
| 17591 |
return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); |
| 17592 |
}, |
| 17593 |
convertTicksToLabels: function() { |
| 17594 |
var me = this; |
| 17595 |
|
| 17596 |
Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); |
| 17597 |
|
| 17598 |
// Point labels |
| 17599 |
me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); |
| 17600 |
}, |
| 17601 |
getLabelForIndex: function(index, datasetIndex) { |
| 17602 |
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
| 17603 |
}, |
| 17604 |
fit: function() { |
| 17605 |
if (this.options.pointLabels.display) { |
| 17606 |
fitWithPointLabels(this); |
| 17607 |
} else { |
| 17608 |
fit(this); |
| 17609 |
} |
| 17610 |
}, |
| 17611 |
/** |
| 17612 |
* Set radius reductions and determine new radius and center point |
| 17613 |
* @private |
| 17614 |
*/ |
| 17615 |
setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { |
| 17616 |
var me = this; |
| 17617 |
var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); |
| 17618 |
var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); |
| 17619 |
var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); |
| 17620 |
var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); |
| 17621 |
|
| 17622 |
radiusReductionLeft = numberOrZero(radiusReductionLeft); |
| 17623 |
radiusReductionRight = numberOrZero(radiusReductionRight); |
| 17624 |
radiusReductionTop = numberOrZero(radiusReductionTop); |
| 17625 |
radiusReductionBottom = numberOrZero(radiusReductionBottom); |
| 17626 |
|
| 17627 |
me.drawingArea = Math.min( |
| 17628 |
Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), |
| 17629 |
Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); |
| 17630 |
me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); |
| 17631 |
}, |
| 17632 |
setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { |
| 17633 |
var me = this; |
| 17634 |
var maxRight = me.width - rightMovement - me.drawingArea; |
| 17635 |
var maxLeft = leftMovement + me.drawingArea; |
| 17636 |
var maxTop = topMovement + me.drawingArea; |
| 17637 |
var maxBottom = me.height - bottomMovement - me.drawingArea; |
| 17638 |
|
| 17639 |
me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); |
| 17640 |
me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); |
| 17641 |
}, |
| 17642 |
|
| 17643 |
getIndexAngle: function(index) { |
| 17644 |
var angleMultiplier = (Math.PI * 2) / getValueCount(this); |
| 17645 |
var startAngle = this.chart.options && this.chart.options.startAngle ? |
| 17646 |
this.chart.options.startAngle : |
| 17647 |
0; |
| 17648 |
|
| 17649 |
var startAngleRadians = startAngle * Math.PI * 2 / 360; |
| 17650 |
|
| 17651 |
// Start from the top instead of right, so remove a quarter of the circle |
| 17652 |
return index * angleMultiplier + startAngleRadians; |
| 17653 |
}, |
| 17654 |
getDistanceFromCenterForValue: function(value) { |
| 17655 |
var me = this; |
| 17656 |
|
| 17657 |
if (value === null) { |
| 17658 |
return 0; // null always in center |
| 17659 |
} |
| 17660 |
|
| 17661 |
// Take into account half font size + the yPadding of the top value |
| 17662 |
var scalingFactor = me.drawingArea / (me.max - me.min); |
| 17663 |
if (me.options.ticks.reverse) { |
| 17664 |
return (me.max - value) * scalingFactor; |
| 17665 |
} |
| 17666 |
return (value - me.min) * scalingFactor; |
| 17667 |
}, |
| 17668 |
getPointPosition: function(index, distanceFromCenter) { |
| 17669 |
var me = this; |
| 17670 |
var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); |
| 17671 |
return { |
| 17672 |
x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, |
| 17673 |
y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter |
| 17674 |
}; |
| 17675 |
}, |
| 17676 |
getPointPositionForValue: function(index, value) { |
| 17677 |
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); |
| 17678 |
}, |
| 17679 |
|
| 17680 |
getBasePosition: function() { |
| 17681 |
var me = this; |
| 17682 |
var min = me.min; |
| 17683 |
var max = me.max; |
| 17684 |
|
| 17685 |
return me.getPointPositionForValue(0, |
| 17686 |
me.beginAtZero ? 0 : |
| 17687 |
min < 0 && max < 0 ? max : |
| 17688 |
min > 0 && max > 0 ? min : |
| 17689 |
0); |
| 17690 |
}, |
| 17691 |
|
| 17692 |
draw: function() { |
| 17693 |
var me = this; |
| 17694 |
var opts = me.options; |
| 17695 |
var gridLineOpts = opts.gridLines; |
| 17696 |
var tickOpts = opts.ticks; |
| 17697 |
var valueOrDefault = helpers.valueOrDefault; |
| 17698 |
|
| 17699 |
if (opts.display) { |
| 17700 |
var ctx = me.ctx; |
| 17701 |
var startAngle = this.getIndexAngle(0); |
| 17702 |
|
| 17703 |
// Tick Font |
| 17704 |
var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
| 17705 |
var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); |
| 17706 |
var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); |
| 17707 |
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
| 17708 |
|
| 17709 |
helpers.each(me.ticks, function(label, index) { |
| 17710 |
// Don't draw a centre value (if it is minimum) |
| 17711 |
if (index > 0 || tickOpts.reverse) { |
| 17712 |
var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); |
| 17713 |
|
| 17714 |
// Draw circular lines around the scale |
| 17715 |
if (gridLineOpts.display && index !== 0) { |
| 17716 |
drawRadiusLine(me, gridLineOpts, yCenterOffset, index); |
| 17717 |
} |
| 17718 |
|
| 17719 |
if (tickOpts.display) { |
| 17720 |
var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); |
| 17721 |
ctx.font = tickLabelFont; |
| 17722 |
|
| 17723 |
ctx.save(); |
| 17724 |
ctx.translate(me.xCenter, me.yCenter); |
| 17725 |
ctx.rotate(startAngle); |
| 17726 |
|
| 17727 |
if (tickOpts.showLabelBackdrop) { |
| 17728 |
var labelWidth = ctx.measureText(label).width; |
| 17729 |
ctx.fillStyle = tickOpts.backdropColor; |
| 17730 |
ctx.fillRect( |
| 17731 |
-labelWidth / 2 - tickOpts.backdropPaddingX, |
| 17732 |
-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, |
| 17733 |
labelWidth + tickOpts.backdropPaddingX * 2, |
| 17734 |
tickFontSize + tickOpts.backdropPaddingY * 2 |
| 17735 |
); |
| 17736 |
} |
| 17737 |
|
| 17738 |
ctx.textAlign = 'center'; |
| 17739 |
ctx.textBaseline = 'middle'; |
| 17740 |
ctx.fillStyle = tickFontColor; |
| 17741 |
ctx.fillText(label, 0, -yCenterOffset); |
| 17742 |
ctx.restore(); |
| 17743 |
} |
| 17744 |
} |
| 17745 |
}); |
| 17746 |
|
| 17747 |
if (opts.angleLines.display || opts.pointLabels.display) { |
| 17748 |
drawPointLabels(me); |
| 17749 |
} |
| 17750 |
} |
| 17751 |
} |
| 17752 |
}); |
| 17753 |
Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); |
| 17754 |
|
| 17755 |
}; |
| 17756 |
|
| 17757 |
},{"25":25,"34":34,"45":45}],57:[function(require,module,exports){ |
| 17758 |
/* global window: false */ |
| 17759 |
'use strict'; |
| 17760 |
|
| 17761 |
var moment = require(6); |
| 17762 |
moment = typeof moment === 'function' ? moment : window.moment; |
| 17763 |
|
| 17764 |
var defaults = require(25); |
| 17765 |
var helpers = require(45); |
| 17766 |
|
| 17767 |
// Integer constants are from the ES6 spec. |
| 17768 |
var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; |
| 17769 |
var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; |
| 17770 |
|
| 17771 |
var INTERVALS = { |
| 17772 |
millisecond: { |
| 17773 |
major: true, |
| 17774 |
size: 1, |
| 17775 |
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] |
| 17776 |
}, |
| 17777 |
second: { |
| 17778 |
major: true, |
| 17779 |
size: 1000, |
| 17780 |
steps: [1, 2, 5, 10, 30] |
| 17781 |
}, |
| 17782 |
minute: { |
| 17783 |
major: true, |
| 17784 |
size: 60000, |
| 17785 |
steps: [1, 2, 5, 10, 30] |
| 17786 |
}, |
| 17787 |
hour: { |
| 17788 |
major: true, |
| 17789 |
size: 3600000, |
| 17790 |
steps: [1, 2, 3, 6, 12] |
| 17791 |
}, |
| 17792 |
day: { |
| 17793 |
major: true, |
| 17794 |
size: 86400000, |
| 17795 |
steps: [1, 2, 5] |
| 17796 |
}, |
| 17797 |
week: { |
| 17798 |
major: false, |
| 17799 |
size: 604800000, |
| 17800 |
steps: [1, 2, 3, 4] |
| 17801 |
}, |
| 17802 |
month: { |
| 17803 |
major: true, |
| 17804 |
size: 2.628e9, |
| 17805 |
steps: [1, 2, 3] |
| 17806 |
}, |
| 17807 |
quarter: { |
| 17808 |
major: false, |
| 17809 |
size: 7.884e9, |
| 17810 |
steps: [1, 2, 3, 4] |
| 17811 |
}, |
| 17812 |
year: { |
| 17813 |
major: true, |
| 17814 |
size: 3.154e10 |
| 17815 |
} |
| 17816 |
}; |
| 17817 |
|
| 17818 |
var UNITS = Object.keys(INTERVALS); |
| 17819 |
|
| 17820 |
function sorter(a, b) { |
| 17821 |
return a - b; |
| 17822 |
} |
| 17823 |
|
| 17824 |
function arrayUnique(items) { |
| 17825 |
var hash = {}; |
| 17826 |
var out = []; |
| 17827 |
var i, ilen, item; |
| 17828 |
|
| 17829 |
for (i = 0, ilen = items.length; i < ilen; ++i) { |
| 17830 |
item = items[i]; |
| 17831 |
if (!hash[item]) { |
| 17832 |
hash[item] = true; |
| 17833 |
out.push(item); |
| 17834 |
} |
| 17835 |
} |
| 17836 |
|
| 17837 |
return out; |
| 17838 |
} |
| 17839 |
|
| 17840 |
/** |
| 17841 |
* Returns an array of {time, pos} objects used to interpolate a specific `time` or position |
| 17842 |
* (`pos`) on the scale, by searching entries before and after the requested value. `pos` is |
| 17843 |
* a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other |
| 17844 |
* extremity (left + width or top + height). Note that it would be more optimized to directly |
| 17845 |
* store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need |
| 17846 |
* to create the lookup table. The table ALWAYS contains at least two items: min and max. |
| 17847 |
* |
| 17848 |
* @param {Number[]} timestamps - timestamps sorted from lowest to highest. |
| 17849 |
* @param {String} distribution - If 'linear', timestamps will be spread linearly along the min |
| 17850 |
* and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. |
| 17851 |
* If 'series', timestamps will be positioned at the same distance from each other. In this |
| 17852 |
* case, only timestamps that break the time linearity are registered, meaning that in the |
| 17853 |
* best case, all timestamps are linear, the table contains only min and max. |
| 17854 |
*/ |
| 17855 |
function buildLookupTable(timestamps, min, max, distribution) { |
| 17856 |
if (distribution === 'linear' || !timestamps.length) { |
| 17857 |
return [ |
| 17858 |
{time: min, pos: 0}, |
| 17859 |
{time: max, pos: 1} |
| 17860 |
]; |
| 17861 |
} |
| 17862 |
|
| 17863 |
var table = []; |
| 17864 |
var items = [min]; |
| 17865 |
var i, ilen, prev, curr, next; |
| 17866 |
|
| 17867 |
for (i = 0, ilen = timestamps.length; i < ilen; ++i) { |
| 17868 |
curr = timestamps[i]; |
| 17869 |
if (curr > min && curr < max) { |
| 17870 |
items.push(curr); |
| 17871 |
} |
| 17872 |
} |
| 17873 |
|
| 17874 |
items.push(max); |
| 17875 |
|
| 17876 |
for (i = 0, ilen = items.length; i < ilen; ++i) { |
| 17877 |
next = items[i + 1]; |
| 17878 |
prev = items[i - 1]; |
| 17879 |
curr = items[i]; |
| 17880 |
|
| 17881 |
// only add points that breaks the scale linearity |
| 17882 |
if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { |
| 17883 |
table.push({time: curr, pos: i / (ilen - 1)}); |
| 17884 |
} |
| 17885 |
} |
| 17886 |
|
| 17887 |
return table; |
| 17888 |
} |
| 17889 |
|
| 17890 |
// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ |
| 17891 |
function lookup(table, key, value) { |
| 17892 |
var lo = 0; |
| 17893 |
var hi = table.length - 1; |
| 17894 |
var mid, i0, i1; |
| 17895 |
|
| 17896 |
while (lo >= 0 && lo <= hi) { |
| 17897 |
mid = (lo + hi) >> 1; |
| 17898 |
i0 = table[mid - 1] || null; |
| 17899 |
i1 = table[mid]; |
| 17900 |
|
| 17901 |
if (!i0) { |
| 17902 |
// given value is outside table (before first item) |
| 17903 |
return {lo: null, hi: i1}; |
| 17904 |
} else if (i1[key] < value) { |
| 17905 |
lo = mid + 1; |
| 17906 |
} else if (i0[key] > value) { |
| 17907 |
hi = mid - 1; |
| 17908 |
} else { |
| 17909 |
return {lo: i0, hi: i1}; |
| 17910 |
} |
| 17911 |
} |
| 17912 |
|
| 17913 |
// given value is outside table (after last item) |
| 17914 |
return {lo: i1, hi: null}; |
| 17915 |
} |
| 17916 |
|
| 17917 |
/** |
| 17918 |
* Linearly interpolates the given source `value` using the table items `skey` values and |
| 17919 |
* returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') |
| 17920 |
* returns the position for a timestamp equal to 42. If value is out of bounds, values at |
| 17921 |
* index [0, 1] or [n - 1, n] are used for the interpolation. |
| 17922 |
*/ |
| 17923 |
function interpolate(table, skey, sval, tkey) { |
| 17924 |
var range = lookup(table, skey, sval); |
| 17925 |
|
| 17926 |
// Note: the lookup table ALWAYS contains at least 2 items (min and max) |
| 17927 |
var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; |
| 17928 |
var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; |
| 17929 |
|
| 17930 |
var span = next[skey] - prev[skey]; |
| 17931 |
var ratio = span ? (sval - prev[skey]) / span : 0; |
| 17932 |
var offset = (next[tkey] - prev[tkey]) * ratio; |
| 17933 |
|
| 17934 |
return prev[tkey] + offset; |
| 17935 |
} |
| 17936 |
|
| 17937 |
/** |
| 17938 |
* Convert the given value to a moment object using the given time options. |
| 17939 |
* @see http://momentjs.com/docs/#/parsing/ |
| 17940 |
*/ |
| 17941 |
function momentify(value, options) { |
| 17942 |
var parser = options.parser; |
| 17943 |
var format = options.parser || options.format; |
| 17944 |
|
| 17945 |
if (typeof parser === 'function') { |
| 17946 |
return parser(value); |
| 17947 |
} |
| 17948 |
|
| 17949 |
if (typeof value === 'string' && typeof format === 'string') { |
| 17950 |
return moment(value, format); |
| 17951 |
} |
| 17952 |
|
| 17953 |
if (!(value instanceof moment)) { |
| 17954 |
value = moment(value); |
| 17955 |
} |
| 17956 |
|
| 17957 |
if (value.isValid()) { |
| 17958 |
return value; |
| 17959 |
} |
| 17960 |
|
| 17961 |
// Labels are in an incompatible moment format and no `parser` has been provided. |
| 17962 |
// The user might still use the deprecated `format` option to convert his inputs. |
| 17963 |
if (typeof format === 'function') { |
| 17964 |
return format(value); |
| 17965 |
} |
| 17966 |
|
| 17967 |
return value; |
| 17968 |
} |
| 17969 |
|
| 17970 |
function parse(input, scale) { |
| 17971 |
if (helpers.isNullOrUndef(input)) { |
| 17972 |
return null; |
| 17973 |
} |
| 17974 |
|
| 17975 |
var options = scale.options.time; |
| 17976 |
var value = momentify(scale.getRightValue(input), options); |
| 17977 |
if (!value.isValid()) { |
| 17978 |
return null; |
| 17979 |
} |
| 17980 |
|
| 17981 |
if (options.round) { |
| 17982 |
value.startOf(options.round); |
| 17983 |
} |
| 17984 |
|
| 17985 |
return value.valueOf(); |
| 17986 |
} |
| 17987 |
|
| 17988 |
/** |
| 17989 |
* Returns the number of unit to skip to be able to display up to `capacity` number of ticks |
| 17990 |
* in `unit` for the given `min` / `max` range and respecting the interval steps constraints. |
| 17991 |
*/ |
| 17992 |
function determineStepSize(min, max, unit, capacity) { |
| 17993 |
var range = max - min; |
| 17994 |
var interval = INTERVALS[unit]; |
| 17995 |
var milliseconds = interval.size; |
| 17996 |
var steps = interval.steps; |
| 17997 |
var i, ilen, factor; |
| 17998 |
|
| 17999 |
if (!steps) { |
| 18000 |
return Math.ceil(range / ((capacity || 1) * milliseconds)); |
| 18001 |
} |
| 18002 |
|
| 18003 |
for (i = 0, ilen = steps.length; i < ilen; ++i) { |
| 18004 |
factor = steps[i]; |
| 18005 |
if (Math.ceil(range / (milliseconds * factor)) <= capacity) { |
| 18006 |
break; |
| 18007 |
} |
| 18008 |
} |
| 18009 |
|
| 18010 |
return factor; |
| 18011 |
} |
| 18012 |
|
| 18013 |
function determineUnit(minUnit, min, max, capacity) { |
| 18014 |
var ilen = UNITS.length; |
| 18015 |
var i, interval, factor; |
| 18016 |
|
| 18017 |
for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { |
| 18018 |
interval = INTERVALS[UNITS[i]]; |
| 18019 |
factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; |
| 18020 |
|
| 18021 |
if (Math.ceil((max - min) / (factor * interval.size)) <= capacity) { |
| 18022 |
return UNITS[i]; |
| 18023 |
} |
| 18024 |
} |
| 18025 |
|
| 18026 |
return UNITS[ilen - 1]; |
| 18027 |
} |
| 18028 |
|
| 18029 |
function determineMajorUnit(unit) { |
| 18030 |
for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { |
| 18031 |
if (INTERVALS[UNITS[i]].major) { |
| 18032 |
return UNITS[i]; |
| 18033 |
} |
| 18034 |
} |
| 18035 |
} |
| 18036 |
|
| 18037 |
/** |
| 18038 |
* Generates a maximum of `capacity` timestamps between min and max, rounded to the |
| 18039 |
* `minor` unit, aligned on the `major` unit and using the given scale time `options`. |
| 18040 |
* Important: this method can return ticks outside the min and max range, it's the |
| 18041 |
* responsibility of the calling code to clamp values if needed. |
| 18042 |
*/ |
| 18043 |
function generate(min, max, minor, major, capacity, options) { |
| 18044 |
var timeOpts = options.time; |
| 18045 |
var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); |
| 18046 |
var weekday = minor === 'week' ? timeOpts.isoWeekday : false; |
| 18047 |
var majorTicksEnabled = options.ticks.major.enabled; |
| 18048 |
var interval = INTERVALS[minor]; |
| 18049 |
var first = moment(min); |
| 18050 |
var last = moment(max); |
| 18051 |
var ticks = []; |
| 18052 |
var time; |
| 18053 |
|
| 18054 |
if (!stepSize) { |
| 18055 |
stepSize = determineStepSize(min, max, minor, capacity); |
| 18056 |
} |
| 18057 |
|
| 18058 |
// For 'week' unit, handle the first day of week option |
| 18059 |
if (weekday) { |
| 18060 |
first = first.isoWeekday(weekday); |
| 18061 |
last = last.isoWeekday(weekday); |
| 18062 |
} |
| 18063 |
|
| 18064 |
// Align first/last ticks on unit |
| 18065 |
first = first.startOf(weekday ? 'day' : minor); |
| 18066 |
last = last.startOf(weekday ? 'day' : minor); |
| 18067 |
|
| 18068 |
// Make sure that the last tick include max |
| 18069 |
if (last < max) { |
| 18070 |
last.add(1, minor); |
| 18071 |
} |
| 18072 |
|
| 18073 |
time = moment(first); |
| 18074 |
|
| 18075 |
if (majorTicksEnabled && major && !weekday && !timeOpts.round) { |
| 18076 |
// Align the first tick on the previous `minor` unit aligned on the `major` unit: |
| 18077 |
// we first aligned time on the previous `major` unit then add the number of full |
| 18078 |
// stepSize there is between first and the previous major time. |
| 18079 |
time.startOf(major); |
| 18080 |
time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); |
| 18081 |
} |
| 18082 |
|
| 18083 |
for (; time < last; time.add(stepSize, minor)) { |
| 18084 |
ticks.push(+time); |
| 18085 |
} |
| 18086 |
|
| 18087 |
ticks.push(+time); |
| 18088 |
|
| 18089 |
return ticks; |
| 18090 |
} |
| 18091 |
|
| 18092 |
/** |
| 18093 |
* Returns the right and left offsets from edges in the form of {left, right}. |
| 18094 |
* Offsets are added when the `offset` option is true. |
| 18095 |
*/ |
| 18096 |
function computeOffsets(table, ticks, min, max, options) { |
| 18097 |
var left = 0; |
| 18098 |
var right = 0; |
| 18099 |
var upper, lower; |
| 18100 |
|
| 18101 |
if (options.offset && ticks.length) { |
| 18102 |
if (!options.time.min) { |
| 18103 |
upper = ticks.length > 1 ? ticks[1] : max; |
| 18104 |
lower = ticks[0]; |
| 18105 |
left = ( |
| 18106 |
interpolate(table, 'time', upper, 'pos') - |
| 18107 |
interpolate(table, 'time', lower, 'pos') |
| 18108 |
) / 2; |
| 18109 |
} |
| 18110 |
if (!options.time.max) { |
| 18111 |
upper = ticks[ticks.length - 1]; |
| 18112 |
lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; |
| 18113 |
right = ( |
| 18114 |
interpolate(table, 'time', upper, 'pos') - |
| 18115 |
interpolate(table, 'time', lower, 'pos') |
| 18116 |
) / 2; |
| 18117 |
} |
| 18118 |
} |
| 18119 |
|
| 18120 |
return {left: left, right: right}; |
| 18121 |
} |
| 18122 |
|
| 18123 |
function ticksFromTimestamps(values, majorUnit) { |
| 18124 |
var ticks = []; |
| 18125 |
var i, ilen, value, major; |
| 18126 |
|
| 18127 |
for (i = 0, ilen = values.length; i < ilen; ++i) { |
| 18128 |
value = values[i]; |
| 18129 |
major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; |
| 18130 |
|
| 18131 |
ticks.push({ |
| 18132 |
value: value, |
| 18133 |
major: major |
| 18134 |
}); |
| 18135 |
} |
| 18136 |
|
| 18137 |
return ticks; |
| 18138 |
} |
| 18139 |
|
| 18140 |
module.exports = function(Chart) { |
| 18141 |
|
| 18142 |
var defaultConfig = { |
| 18143 |
position: 'bottom', |
| 18144 |
|
| 18145 |
/** |
| 18146 |
* Data distribution along the scale: |
| 18147 |
* - 'linear': data are spread according to their time (distances can vary), |
| 18148 |
* - 'series': data are spread at the same distance from each other. |
| 18149 |
* @see https://github.com/chartjs/Chart.js/pull/4507 |
| 18150 |
* @since 2.7.0 |
| 18151 |
*/ |
| 18152 |
distribution: 'linear', |
| 18153 |
|
| 18154 |
/** |
| 18155 |
* Scale boundary strategy (bypassed by min/max time options) |
| 18156 |
* - `data`: make sure data are fully visible, ticks outside are removed |
| 18157 |
* - `ticks`: make sure ticks are fully visible, data outside are truncated |
| 18158 |
* @see https://github.com/chartjs/Chart.js/pull/4556 |
| 18159 |
* @since 2.7.0 |
| 18160 |
*/ |
| 18161 |
bounds: 'data', |
| 18162 |
|
| 18163 |
time: { |
| 18164 |
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 |
| 18165 |
format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ |
| 18166 |
unit: false, // false == automatic or override with week, month, year, etc. |
| 18167 |
round: false, // none, or override with week, month, year, etc. |
| 18168 |
displayFormat: false, // DEPRECATED |
| 18169 |
isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ |
| 18170 |
minUnit: 'millisecond', |
| 18171 |
|
| 18172 |
// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ |
| 18173 |
displayFormats: { |
| 18174 |
millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, |
| 18175 |
second: 'h:mm:ss a', // 11:20:01 AM |
| 18176 |
minute: 'h:mm a', // 11:20 AM |
| 18177 |
hour: 'hA', // 5PM |
| 18178 |
day: 'MMM D', // Sep 4 |
| 18179 |
week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? |
| 18180 |
month: 'MMM YYYY', // Sept 2015 |
| 18181 |
quarter: '[Q]Q - YYYY', // Q3 |
| 18182 |
year: 'YYYY' // 2015 |
| 18183 |
}, |
| 18184 |
}, |
| 18185 |
ticks: { |
| 18186 |
autoSkip: false, |
| 18187 |
|
| 18188 |
/** |
| 18189 |
* Ticks generation input values: |
| 18190 |
* - 'auto': generates "optimal" ticks based on scale size and time options. |
| 18191 |
* - 'data': generates ticks from data (including labels from data {t|x|y} objects). |
| 18192 |
* - 'labels': generates ticks from user given `data.labels` values ONLY. |
| 18193 |
* @see https://github.com/chartjs/Chart.js/pull/4507 |
| 18194 |
* @since 2.7.0 |
| 18195 |
*/ |
| 18196 |
source: 'auto', |
| 18197 |
|
| 18198 |
major: { |
| 18199 |
enabled: false |
| 18200 |
} |
| 18201 |
} |
| 18202 |
}; |
| 18203 |
|
| 18204 |
var TimeScale = Chart.Scale.extend({ |
| 18205 |
initialize: function() { |
| 18206 |
if (!moment) { |
| 18207 |
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'); |
| 18208 |
} |
| 18209 |
|
| 18210 |
this.mergeTicksOptions(); |
| 18211 |
|
| 18212 |
Chart.Scale.prototype.initialize.call(this); |
| 18213 |
}, |
| 18214 |
|
| 18215 |
update: function() { |
| 18216 |
var me = this; |
| 18217 |
var options = me.options; |
| 18218 |
|
| 18219 |
// DEPRECATIONS: output a message only one time per update |
| 18220 |
if (options.time && options.time.format) { |
| 18221 |
console.warn('options.time.format is deprecated and replaced by options.time.parser.'); |
| 18222 |
} |
| 18223 |
|
| 18224 |
return Chart.Scale.prototype.update.apply(me, arguments); |
| 18225 |
}, |
| 18226 |
|
| 18227 |
/** |
| 18228 |
* Allows data to be referenced via 't' attribute |
| 18229 |
*/ |
| 18230 |
getRightValue: function(rawValue) { |
| 18231 |
if (rawValue && rawValue.t !== undefined) { |
| 18232 |
rawValue = rawValue.t; |
| 18233 |
} |
| 18234 |
return Chart.Scale.prototype.getRightValue.call(this, rawValue); |
| 18235 |
}, |
| 18236 |
|
| 18237 |
determineDataLimits: function() { |
| 18238 |
var me = this; |
| 18239 |
var chart = me.chart; |
| 18240 |
var timeOpts = me.options.time; |
| 18241 |
var min = parse(timeOpts.min, me) || MAX_INTEGER; |
| 18242 |
var max = parse(timeOpts.max, me) || MIN_INTEGER; |
| 18243 |
var timestamps = []; |
| 18244 |
var datasets = []; |
| 18245 |
var labels = []; |
| 18246 |
var i, j, ilen, jlen, data, timestamp; |
| 18247 |
|
| 18248 |
// Convert labels to timestamps |
| 18249 |
for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { |
| 18250 |
labels.push(parse(chart.data.labels[i], me)); |
| 18251 |
} |
| 18252 |
|
| 18253 |
// Convert data to timestamps |
| 18254 |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
| 18255 |
if (chart.isDatasetVisible(i)) { |
| 18256 |
data = chart.data.datasets[i].data; |
| 18257 |
|
| 18258 |
// Let's consider that all data have the same format. |
| 18259 |
if (helpers.isObject(data[0])) { |
| 18260 |
datasets[i] = []; |
| 18261 |
|
| 18262 |
for (j = 0, jlen = data.length; j < jlen; ++j) { |
| 18263 |
timestamp = parse(data[j], me); |
| 18264 |
timestamps.push(timestamp); |
| 18265 |
datasets[i][j] = timestamp; |
| 18266 |
} |
| 18267 |
} else { |
| 18268 |
timestamps.push.apply(timestamps, labels); |
| 18269 |
datasets[i] = labels.slice(0); |
| 18270 |
} |
| 18271 |
} else { |
| 18272 |
datasets[i] = []; |
| 18273 |
} |
| 18274 |
} |
| 18275 |
|
| 18276 |
if (labels.length) { |
| 18277 |
// Sort labels **after** data have been converted |
| 18278 |
labels = arrayUnique(labels).sort(sorter); |
| 18279 |
min = Math.min(min, labels[0]); |
| 18280 |
max = Math.max(max, labels[labels.length - 1]); |
| 18281 |
} |
| 18282 |
|
| 18283 |
if (timestamps.length) { |
| 18284 |
timestamps = arrayUnique(timestamps).sort(sorter); |
| 18285 |
min = Math.min(min, timestamps[0]); |
| 18286 |
max = Math.max(max, timestamps[timestamps.length - 1]); |
| 18287 |
} |
| 18288 |
|
| 18289 |
// In case there is no valid min/max, let's use today limits |
| 18290 |
min = min === MAX_INTEGER ? +moment().startOf('day') : min; |
| 18291 |
max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max; |
| 18292 |
|
| 18293 |
// Make sure that max is strictly higher than min (required by the lookup table) |
| 18294 |
me.min = Math.min(min, max); |
| 18295 |
me.max = Math.max(min + 1, max); |
| 18296 |
|
| 18297 |
// PRIVATE |
| 18298 |
me._horizontal = me.isHorizontal(); |
| 18299 |
me._table = []; |
| 18300 |
me._timestamps = { |
| 18301 |
data: timestamps, |
| 18302 |
datasets: datasets, |
| 18303 |
labels: labels |
| 18304 |
}; |
| 18305 |
}, |
| 18306 |
|
| 18307 |
buildTicks: function() { |
| 18308 |
var me = this; |
| 18309 |
var min = me.min; |
| 18310 |
var max = me.max; |
| 18311 |
var options = me.options; |
| 18312 |
var timeOpts = options.time; |
| 18313 |
var formats = timeOpts.displayFormats; |
| 18314 |
var capacity = me.getLabelCapacity(min); |
| 18315 |
var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, min, max, capacity); |
| 18316 |
var majorUnit = determineMajorUnit(unit); |
| 18317 |
var timestamps = []; |
| 18318 |
var ticks = []; |
| 18319 |
var i, ilen, timestamp; |
| 18320 |
|
| 18321 |
switch (options.ticks.source) { |
| 18322 |
case 'data': |
| 18323 |
timestamps = me._timestamps.data; |
| 18324 |
break; |
| 18325 |
case 'labels': |
| 18326 |
timestamps = me._timestamps.labels; |
| 18327 |
break; |
| 18328 |
case 'auto': |
| 18329 |
default: |
| 18330 |
timestamps = generate(min, max, unit, majorUnit, capacity, options); |
| 18331 |
} |
| 18332 |
|
| 18333 |
if (options.bounds === 'ticks' && timestamps.length) { |
| 18334 |
min = timestamps[0]; |
| 18335 |
max = timestamps[timestamps.length - 1]; |
| 18336 |
} |
| 18337 |
|
| 18338 |
// Enforce limits with user min/max options |
| 18339 |
min = parse(timeOpts.min, me) || min; |
| 18340 |
max = parse(timeOpts.max, me) || max; |
| 18341 |
|
| 18342 |
// Remove ticks outside the min/max range |
| 18343 |
for (i = 0, ilen = timestamps.length; i < ilen; ++i) { |
| 18344 |
timestamp = timestamps[i]; |
| 18345 |
if (timestamp >= min && timestamp <= max) { |
| 18346 |
ticks.push(timestamp); |
| 18347 |
} |
| 18348 |
} |
| 18349 |
|
| 18350 |
me.min = min; |
| 18351 |
me.max = max; |
| 18352 |
|
| 18353 |
// PRIVATE |
| 18354 |
me._unit = unit; |
| 18355 |
me._majorUnit = majorUnit; |
| 18356 |
me._minorFormat = formats[unit]; |
| 18357 |
me._majorFormat = formats[majorUnit]; |
| 18358 |
me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); |
| 18359 |
me._offsets = computeOffsets(me._table, ticks, min, max, options); |
| 18360 |
|
| 18361 |
return ticksFromTimestamps(ticks, majorUnit); |
| 18362 |
}, |
| 18363 |
|
| 18364 |
getLabelForIndex: function(index, datasetIndex) { |
| 18365 |
var me = this; |
| 18366 |
var data = me.chart.data; |
| 18367 |
var timeOpts = me.options.time; |
| 18368 |
var label = data.labels && index < data.labels.length ? data.labels[index] : ''; |
| 18369 |
var value = data.datasets[datasetIndex].data[index]; |
| 18370 |
|
| 18371 |
if (helpers.isObject(value)) { |
| 18372 |
label = me.getRightValue(value); |
| 18373 |
} |
| 18374 |
if (timeOpts.tooltipFormat) { |
| 18375 |
label = momentify(label, timeOpts).format(timeOpts.tooltipFormat); |
| 18376 |
} |
| 18377 |
|
| 18378 |
return label; |
| 18379 |
}, |
| 18380 |
|
| 18381 |
/** |
| 18382 |
* Function to format an individual tick mark |
| 18383 |
* @private |
| 18384 |
*/ |
| 18385 |
tickFormatFunction: function(tick, index, ticks) { |
| 18386 |
var me = this; |
| 18387 |
var options = me.options; |
| 18388 |
var time = tick.valueOf(); |
| 18389 |
var majorUnit = me._majorUnit; |
| 18390 |
var majorFormat = me._majorFormat; |
| 18391 |
var majorTime = tick.clone().startOf(me._majorUnit).valueOf(); |
| 18392 |
var majorTickOpts = options.ticks.major; |
| 18393 |
var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; |
| 18394 |
var label = tick.format(major ? majorFormat : me._minorFormat); |
| 18395 |
var tickOpts = major ? majorTickOpts : options.ticks.minor; |
| 18396 |
var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); |
| 18397 |
|
| 18398 |
return formatter ? formatter(label, index, ticks) : label; |
| 18399 |
}, |
| 18400 |
|
| 18401 |
convertTicksToLabels: function(ticks) { |
| 18402 |
var labels = []; |
| 18403 |
var i, ilen; |
| 18404 |
|
| 18405 |
for (i = 0, ilen = ticks.length; i < ilen; ++i) { |
| 18406 |
labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); |
| 18407 |
} |
| 18408 |
|
| 18409 |
return labels; |
| 18410 |
}, |
| 18411 |
|
| 18412 |
/** |
| 18413 |
* @private |
| 18414 |
*/ |
| 18415 |
getPixelForOffset: function(time) { |
| 18416 |
var me = this; |
| 18417 |
var size = me._horizontal ? me.width : me.height; |
| 18418 |
var start = me._horizontal ? me.left : me.top; |
| 18419 |
var pos = interpolate(me._table, 'time', time, 'pos'); |
| 18420 |
|
| 18421 |
return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); |
| 18422 |
}, |
| 18423 |
|
| 18424 |
getPixelForValue: function(value, index, datasetIndex) { |
| 18425 |
var me = this; |
| 18426 |
var time = null; |
| 18427 |
|
| 18428 |
if (index !== undefined && datasetIndex !== undefined) { |
| 18429 |
time = me._timestamps.datasets[datasetIndex][index]; |
| 18430 |
} |
| 18431 |
|
| 18432 |
if (time === null) { |
| 18433 |
time = parse(value, me); |
| 18434 |
} |
| 18435 |
|
| 18436 |
if (time !== null) { |
| 18437 |
return me.getPixelForOffset(time); |
| 18438 |
} |
| 18439 |
}, |
| 18440 |
|
| 18441 |
getPixelForTick: function(index) { |
| 18442 |
var ticks = this.getTicks(); |
| 18443 |
return index >= 0 && index < ticks.length ? |
| 18444 |
this.getPixelForOffset(ticks[index].value) : |
| 18445 |
null; |
| 18446 |
}, |
| 18447 |
|
| 18448 |
getValueForPixel: function(pixel) { |
| 18449 |
var me = this; |
| 18450 |
var size = me._horizontal ? me.width : me.height; |
| 18451 |
var start = me._horizontal ? me.left : me.top; |
| 18452 |
var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; |
| 18453 |
var time = interpolate(me._table, 'pos', pos, 'time'); |
| 18454 |
|
| 18455 |
return moment(time); |
| 18456 |
}, |
| 18457 |
|
| 18458 |
/** |
| 18459 |
* Crude approximation of what the label width might be |
| 18460 |
* @private |
| 18461 |
*/ |
| 18462 |
getLabelWidth: function(label) { |
| 18463 |
var me = this; |
| 18464 |
var ticksOpts = me.options.ticks; |
| 18465 |
var tickLabelWidth = me.ctx.measureText(label).width; |
| 18466 |
var angle = helpers.toRadians(ticksOpts.maxRotation); |
| 18467 |
var cosRotation = Math.cos(angle); |
| 18468 |
var sinRotation = Math.sin(angle); |
| 18469 |
var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); |
| 18470 |
|
| 18471 |
return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); |
| 18472 |
}, |
| 18473 |
|
| 18474 |
/** |
| 18475 |
* @private |
| 18476 |
*/ |
| 18477 |
getLabelCapacity: function(exampleTime) { |
| 18478 |
var me = this; |
| 18479 |
|
| 18480 |
me._minorFormat = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation |
| 18481 |
|
| 18482 |
var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []); |
| 18483 |
var tickLabelWidth = me.getLabelWidth(exampleLabel); |
| 18484 |
var innerWidth = me.isHorizontal() ? me.width : me.height; |
| 18485 |
|
| 18486 |
return Math.floor(innerWidth / tickLabelWidth); |
| 18487 |
} |
| 18488 |
}); |
| 18489 |
|
| 18490 |
Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); |
| 18491 |
}; |
| 18492 |
|
| 18493 |
},{"25":25,"45":45,"6":6}]},{},[7])(7) |
| 18494 |
}); |