Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

14282 lines
437KB

  1. /*!
  2. * Chart.js
  3. * http://chartjs.org/
  4. * Version: 2.1.6
  5. *
  6. * Copyright 2016 Nick Downie
  7. * Released under the MIT license
  8. * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
  9. */
  10. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  11. /* MIT license */
  12. var colorNames = require(5);
  13. module.exports = {
  14. getRgba: getRgba,
  15. getHsla: getHsla,
  16. getRgb: getRgb,
  17. getHsl: getHsl,
  18. getHwb: getHwb,
  19. getAlpha: getAlpha,
  20. hexString: hexString,
  21. rgbString: rgbString,
  22. rgbaString: rgbaString,
  23. percentString: percentString,
  24. percentaString: percentaString,
  25. hslString: hslString,
  26. hslaString: hslaString,
  27. hwbString: hwbString,
  28. keyword: keyword
  29. }
  30. function getRgba(string) {
  31. if (!string) {
  32. return;
  33. }
  34. var abbr = /^#([a-fA-F0-9]{3})$/,
  35. hex = /^#([a-fA-F0-9]{6})$/,
  36. rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
  37. per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
  38. keyword = /(\w+)/;
  39. var rgb = [0, 0, 0],
  40. a = 1,
  41. match = string.match(abbr);
  42. if (match) {
  43. match = match[1];
  44. for (var i = 0; i < rgb.length; i++) {
  45. rgb[i] = parseInt(match[i] + match[i], 16);
  46. }
  47. }
  48. else if (match = string.match(hex)) {
  49. match = match[1];
  50. for (var i = 0; i < rgb.length; i++) {
  51. rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
  52. }
  53. }
  54. else if (match = string.match(rgba)) {
  55. for (var i = 0; i < rgb.length; i++) {
  56. rgb[i] = parseInt(match[i + 1]);
  57. }
  58. a = parseFloat(match[4]);
  59. }
  60. else if (match = string.match(per)) {
  61. for (var i = 0; i < rgb.length; i++) {
  62. rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
  63. }
  64. a = parseFloat(match[4]);
  65. }
  66. else if (match = string.match(keyword)) {
  67. if (match[1] == "transparent") {
  68. return [0, 0, 0, 0];
  69. }
  70. rgb = colorNames[match[1]];
  71. if (!rgb) {
  72. return;
  73. }
  74. }
  75. for (var i = 0; i < rgb.length; i++) {
  76. rgb[i] = scale(rgb[i], 0, 255);
  77. }
  78. if (!a && a != 0) {
  79. a = 1;
  80. }
  81. else {
  82. a = scale(a, 0, 1);
  83. }
  84. rgb[3] = a;
  85. return rgb;
  86. }
  87. function getHsla(string) {
  88. if (!string) {
  89. return;
  90. }
  91. var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
  92. var match = string.match(hsl);
  93. if (match) {
  94. var alpha = parseFloat(match[4]);
  95. var h = scale(parseInt(match[1]), 0, 360),
  96. s = scale(parseFloat(match[2]), 0, 100),
  97. l = scale(parseFloat(match[3]), 0, 100),
  98. a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
  99. return [h, s, l, a];
  100. }
  101. }
  102. function getHwb(string) {
  103. if (!string) {
  104. return;
  105. }
  106. var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
  107. var match = string.match(hwb);
  108. if (match) {
  109. var alpha = parseFloat(match[4]);
  110. var h = scale(parseInt(match[1]), 0, 360),
  111. w = scale(parseFloat(match[2]), 0, 100),
  112. b = scale(parseFloat(match[3]), 0, 100),
  113. a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
  114. return [h, w, b, a];
  115. }
  116. }
  117. function getRgb(string) {
  118. var rgba = getRgba(string);
  119. return rgba && rgba.slice(0, 3);
  120. }
  121. function getHsl(string) {
  122. var hsla = getHsla(string);
  123. return hsla && hsla.slice(0, 3);
  124. }
  125. function getAlpha(string) {
  126. var vals = getRgba(string);
  127. if (vals) {
  128. return vals[3];
  129. }
  130. else if (vals = getHsla(string)) {
  131. return vals[3];
  132. }
  133. else if (vals = getHwb(string)) {
  134. return vals[3];
  135. }
  136. }
  137. // generators
  138. function hexString(rgb) {
  139. return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
  140. + hexDouble(rgb[2]);
  141. }
  142. function rgbString(rgba, alpha) {
  143. if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
  144. return rgbaString(rgba, alpha);
  145. }
  146. return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
  147. }
  148. function rgbaString(rgba, alpha) {
  149. if (alpha === undefined) {
  150. alpha = (rgba[3] !== undefined ? rgba[3] : 1);
  151. }
  152. return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
  153. + ", " + alpha + ")";
  154. }
  155. function percentString(rgba, alpha) {
  156. if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
  157. return percentaString(rgba, alpha);
  158. }
  159. var r = Math.round(rgba[0]/255 * 100),
  160. g = Math.round(rgba[1]/255 * 100),
  161. b = Math.round(rgba[2]/255 * 100);
  162. return "rgb(" + r + "%, " + g + "%, " + b + "%)";
  163. }
  164. function percentaString(rgba, alpha) {
  165. var r = Math.round(rgba[0]/255 * 100),
  166. g = Math.round(rgba[1]/255 * 100),
  167. b = Math.round(rgba[2]/255 * 100);
  168. return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
  169. }
  170. function hslString(hsla, alpha) {
  171. if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
  172. return hslaString(hsla, alpha);
  173. }
  174. return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
  175. }
  176. function hslaString(hsla, alpha) {
  177. if (alpha === undefined) {
  178. alpha = (hsla[3] !== undefined ? hsla[3] : 1);
  179. }
  180. return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
  181. + alpha + ")";
  182. }
  183. // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
  184. // (hwb have alpha optional & 1 is default value)
  185. function hwbString(hwb, alpha) {
  186. if (alpha === undefined) {
  187. alpha = (hwb[3] !== undefined ? hwb[3] : 1);
  188. }
  189. return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
  190. + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
  191. }
  192. function keyword(rgb) {
  193. return reverseNames[rgb.slice(0, 3)];
  194. }
  195. // helpers
  196. function scale(num, min, max) {
  197. return Math.min(Math.max(min, num), max);
  198. }
  199. function hexDouble(num) {
  200. var str = num.toString(16).toUpperCase();
  201. return (str.length < 2) ? "0" + str : str;
  202. }
  203. //create a list of reverse color names
  204. var reverseNames = {};
  205. for (var name in colorNames) {
  206. reverseNames[colorNames[name]] = name;
  207. }
  208. },{"5":5}],2:[function(require,module,exports){
  209. /* MIT license */
  210. var convert = require(4);
  211. var string = require(1);
  212. var Color = function (obj) {
  213. if (obj instanceof Color) {
  214. return obj;
  215. }
  216. if (!(this instanceof Color)) {
  217. return new Color(obj);
  218. }
  219. this.values = {
  220. rgb: [0, 0, 0],
  221. hsl: [0, 0, 0],
  222. hsv: [0, 0, 0],
  223. hwb: [0, 0, 0],
  224. cmyk: [0, 0, 0, 0],
  225. alpha: 1
  226. };
  227. // parse Color() argument
  228. var vals;
  229. if (typeof obj === 'string') {
  230. vals = string.getRgba(obj);
  231. if (vals) {
  232. this.setValues('rgb', vals);
  233. } else if (vals = string.getHsla(obj)) {
  234. this.setValues('hsl', vals);
  235. } else if (vals = string.getHwb(obj)) {
  236. this.setValues('hwb', vals);
  237. } else {
  238. throw new Error('Unable to parse color from string "' + obj + '"');
  239. }
  240. } else if (typeof obj === 'object') {
  241. vals = obj;
  242. if (vals.r !== undefined || vals.red !== undefined) {
  243. this.setValues('rgb', vals);
  244. } else if (vals.l !== undefined || vals.lightness !== undefined) {
  245. this.setValues('hsl', vals);
  246. } else if (vals.v !== undefined || vals.value !== undefined) {
  247. this.setValues('hsv', vals);
  248. } else if (vals.w !== undefined || vals.whiteness !== undefined) {
  249. this.setValues('hwb', vals);
  250. } else if (vals.c !== undefined || vals.cyan !== undefined) {
  251. this.setValues('cmyk', vals);
  252. } else {
  253. throw new Error('Unable to parse color from object ' + JSON.stringify(obj));
  254. }
  255. }
  256. };
  257. Color.prototype = {
  258. rgb: function () {
  259. return this.setSpace('rgb', arguments);
  260. },
  261. hsl: function () {
  262. return this.setSpace('hsl', arguments);
  263. },
  264. hsv: function () {
  265. return this.setSpace('hsv', arguments);
  266. },
  267. hwb: function () {
  268. return this.setSpace('hwb', arguments);
  269. },
  270. cmyk: function () {
  271. return this.setSpace('cmyk', arguments);
  272. },
  273. rgbArray: function () {
  274. return this.values.rgb;
  275. },
  276. hslArray: function () {
  277. return this.values.hsl;
  278. },
  279. hsvArray: function () {
  280. return this.values.hsv;
  281. },
  282. hwbArray: function () {
  283. var values = this.values;
  284. if (values.alpha !== 1) {
  285. return values.hwb.concat([values.alpha]);
  286. }
  287. return values.hwb;
  288. },
  289. cmykArray: function () {
  290. return this.values.cmyk;
  291. },
  292. rgbaArray: function () {
  293. var values = this.values;
  294. return values.rgb.concat([values.alpha]);
  295. },
  296. hslaArray: function () {
  297. var values = this.values;
  298. return values.hsl.concat([values.alpha]);
  299. },
  300. alpha: function (val) {
  301. if (val === undefined) {
  302. return this.values.alpha;
  303. }
  304. this.setValues('alpha', val);
  305. return this;
  306. },
  307. red: function (val) {
  308. return this.setChannel('rgb', 0, val);
  309. },
  310. green: function (val) {
  311. return this.setChannel('rgb', 1, val);
  312. },
  313. blue: function (val) {
  314. return this.setChannel('rgb', 2, val);
  315. },
  316. hue: function (val) {
  317. if (val) {
  318. val %= 360;
  319. val = val < 0 ? 360 + val : val;
  320. }
  321. return this.setChannel('hsl', 0, val);
  322. },
  323. saturation: function (val) {
  324. return this.setChannel('hsl', 1, val);
  325. },
  326. lightness: function (val) {
  327. return this.setChannel('hsl', 2, val);
  328. },
  329. saturationv: function (val) {
  330. return this.setChannel('hsv', 1, val);
  331. },
  332. whiteness: function (val) {
  333. return this.setChannel('hwb', 1, val);
  334. },
  335. blackness: function (val) {
  336. return this.setChannel('hwb', 2, val);
  337. },
  338. value: function (val) {
  339. return this.setChannel('hsv', 2, val);
  340. },
  341. cyan: function (val) {
  342. return this.setChannel('cmyk', 0, val);
  343. },
  344. magenta: function (val) {
  345. return this.setChannel('cmyk', 1, val);
  346. },
  347. yellow: function (val) {
  348. return this.setChannel('cmyk', 2, val);
  349. },
  350. black: function (val) {
  351. return this.setChannel('cmyk', 3, val);
  352. },
  353. hexString: function () {
  354. return string.hexString(this.values.rgb);
  355. },
  356. rgbString: function () {
  357. return string.rgbString(this.values.rgb, this.values.alpha);
  358. },
  359. rgbaString: function () {
  360. return string.rgbaString(this.values.rgb, this.values.alpha);
  361. },
  362. percentString: function () {
  363. return string.percentString(this.values.rgb, this.values.alpha);
  364. },
  365. hslString: function () {
  366. return string.hslString(this.values.hsl, this.values.alpha);
  367. },
  368. hslaString: function () {
  369. return string.hslaString(this.values.hsl, this.values.alpha);
  370. },
  371. hwbString: function () {
  372. return string.hwbString(this.values.hwb, this.values.alpha);
  373. },
  374. keyword: function () {
  375. return string.keyword(this.values.rgb, this.values.alpha);
  376. },
  377. rgbNumber: function () {
  378. var rgb = this.values.rgb;
  379. return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
  380. },
  381. luminosity: function () {
  382. // http://www.w3.org/TR/WCAG20/#relativeluminancedef
  383. var rgb = this.values.rgb;
  384. var lum = [];
  385. for (var i = 0; i < rgb.length; i++) {
  386. var chan = rgb[i] / 255;
  387. lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
  388. }
  389. return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
  390. },
  391. contrast: function (color2) {
  392. // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
  393. var lum1 = this.luminosity();
  394. var lum2 = color2.luminosity();
  395. if (lum1 > lum2) {
  396. return (lum1 + 0.05) / (lum2 + 0.05);
  397. }
  398. return (lum2 + 0.05) / (lum1 + 0.05);
  399. },
  400. level: function (color2) {
  401. var contrastRatio = this.contrast(color2);
  402. if (contrastRatio >= 7.1) {
  403. return 'AAA';
  404. }
  405. return (contrastRatio >= 4.5) ? 'AA' : '';
  406. },
  407. dark: function () {
  408. // YIQ equation from http://24ways.org/2010/calculating-color-contrast
  409. var rgb = this.values.rgb;
  410. var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
  411. return yiq < 128;
  412. },
  413. light: function () {
  414. return !this.dark();
  415. },
  416. negate: function () {
  417. var rgb = [];
  418. for (var i = 0; i < 3; i++) {
  419. rgb[i] = 255 - this.values.rgb[i];
  420. }
  421. this.setValues('rgb', rgb);
  422. return this;
  423. },
  424. lighten: function (ratio) {
  425. var hsl = this.values.hsl;
  426. hsl[2] += hsl[2] * ratio;
  427. this.setValues('hsl', hsl);
  428. return this;
  429. },
  430. darken: function (ratio) {
  431. var hsl = this.values.hsl;
  432. hsl[2] -= hsl[2] * ratio;
  433. this.setValues('hsl', hsl);
  434. return this;
  435. },
  436. saturate: function (ratio) {
  437. var hsl = this.values.hsl;
  438. hsl[1] += hsl[1] * ratio;
  439. this.setValues('hsl', hsl);
  440. return this;
  441. },
  442. desaturate: function (ratio) {
  443. var hsl = this.values.hsl;
  444. hsl[1] -= hsl[1] * ratio;
  445. this.setValues('hsl', hsl);
  446. return this;
  447. },
  448. whiten: function (ratio) {
  449. var hwb = this.values.hwb;
  450. hwb[1] += hwb[1] * ratio;
  451. this.setValues('hwb', hwb);
  452. return this;
  453. },
  454. blacken: function (ratio) {
  455. var hwb = this.values.hwb;
  456. hwb[2] += hwb[2] * ratio;
  457. this.setValues('hwb', hwb);
  458. return this;
  459. },
  460. greyscale: function () {
  461. var rgb = this.values.rgb;
  462. // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
  463. var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
  464. this.setValues('rgb', [val, val, val]);
  465. return this;
  466. },
  467. clearer: function (ratio) {
  468. var alpha = this.values.alpha;
  469. this.setValues('alpha', alpha - (alpha * ratio));
  470. return this;
  471. },
  472. opaquer: function (ratio) {
  473. var alpha = this.values.alpha;
  474. this.setValues('alpha', alpha + (alpha * ratio));
  475. return this;
  476. },
  477. rotate: function (degrees) {
  478. var hsl = this.values.hsl;
  479. var hue = (hsl[0] + degrees) % 360;
  480. hsl[0] = hue < 0 ? 360 + hue : hue;
  481. this.setValues('hsl', hsl);
  482. return this;
  483. },
  484. /**
  485. * Ported from sass implementation in C
  486. * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
  487. */
  488. mix: function (mixinColor, weight) {
  489. var color1 = this;
  490. var color2 = mixinColor;
  491. var p = weight === undefined ? 0.5 : weight;
  492. var w = 2 * p - 1;
  493. var a = color1.alpha() - color2.alpha();
  494. var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
  495. var w2 = 1 - w1;
  496. return this
  497. .rgb(
  498. w1 * color1.red() + w2 * color2.red(),
  499. w1 * color1.green() + w2 * color2.green(),
  500. w1 * color1.blue() + w2 * color2.blue()
  501. )
  502. .alpha(color1.alpha() * p + color2.alpha() * (1 - p));
  503. },
  504. toJSON: function () {
  505. return this.rgb();
  506. },
  507. clone: function () {
  508. // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
  509. // making the final build way to big to embed in Chart.js. So let's do it manually,
  510. // assuming that values to clone are 1 dimension arrays containing only numbers,
  511. // except 'alpha' which is a number.
  512. var result = new Color();
  513. var source = this.values;
  514. var target = result.values;
  515. var value, type;
  516. for (var prop in source) {
  517. if (source.hasOwnProperty(prop)) {
  518. value = source[prop];
  519. type = ({}).toString.call(value);
  520. if (type === '[object Array]') {
  521. target[prop] = value.slice(0);
  522. } else if (type === '[object Number]') {
  523. target[prop] = value;
  524. } else {
  525. console.error('unexpected color value:', value);
  526. }
  527. }
  528. }
  529. return result;
  530. }
  531. };
  532. Color.prototype.spaces = {
  533. rgb: ['red', 'green', 'blue'],
  534. hsl: ['hue', 'saturation', 'lightness'],
  535. hsv: ['hue', 'saturation', 'value'],
  536. hwb: ['hue', 'whiteness', 'blackness'],
  537. cmyk: ['cyan', 'magenta', 'yellow', 'black']
  538. };
  539. Color.prototype.maxes = {
  540. rgb: [255, 255, 255],
  541. hsl: [360, 100, 100],
  542. hsv: [360, 100, 100],
  543. hwb: [360, 100, 100],
  544. cmyk: [100, 100, 100, 100]
  545. };
  546. Color.prototype.getValues = function (space) {
  547. var values = this.values;
  548. var vals = {};
  549. for (var i = 0; i < space.length; i++) {
  550. vals[space.charAt(i)] = values[space][i];
  551. }
  552. if (values.alpha !== 1) {
  553. vals.a = values.alpha;
  554. }
  555. // {r: 255, g: 255, b: 255, a: 0.4}
  556. return vals;
  557. };
  558. Color.prototype.setValues = function (space, vals) {
  559. var values = this.values;
  560. var spaces = this.spaces;
  561. var maxes = this.maxes;
  562. var alpha = 1;
  563. var i;
  564. if (space === 'alpha') {
  565. alpha = vals;
  566. } else if (vals.length) {
  567. // [10, 10, 10]
  568. values[space] = vals.slice(0, space.length);
  569. alpha = vals[space.length];
  570. } else if (vals[space.charAt(0)] !== undefined) {
  571. // {r: 10, g: 10, b: 10}
  572. for (i = 0; i < space.length; i++) {
  573. values[space][i] = vals[space.charAt(i)];
  574. }
  575. alpha = vals.a;
  576. } else if (vals[spaces[space][0]] !== undefined) {
  577. // {red: 10, green: 10, blue: 10}
  578. var chans = spaces[space];
  579. for (i = 0; i < space.length; i++) {
  580. values[space][i] = vals[chans[i]];
  581. }
  582. alpha = vals.alpha;
  583. }
  584. values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
  585. if (space === 'alpha') {
  586. return false;
  587. }
  588. var capped;
  589. // cap values of the space prior converting all values
  590. for (i = 0; i < space.length; i++) {
  591. capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
  592. values[space][i] = Math.round(capped);
  593. }
  594. // convert to all the other color spaces
  595. for (var sname in spaces) {
  596. if (sname !== space) {
  597. values[sname] = convert[space][sname](values[space]);
  598. }
  599. }
  600. return true;
  601. };
  602. Color.prototype.setSpace = function (space, args) {
  603. var vals = args[0];
  604. if (vals === undefined) {
  605. // color.rgb()
  606. return this.getValues(space);
  607. }
  608. // color.rgb(10, 10, 10)
  609. if (typeof vals === 'number') {
  610. vals = Array.prototype.slice.call(args);
  611. }
  612. this.setValues(space, vals);
  613. return this;
  614. };
  615. Color.prototype.setChannel = function (space, index, val) {
  616. var svalues = this.values[space];
  617. if (val === undefined) {
  618. // color.red()
  619. return svalues[index];
  620. } else if (val === svalues[index]) {
  621. // color.red(color.red())
  622. return this;
  623. }
  624. // color.red(100)
  625. svalues[index] = val;
  626. this.setValues(space, svalues);
  627. return this;
  628. };
  629. if (typeof window !== 'undefined') {
  630. window.Color = Color;
  631. }
  632. module.exports = Color;
  633. },{"1":1,"4":4}],3:[function(require,module,exports){
  634. /* MIT license */
  635. module.exports = {
  636. rgb2hsl: rgb2hsl,
  637. rgb2hsv: rgb2hsv,
  638. rgb2hwb: rgb2hwb,
  639. rgb2cmyk: rgb2cmyk,
  640. rgb2keyword: rgb2keyword,
  641. rgb2xyz: rgb2xyz,
  642. rgb2lab: rgb2lab,
  643. rgb2lch: rgb2lch,
  644. hsl2rgb: hsl2rgb,
  645. hsl2hsv: hsl2hsv,
  646. hsl2hwb: hsl2hwb,
  647. hsl2cmyk: hsl2cmyk,
  648. hsl2keyword: hsl2keyword,
  649. hsv2rgb: hsv2rgb,
  650. hsv2hsl: hsv2hsl,
  651. hsv2hwb: hsv2hwb,
  652. hsv2cmyk: hsv2cmyk,
  653. hsv2keyword: hsv2keyword,
  654. hwb2rgb: hwb2rgb,
  655. hwb2hsl: hwb2hsl,
  656. hwb2hsv: hwb2hsv,
  657. hwb2cmyk: hwb2cmyk,
  658. hwb2keyword: hwb2keyword,
  659. cmyk2rgb: cmyk2rgb,
  660. cmyk2hsl: cmyk2hsl,
  661. cmyk2hsv: cmyk2hsv,
  662. cmyk2hwb: cmyk2hwb,
  663. cmyk2keyword: cmyk2keyword,
  664. keyword2rgb: keyword2rgb,
  665. keyword2hsl: keyword2hsl,
  666. keyword2hsv: keyword2hsv,
  667. keyword2hwb: keyword2hwb,
  668. keyword2cmyk: keyword2cmyk,
  669. keyword2lab: keyword2lab,
  670. keyword2xyz: keyword2xyz,
  671. xyz2rgb: xyz2rgb,
  672. xyz2lab: xyz2lab,
  673. xyz2lch: xyz2lch,
  674. lab2xyz: lab2xyz,
  675. lab2rgb: lab2rgb,
  676. lab2lch: lab2lch,
  677. lch2lab: lch2lab,
  678. lch2xyz: lch2xyz,
  679. lch2rgb: lch2rgb
  680. }
  681. function rgb2hsl(rgb) {
  682. var r = rgb[0]/255,
  683. g = rgb[1]/255,
  684. b = rgb[2]/255,
  685. min = Math.min(r, g, b),
  686. max = Math.max(r, g, b),
  687. delta = max - min,
  688. h, s, l;
  689. if (max == min)
  690. h = 0;
  691. else if (r == max)
  692. h = (g - b) / delta;
  693. else if (g == max)
  694. h = 2 + (b - r) / delta;
  695. else if (b == max)
  696. h = 4 + (r - g)/ delta;
  697. h = Math.min(h * 60, 360);
  698. if (h < 0)
  699. h += 360;
  700. l = (min + max) / 2;
  701. if (max == min)
  702. s = 0;
  703. else if (l <= 0.5)
  704. s = delta / (max + min);
  705. else
  706. s = delta / (2 - max - min);
  707. return [h, s * 100, l * 100];
  708. }
  709. function rgb2hsv(rgb) {
  710. var r = rgb[0],
  711. g = rgb[1],
  712. b = rgb[2],
  713. min = Math.min(r, g, b),
  714. max = Math.max(r, g, b),
  715. delta = max - min,
  716. h, s, v;
  717. if (max == 0)
  718. s = 0;
  719. else
  720. s = (delta/max * 1000)/10;
  721. if (max == min)
  722. h = 0;
  723. else if (r == max)
  724. h = (g - b) / delta;
  725. else if (g == max)
  726. h = 2 + (b - r) / delta;
  727. else if (b == max)
  728. h = 4 + (r - g) / delta;
  729. h = Math.min(h * 60, 360);
  730. if (h < 0)
  731. h += 360;
  732. v = ((max / 255) * 1000) / 10;
  733. return [h, s, v];
  734. }
  735. function rgb2hwb(rgb) {
  736. var r = rgb[0],
  737. g = rgb[1],
  738. b = rgb[2],
  739. h = rgb2hsl(rgb)[0],
  740. w = 1/255 * Math.min(r, Math.min(g, b)),
  741. b = 1 - 1/255 * Math.max(r, Math.max(g, b));
  742. return [h, w * 100, b * 100];
  743. }
  744. function rgb2cmyk(rgb) {
  745. var r = rgb[0] / 255,
  746. g = rgb[1] / 255,
  747. b = rgb[2] / 255,
  748. c, m, y, k;
  749. k = Math.min(1 - r, 1 - g, 1 - b);
  750. c = (1 - r - k) / (1 - k) || 0;
  751. m = (1 - g - k) / (1 - k) || 0;
  752. y = (1 - b - k) / (1 - k) || 0;
  753. return [c * 100, m * 100, y * 100, k * 100];
  754. }
  755. function rgb2keyword(rgb) {
  756. return reverseKeywords[JSON.stringify(rgb)];
  757. }
  758. function rgb2xyz(rgb) {
  759. var r = rgb[0] / 255,
  760. g = rgb[1] / 255,
  761. b = rgb[2] / 255;
  762. // assume sRGB
  763. r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
  764. g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
  765. b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
  766. var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
  767. var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
  768. var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
  769. return [x * 100, y *100, z * 100];
  770. }
  771. function rgb2lab(rgb) {
  772. var xyz = rgb2xyz(rgb),
  773. x = xyz[0],
  774. y = xyz[1],
  775. z = xyz[2],
  776. l, a, b;
  777. x /= 95.047;
  778. y /= 100;
  779. z /= 108.883;
  780. x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
  781. y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
  782. z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
  783. l = (116 * y) - 16;
  784. a = 500 * (x - y);
  785. b = 200 * (y - z);
  786. return [l, a, b];
  787. }
  788. function rgb2lch(args) {
  789. return lab2lch(rgb2lab(args));
  790. }
  791. function hsl2rgb(hsl) {
  792. var h = hsl[0] / 360,
  793. s = hsl[1] / 100,
  794. l = hsl[2] / 100,
  795. t1, t2, t3, rgb, val;
  796. if (s == 0) {
  797. val = l * 255;
  798. return [val, val, val];
  799. }
  800. if (l < 0.5)
  801. t2 = l * (1 + s);
  802. else
  803. t2 = l + s - l * s;
  804. t1 = 2 * l - t2;
  805. rgb = [0, 0, 0];
  806. for (var i = 0; i < 3; i++) {
  807. t3 = h + 1 / 3 * - (i - 1);
  808. t3 < 0 && t3++;
  809. t3 > 1 && t3--;
  810. if (6 * t3 < 1)
  811. val = t1 + (t2 - t1) * 6 * t3;
  812. else if (2 * t3 < 1)
  813. val = t2;
  814. else if (3 * t3 < 2)
  815. val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
  816. else
  817. val = t1;
  818. rgb[i] = val * 255;
  819. }
  820. return rgb;
  821. }
  822. function hsl2hsv(hsl) {
  823. var h = hsl[0],
  824. s = hsl[1] / 100,
  825. l = hsl[2] / 100,
  826. sv, v;
  827. if(l === 0) {
  828. // no need to do calc on black
  829. // also avoids divide by 0 error
  830. return [0, 0, 0];
  831. }
  832. l *= 2;
  833. s *= (l <= 1) ? l : 2 - l;
  834. v = (l + s) / 2;
  835. sv = (2 * s) / (l + s);
  836. return [h, sv * 100, v * 100];
  837. }
  838. function hsl2hwb(args) {
  839. return rgb2hwb(hsl2rgb(args));
  840. }
  841. function hsl2cmyk(args) {
  842. return rgb2cmyk(hsl2rgb(args));
  843. }
  844. function hsl2keyword(args) {
  845. return rgb2keyword(hsl2rgb(args));
  846. }
  847. function hsv2rgb(hsv) {
  848. var h = hsv[0] / 60,
  849. s = hsv[1] / 100,
  850. v = hsv[2] / 100,
  851. hi = Math.floor(h) % 6;
  852. var f = h - Math.floor(h),
  853. p = 255 * v * (1 - s),
  854. q = 255 * v * (1 - (s * f)),
  855. t = 255 * v * (1 - (s * (1 - f))),
  856. v = 255 * v;
  857. switch(hi) {
  858. case 0:
  859. return [v, t, p];
  860. case 1:
  861. return [q, v, p];
  862. case 2:
  863. return [p, v, t];
  864. case 3:
  865. return [p, q, v];
  866. case 4:
  867. return [t, p, v];
  868. case 5:
  869. return [v, p, q];
  870. }
  871. }
  872. function hsv2hsl(hsv) {
  873. var h = hsv[0],
  874. s = hsv[1] / 100,
  875. v = hsv[2] / 100,
  876. sl, l;
  877. l = (2 - s) * v;
  878. sl = s * v;
  879. sl /= (l <= 1) ? l : 2 - l;
  880. sl = sl || 0;
  881. l /= 2;
  882. return [h, sl * 100, l * 100];
  883. }
  884. function hsv2hwb(args) {
  885. return rgb2hwb(hsv2rgb(args))
  886. }
  887. function hsv2cmyk(args) {
  888. return rgb2cmyk(hsv2rgb(args));
  889. }
  890. function hsv2keyword(args) {
  891. return rgb2keyword(hsv2rgb(args));
  892. }
  893. // http://dev.w3.org/csswg/css-color/#hwb-to-rgb
  894. function hwb2rgb(hwb) {
  895. var h = hwb[0] / 360,
  896. wh = hwb[1] / 100,
  897. bl = hwb[2] / 100,
  898. ratio = wh + bl,
  899. i, v, f, n;
  900. // wh + bl cant be > 1
  901. if (ratio > 1) {
  902. wh /= ratio;
  903. bl /= ratio;
  904. }
  905. i = Math.floor(6 * h);
  906. v = 1 - bl;
  907. f = 6 * h - i;
  908. if ((i & 0x01) != 0) {
  909. f = 1 - f;
  910. }
  911. n = wh + f * (v - wh); // linear interpolation
  912. switch (i) {
  913. default:
  914. case 6:
  915. case 0: r = v; g = n; b = wh; break;
  916. case 1: r = n; g = v; b = wh; break;
  917. case 2: r = wh; g = v; b = n; break;
  918. case 3: r = wh; g = n; b = v; break;
  919. case 4: r = n; g = wh; b = v; break;
  920. case 5: r = v; g = wh; b = n; break;
  921. }
  922. return [r * 255, g * 255, b * 255];
  923. }
  924. function hwb2hsl(args) {
  925. return rgb2hsl(hwb2rgb(args));
  926. }
  927. function hwb2hsv(args) {
  928. return rgb2hsv(hwb2rgb(args));
  929. }
  930. function hwb2cmyk(args) {
  931. return rgb2cmyk(hwb2rgb(args));
  932. }
  933. function hwb2keyword(args) {
  934. return rgb2keyword(hwb2rgb(args));
  935. }
  936. function cmyk2rgb(cmyk) {
  937. var c = cmyk[0] / 100,
  938. m = cmyk[1] / 100,
  939. y = cmyk[2] / 100,
  940. k = cmyk[3] / 100,
  941. r, g, b;
  942. r = 1 - Math.min(1, c * (1 - k) + k);
  943. g = 1 - Math.min(1, m * (1 - k) + k);
  944. b = 1 - Math.min(1, y * (1 - k) + k);
  945. return [r * 255, g * 255, b * 255];
  946. }
  947. function cmyk2hsl(args) {
  948. return rgb2hsl(cmyk2rgb(args));
  949. }
  950. function cmyk2hsv(args) {
  951. return rgb2hsv(cmyk2rgb(args));
  952. }
  953. function cmyk2hwb(args) {
  954. return rgb2hwb(cmyk2rgb(args));
  955. }
  956. function cmyk2keyword(args) {
  957. return rgb2keyword(cmyk2rgb(args));
  958. }
  959. function xyz2rgb(xyz) {
  960. var x = xyz[0] / 100,
  961. y = xyz[1] / 100,
  962. z = xyz[2] / 100,
  963. r, g, b;
  964. r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
  965. g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
  966. b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
  967. // assume sRGB
  968. r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
  969. : r = (r * 12.92);
  970. g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
  971. : g = (g * 12.92);
  972. b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
  973. : b = (b * 12.92);
  974. r = Math.min(Math.max(0, r), 1);
  975. g = Math.min(Math.max(0, g), 1);
  976. b = Math.min(Math.max(0, b), 1);
  977. return [r * 255, g * 255, b * 255];
  978. }
  979. function xyz2lab(xyz) {
  980. var x = xyz[0],
  981. y = xyz[1],
  982. z = xyz[2],
  983. l, a, b;
  984. x /= 95.047;
  985. y /= 100;
  986. z /= 108.883;
  987. x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
  988. y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
  989. z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
  990. l = (116 * y) - 16;
  991. a = 500 * (x - y);
  992. b = 200 * (y - z);
  993. return [l, a, b];
  994. }
  995. function xyz2lch(args) {
  996. return lab2lch(xyz2lab(args));
  997. }
  998. function lab2xyz(lab) {
  999. var l = lab[0],
  1000. a = lab[1],
  1001. b = lab[2],
  1002. x, y, z, y2;
  1003. if (l <= 8) {
  1004. y = (l * 100) / 903.3;
  1005. y2 = (7.787 * (y / 100)) + (16 / 116);
  1006. } else {
  1007. y = 100 * Math.pow((l + 16) / 116, 3);
  1008. y2 = Math.pow(y / 100, 1/3);
  1009. }
  1010. x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
  1011. z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
  1012. return [x, y, z];
  1013. }
  1014. function lab2lch(lab) {
  1015. var l = lab[0],
  1016. a = lab[1],
  1017. b = lab[2],
  1018. hr, h, c;
  1019. hr = Math.atan2(b, a);
  1020. h = hr * 360 / 2 / Math.PI;
  1021. if (h < 0) {
  1022. h += 360;
  1023. }
  1024. c = Math.sqrt(a * a + b * b);
  1025. return [l, c, h];
  1026. }
  1027. function lab2rgb(args) {
  1028. return xyz2rgb(lab2xyz(args));
  1029. }
  1030. function lch2lab(lch) {
  1031. var l = lch[0],
  1032. c = lch[1],
  1033. h = lch[2],
  1034. a, b, hr;
  1035. hr = h / 360 * 2 * Math.PI;
  1036. a = c * Math.cos(hr);
  1037. b = c * Math.sin(hr);
  1038. return [l, a, b];
  1039. }
  1040. function lch2xyz(args) {
  1041. return lab2xyz(lch2lab(args));
  1042. }
  1043. function lch2rgb(args) {
  1044. return lab2rgb(lch2lab(args));
  1045. }
  1046. function keyword2rgb(keyword) {
  1047. return cssKeywords[keyword];
  1048. }
  1049. function keyword2hsl(args) {
  1050. return rgb2hsl(keyword2rgb(args));
  1051. }
  1052. function keyword2hsv(args) {
  1053. return rgb2hsv(keyword2rgb(args));
  1054. }
  1055. function keyword2hwb(args) {
  1056. return rgb2hwb(keyword2rgb(args));
  1057. }
  1058. function keyword2cmyk(args) {
  1059. return rgb2cmyk(keyword2rgb(args));
  1060. }
  1061. function keyword2lab(args) {
  1062. return rgb2lab(keyword2rgb(args));
  1063. }
  1064. function keyword2xyz(args) {
  1065. return rgb2xyz(keyword2rgb(args));
  1066. }
  1067. var cssKeywords = {
  1068. aliceblue: [240,248,255],
  1069. antiquewhite: [250,235,215],
  1070. aqua: [0,255,255],
  1071. aquamarine: [127,255,212],
  1072. azure: [240,255,255],
  1073. beige: [245,245,220],
  1074. bisque: [255,228,196],
  1075. black: [0,0,0],
  1076. blanchedalmond: [255,235,205],
  1077. blue: [0,0,255],
  1078. blueviolet: [138,43,226],
  1079. brown: [165,42,42],
  1080. burlywood: [222,184,135],
  1081. cadetblue: [95,158,160],
  1082. chartreuse: [127,255,0],
  1083. chocolate: [210,105,30],
  1084. coral: [255,127,80],
  1085. cornflowerblue: [100,149,237],
  1086. cornsilk: [255,248,220],
  1087. crimson: [220,20,60],
  1088. cyan: [0,255,255],
  1089. darkblue: [0,0,139],
  1090. darkcyan: [0,139,139],
  1091. darkgoldenrod: [184,134,11],
  1092. darkgray: [169,169,169],
  1093. darkgreen: [0,100,0],
  1094. darkgrey: [169,169,169],
  1095. darkkhaki: [189,183,107],
  1096. darkmagenta: [139,0,139],
  1097. darkolivegreen: [85,107,47],
  1098. darkorange: [255,140,0],
  1099. darkorchid: [153,50,204],
  1100. darkred: [139,0,0],
  1101. darksalmon: [233,150,122],
  1102. darkseagreen: [143,188,143],
  1103. darkslateblue: [72,61,139],
  1104. darkslategray: [47,79,79],
  1105. darkslategrey: [47,79,79],
  1106. darkturquoise: [0,206,209],
  1107. darkviolet: [148,0,211],
  1108. deeppink: [255,20,147],
  1109. deepskyblue: [0,191,255],
  1110. dimgray: [105,105,105],
  1111. dimgrey: [105,105,105],
  1112. dodgerblue: [30,144,255],
  1113. firebrick: [178,34,34],
  1114. floralwhite: [255,250,240],
  1115. forestgreen: [34,139,34],
  1116. fuchsia: [255,0,255],
  1117. gainsboro: [220,220,220],
  1118. ghostwhite: [248,248,255],
  1119. gold: [255,215,0],
  1120. goldenrod: [218,165,32],
  1121. gray: [128,128,128],
  1122. green: [0,128,0],
  1123. greenyellow: [173,255,47],
  1124. grey: [128,128,128],
  1125. honeydew: [240,255,240],
  1126. hotpink: [255,105,180],
  1127. indianred: [205,92,92],
  1128. indigo: [75,0,130],
  1129. ivory: [255,255,240],
  1130. khaki: [240,230,140],
  1131. lavender: [230,230,250],
  1132. lavenderblush: [255,240,245],
  1133. lawngreen: [124,252,0],
  1134. lemonchiffon: [255,250,205],
  1135. lightblue: [173,216,230],
  1136. lightcoral: [240,128,128],
  1137. lightcyan: [224,255,255],
  1138. lightgoldenrodyellow: [250,250,210],
  1139. lightgray: [211,211,211],
  1140. lightgreen: [144,238,144],
  1141. lightgrey: [211,211,211],
  1142. lightpink: [255,182,193],
  1143. lightsalmon: [255,160,122],
  1144. lightseagreen: [32,178,170],
  1145. lightskyblue: [135,206,250],
  1146. lightslategray: [119,136,153],
  1147. lightslategrey: [119,136,153],
  1148. lightsteelblue: [176,196,222],
  1149. lightyellow: [255,255,224],
  1150. lime: [0,255,0],
  1151. limegreen: [50,205,50],
  1152. linen: [250,240,230],
  1153. magenta: [255,0,255],
  1154. maroon: [128,0,0],
  1155. mediumaquamarine: [102,205,170],
  1156. mediumblue: [0,0,205],
  1157. mediumorchid: [186,85,211],
  1158. mediumpurple: [147,112,219],
  1159. mediumseagreen: [60,179,113],
  1160. mediumslateblue: [123,104,238],
  1161. mediumspringgreen: [0,250,154],
  1162. mediumturquoise: [72,209,204],
  1163. mediumvioletred: [199,21,133],
  1164. midnightblue: [25,25,112],
  1165. mintcream: [245,255,250],
  1166. mistyrose: [255,228,225],
  1167. moccasin: [255,228,181],
  1168. navajowhite: [255,222,173],
  1169. navy: [0,0,128],
  1170. oldlace: [253,245,230],
  1171. olive: [128,128,0],
  1172. olivedrab: [107,142,35],
  1173. orange: [255,165,0],
  1174. orangered: [255,69,0],
  1175. orchid: [218,112,214],
  1176. palegoldenrod: [238,232,170],
  1177. palegreen: [152,251,152],
  1178. paleturquoise: [175,238,238],
  1179. palevioletred: [219,112,147],
  1180. papayawhip: [255,239,213],
  1181. peachpuff: [255,218,185],
  1182. peru: [205,133,63],
  1183. pink: [255,192,203],
  1184. plum: [221,160,221],
  1185. powderblue: [176,224,230],
  1186. purple: [128,0,128],
  1187. rebeccapurple: [102, 51, 153],
  1188. red: [255,0,0],
  1189. rosybrown: [188,143,143],
  1190. royalblue: [65,105,225],
  1191. saddlebrown: [139,69,19],
  1192. salmon: [250,128,114],
  1193. sandybrown: [244,164,96],
  1194. seagreen: [46,139,87],
  1195. seashell: [255,245,238],
  1196. sienna: [160,82,45],
  1197. silver: [192,192,192],
  1198. skyblue: [135,206,235],
  1199. slateblue: [106,90,205],
  1200. slategray: [112,128,144],
  1201. slategrey: [112,128,144],
  1202. snow: [255,250,250],
  1203. springgreen: [0,255,127],
  1204. steelblue: [70,130,180],
  1205. tan: [210,180,140],
  1206. teal: [0,128,128],
  1207. thistle: [216,191,216],
  1208. tomato: [255,99,71],
  1209. turquoise: [64,224,208],
  1210. violet: [238,130,238],
  1211. wheat: [245,222,179],
  1212. white: [255,255,255],
  1213. whitesmoke: [245,245,245],
  1214. yellow: [255,255,0],
  1215. yellowgreen: [154,205,50]
  1216. };
  1217. var reverseKeywords = {};
  1218. for (var key in cssKeywords) {
  1219. reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
  1220. }
  1221. },{}],4:[function(require,module,exports){
  1222. var conversions = require(3);
  1223. var convert = function() {
  1224. return new Converter();
  1225. }
  1226. for (var func in conversions) {
  1227. // export Raw versions
  1228. convert[func + "Raw"] = (function(func) {
  1229. // accept array or plain args
  1230. return function(arg) {
  1231. if (typeof arg == "number")
  1232. arg = Array.prototype.slice.call(arguments);
  1233. return conversions[func](arg);
  1234. }
  1235. })(func);
  1236. var pair = /(\w+)2(\w+)/.exec(func),
  1237. from = pair[1],
  1238. to = pair[2];
  1239. // export rgb2hsl and ["rgb"]["hsl"]
  1240. convert[from] = convert[from] || {};
  1241. convert[from][to] = convert[func] = (function(func) {
  1242. return function(arg) {
  1243. if (typeof arg == "number")
  1244. arg = Array.prototype.slice.call(arguments);
  1245. var val = conversions[func](arg);
  1246. if (typeof val == "string" || val === undefined)
  1247. return val; // keyword
  1248. for (var i = 0; i < val.length; i++)
  1249. val[i] = Math.round(val[i]);
  1250. return val;
  1251. }
  1252. })(func);
  1253. }
  1254. /* Converter does lazy conversion and caching */
  1255. var Converter = function() {
  1256. this.convs = {};
  1257. };
  1258. /* Either get the values for a space or
  1259. set the values for a space, depending on args */
  1260. Converter.prototype.routeSpace = function(space, args) {
  1261. var values = args[0];
  1262. if (values === undefined) {
  1263. // color.rgb()
  1264. return this.getValues(space);
  1265. }
  1266. // color.rgb(10, 10, 10)
  1267. if (typeof values == "number") {
  1268. values = Array.prototype.slice.call(args);
  1269. }
  1270. return this.setValues(space, values);
  1271. };
  1272. /* Set the values for a space, invalidating cache */
  1273. Converter.prototype.setValues = function(space, values) {
  1274. this.space = space;
  1275. this.convs = {};
  1276. this.convs[space] = values;
  1277. return this;
  1278. };
  1279. /* Get the values for a space. If there's already
  1280. a conversion for the space, fetch it, otherwise
  1281. compute it */
  1282. Converter.prototype.getValues = function(space) {
  1283. var vals = this.convs[space];
  1284. if (!vals) {
  1285. var fspace = this.space,
  1286. from = this.convs[fspace];
  1287. vals = convert[fspace][space](from);
  1288. this.convs[space] = vals;
  1289. }
  1290. return vals;
  1291. };
  1292. ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
  1293. Converter.prototype[space] = function(vals) {
  1294. return this.routeSpace(space, arguments);
  1295. }
  1296. });
  1297. module.exports = convert;
  1298. },{"3":3}],5:[function(require,module,exports){
  1299. module.exports = {
  1300. "aliceblue": [240, 248, 255],
  1301. "antiquewhite": [250, 235, 215],
  1302. "aqua": [0, 255, 255],
  1303. "aquamarine": [127, 255, 212],
  1304. "azure": [240, 255, 255],
  1305. "beige": [245, 245, 220],
  1306. "bisque": [255, 228, 196],
  1307. "black": [0, 0, 0],
  1308. "blanchedalmond": [255, 235, 205],
  1309. "blue": [0, 0, 255],
  1310. "blueviolet": [138, 43, 226],
  1311. "brown": [165, 42, 42],
  1312. "burlywood": [222, 184, 135],
  1313. "cadetblue": [95, 158, 160],
  1314. "chartreuse": [127, 255, 0],
  1315. "chocolate": [210, 105, 30],
  1316. "coral": [255, 127, 80],
  1317. "cornflowerblue": [100, 149, 237],
  1318. "cornsilk": [255, 248, 220],
  1319. "crimson": [220, 20, 60],
  1320. "cyan": [0, 255, 255],
  1321. "darkblue": [0, 0, 139],
  1322. "darkcyan": [0, 139, 139],
  1323. "darkgoldenrod": [184, 134, 11],
  1324. "darkgray": [169, 169, 169],
  1325. "darkgreen": [0, 100, 0],
  1326. "darkgrey": [169, 169, 169],
  1327. "darkkhaki": [189, 183, 107],
  1328. "darkmagenta": [139, 0, 139],
  1329. "darkolivegreen": [85, 107, 47],
  1330. "darkorange": [255, 140, 0],
  1331. "darkorchid": [153, 50, 204],
  1332. "darkred": [139, 0, 0],
  1333. "darksalmon": [233, 150, 122],
  1334. "darkseagreen": [143, 188, 143],
  1335. "darkslateblue": [72, 61, 139],
  1336. "darkslategray": [47, 79, 79],
  1337. "darkslategrey": [47, 79, 79],
  1338. "darkturquoise": [0, 206, 209],
  1339. "darkviolet": [148, 0, 211],
  1340. "deeppink": [255, 20, 147],
  1341. "deepskyblue": [0, 191, 255],
  1342. "dimgray": [105, 105, 105],
  1343. "dimgrey": [105, 105, 105],
  1344. "dodgerblue": [30, 144, 255],
  1345. "firebrick": [178, 34, 34],
  1346. "floralwhite": [255, 250, 240],
  1347. "forestgreen": [34, 139, 34],
  1348. "fuchsia": [255, 0, 255],
  1349. "gainsboro": [220, 220, 220],
  1350. "ghostwhite": [248, 248, 255],
  1351. "gold": [255, 215, 0],
  1352. "goldenrod": [218, 165, 32],
  1353. "gray": [128, 128, 128],
  1354. "green": [0, 128, 0],
  1355. "greenyellow": [173, 255, 47],
  1356. "grey": [128, 128, 128],
  1357. "honeydew": [240, 255, 240],
  1358. "hotpink": [255, 105, 180],
  1359. "indianred": [205, 92, 92],
  1360. "indigo": [75, 0, 130],
  1361. "ivory": [255, 255, 240],
  1362. "khaki": [240, 230, 140],
  1363. "lavender": [230, 230, 250],
  1364. "lavenderblush": [255, 240, 245],
  1365. "lawngreen": [124, 252, 0],
  1366. "lemonchiffon": [255, 250, 205],
  1367. "lightblue": [173, 216, 230],
  1368. "lightcoral": [240, 128, 128],
  1369. "lightcyan": [224, 255, 255],
  1370. "lightgoldenrodyellow": [250, 250, 210],
  1371. "lightgray": [211, 211, 211],
  1372. "lightgreen": [144, 238, 144],
  1373. "lightgrey": [211, 211, 211],
  1374. "lightpink": [255, 182, 193],
  1375. "lightsalmon": [255, 160, 122],
  1376. "lightseagreen": [32, 178, 170],
  1377. "lightskyblue": [135, 206, 250],
  1378. "lightslategray": [119, 136, 153],
  1379. "lightslategrey": [119, 136, 153],
  1380. "lightsteelblue": [176, 196, 222],
  1381. "lightyellow": [255, 255, 224],
  1382. "lime": [0, 255, 0],
  1383. "limegreen": [50, 205, 50],
  1384. "linen": [250, 240, 230],
  1385. "magenta": [255, 0, 255],
  1386. "maroon": [128, 0, 0],
  1387. "mediumaquamarine": [102, 205, 170],
  1388. "mediumblue": [0, 0, 205],
  1389. "mediumorchid": [186, 85, 211],
  1390. "mediumpurple": [147, 112, 219],
  1391. "mediumseagreen": [60, 179, 113],
  1392. "mediumslateblue": [123, 104, 238],
  1393. "mediumspringgreen": [0, 250, 154],
  1394. "mediumturquoise": [72, 209, 204],
  1395. "mediumvioletred": [199, 21, 133],
  1396. "midnightblue": [25, 25, 112],
  1397. "mintcream": [245, 255, 250],
  1398. "mistyrose": [255, 228, 225],
  1399. "moccasin": [255, 228, 181],
  1400. "navajowhite": [255, 222, 173],
  1401. "navy": [0, 0, 128],
  1402. "oldlace": [253, 245, 230],
  1403. "olive": [128, 128, 0],
  1404. "olivedrab": [107, 142, 35],
  1405. "orange": [255, 165, 0],
  1406. "orangered": [255, 69, 0],
  1407. "orchid": [218, 112, 214],
  1408. "palegoldenrod": [238, 232, 170],
  1409. "palegreen": [152, 251, 152],
  1410. "paleturquoise": [175, 238, 238],
  1411. "palevioletred": [219, 112, 147],
  1412. "papayawhip": [255, 239, 213],
  1413. "peachpuff": [255, 218, 185],
  1414. "peru": [205, 133, 63],
  1415. "pink": [255, 192, 203],
  1416. "plum": [221, 160, 221],
  1417. "powderblue": [176, 224, 230],
  1418. "purple": [128, 0, 128],
  1419. "rebeccapurple": [102, 51, 153],
  1420. "red": [255, 0, 0],
  1421. "rosybrown": [188, 143, 143],
  1422. "royalblue": [65, 105, 225],
  1423. "saddlebrown": [139, 69, 19],
  1424. "salmon": [250, 128, 114],
  1425. "sandybrown": [244, 164, 96],
  1426. "seagreen": [46, 139, 87],
  1427. "seashell": [255, 245, 238],
  1428. "sienna": [160, 82, 45],
  1429. "silver": [192, 192, 192],
  1430. "skyblue": [135, 206, 235],
  1431. "slateblue": [106, 90, 205],
  1432. "slategray": [112, 128, 144],
  1433. "slategrey": [112, 128, 144],
  1434. "snow": [255, 250, 250],
  1435. "springgreen": [0, 255, 127],
  1436. "steelblue": [70, 130, 180],
  1437. "tan": [210, 180, 140],
  1438. "teal": [0, 128, 128],
  1439. "thistle": [216, 191, 216],
  1440. "tomato": [255, 99, 71],
  1441. "turquoise": [64, 224, 208],
  1442. "violet": [238, 130, 238],
  1443. "wheat": [245, 222, 179],
  1444. "white": [255, 255, 255],
  1445. "whitesmoke": [245, 245, 245],
  1446. "yellow": [255, 255, 0],
  1447. "yellowgreen": [154, 205, 50]
  1448. };
  1449. },{}],6:[function(require,module,exports){
  1450. //! moment.js
  1451. //! version : 2.13.0
  1452. //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
  1453. //! license : MIT
  1454. //! momentjs.com
  1455. ;(function (global, factory) {
  1456. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  1457. typeof define === 'function' && define.amd ? define(factory) :
  1458. global.moment = factory()
  1459. }(this, function () { 'use strict';
  1460. var hookCallback;
  1461. function utils_hooks__hooks () {
  1462. return hookCallback.apply(null, arguments);
  1463. }
  1464. // This is done to register the method called with moment()
  1465. // without creating circular dependencies.
  1466. function setHookCallback (callback) {
  1467. hookCallback = callback;
  1468. }
  1469. function isArray(input) {
  1470. return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
  1471. }
  1472. function isDate(input) {
  1473. return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
  1474. }
  1475. function map(arr, fn) {
  1476. var res = [], i;
  1477. for (i = 0; i < arr.length; ++i) {
  1478. res.push(fn(arr[i], i));
  1479. }
  1480. return res;
  1481. }
  1482. function hasOwnProp(a, b) {
  1483. return Object.prototype.hasOwnProperty.call(a, b);
  1484. }
  1485. function extend(a, b) {
  1486. for (var i in b) {
  1487. if (hasOwnProp(b, i)) {
  1488. a[i] = b[i];
  1489. }
  1490. }
  1491. if (hasOwnProp(b, 'toString')) {
  1492. a.toString = b.toString;
  1493. }
  1494. if (hasOwnProp(b, 'valueOf')) {
  1495. a.valueOf = b.valueOf;
  1496. }
  1497. return a;
  1498. }
  1499. function create_utc__createUTC (input, format, locale, strict) {
  1500. return createLocalOrUTC(input, format, locale, strict, true).utc();
  1501. }
  1502. function defaultParsingFlags() {
  1503. // We need to deep clone this object.
  1504. return {
  1505. empty : false,
  1506. unusedTokens : [],
  1507. unusedInput : [],
  1508. overflow : -2,
  1509. charsLeftOver : 0,
  1510. nullInput : false,
  1511. invalidMonth : null,
  1512. invalidFormat : false,
  1513. userInvalidated : false,
  1514. iso : false,
  1515. parsedDateParts : [],
  1516. meridiem : null
  1517. };
  1518. }
  1519. function getParsingFlags(m) {
  1520. if (m._pf == null) {
  1521. m._pf = defaultParsingFlags();
  1522. }
  1523. return m._pf;
  1524. }
  1525. var some;
  1526. if (Array.prototype.some) {
  1527. some = Array.prototype.some;
  1528. } else {
  1529. some = function (fun) {
  1530. var t = Object(this);
  1531. var len = t.length >>> 0;
  1532. for (var i = 0; i < len; i++) {
  1533. if (i in t && fun.call(this, t[i], i, t)) {
  1534. return true;
  1535. }
  1536. }
  1537. return false;
  1538. };
  1539. }
  1540. function valid__isValid(m) {
  1541. if (m._isValid == null) {
  1542. var flags = getParsingFlags(m);
  1543. var parsedParts = some.call(flags.parsedDateParts, function (i) {
  1544. return i != null;
  1545. });
  1546. m._isValid = !isNaN(m._d.getTime()) &&
  1547. flags.overflow < 0 &&
  1548. !flags.empty &&
  1549. !flags.invalidMonth &&
  1550. !flags.invalidWeekday &&
  1551. !flags.nullInput &&
  1552. !flags.invalidFormat &&
  1553. !flags.userInvalidated &&
  1554. (!flags.meridiem || (flags.meridiem && parsedParts));
  1555. if (m._strict) {
  1556. m._isValid = m._isValid &&
  1557. flags.charsLeftOver === 0 &&
  1558. flags.unusedTokens.length === 0 &&
  1559. flags.bigHour === undefined;
  1560. }
  1561. }
  1562. return m._isValid;
  1563. }
  1564. function valid__createInvalid (flags) {
  1565. var m = create_utc__createUTC(NaN);
  1566. if (flags != null) {
  1567. extend(getParsingFlags(m), flags);
  1568. }
  1569. else {
  1570. getParsingFlags(m).userInvalidated = true;
  1571. }
  1572. return m;
  1573. }
  1574. function isUndefined(input) {
  1575. return input === void 0;
  1576. }
  1577. // Plugins that add properties should also add the key here (null value),
  1578. // so we can properly clone ourselves.
  1579. var momentProperties = utils_hooks__hooks.momentProperties = [];
  1580. function copyConfig(to, from) {
  1581. var i, prop, val;
  1582. if (!isUndefined(from._isAMomentObject)) {
  1583. to._isAMomentObject = from._isAMomentObject;
  1584. }
  1585. if (!isUndefined(from._i)) {
  1586. to._i = from._i;
  1587. }
  1588. if (!isUndefined(from._f)) {
  1589. to._f = from._f;
  1590. }
  1591. if (!isUndefined(from._l)) {
  1592. to._l = from._l;
  1593. }
  1594. if (!isUndefined(from._strict)) {
  1595. to._strict = from._strict;
  1596. }
  1597. if (!isUndefined(from._tzm)) {
  1598. to._tzm = from._tzm;
  1599. }
  1600. if (!isUndefined(from._isUTC)) {
  1601. to._isUTC = from._isUTC;
  1602. }
  1603. if (!isUndefined(from._offset)) {
  1604. to._offset = from._offset;
  1605. }
  1606. if (!isUndefined(from._pf)) {
  1607. to._pf = getParsingFlags(from);
  1608. }
  1609. if (!isUndefined(from._locale)) {
  1610. to._locale = from._locale;
  1611. }
  1612. if (momentProperties.length > 0) {
  1613. for (i in momentProperties) {
  1614. prop = momentProperties[i];
  1615. val = from[prop];
  1616. if (!isUndefined(val)) {
  1617. to[prop] = val;
  1618. }
  1619. }
  1620. }
  1621. return to;
  1622. }
  1623. var updateInProgress = false;
  1624. // Moment prototype object
  1625. function Moment(config) {
  1626. copyConfig(this, config);
  1627. this._d = new Date(config._d != null ? config._d.getTime() : NaN);
  1628. // Prevent infinite loop in case updateOffset creates new moment
  1629. // objects.
  1630. if (updateInProgress === false) {
  1631. updateInProgress = true;
  1632. utils_hooks__hooks.updateOffset(this);
  1633. updateInProgress = false;
  1634. }
  1635. }
  1636. function isMoment (obj) {
  1637. return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
  1638. }
  1639. function absFloor (number) {
  1640. if (number < 0) {
  1641. return Math.ceil(number);
  1642. } else {
  1643. return Math.floor(number);
  1644. }
  1645. }
  1646. function toInt(argumentForCoercion) {
  1647. var coercedNumber = +argumentForCoercion,
  1648. value = 0;
  1649. if (coercedNumber !== 0 && isFinite(coercedNumber)) {
  1650. value = absFloor(coercedNumber);
  1651. }
  1652. return value;
  1653. }
  1654. // compare two arrays, return the number of differences
  1655. function compareArrays(array1, array2, dontConvert) {
  1656. var len = Math.min(array1.length, array2.length),
  1657. lengthDiff = Math.abs(array1.length - array2.length),
  1658. diffs = 0,
  1659. i;
  1660. for (i = 0; i < len; i++) {
  1661. if ((dontConvert && array1[i] !== array2[i]) ||
  1662. (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
  1663. diffs++;
  1664. }
  1665. }
  1666. return diffs + lengthDiff;
  1667. }
  1668. function warn(msg) {
  1669. if (utils_hooks__hooks.suppressDeprecationWarnings === false &&
  1670. (typeof console !== 'undefined') && console.warn) {
  1671. console.warn('Deprecation warning: ' + msg);
  1672. }
  1673. }
  1674. function deprecate(msg, fn) {
  1675. var firstTime = true;
  1676. return extend(function () {
  1677. if (utils_hooks__hooks.deprecationHandler != null) {
  1678. utils_hooks__hooks.deprecationHandler(null, msg);
  1679. }
  1680. if (firstTime) {
  1681. warn(msg + '\nArguments: ' + Array.prototype.slice.call(arguments).join(', ') + '\n' + (new Error()).stack);
  1682. firstTime = false;
  1683. }
  1684. return fn.apply(this, arguments);
  1685. }, fn);
  1686. }
  1687. var deprecations = {};
  1688. function deprecateSimple(name, msg) {
  1689. if (utils_hooks__hooks.deprecationHandler != null) {
  1690. utils_hooks__hooks.deprecationHandler(name, msg);
  1691. }
  1692. if (!deprecations[name]) {
  1693. warn(msg);
  1694. deprecations[name] = true;
  1695. }
  1696. }
  1697. utils_hooks__hooks.suppressDeprecationWarnings = false;
  1698. utils_hooks__hooks.deprecationHandler = null;
  1699. function isFunction(input) {
  1700. return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
  1701. }
  1702. function isObject(input) {
  1703. return Object.prototype.toString.call(input) === '[object Object]';
  1704. }
  1705. function locale_set__set (config) {
  1706. var prop, i;
  1707. for (i in config) {
  1708. prop = config[i];
  1709. if (isFunction(prop)) {
  1710. this[i] = prop;
  1711. } else {
  1712. this['_' + i] = prop;
  1713. }
  1714. }
  1715. this._config = config;
  1716. // Lenient ordinal parsing accepts just a number in addition to
  1717. // number + (possibly) stuff coming from _ordinalParseLenient.
  1718. this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
  1719. }
  1720. function mergeConfigs(parentConfig, childConfig) {
  1721. var res = extend({}, parentConfig), prop;
  1722. for (prop in childConfig) {
  1723. if (hasOwnProp(childConfig, prop)) {
  1724. if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
  1725. res[prop] = {};
  1726. extend(res[prop], parentConfig[prop]);
  1727. extend(res[prop], childConfig[prop]);
  1728. } else if (childConfig[prop] != null) {
  1729. res[prop] = childConfig[prop];
  1730. } else {
  1731. delete res[prop];
  1732. }
  1733. }
  1734. }
  1735. return res;
  1736. }
  1737. function Locale(config) {
  1738. if (config != null) {
  1739. this.set(config);
  1740. }
  1741. }
  1742. var keys;
  1743. if (Object.keys) {
  1744. keys = Object.keys;
  1745. } else {
  1746. keys = function (obj) {
  1747. var i, res = [];
  1748. for (i in obj) {
  1749. if (hasOwnProp(obj, i)) {
  1750. res.push(i);
  1751. }
  1752. }
  1753. return res;
  1754. };
  1755. }
  1756. // internal storage for locale config files
  1757. var locales = {};
  1758. var globalLocale;
  1759. function normalizeLocale(key) {
  1760. return key ? key.toLowerCase().replace('_', '-') : key;
  1761. }
  1762. // pick the locale from the array
  1763. // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
  1764. // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
  1765. function chooseLocale(names) {
  1766. var i = 0, j, next, locale, split;
  1767. while (i < names.length) {
  1768. split = normalizeLocale(names[i]).split('-');
  1769. j = split.length;
  1770. next = normalizeLocale(names[i + 1]);
  1771. next = next ? next.split('-') : null;
  1772. while (j > 0) {
  1773. locale = loadLocale(split.slice(0, j).join('-'));
  1774. if (locale) {
  1775. return locale;
  1776. }
  1777. if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
  1778. //the next array item is better than a shallower substring of this one
  1779. break;
  1780. }
  1781. j--;
  1782. }
  1783. i++;
  1784. }
  1785. return null;
  1786. }
  1787. function loadLocale(name) {
  1788. var oldLocale = null;
  1789. // TODO: Find a better way to register and load all the locales in Node
  1790. if (!locales[name] && (typeof module !== 'undefined') &&
  1791. module && module.exports) {
  1792. try {
  1793. oldLocale = globalLocale._abbr;
  1794. require('./locale/' + name);
  1795. // because defineLocale currently also sets the global locale, we
  1796. // want to undo that for lazy loaded locales
  1797. locale_locales__getSetGlobalLocale(oldLocale);
  1798. } catch (e) { }
  1799. }
  1800. return locales[name];
  1801. }
  1802. // This function will load locale and then set the global locale. If
  1803. // no arguments are passed in, it will simply return the current global
  1804. // locale key.
  1805. function locale_locales__getSetGlobalLocale (key, values) {
  1806. var data;
  1807. if (key) {
  1808. if (isUndefined(values)) {
  1809. data = locale_locales__getLocale(key);
  1810. }
  1811. else {
  1812. data = defineLocale(key, values);
  1813. }
  1814. if (data) {
  1815. // moment.duration._locale = moment._locale = data;
  1816. globalLocale = data;
  1817. }
  1818. }
  1819. return globalLocale._abbr;
  1820. }
  1821. function defineLocale (name, config) {
  1822. if (config !== null) {
  1823. config.abbr = name;
  1824. if (locales[name] != null) {
  1825. deprecateSimple('defineLocaleOverride',
  1826. 'use moment.updateLocale(localeName, config) to change ' +
  1827. 'an existing locale. moment.defineLocale(localeName, ' +
  1828. 'config) should only be used for creating a new locale');
  1829. config = mergeConfigs(locales[name]._config, config);
  1830. } else if (config.parentLocale != null) {
  1831. if (locales[config.parentLocale] != null) {
  1832. config = mergeConfigs(locales[config.parentLocale]._config, config);
  1833. } else {
  1834. // treat as if there is no base config
  1835. deprecateSimple('parentLocaleUndefined',
  1836. 'specified parentLocale is not defined yet');
  1837. }
  1838. }
  1839. locales[name] = new Locale(config);
  1840. // backwards compat for now: also set the locale
  1841. locale_locales__getSetGlobalLocale(name);
  1842. return locales[name];
  1843. } else {
  1844. // useful for testing
  1845. delete locales[name];
  1846. return null;
  1847. }
  1848. }
  1849. function updateLocale(name, config) {
  1850. if (config != null) {
  1851. var locale;
  1852. if (locales[name] != null) {
  1853. config = mergeConfigs(locales[name]._config, config);
  1854. }
  1855. locale = new Locale(config);
  1856. locale.parentLocale = locales[name];
  1857. locales[name] = locale;
  1858. // backwards compat for now: also set the locale
  1859. locale_locales__getSetGlobalLocale(name);
  1860. } else {
  1861. // pass null for config to unupdate, useful for tests
  1862. if (locales[name] != null) {
  1863. if (locales[name].parentLocale != null) {
  1864. locales[name] = locales[name].parentLocale;
  1865. } else if (locales[name] != null) {
  1866. delete locales[name];
  1867. }
  1868. }
  1869. }
  1870. return locales[name];
  1871. }
  1872. // returns locale data
  1873. function locale_locales__getLocale (key) {
  1874. var locale;
  1875. if (key && key._locale && key._locale._abbr) {
  1876. key = key._locale._abbr;
  1877. }
  1878. if (!key) {
  1879. return globalLocale;
  1880. }
  1881. if (!isArray(key)) {
  1882. //short-circuit everything else
  1883. locale = loadLocale(key);
  1884. if (locale) {
  1885. return locale;
  1886. }
  1887. key = [key];
  1888. }
  1889. return chooseLocale(key);
  1890. }
  1891. function locale_locales__listLocales() {
  1892. return keys(locales);
  1893. }
  1894. var aliases = {};
  1895. function addUnitAlias (unit, shorthand) {
  1896. var lowerCase = unit.toLowerCase();
  1897. aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
  1898. }
  1899. function normalizeUnits(units) {
  1900. return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
  1901. }
  1902. function normalizeObjectUnits(inputObject) {
  1903. var normalizedInput = {},
  1904. normalizedProp,
  1905. prop;
  1906. for (prop in inputObject) {
  1907. if (hasOwnProp(inputObject, prop)) {
  1908. normalizedProp = normalizeUnits(prop);
  1909. if (normalizedProp) {
  1910. normalizedInput[normalizedProp] = inputObject[prop];
  1911. }
  1912. }
  1913. }
  1914. return normalizedInput;
  1915. }
  1916. function makeGetSet (unit, keepTime) {
  1917. return function (value) {
  1918. if (value != null) {
  1919. get_set__set(this, unit, value);
  1920. utils_hooks__hooks.updateOffset(this, keepTime);
  1921. return this;
  1922. } else {
  1923. return get_set__get(this, unit);
  1924. }
  1925. };
  1926. }
  1927. function get_set__get (mom, unit) {
  1928. return mom.isValid() ?
  1929. mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
  1930. }
  1931. function get_set__set (mom, unit, value) {
  1932. if (mom.isValid()) {
  1933. mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
  1934. }
  1935. }
  1936. // MOMENTS
  1937. function getSet (units, value) {
  1938. var unit;
  1939. if (typeof units === 'object') {
  1940. for (unit in units) {
  1941. this.set(unit, units[unit]);
  1942. }
  1943. } else {
  1944. units = normalizeUnits(units);
  1945. if (isFunction(this[units])) {
  1946. return this[units](value);
  1947. }
  1948. }
  1949. return this;
  1950. }
  1951. function zeroFill(number, targetLength, forceSign) {
  1952. var absNumber = '' + Math.abs(number),
  1953. zerosToFill = targetLength - absNumber.length,
  1954. sign = number >= 0;
  1955. return (sign ? (forceSign ? '+' : '') : '-') +
  1956. Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
  1957. }
  1958. 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;
  1959. var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
  1960. var formatFunctions = {};
  1961. var formatTokenFunctions = {};
  1962. // token: 'M'
  1963. // padded: ['MM', 2]
  1964. // ordinal: 'Mo'
  1965. // callback: function () { this.month() + 1 }
  1966. function addFormatToken (token, padded, ordinal, callback) {
  1967. var func = callback;
  1968. if (typeof callback === 'string') {
  1969. func = function () {
  1970. return this[callback]();
  1971. };
  1972. }
  1973. if (token) {
  1974. formatTokenFunctions[token] = func;
  1975. }
  1976. if (padded) {
  1977. formatTokenFunctions[padded[0]] = function () {
  1978. return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
  1979. };
  1980. }
  1981. if (ordinal) {
  1982. formatTokenFunctions[ordinal] = function () {
  1983. return this.localeData().ordinal(func.apply(this, arguments), token);
  1984. };
  1985. }
  1986. }
  1987. function removeFormattingTokens(input) {
  1988. if (input.match(/\[[\s\S]/)) {
  1989. return input.replace(/^\[|\]$/g, '');
  1990. }
  1991. return input.replace(/\\/g, '');
  1992. }
  1993. function makeFormatFunction(format) {
  1994. var array = format.match(formattingTokens), i, length;
  1995. for (i = 0, length = array.length; i < length; i++) {
  1996. if (formatTokenFunctions[array[i]]) {
  1997. array[i] = formatTokenFunctions[array[i]];
  1998. } else {
  1999. array[i] = removeFormattingTokens(array[i]);
  2000. }
  2001. }
  2002. return function (mom) {
  2003. var output = '', i;
  2004. for (i = 0; i < length; i++) {
  2005. output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
  2006. }
  2007. return output;
  2008. };
  2009. }
  2010. // format date using native date object
  2011. function formatMoment(m, format) {
  2012. if (!m.isValid()) {
  2013. return m.localeData().invalidDate();
  2014. }
  2015. format = expandFormat(format, m.localeData());
  2016. formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
  2017. return formatFunctions[format](m);
  2018. }
  2019. function expandFormat(format, locale) {
  2020. var i = 5;
  2021. function replaceLongDateFormatTokens(input) {
  2022. return locale.longDateFormat(input) || input;
  2023. }
  2024. localFormattingTokens.lastIndex = 0;
  2025. while (i >= 0 && localFormattingTokens.test(format)) {
  2026. format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
  2027. localFormattingTokens.lastIndex = 0;
  2028. i -= 1;
  2029. }
  2030. return format;
  2031. }
  2032. var match1 = /\d/; // 0 - 9
  2033. var match2 = /\d\d/; // 00 - 99
  2034. var match3 = /\d{3}/; // 000 - 999
  2035. var match4 = /\d{4}/; // 0000 - 9999
  2036. var match6 = /[+-]?\d{6}/; // -999999 - 999999
  2037. var match1to2 = /\d\d?/; // 0 - 99
  2038. var match3to4 = /\d\d\d\d?/; // 999 - 9999
  2039. var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
  2040. var match1to3 = /\d{1,3}/; // 0 - 999
  2041. var match1to4 = /\d{1,4}/; // 0 - 9999
  2042. var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
  2043. var matchUnsigned = /\d+/; // 0 - inf
  2044. var matchSigned = /[+-]?\d+/; // -inf - inf
  2045. var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
  2046. var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
  2047. var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
  2048. // any word (or two) characters or numbers including two/three word month in arabic.
  2049. // includes scottish gaelic two word and hyphenated months
  2050. var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
  2051. var regexes = {};
  2052. function addRegexToken (token, regex, strictRegex) {
  2053. regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
  2054. return (isStrict && strictRegex) ? strictRegex : regex;
  2055. };
  2056. }
  2057. function getParseRegexForToken (token, config) {
  2058. if (!hasOwnProp(regexes, token)) {
  2059. return new RegExp(unescapeFormat(token));
  2060. }
  2061. return regexes[token](config._strict, config._locale);
  2062. }
  2063. // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
  2064. function unescapeFormat(s) {
  2065. return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
  2066. return p1 || p2 || p3 || p4;
  2067. }));
  2068. }
  2069. function regexEscape(s) {
  2070. return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  2071. }
  2072. var tokens = {};
  2073. function addParseToken (token, callback) {
  2074. var i, func = callback;
  2075. if (typeof token === 'string') {
  2076. token = [token];
  2077. }
  2078. if (typeof callback === 'number') {
  2079. func = function (input, array) {
  2080. array[callback] = toInt(input);
  2081. };
  2082. }
  2083. for (i = 0; i < token.length; i++) {
  2084. tokens[token[i]] = func;
  2085. }
  2086. }
  2087. function addWeekParseToken (token, callback) {
  2088. addParseToken(token, function (input, array, config, token) {
  2089. config._w = config._w || {};
  2090. callback(input, config._w, config, token);
  2091. });
  2092. }
  2093. function addTimeToArrayFromToken(token, input, config) {
  2094. if (input != null && hasOwnProp(tokens, token)) {
  2095. tokens[token](input, config._a, config, token);
  2096. }
  2097. }
  2098. var YEAR = 0;
  2099. var MONTH = 1;
  2100. var DATE = 2;
  2101. var HOUR = 3;
  2102. var MINUTE = 4;
  2103. var SECOND = 5;
  2104. var MILLISECOND = 6;
  2105. var WEEK = 7;
  2106. var WEEKDAY = 8;
  2107. var indexOf;
  2108. if (Array.prototype.indexOf) {
  2109. indexOf = Array.prototype.indexOf;
  2110. } else {
  2111. indexOf = function (o) {
  2112. // I know
  2113. var i;
  2114. for (i = 0; i < this.length; ++i) {
  2115. if (this[i] === o) {
  2116. return i;
  2117. }
  2118. }
  2119. return -1;
  2120. };
  2121. }
  2122. function daysInMonth(year, month) {
  2123. return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
  2124. }
  2125. // FORMATTING
  2126. addFormatToken('M', ['MM', 2], 'Mo', function () {
  2127. return this.month() + 1;
  2128. });
  2129. addFormatToken('MMM', 0, 0, function (format) {
  2130. return this.localeData().monthsShort(this, format);
  2131. });
  2132. addFormatToken('MMMM', 0, 0, function (format) {
  2133. return this.localeData().months(this, format);
  2134. });
  2135. // ALIASES
  2136. addUnitAlias('month', 'M');
  2137. // PARSING
  2138. addRegexToken('M', match1to2);
  2139. addRegexToken('MM', match1to2, match2);
  2140. addRegexToken('MMM', function (isStrict, locale) {
  2141. return locale.monthsShortRegex(isStrict);
  2142. });
  2143. addRegexToken('MMMM', function (isStrict, locale) {
  2144. return locale.monthsRegex(isStrict);
  2145. });
  2146. addParseToken(['M', 'MM'], function (input, array) {
  2147. array[MONTH] = toInt(input) - 1;
  2148. });
  2149. addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
  2150. var month = config._locale.monthsParse(input, token, config._strict);
  2151. // if we didn't find a month name, mark the date as invalid.
  2152. if (month != null) {
  2153. array[MONTH] = month;
  2154. } else {
  2155. getParsingFlags(config).invalidMonth = input;
  2156. }
  2157. });
  2158. // LOCALES
  2159. var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/;
  2160. var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
  2161. function localeMonths (m, format) {
  2162. return isArray(this._months) ? this._months[m.month()] :
  2163. this._months[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
  2164. }
  2165. var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
  2166. function localeMonthsShort (m, format) {
  2167. return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
  2168. this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
  2169. }
  2170. function units_month__handleStrictParse(monthName, format, strict) {
  2171. var i, ii, mom, llc = monthName.toLocaleLowerCase();
  2172. if (!this._monthsParse) {
  2173. // this is not used
  2174. this._monthsParse = [];
  2175. this._longMonthsParse = [];
  2176. this._shortMonthsParse = [];
  2177. for (i = 0; i < 12; ++i) {
  2178. mom = create_utc__createUTC([2000, i]);
  2179. this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
  2180. this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
  2181. }
  2182. }
  2183. if (strict) {
  2184. if (format === 'MMM') {
  2185. ii = indexOf.call(this._shortMonthsParse, llc);
  2186. return ii !== -1 ? ii : null;
  2187. } else {
  2188. ii = indexOf.call(this._longMonthsParse, llc);
  2189. return ii !== -1 ? ii : null;
  2190. }
  2191. } else {
  2192. if (format === 'MMM') {
  2193. ii = indexOf.call(this._shortMonthsParse, llc);
  2194. if (ii !== -1) {
  2195. return ii;
  2196. }
  2197. ii = indexOf.call(this._longMonthsParse, llc);
  2198. return ii !== -1 ? ii : null;
  2199. } else {
  2200. ii = indexOf.call(this._longMonthsParse, llc);
  2201. if (ii !== -1) {
  2202. return ii;
  2203. }
  2204. ii = indexOf.call(this._shortMonthsParse, llc);
  2205. return ii !== -1 ? ii : null;
  2206. }
  2207. }
  2208. }
  2209. function localeMonthsParse (monthName, format, strict) {
  2210. var i, mom, regex;
  2211. if (this._monthsParseExact) {
  2212. return units_month__handleStrictParse.call(this, monthName, format, strict);
  2213. }
  2214. if (!this._monthsParse) {
  2215. this._monthsParse = [];
  2216. this._longMonthsParse = [];
  2217. this._shortMonthsParse = [];
  2218. }
  2219. // TODO: add sorting
  2220. // Sorting makes sure if one month (or abbr) is a prefix of another
  2221. // see sorting in computeMonthsParse
  2222. for (i = 0; i < 12; i++) {
  2223. // make the regex if we don't have it already
  2224. mom = create_utc__createUTC([2000, i]);
  2225. if (strict && !this._longMonthsParse[i]) {
  2226. this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
  2227. this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
  2228. }
  2229. if (!strict && !this._monthsParse[i]) {
  2230. regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
  2231. this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
  2232. }
  2233. // test the regex
  2234. if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
  2235. return i;
  2236. } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
  2237. return i;
  2238. } else if (!strict && this._monthsParse[i].test(monthName)) {
  2239. return i;
  2240. }
  2241. }
  2242. }
  2243. // MOMENTS
  2244. function setMonth (mom, value) {
  2245. var dayOfMonth;
  2246. if (!mom.isValid()) {
  2247. // No op
  2248. return mom;
  2249. }
  2250. if (typeof value === 'string') {
  2251. if (/^\d+$/.test(value)) {
  2252. value = toInt(value);
  2253. } else {
  2254. value = mom.localeData().monthsParse(value);
  2255. // TODO: Another silent failure?
  2256. if (typeof value !== 'number') {
  2257. return mom;
  2258. }
  2259. }
  2260. }
  2261. dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
  2262. mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
  2263. return mom;
  2264. }
  2265. function getSetMonth (value) {
  2266. if (value != null) {
  2267. setMonth(this, value);
  2268. utils_hooks__hooks.updateOffset(this, true);
  2269. return this;
  2270. } else {
  2271. return get_set__get(this, 'Month');
  2272. }
  2273. }
  2274. function getDaysInMonth () {
  2275. return daysInMonth(this.year(), this.month());
  2276. }
  2277. var defaultMonthsShortRegex = matchWord;
  2278. function monthsShortRegex (isStrict) {
  2279. if (this._monthsParseExact) {
  2280. if (!hasOwnProp(this, '_monthsRegex')) {
  2281. computeMonthsParse.call(this);
  2282. }
  2283. if (isStrict) {
  2284. return this._monthsShortStrictRegex;
  2285. } else {
  2286. return this._monthsShortRegex;
  2287. }
  2288. } else {
  2289. return this._monthsShortStrictRegex && isStrict ?
  2290. this._monthsShortStrictRegex : this._monthsShortRegex;
  2291. }
  2292. }
  2293. var defaultMonthsRegex = matchWord;
  2294. function monthsRegex (isStrict) {
  2295. if (this._monthsParseExact) {
  2296. if (!hasOwnProp(this, '_monthsRegex')) {
  2297. computeMonthsParse.call(this);
  2298. }
  2299. if (isStrict) {
  2300. return this._monthsStrictRegex;
  2301. } else {
  2302. return this._monthsRegex;
  2303. }
  2304. } else {
  2305. return this._monthsStrictRegex && isStrict ?
  2306. this._monthsStrictRegex : this._monthsRegex;
  2307. }
  2308. }
  2309. function computeMonthsParse () {
  2310. function cmpLenRev(a, b) {
  2311. return b.length - a.length;
  2312. }
  2313. var shortPieces = [], longPieces = [], mixedPieces = [],
  2314. i, mom;
  2315. for (i = 0; i < 12; i++) {
  2316. // make the regex if we don't have it already
  2317. mom = create_utc__createUTC([2000, i]);
  2318. shortPieces.push(this.monthsShort(mom, ''));
  2319. longPieces.push(this.months(mom, ''));
  2320. mixedPieces.push(this.months(mom, ''));
  2321. mixedPieces.push(this.monthsShort(mom, ''));
  2322. }
  2323. // Sorting makes sure if one month (or abbr) is a prefix of another it
  2324. // will match the longer piece.
  2325. shortPieces.sort(cmpLenRev);
  2326. longPieces.sort(cmpLenRev);
  2327. mixedPieces.sort(cmpLenRev);
  2328. for (i = 0; i < 12; i++) {
  2329. shortPieces[i] = regexEscape(shortPieces[i]);
  2330. longPieces[i] = regexEscape(longPieces[i]);
  2331. mixedPieces[i] = regexEscape(mixedPieces[i]);
  2332. }
  2333. this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
  2334. this._monthsShortRegex = this._monthsRegex;
  2335. this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
  2336. this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
  2337. }
  2338. function checkOverflow (m) {
  2339. var overflow;
  2340. var a = m._a;
  2341. if (a && getParsingFlags(m).overflow === -2) {
  2342. overflow =
  2343. a[MONTH] < 0 || a[MONTH] > 11 ? MONTH :
  2344. a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
  2345. a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
  2346. a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE :
  2347. a[SECOND] < 0 || a[SECOND] > 59 ? SECOND :
  2348. a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
  2349. -1;
  2350. if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
  2351. overflow = DATE;
  2352. }
  2353. if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
  2354. overflow = WEEK;
  2355. }
  2356. if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
  2357. overflow = WEEKDAY;
  2358. }
  2359. getParsingFlags(m).overflow = overflow;
  2360. }
  2361. return m;
  2362. }
  2363. // iso 8601 regex
  2364. // 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)
  2365. 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)?)?/;
  2366. 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)?)?/;
  2367. var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
  2368. var isoDates = [
  2369. ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
  2370. ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
  2371. ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
  2372. ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
  2373. ['YYYY-DDD', /\d{4}-\d{3}/],
  2374. ['YYYY-MM', /\d{4}-\d\d/, false],
  2375. ['YYYYYYMMDD', /[+-]\d{10}/],
  2376. ['YYYYMMDD', /\d{8}/],
  2377. // YYYYMM is NOT allowed by the standard
  2378. ['GGGG[W]WWE', /\d{4}W\d{3}/],
  2379. ['GGGG[W]WW', /\d{4}W\d{2}/, false],
  2380. ['YYYYDDD', /\d{7}/]
  2381. ];
  2382. // iso time formats and regexes
  2383. var isoTimes = [
  2384. ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
  2385. ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
  2386. ['HH:mm:ss', /\d\d:\d\d:\d\d/],
  2387. ['HH:mm', /\d\d:\d\d/],
  2388. ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
  2389. ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
  2390. ['HHmmss', /\d\d\d\d\d\d/],
  2391. ['HHmm', /\d\d\d\d/],
  2392. ['HH', /\d\d/]
  2393. ];
  2394. var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
  2395. // date from iso format
  2396. function configFromISO(config) {
  2397. var i, l,
  2398. string = config._i,
  2399. match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
  2400. allowTime, dateFormat, timeFormat, tzFormat;
  2401. if (match) {
  2402. getParsingFlags(config).iso = true;
  2403. for (i = 0, l = isoDates.length; i < l; i++) {
  2404. if (isoDates[i][1].exec(match[1])) {
  2405. dateFormat = isoDates[i][0];
  2406. allowTime = isoDates[i][2] !== false;
  2407. break;
  2408. }
  2409. }
  2410. if (dateFormat == null) {
  2411. config._isValid = false;
  2412. return;
  2413. }
  2414. if (match[3]) {
  2415. for (i = 0, l = isoTimes.length; i < l; i++) {
  2416. if (isoTimes[i][1].exec(match[3])) {
  2417. // match[2] should be 'T' or space
  2418. timeFormat = (match[2] || ' ') + isoTimes[i][0];
  2419. break;
  2420. }
  2421. }
  2422. if (timeFormat == null) {
  2423. config._isValid = false;
  2424. return;
  2425. }
  2426. }
  2427. if (!allowTime && timeFormat != null) {
  2428. config._isValid = false;
  2429. return;
  2430. }
  2431. if (match[4]) {
  2432. if (tzRegex.exec(match[4])) {
  2433. tzFormat = 'Z';
  2434. } else {
  2435. config._isValid = false;
  2436. return;
  2437. }
  2438. }
  2439. config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
  2440. configFromStringAndFormat(config);
  2441. } else {
  2442. config._isValid = false;
  2443. }
  2444. }
  2445. // date from iso format or fallback
  2446. function configFromString(config) {
  2447. var matched = aspNetJsonRegex.exec(config._i);
  2448. if (matched !== null) {
  2449. config._d = new Date(+matched[1]);
  2450. return;
  2451. }
  2452. configFromISO(config);
  2453. if (config._isValid === false) {
  2454. delete config._isValid;
  2455. utils_hooks__hooks.createFromInputFallback(config);
  2456. }
  2457. }
  2458. utils_hooks__hooks.createFromInputFallback = deprecate(
  2459. 'moment construction falls back to js Date. This is ' +
  2460. 'discouraged and will be removed in upcoming major ' +
  2461. 'release. Please refer to ' +
  2462. 'https://github.com/moment/moment/issues/1407 for more info.',
  2463. function (config) {
  2464. config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
  2465. }
  2466. );
  2467. function createDate (y, m, d, h, M, s, ms) {
  2468. //can't just apply() to create a date:
  2469. //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
  2470. var date = new Date(y, m, d, h, M, s, ms);
  2471. //the date constructor remaps years 0-99 to 1900-1999
  2472. if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
  2473. date.setFullYear(y);
  2474. }
  2475. return date;
  2476. }
  2477. function createUTCDate (y) {
  2478. var date = new Date(Date.UTC.apply(null, arguments));
  2479. //the Date.UTC function remaps years 0-99 to 1900-1999
  2480. if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
  2481. date.setUTCFullYear(y);
  2482. }
  2483. return date;
  2484. }
  2485. // FORMATTING
  2486. addFormatToken('Y', 0, 0, function () {
  2487. var y = this.year();
  2488. return y <= 9999 ? '' + y : '+' + y;
  2489. });
  2490. addFormatToken(0, ['YY', 2], 0, function () {
  2491. return this.year() % 100;
  2492. });
  2493. addFormatToken(0, ['YYYY', 4], 0, 'year');
  2494. addFormatToken(0, ['YYYYY', 5], 0, 'year');
  2495. addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
  2496. // ALIASES
  2497. addUnitAlias('year', 'y');
  2498. // PARSING
  2499. addRegexToken('Y', matchSigned);
  2500. addRegexToken('YY', match1to2, match2);
  2501. addRegexToken('YYYY', match1to4, match4);
  2502. addRegexToken('YYYYY', match1to6, match6);
  2503. addRegexToken('YYYYYY', match1to6, match6);
  2504. addParseToken(['YYYYY', 'YYYYYY'], YEAR);
  2505. addParseToken('YYYY', function (input, array) {
  2506. array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input);
  2507. });
  2508. addParseToken('YY', function (input, array) {
  2509. array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input);
  2510. });
  2511. addParseToken('Y', function (input, array) {
  2512. array[YEAR] = parseInt(input, 10);
  2513. });
  2514. // HELPERS
  2515. function daysInYear(year) {
  2516. return isLeapYear(year) ? 366 : 365;
  2517. }
  2518. function isLeapYear(year) {
  2519. return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
  2520. }
  2521. // HOOKS
  2522. utils_hooks__hooks.parseTwoDigitYear = function (input) {
  2523. return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
  2524. };
  2525. // MOMENTS
  2526. var getSetYear = makeGetSet('FullYear', true);
  2527. function getIsLeapYear () {
  2528. return isLeapYear(this.year());
  2529. }
  2530. // start-of-first-week - start-of-year
  2531. function firstWeekOffset(year, dow, doy) {
  2532. var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
  2533. fwd = 7 + dow - doy,
  2534. // first-week day local weekday -- which local weekday is fwd
  2535. fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
  2536. return -fwdlw + fwd - 1;
  2537. }
  2538. //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
  2539. function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
  2540. var localWeekday = (7 + weekday - dow) % 7,
  2541. weekOffset = firstWeekOffset(year, dow, doy),
  2542. dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
  2543. resYear, resDayOfYear;
  2544. if (dayOfYear <= 0) {
  2545. resYear = year - 1;
  2546. resDayOfYear = daysInYear(resYear) + dayOfYear;
  2547. } else if (dayOfYear > daysInYear(year)) {
  2548. resYear = year + 1;
  2549. resDayOfYear = dayOfYear - daysInYear(year);
  2550. } else {
  2551. resYear = year;
  2552. resDayOfYear = dayOfYear;
  2553. }
  2554. return {
  2555. year: resYear,
  2556. dayOfYear: resDayOfYear
  2557. };
  2558. }
  2559. function weekOfYear(mom, dow, doy) {
  2560. var weekOffset = firstWeekOffset(mom.year(), dow, doy),
  2561. week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
  2562. resWeek, resYear;
  2563. if (week < 1) {
  2564. resYear = mom.year() - 1;
  2565. resWeek = week + weeksInYear(resYear, dow, doy);
  2566. } else if (week > weeksInYear(mom.year(), dow, doy)) {
  2567. resWeek = week - weeksInYear(mom.year(), dow, doy);
  2568. resYear = mom.year() + 1;
  2569. } else {
  2570. resYear = mom.year();
  2571. resWeek = week;
  2572. }
  2573. return {
  2574. week: resWeek,
  2575. year: resYear
  2576. };
  2577. }
  2578. function weeksInYear(year, dow, doy) {
  2579. var weekOffset = firstWeekOffset(year, dow, doy),
  2580. weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
  2581. return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
  2582. }
  2583. // Pick the first defined of two or three arguments.
  2584. function defaults(a, b, c) {
  2585. if (a != null) {
  2586. return a;
  2587. }
  2588. if (b != null) {
  2589. return b;
  2590. }
  2591. return c;
  2592. }
  2593. function currentDateArray(config) {
  2594. // hooks is actually the exported moment object
  2595. var nowValue = new Date(utils_hooks__hooks.now());
  2596. if (config._useUTC) {
  2597. return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
  2598. }
  2599. return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
  2600. }
  2601. // convert an array to a date.
  2602. // the array should mirror the parameters below
  2603. // note: all values past the year are optional and will default to the lowest possible value.
  2604. // [year, month, day , hour, minute, second, millisecond]
  2605. function configFromArray (config) {
  2606. var i, date, input = [], currentDate, yearToUse;
  2607. if (config._d) {
  2608. return;
  2609. }
  2610. currentDate = currentDateArray(config);
  2611. //compute day of the year from weeks and weekdays
  2612. if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
  2613. dayOfYearFromWeekInfo(config);
  2614. }
  2615. //if the day of the year is set, figure out what it is
  2616. if (config._dayOfYear) {
  2617. yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
  2618. if (config._dayOfYear > daysInYear(yearToUse)) {
  2619. getParsingFlags(config)._overflowDayOfYear = true;
  2620. }
  2621. date = createUTCDate(yearToUse, 0, config._dayOfYear);
  2622. config._a[MONTH] = date.getUTCMonth();
  2623. config._a[DATE] = date.getUTCDate();
  2624. }
  2625. // Default to current date.
  2626. // * if no year, month, day of month are given, default to today
  2627. // * if day of month is given, default month and year
  2628. // * if month is given, default only year
  2629. // * if year is given, don't default anything
  2630. for (i = 0; i < 3 && config._a[i] == null; ++i) {
  2631. config._a[i] = input[i] = currentDate[i];
  2632. }
  2633. // Zero out whatever was not defaulted, including time
  2634. for (; i < 7; i++) {
  2635. config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
  2636. }
  2637. // Check for 24:00:00.000
  2638. if (config._a[HOUR] === 24 &&
  2639. config._a[MINUTE] === 0 &&
  2640. config._a[SECOND] === 0 &&
  2641. config._a[MILLISECOND] === 0) {
  2642. config._nextDay = true;
  2643. config._a[HOUR] = 0;
  2644. }
  2645. config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
  2646. // Apply timezone offset from input. The actual utcOffset can be changed
  2647. // with parseZone.
  2648. if (config._tzm != null) {
  2649. config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
  2650. }
  2651. if (config._nextDay) {
  2652. config._a[HOUR] = 24;
  2653. }
  2654. }
  2655. function dayOfYearFromWeekInfo(config) {
  2656. var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
  2657. w = config._w;
  2658. if (w.GG != null || w.W != null || w.E != null) {
  2659. dow = 1;
  2660. doy = 4;
  2661. // TODO: We need to take the current isoWeekYear, but that depends on
  2662. // how we interpret now (local, utc, fixed offset). So create
  2663. // a now version of current config (take local/utc/offset flags, and
  2664. // create now).
  2665. weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year);
  2666. week = defaults(w.W, 1);
  2667. weekday = defaults(w.E, 1);
  2668. if (weekday < 1 || weekday > 7) {
  2669. weekdayOverflow = true;
  2670. }
  2671. } else {
  2672. dow = config._locale._week.dow;
  2673. doy = config._locale._week.doy;
  2674. weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year);
  2675. week = defaults(w.w, 1);
  2676. if (w.d != null) {
  2677. // weekday -- low day numbers are considered next week
  2678. weekday = w.d;
  2679. if (weekday < 0 || weekday > 6) {
  2680. weekdayOverflow = true;
  2681. }
  2682. } else if (w.e != null) {
  2683. // local weekday -- counting starts from begining of week
  2684. weekday = w.e + dow;
  2685. if (w.e < 0 || w.e > 6) {
  2686. weekdayOverflow = true;
  2687. }
  2688. } else {
  2689. // default to begining of week
  2690. weekday = dow;
  2691. }
  2692. }
  2693. if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
  2694. getParsingFlags(config)._overflowWeeks = true;
  2695. } else if (weekdayOverflow != null) {
  2696. getParsingFlags(config)._overflowWeekday = true;
  2697. } else {
  2698. temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
  2699. config._a[YEAR] = temp.year;
  2700. config._dayOfYear = temp.dayOfYear;
  2701. }
  2702. }
  2703. // constant that refers to the ISO standard
  2704. utils_hooks__hooks.ISO_8601 = function () {};
  2705. // date from string and format string
  2706. function configFromStringAndFormat(config) {
  2707. // TODO: Move this to another part of the creation flow to prevent circular deps
  2708. if (config._f === utils_hooks__hooks.ISO_8601) {
  2709. configFromISO(config);
  2710. return;
  2711. }
  2712. config._a = [];
  2713. getParsingFlags(config).empty = true;
  2714. // This array is used to make a Date, either with `new Date` or `Date.UTC`
  2715. var string = '' + config._i,
  2716. i, parsedInput, tokens, token, skipped,
  2717. stringLength = string.length,
  2718. totalParsedInputLength = 0;
  2719. tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
  2720. for (i = 0; i < tokens.length; i++) {
  2721. token = tokens[i];
  2722. parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
  2723. // console.log('token', token, 'parsedInput', parsedInput,
  2724. // 'regex', getParseRegexForToken(token, config));
  2725. if (parsedInput) {
  2726. skipped = string.substr(0, string.indexOf(parsedInput));
  2727. if (skipped.length > 0) {
  2728. getParsingFlags(config).unusedInput.push(skipped);
  2729. }
  2730. string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
  2731. totalParsedInputLength += parsedInput.length;
  2732. }
  2733. // don't parse if it's not a known token
  2734. if (formatTokenFunctions[token]) {
  2735. if (parsedInput) {
  2736. getParsingFlags(config).empty = false;
  2737. }
  2738. else {
  2739. getParsingFlags(config).unusedTokens.push(token);
  2740. }
  2741. addTimeToArrayFromToken(token, parsedInput, config);
  2742. }
  2743. else if (config._strict && !parsedInput) {
  2744. getParsingFlags(config).unusedTokens.push(token);
  2745. }
  2746. }
  2747. // add remaining unparsed input length to the string
  2748. getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
  2749. if (string.length > 0) {
  2750. getParsingFlags(config).unusedInput.push(string);
  2751. }
  2752. // clear _12h flag if hour is <= 12
  2753. if (getParsingFlags(config).bigHour === true &&
  2754. config._a[HOUR] <= 12 &&
  2755. config._a[HOUR] > 0) {
  2756. getParsingFlags(config).bigHour = undefined;
  2757. }
  2758. getParsingFlags(config).parsedDateParts = config._a.slice(0);
  2759. getParsingFlags(config).meridiem = config._meridiem;
  2760. // handle meridiem
  2761. config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
  2762. configFromArray(config);
  2763. checkOverflow(config);
  2764. }
  2765. function meridiemFixWrap (locale, hour, meridiem) {
  2766. var isPm;
  2767. if (meridiem == null) {
  2768. // nothing to do
  2769. return hour;
  2770. }
  2771. if (locale.meridiemHour != null) {
  2772. return locale.meridiemHour(hour, meridiem);
  2773. } else if (locale.isPM != null) {
  2774. // Fallback
  2775. isPm = locale.isPM(meridiem);
  2776. if (isPm && hour < 12) {
  2777. hour += 12;
  2778. }
  2779. if (!isPm && hour === 12) {
  2780. hour = 0;
  2781. }
  2782. return hour;
  2783. } else {
  2784. // this is not supposed to happen
  2785. return hour;
  2786. }
  2787. }
  2788. // date from string and array of format strings
  2789. function configFromStringAndArray(config) {
  2790. var tempConfig,
  2791. bestMoment,
  2792. scoreToBeat,
  2793. i,
  2794. currentScore;
  2795. if (config._f.length === 0) {
  2796. getParsingFlags(config).invalidFormat = true;
  2797. config._d = new Date(NaN);
  2798. return;
  2799. }
  2800. for (i = 0; i < config._f.length; i++) {
  2801. currentScore = 0;
  2802. tempConfig = copyConfig({}, config);
  2803. if (config._useUTC != null) {
  2804. tempConfig._useUTC = config._useUTC;
  2805. }
  2806. tempConfig._f = config._f[i];
  2807. configFromStringAndFormat(tempConfig);
  2808. if (!valid__isValid(tempConfig)) {
  2809. continue;
  2810. }
  2811. // if there is any input that was not parsed add a penalty for that format
  2812. currentScore += getParsingFlags(tempConfig).charsLeftOver;
  2813. //or tokens
  2814. currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
  2815. getParsingFlags(tempConfig).score = currentScore;
  2816. if (scoreToBeat == null || currentScore < scoreToBeat) {
  2817. scoreToBeat = currentScore;
  2818. bestMoment = tempConfig;
  2819. }
  2820. }
  2821. extend(config, bestMoment || tempConfig);
  2822. }
  2823. function configFromObject(config) {
  2824. if (config._d) {
  2825. return;
  2826. }
  2827. var i = normalizeObjectUnits(config._i);
  2828. config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
  2829. return obj && parseInt(obj, 10);
  2830. });
  2831. configFromArray(config);
  2832. }
  2833. function createFromConfig (config) {
  2834. var res = new Moment(checkOverflow(prepareConfig(config)));
  2835. if (res._nextDay) {
  2836. // Adding is smart enough around DST
  2837. res.add(1, 'd');
  2838. res._nextDay = undefined;
  2839. }
  2840. return res;
  2841. }
  2842. function prepareConfig (config) {
  2843. var input = config._i,
  2844. format = config._f;
  2845. config._locale = config._locale || locale_locales__getLocale(config._l);
  2846. if (input === null || (format === undefined && input === '')) {
  2847. return valid__createInvalid({nullInput: true});
  2848. }
  2849. if (typeof input === 'string') {
  2850. config._i = input = config._locale.preparse(input);
  2851. }
  2852. if (isMoment(input)) {
  2853. return new Moment(checkOverflow(input));
  2854. } else if (isArray(format)) {
  2855. configFromStringAndArray(config);
  2856. } else if (format) {
  2857. configFromStringAndFormat(config);
  2858. } else if (isDate(input)) {
  2859. config._d = input;
  2860. } else {
  2861. configFromInput(config);
  2862. }
  2863. if (!valid__isValid(config)) {
  2864. config._d = null;
  2865. }
  2866. return config;
  2867. }
  2868. function configFromInput(config) {
  2869. var input = config._i;
  2870. if (input === undefined) {
  2871. config._d = new Date(utils_hooks__hooks.now());
  2872. } else if (isDate(input)) {
  2873. config._d = new Date(input.valueOf());
  2874. } else if (typeof input === 'string') {
  2875. configFromString(config);
  2876. } else if (isArray(input)) {
  2877. config._a = map(input.slice(0), function (obj) {
  2878. return parseInt(obj, 10);
  2879. });
  2880. configFromArray(config);
  2881. } else if (typeof(input) === 'object') {
  2882. configFromObject(config);
  2883. } else if (typeof(input) === 'number') {
  2884. // from milliseconds
  2885. config._d = new Date(input);
  2886. } else {
  2887. utils_hooks__hooks.createFromInputFallback(config);
  2888. }
  2889. }
  2890. function createLocalOrUTC (input, format, locale, strict, isUTC) {
  2891. var c = {};
  2892. if (typeof(locale) === 'boolean') {
  2893. strict = locale;
  2894. locale = undefined;
  2895. }
  2896. // object construction must be done this way.
  2897. // https://github.com/moment/moment/issues/1423
  2898. c._isAMomentObject = true;
  2899. c._useUTC = c._isUTC = isUTC;
  2900. c._l = locale;
  2901. c._i = input;
  2902. c._f = format;
  2903. c._strict = strict;
  2904. return createFromConfig(c);
  2905. }
  2906. function local__createLocal (input, format, locale, strict) {
  2907. return createLocalOrUTC(input, format, locale, strict, false);
  2908. }
  2909. var prototypeMin = deprecate(
  2910. 'moment().min is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
  2911. function () {
  2912. var other = local__createLocal.apply(null, arguments);
  2913. if (this.isValid() && other.isValid()) {
  2914. return other < this ? this : other;
  2915. } else {
  2916. return valid__createInvalid();
  2917. }
  2918. }
  2919. );
  2920. var prototypeMax = deprecate(
  2921. 'moment().max is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
  2922. function () {
  2923. var other = local__createLocal.apply(null, arguments);
  2924. if (this.isValid() && other.isValid()) {
  2925. return other > this ? this : other;
  2926. } else {
  2927. return valid__createInvalid();
  2928. }
  2929. }
  2930. );
  2931. // Pick a moment m from moments so that m[fn](other) is true for all
  2932. // other. This relies on the function fn to be transitive.
  2933. //
  2934. // moments should either be an array of moment objects or an array, whose
  2935. // first element is an array of moment objects.
  2936. function pickBy(fn, moments) {
  2937. var res, i;
  2938. if (moments.length === 1 && isArray(moments[0])) {
  2939. moments = moments[0];
  2940. }
  2941. if (!moments.length) {
  2942. return local__createLocal();
  2943. }
  2944. res = moments[0];
  2945. for (i = 1; i < moments.length; ++i) {
  2946. if (!moments[i].isValid() || moments[i][fn](res)) {
  2947. res = moments[i];
  2948. }
  2949. }
  2950. return res;
  2951. }
  2952. // TODO: Use [].sort instead?
  2953. function min () {
  2954. var args = [].slice.call(arguments, 0);
  2955. return pickBy('isBefore', args);
  2956. }
  2957. function max () {
  2958. var args = [].slice.call(arguments, 0);
  2959. return pickBy('isAfter', args);
  2960. }
  2961. var now = function () {
  2962. return Date.now ? Date.now() : +(new Date());
  2963. };
  2964. function Duration (duration) {
  2965. var normalizedInput = normalizeObjectUnits(duration),
  2966. years = normalizedInput.year || 0,
  2967. quarters = normalizedInput.quarter || 0,
  2968. months = normalizedInput.month || 0,
  2969. weeks = normalizedInput.week || 0,
  2970. days = normalizedInput.day || 0,
  2971. hours = normalizedInput.hour || 0,
  2972. minutes = normalizedInput.minute || 0,
  2973. seconds = normalizedInput.second || 0,
  2974. milliseconds = normalizedInput.millisecond || 0;
  2975. // representation for dateAddRemove
  2976. this._milliseconds = +milliseconds +
  2977. seconds * 1e3 + // 1000
  2978. minutes * 6e4 + // 1000 * 60
  2979. hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
  2980. // Because of dateAddRemove treats 24 hours as different from a
  2981. // day when working around DST, we need to store them separately
  2982. this._days = +days +
  2983. weeks * 7;
  2984. // It is impossible translate months into days without knowing
  2985. // which months you are are talking about, so we have to store
  2986. // it separately.
  2987. this._months = +months +
  2988. quarters * 3 +
  2989. years * 12;
  2990. this._data = {};
  2991. this._locale = locale_locales__getLocale();
  2992. this._bubble();
  2993. }
  2994. function isDuration (obj) {
  2995. return obj instanceof Duration;
  2996. }
  2997. // FORMATTING
  2998. function offset (token, separator) {
  2999. addFormatToken(token, 0, 0, function () {
  3000. var offset = this.utcOffset();
  3001. var sign = '+';
  3002. if (offset < 0) {
  3003. offset = -offset;
  3004. sign = '-';
  3005. }
  3006. return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
  3007. });
  3008. }
  3009. offset('Z', ':');
  3010. offset('ZZ', '');
  3011. // PARSING
  3012. addRegexToken('Z', matchShortOffset);
  3013. addRegexToken('ZZ', matchShortOffset);
  3014. addParseToken(['Z', 'ZZ'], function (input, array, config) {
  3015. config._useUTC = true;
  3016. config._tzm = offsetFromString(matchShortOffset, input);
  3017. });
  3018. // HELPERS
  3019. // timezone chunker
  3020. // '+10:00' > ['10', '00']
  3021. // '-1530' > ['-15', '30']
  3022. var chunkOffset = /([\+\-]|\d\d)/gi;
  3023. function offsetFromString(matcher, string) {
  3024. var matches = ((string || '').match(matcher) || []);
  3025. var chunk = matches[matches.length - 1] || [];
  3026. var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
  3027. var minutes = +(parts[1] * 60) + toInt(parts[2]);
  3028. return parts[0] === '+' ? minutes : -minutes;
  3029. }
  3030. // Return a moment from input, that is local/utc/zone equivalent to model.
  3031. function cloneWithOffset(input, model) {
  3032. var res, diff;
  3033. if (model._isUTC) {
  3034. res = model.clone();
  3035. diff = (isMoment(input) || isDate(input) ? input.valueOf() : local__createLocal(input).valueOf()) - res.valueOf();
  3036. // Use low-level api, because this fn is low-level api.
  3037. res._d.setTime(res._d.valueOf() + diff);
  3038. utils_hooks__hooks.updateOffset(res, false);
  3039. return res;
  3040. } else {
  3041. return local__createLocal(input).local();
  3042. }
  3043. }
  3044. function getDateOffset (m) {
  3045. // On Firefox.24 Date#getTimezoneOffset returns a floating point.
  3046. // https://github.com/moment/moment/pull/1871
  3047. return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
  3048. }
  3049. // HOOKS
  3050. // This function will be called whenever a moment is mutated.
  3051. // It is intended to keep the offset in sync with the timezone.
  3052. utils_hooks__hooks.updateOffset = function () {};
  3053. // MOMENTS
  3054. // keepLocalTime = true means only change the timezone, without
  3055. // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
  3056. // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
  3057. // +0200, so we adjust the time as needed, to be valid.
  3058. //
  3059. // Keeping the time actually adds/subtracts (one hour)
  3060. // from the actual represented time. That is why we call updateOffset
  3061. // a second time. In case it wants us to change the offset again
  3062. // _changeInProgress == true case, then we have to adjust, because
  3063. // there is no such time in the given timezone.
  3064. function getSetOffset (input, keepLocalTime) {
  3065. var offset = this._offset || 0,
  3066. localAdjust;
  3067. if (!this.isValid()) {
  3068. return input != null ? this : NaN;
  3069. }
  3070. if (input != null) {
  3071. if (typeof input === 'string') {
  3072. input = offsetFromString(matchShortOffset, input);
  3073. } else if (Math.abs(input) < 16) {
  3074. input = input * 60;
  3075. }
  3076. if (!this._isUTC && keepLocalTime) {
  3077. localAdjust = getDateOffset(this);
  3078. }
  3079. this._offset = input;
  3080. this._isUTC = true;
  3081. if (localAdjust != null) {
  3082. this.add(localAdjust, 'm');
  3083. }
  3084. if (offset !== input) {
  3085. if (!keepLocalTime || this._changeInProgress) {
  3086. add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false);
  3087. } else if (!this._changeInProgress) {
  3088. this._changeInProgress = true;
  3089. utils_hooks__hooks.updateOffset(this, true);
  3090. this._changeInProgress = null;
  3091. }
  3092. }
  3093. return this;
  3094. } else {
  3095. return this._isUTC ? offset : getDateOffset(this);
  3096. }
  3097. }
  3098. function getSetZone (input, keepLocalTime) {
  3099. if (input != null) {
  3100. if (typeof input !== 'string') {
  3101. input = -input;
  3102. }
  3103. this.utcOffset(input, keepLocalTime);
  3104. return this;
  3105. } else {
  3106. return -this.utcOffset();
  3107. }
  3108. }
  3109. function setOffsetToUTC (keepLocalTime) {
  3110. return this.utcOffset(0, keepLocalTime);
  3111. }
  3112. function setOffsetToLocal (keepLocalTime) {
  3113. if (this._isUTC) {
  3114. this.utcOffset(0, keepLocalTime);
  3115. this._isUTC = false;
  3116. if (keepLocalTime) {
  3117. this.subtract(getDateOffset(this), 'm');
  3118. }
  3119. }
  3120. return this;
  3121. }
  3122. function setOffsetToParsedOffset () {
  3123. if (this._tzm) {
  3124. this.utcOffset(this._tzm);
  3125. } else if (typeof this._i === 'string') {
  3126. this.utcOffset(offsetFromString(matchOffset, this._i));
  3127. }
  3128. return this;
  3129. }
  3130. function hasAlignedHourOffset (input) {
  3131. if (!this.isValid()) {
  3132. return false;
  3133. }
  3134. input = input ? local__createLocal(input).utcOffset() : 0;
  3135. return (this.utcOffset() - input) % 60 === 0;
  3136. }
  3137. function isDaylightSavingTime () {
  3138. return (
  3139. this.utcOffset() > this.clone().month(0).utcOffset() ||
  3140. this.utcOffset() > this.clone().month(5).utcOffset()
  3141. );
  3142. }
  3143. function isDaylightSavingTimeShifted () {
  3144. if (!isUndefined(this._isDSTShifted)) {
  3145. return this._isDSTShifted;
  3146. }
  3147. var c = {};
  3148. copyConfig(c, this);
  3149. c = prepareConfig(c);
  3150. if (c._a) {
  3151. var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a);
  3152. this._isDSTShifted = this.isValid() &&
  3153. compareArrays(c._a, other.toArray()) > 0;
  3154. } else {
  3155. this._isDSTShifted = false;
  3156. }
  3157. return this._isDSTShifted;
  3158. }
  3159. function isLocal () {
  3160. return this.isValid() ? !this._isUTC : false;
  3161. }
  3162. function isUtcOffset () {
  3163. return this.isValid() ? this._isUTC : false;
  3164. }
  3165. function isUtc () {
  3166. return this.isValid() ? this._isUTC && this._offset === 0 : false;
  3167. }
  3168. // ASP.NET json date format regex
  3169. var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/;
  3170. // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
  3171. // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
  3172. // and further modified to allow for strings containing both week and day
  3173. 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)?)?$/;
  3174. function create__createDuration (input, key) {
  3175. var duration = input,
  3176. // matching against regexp is expensive, do it on demand
  3177. match = null,
  3178. sign,
  3179. ret,
  3180. diffRes;
  3181. if (isDuration(input)) {
  3182. duration = {
  3183. ms : input._milliseconds,
  3184. d : input._days,
  3185. M : input._months
  3186. };
  3187. } else if (typeof input === 'number') {
  3188. duration = {};
  3189. if (key) {
  3190. duration[key] = input;
  3191. } else {
  3192. duration.milliseconds = input;
  3193. }
  3194. } else if (!!(match = aspNetRegex.exec(input))) {
  3195. sign = (match[1] === '-') ? -1 : 1;
  3196. duration = {
  3197. y : 0,
  3198. d : toInt(match[DATE]) * sign,
  3199. h : toInt(match[HOUR]) * sign,
  3200. m : toInt(match[MINUTE]) * sign,
  3201. s : toInt(match[SECOND]) * sign,
  3202. ms : toInt(match[MILLISECOND]) * sign
  3203. };
  3204. } else if (!!(match = isoRegex.exec(input))) {
  3205. sign = (match[1] === '-') ? -1 : 1;
  3206. duration = {
  3207. y : parseIso(match[2], sign),
  3208. M : parseIso(match[3], sign),
  3209. w : parseIso(match[4], sign),
  3210. d : parseIso(match[5], sign),
  3211. h : parseIso(match[6], sign),
  3212. m : parseIso(match[7], sign),
  3213. s : parseIso(match[8], sign)
  3214. };
  3215. } else if (duration == null) {// checks for null or undefined
  3216. duration = {};
  3217. } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
  3218. diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to));
  3219. duration = {};
  3220. duration.ms = diffRes.milliseconds;
  3221. duration.M = diffRes.months;
  3222. }
  3223. ret = new Duration(duration);
  3224. if (isDuration(input) && hasOwnProp(input, '_locale')) {
  3225. ret._locale = input._locale;
  3226. }
  3227. return ret;
  3228. }
  3229. create__createDuration.fn = Duration.prototype;
  3230. function parseIso (inp, sign) {
  3231. // We'd normally use ~~inp for this, but unfortunately it also
  3232. // converts floats to ints.
  3233. // inp may be undefined, so careful calling replace on it.
  3234. var res = inp && parseFloat(inp.replace(',', '.'));
  3235. // apply sign while we're at it
  3236. return (isNaN(res) ? 0 : res) * sign;
  3237. }
  3238. function positiveMomentsDifference(base, other) {
  3239. var res = {milliseconds: 0, months: 0};
  3240. res.months = other.month() - base.month() +
  3241. (other.year() - base.year()) * 12;
  3242. if (base.clone().add(res.months, 'M').isAfter(other)) {
  3243. --res.months;
  3244. }
  3245. res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
  3246. return res;
  3247. }
  3248. function momentsDifference(base, other) {
  3249. var res;
  3250. if (!(base.isValid() && other.isValid())) {
  3251. return {milliseconds: 0, months: 0};
  3252. }
  3253. other = cloneWithOffset(other, base);
  3254. if (base.isBefore(other)) {
  3255. res = positiveMomentsDifference(base, other);
  3256. } else {
  3257. res = positiveMomentsDifference(other, base);
  3258. res.milliseconds = -res.milliseconds;
  3259. res.months = -res.months;
  3260. }
  3261. return res;
  3262. }
  3263. function absRound (number) {
  3264. if (number < 0) {
  3265. return Math.round(-1 * number) * -1;
  3266. } else {
  3267. return Math.round(number);
  3268. }
  3269. }
  3270. // TODO: remove 'name' arg after deprecation is removed
  3271. function createAdder(direction, name) {
  3272. return function (val, period) {
  3273. var dur, tmp;
  3274. //invert the arguments, but complain about it
  3275. if (period !== null && !isNaN(+period)) {
  3276. deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
  3277. tmp = val; val = period; period = tmp;
  3278. }
  3279. val = typeof val === 'string' ? +val : val;
  3280. dur = create__createDuration(val, period);
  3281. add_subtract__addSubtract(this, dur, direction);
  3282. return this;
  3283. };
  3284. }
  3285. function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) {
  3286. var milliseconds = duration._milliseconds,
  3287. days = absRound(duration._days),
  3288. months = absRound(duration._months);
  3289. if (!mom.isValid()) {
  3290. // No op
  3291. return;
  3292. }
  3293. updateOffset = updateOffset == null ? true : updateOffset;
  3294. if (milliseconds) {
  3295. mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
  3296. }
  3297. if (days) {
  3298. get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding);
  3299. }
  3300. if (months) {
  3301. setMonth(mom, get_set__get(mom, 'Month') + months * isAdding);
  3302. }
  3303. if (updateOffset) {
  3304. utils_hooks__hooks.updateOffset(mom, days || months);
  3305. }
  3306. }
  3307. var add_subtract__add = createAdder(1, 'add');
  3308. var add_subtract__subtract = createAdder(-1, 'subtract');
  3309. function moment_calendar__calendar (time, formats) {
  3310. // We want to compare the start of today, vs this.
  3311. // Getting start-of-today depends on whether we're local/utc/offset or not.
  3312. var now = time || local__createLocal(),
  3313. sod = cloneWithOffset(now, this).startOf('day'),
  3314. diff = this.diff(sod, 'days', true),
  3315. format = diff < -6 ? 'sameElse' :
  3316. diff < -1 ? 'lastWeek' :
  3317. diff < 0 ? 'lastDay' :
  3318. diff < 1 ? 'sameDay' :
  3319. diff < 2 ? 'nextDay' :
  3320. diff < 7 ? 'nextWeek' : 'sameElse';
  3321. var output = formats && (isFunction(formats[format]) ? formats[format]() : formats[format]);
  3322. return this.format(output || this.localeData().calendar(format, this, local__createLocal(now)));
  3323. }
  3324. function clone () {
  3325. return new Moment(this);
  3326. }
  3327. function isAfter (input, units) {
  3328. var localInput = isMoment(input) ? input : local__createLocal(input);
  3329. if (!(this.isValid() && localInput.isValid())) {
  3330. return false;
  3331. }
  3332. units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
  3333. if (units === 'millisecond') {
  3334. return this.valueOf() > localInput.valueOf();
  3335. } else {
  3336. return localInput.valueOf() < this.clone().startOf(units).valueOf();
  3337. }
  3338. }
  3339. function isBefore (input, units) {
  3340. var localInput = isMoment(input) ? input : local__createLocal(input);
  3341. if (!(this.isValid() && localInput.isValid())) {
  3342. return false;
  3343. }
  3344. units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
  3345. if (units === 'millisecond') {
  3346. return this.valueOf() < localInput.valueOf();
  3347. } else {
  3348. return this.clone().endOf(units).valueOf() < localInput.valueOf();
  3349. }
  3350. }
  3351. function isBetween (from, to, units, inclusivity) {
  3352. inclusivity = inclusivity || '()';
  3353. return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
  3354. (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
  3355. }
  3356. function isSame (input, units) {
  3357. var localInput = isMoment(input) ? input : local__createLocal(input),
  3358. inputMs;
  3359. if (!(this.isValid() && localInput.isValid())) {
  3360. return false;
  3361. }
  3362. units = normalizeUnits(units || 'millisecond');
  3363. if (units === 'millisecond') {
  3364. return this.valueOf() === localInput.valueOf();
  3365. } else {
  3366. inputMs = localInput.valueOf();
  3367. return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
  3368. }
  3369. }
  3370. function isSameOrAfter (input, units) {
  3371. return this.isSame(input, units) || this.isAfter(input,units);
  3372. }
  3373. function isSameOrBefore (input, units) {
  3374. return this.isSame(input, units) || this.isBefore(input,units);
  3375. }
  3376. function diff (input, units, asFloat) {
  3377. var that,
  3378. zoneDelta,
  3379. delta, output;
  3380. if (!this.isValid()) {
  3381. return NaN;
  3382. }
  3383. that = cloneWithOffset(input, this);
  3384. if (!that.isValid()) {
  3385. return NaN;
  3386. }
  3387. zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
  3388. units = normalizeUnits(units);
  3389. if (units === 'year' || units === 'month' || units === 'quarter') {
  3390. output = monthDiff(this, that);
  3391. if (units === 'quarter') {
  3392. output = output / 3;
  3393. } else if (units === 'year') {
  3394. output = output / 12;
  3395. }
  3396. } else {
  3397. delta = this - that;
  3398. output = units === 'second' ? delta / 1e3 : // 1000
  3399. units === 'minute' ? delta / 6e4 : // 1000 * 60
  3400. units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
  3401. units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
  3402. units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
  3403. delta;
  3404. }
  3405. return asFloat ? output : absFloor(output);
  3406. }
  3407. function monthDiff (a, b) {
  3408. // difference in months
  3409. var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
  3410. // b is in (anchor - 1 month, anchor + 1 month)
  3411. anchor = a.clone().add(wholeMonthDiff, 'months'),
  3412. anchor2, adjust;
  3413. if (b - anchor < 0) {
  3414. anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
  3415. // linear across the month
  3416. adjust = (b - anchor) / (anchor - anchor2);
  3417. } else {
  3418. anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
  3419. // linear across the month
  3420. adjust = (b - anchor) / (anchor2 - anchor);
  3421. }
  3422. //check for negative zero, return zero if negative zero
  3423. return -(wholeMonthDiff + adjust) || 0;
  3424. }
  3425. utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
  3426. utils_hooks__hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
  3427. function toString () {
  3428. return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
  3429. }
  3430. function moment_format__toISOString () {
  3431. var m = this.clone().utc();
  3432. if (0 < m.year() && m.year() <= 9999) {
  3433. if (isFunction(Date.prototype.toISOString)) {
  3434. // native implementation is ~50x faster, use it when we can
  3435. return this.toDate().toISOString();
  3436. } else {
  3437. return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
  3438. }
  3439. } else {
  3440. return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
  3441. }
  3442. }
  3443. function format (inputString) {
  3444. if (!inputString) {
  3445. inputString = this.isUtc() ? utils_hooks__hooks.defaultFormatUtc : utils_hooks__hooks.defaultFormat;
  3446. }
  3447. var output = formatMoment(this, inputString);
  3448. return this.localeData().postformat(output);
  3449. }
  3450. function from (time, withoutSuffix) {
  3451. if (this.isValid() &&
  3452. ((isMoment(time) && time.isValid()) ||
  3453. local__createLocal(time).isValid())) {
  3454. return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
  3455. } else {
  3456. return this.localeData().invalidDate();
  3457. }
  3458. }
  3459. function fromNow (withoutSuffix) {
  3460. return this.from(local__createLocal(), withoutSuffix);
  3461. }
  3462. function to (time, withoutSuffix) {
  3463. if (this.isValid() &&
  3464. ((isMoment(time) && time.isValid()) ||
  3465. local__createLocal(time).isValid())) {
  3466. return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
  3467. } else {
  3468. return this.localeData().invalidDate();
  3469. }
  3470. }
  3471. function toNow (withoutSuffix) {
  3472. return this.to(local__createLocal(), withoutSuffix);
  3473. }
  3474. // If passed a locale key, it will set the locale for this
  3475. // instance. Otherwise, it will return the locale configuration
  3476. // variables for this instance.
  3477. function locale (key) {
  3478. var newLocaleData;
  3479. if (key === undefined) {
  3480. return this._locale._abbr;
  3481. } else {
  3482. newLocaleData = locale_locales__getLocale(key);
  3483. if (newLocaleData != null) {
  3484. this._locale = newLocaleData;
  3485. }
  3486. return this;
  3487. }
  3488. }
  3489. var lang = deprecate(
  3490. 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
  3491. function (key) {
  3492. if (key === undefined) {
  3493. return this.localeData();
  3494. } else {
  3495. return this.locale(key);
  3496. }
  3497. }
  3498. );
  3499. function localeData () {
  3500. return this._locale;
  3501. }
  3502. function startOf (units) {
  3503. units = normalizeUnits(units);
  3504. // the following switch intentionally omits break keywords
  3505. // to utilize falling through the cases.
  3506. switch (units) {
  3507. case 'year':
  3508. this.month(0);
  3509. /* falls through */
  3510. case 'quarter':
  3511. case 'month':
  3512. this.date(1);
  3513. /* falls through */
  3514. case 'week':
  3515. case 'isoWeek':
  3516. case 'day':
  3517. case 'date':
  3518. this.hours(0);
  3519. /* falls through */
  3520. case 'hour':
  3521. this.minutes(0);
  3522. /* falls through */
  3523. case 'minute':
  3524. this.seconds(0);
  3525. /* falls through */
  3526. case 'second':
  3527. this.milliseconds(0);
  3528. }
  3529. // weeks are a special case
  3530. if (units === 'week') {
  3531. this.weekday(0);
  3532. }
  3533. if (units === 'isoWeek') {
  3534. this.isoWeekday(1);
  3535. }
  3536. // quarters are also special
  3537. if (units === 'quarter') {
  3538. this.month(Math.floor(this.month() / 3) * 3);
  3539. }
  3540. return this;
  3541. }
  3542. function endOf (units) {
  3543. units = normalizeUnits(units);
  3544. if (units === undefined || units === 'millisecond') {
  3545. return this;
  3546. }
  3547. // 'date' is an alias for 'day', so it should be considered as such.
  3548. if (units === 'date') {
  3549. units = 'day';
  3550. }
  3551. return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
  3552. }
  3553. function to_type__valueOf () {
  3554. return this._d.valueOf() - ((this._offset || 0) * 60000);
  3555. }
  3556. function unix () {
  3557. return Math.floor(this.valueOf() / 1000);
  3558. }
  3559. function toDate () {
  3560. return this._offset ? new Date(this.valueOf()) : this._d;
  3561. }
  3562. function toArray () {
  3563. var m = this;
  3564. return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
  3565. }
  3566. function toObject () {
  3567. var m = this;
  3568. return {
  3569. years: m.year(),
  3570. months: m.month(),
  3571. date: m.date(),
  3572. hours: m.hours(),
  3573. minutes: m.minutes(),
  3574. seconds: m.seconds(),
  3575. milliseconds: m.milliseconds()
  3576. };
  3577. }
  3578. function toJSON () {
  3579. // new Date(NaN).toJSON() === null
  3580. return this.isValid() ? this.toISOString() : null;
  3581. }
  3582. function moment_valid__isValid () {
  3583. return valid__isValid(this);
  3584. }
  3585. function parsingFlags () {
  3586. return extend({}, getParsingFlags(this));
  3587. }
  3588. function invalidAt () {
  3589. return getParsingFlags(this).overflow;
  3590. }
  3591. function creationData() {
  3592. return {
  3593. input: this._i,
  3594. format: this._f,
  3595. locale: this._locale,
  3596. isUTC: this._isUTC,
  3597. strict: this._strict
  3598. };
  3599. }
  3600. // FORMATTING
  3601. addFormatToken(0, ['gg', 2], 0, function () {
  3602. return this.weekYear() % 100;
  3603. });
  3604. addFormatToken(0, ['GG', 2], 0, function () {
  3605. return this.isoWeekYear() % 100;
  3606. });
  3607. function addWeekYearFormatToken (token, getter) {
  3608. addFormatToken(0, [token, token.length], 0, getter);
  3609. }
  3610. addWeekYearFormatToken('gggg', 'weekYear');
  3611. addWeekYearFormatToken('ggggg', 'weekYear');
  3612. addWeekYearFormatToken('GGGG', 'isoWeekYear');
  3613. addWeekYearFormatToken('GGGGG', 'isoWeekYear');
  3614. // ALIASES
  3615. addUnitAlias('weekYear', 'gg');
  3616. addUnitAlias('isoWeekYear', 'GG');
  3617. // PARSING
  3618. addRegexToken('G', matchSigned);
  3619. addRegexToken('g', matchSigned);
  3620. addRegexToken('GG', match1to2, match2);
  3621. addRegexToken('gg', match1to2, match2);
  3622. addRegexToken('GGGG', match1to4, match4);
  3623. addRegexToken('gggg', match1to4, match4);
  3624. addRegexToken('GGGGG', match1to6, match6);
  3625. addRegexToken('ggggg', match1to6, match6);
  3626. addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
  3627. week[token.substr(0, 2)] = toInt(input);
  3628. });
  3629. addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
  3630. week[token] = utils_hooks__hooks.parseTwoDigitYear(input);
  3631. });
  3632. // MOMENTS
  3633. function getSetWeekYear (input) {
  3634. return getSetWeekYearHelper.call(this,
  3635. input,
  3636. this.week(),
  3637. this.weekday(),
  3638. this.localeData()._week.dow,
  3639. this.localeData()._week.doy);
  3640. }
  3641. function getSetISOWeekYear (input) {
  3642. return getSetWeekYearHelper.call(this,
  3643. input, this.isoWeek(), this.isoWeekday(), 1, 4);
  3644. }
  3645. function getISOWeeksInYear () {
  3646. return weeksInYear(this.year(), 1, 4);
  3647. }
  3648. function getWeeksInYear () {
  3649. var weekInfo = this.localeData()._week;
  3650. return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
  3651. }
  3652. function getSetWeekYearHelper(input, week, weekday, dow, doy) {
  3653. var weeksTarget;
  3654. if (input == null) {
  3655. return weekOfYear(this, dow, doy).year;
  3656. } else {
  3657. weeksTarget = weeksInYear(input, dow, doy);
  3658. if (week > weeksTarget) {
  3659. week = weeksTarget;
  3660. }
  3661. return setWeekAll.call(this, input, week, weekday, dow, doy);
  3662. }
  3663. }
  3664. function setWeekAll(weekYear, week, weekday, dow, doy) {
  3665. var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
  3666. date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
  3667. this.year(date.getUTCFullYear());
  3668. this.month(date.getUTCMonth());
  3669. this.date(date.getUTCDate());
  3670. return this;
  3671. }
  3672. // FORMATTING
  3673. addFormatToken('Q', 0, 'Qo', 'quarter');
  3674. // ALIASES
  3675. addUnitAlias('quarter', 'Q');
  3676. // PARSING
  3677. addRegexToken('Q', match1);
  3678. addParseToken('Q', function (input, array) {
  3679. array[MONTH] = (toInt(input) - 1) * 3;
  3680. });
  3681. // MOMENTS
  3682. function getSetQuarter (input) {
  3683. return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
  3684. }
  3685. // FORMATTING
  3686. addFormatToken('w', ['ww', 2], 'wo', 'week');
  3687. addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
  3688. // ALIASES
  3689. addUnitAlias('week', 'w');
  3690. addUnitAlias('isoWeek', 'W');
  3691. // PARSING
  3692. addRegexToken('w', match1to2);
  3693. addRegexToken('ww', match1to2, match2);
  3694. addRegexToken('W', match1to2);
  3695. addRegexToken('WW', match1to2, match2);
  3696. addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
  3697. week[token.substr(0, 1)] = toInt(input);
  3698. });
  3699. // HELPERS
  3700. // LOCALES
  3701. function localeWeek (mom) {
  3702. return weekOfYear(mom, this._week.dow, this._week.doy).week;
  3703. }
  3704. var defaultLocaleWeek = {
  3705. dow : 0, // Sunday is the first day of the week.
  3706. doy : 6 // The week that contains Jan 1st is the first week of the year.
  3707. };
  3708. function localeFirstDayOfWeek () {
  3709. return this._week.dow;
  3710. }
  3711. function localeFirstDayOfYear () {
  3712. return this._week.doy;
  3713. }
  3714. // MOMENTS
  3715. function getSetWeek (input) {
  3716. var week = this.localeData().week(this);
  3717. return input == null ? week : this.add((input - week) * 7, 'd');
  3718. }
  3719. function getSetISOWeek (input) {
  3720. var week = weekOfYear(this, 1, 4).week;
  3721. return input == null ? week : this.add((input - week) * 7, 'd');
  3722. }
  3723. // FORMATTING
  3724. addFormatToken('D', ['DD', 2], 'Do', 'date');
  3725. // ALIASES
  3726. addUnitAlias('date', 'D');
  3727. // PARSING
  3728. addRegexToken('D', match1to2);
  3729. addRegexToken('DD', match1to2, match2);
  3730. addRegexToken('Do', function (isStrict, locale) {
  3731. return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
  3732. });
  3733. addParseToken(['D', 'DD'], DATE);
  3734. addParseToken('Do', function (input, array) {
  3735. array[DATE] = toInt(input.match(match1to2)[0], 10);
  3736. });
  3737. // MOMENTS
  3738. var getSetDayOfMonth = makeGetSet('Date', true);
  3739. // FORMATTING
  3740. addFormatToken('d', 0, 'do', 'day');
  3741. addFormatToken('dd', 0, 0, function (format) {
  3742. return this.localeData().weekdaysMin(this, format);
  3743. });
  3744. addFormatToken('ddd', 0, 0, function (format) {
  3745. return this.localeData().weekdaysShort(this, format);
  3746. });
  3747. addFormatToken('dddd', 0, 0, function (format) {
  3748. return this.localeData().weekdays(this, format);
  3749. });
  3750. addFormatToken('e', 0, 0, 'weekday');
  3751. addFormatToken('E', 0, 0, 'isoWeekday');
  3752. // ALIASES
  3753. addUnitAlias('day', 'd');
  3754. addUnitAlias('weekday', 'e');
  3755. addUnitAlias('isoWeekday', 'E');
  3756. // PARSING
  3757. addRegexToken('d', match1to2);
  3758. addRegexToken('e', match1to2);
  3759. addRegexToken('E', match1to2);
  3760. addRegexToken('dd', function (isStrict, locale) {
  3761. return locale.weekdaysMinRegex(isStrict);
  3762. });
  3763. addRegexToken('ddd', function (isStrict, locale) {
  3764. return locale.weekdaysShortRegex(isStrict);
  3765. });
  3766. addRegexToken('dddd', function (isStrict, locale) {
  3767. return locale.weekdaysRegex(isStrict);
  3768. });
  3769. addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
  3770. var weekday = config._locale.weekdaysParse(input, token, config._strict);
  3771. // if we didn't get a weekday name, mark the date as invalid
  3772. if (weekday != null) {
  3773. week.d = weekday;
  3774. } else {
  3775. getParsingFlags(config).invalidWeekday = input;
  3776. }
  3777. });
  3778. addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
  3779. week[token] = toInt(input);
  3780. });
  3781. // HELPERS
  3782. function parseWeekday(input, locale) {
  3783. if (typeof input !== 'string') {
  3784. return input;
  3785. }
  3786. if (!isNaN(input)) {
  3787. return parseInt(input, 10);
  3788. }
  3789. input = locale.weekdaysParse(input);
  3790. if (typeof input === 'number') {
  3791. return input;
  3792. }
  3793. return null;
  3794. }
  3795. // LOCALES
  3796. var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
  3797. function localeWeekdays (m, format) {
  3798. return isArray(this._weekdays) ? this._weekdays[m.day()] :
  3799. this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
  3800. }
  3801. var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
  3802. function localeWeekdaysShort (m) {
  3803. return this._weekdaysShort[m.day()];
  3804. }
  3805. var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
  3806. function localeWeekdaysMin (m) {
  3807. return this._weekdaysMin[m.day()];
  3808. }
  3809. function day_of_week__handleStrictParse(weekdayName, format, strict) {
  3810. var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
  3811. if (!this._weekdaysParse) {
  3812. this._weekdaysParse = [];
  3813. this._shortWeekdaysParse = [];
  3814. this._minWeekdaysParse = [];
  3815. for (i = 0; i < 7; ++i) {
  3816. mom = create_utc__createUTC([2000, 1]).day(i);
  3817. this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
  3818. this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
  3819. this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
  3820. }
  3821. }
  3822. if (strict) {
  3823. if (format === 'dddd') {
  3824. ii = indexOf.call(this._weekdaysParse, llc);
  3825. return ii !== -1 ? ii : null;
  3826. } else if (format === 'ddd') {
  3827. ii = indexOf.call(this._shortWeekdaysParse, llc);
  3828. return ii !== -1 ? ii : null;
  3829. } else {
  3830. ii = indexOf.call(this._minWeekdaysParse, llc);
  3831. return ii !== -1 ? ii : null;
  3832. }
  3833. } else {
  3834. if (format === 'dddd') {
  3835. ii = indexOf.call(this._weekdaysParse, llc);
  3836. if (ii !== -1) {
  3837. return ii;
  3838. }
  3839. ii = indexOf.call(this._shortWeekdaysParse, llc);
  3840. if (ii !== -1) {
  3841. return ii;
  3842. }
  3843. ii = indexOf.call(this._minWeekdaysParse, llc);
  3844. return ii !== -1 ? ii : null;
  3845. } else if (format === 'ddd') {
  3846. ii = indexOf.call(this._shortWeekdaysParse, llc);
  3847. if (ii !== -1) {
  3848. return ii;
  3849. }
  3850. ii = indexOf.call(this._weekdaysParse, llc);
  3851. if (ii !== -1) {
  3852. return ii;
  3853. }
  3854. ii = indexOf.call(this._minWeekdaysParse, llc);
  3855. return ii !== -1 ? ii : null;
  3856. } else {
  3857. ii = indexOf.call(this._minWeekdaysParse, llc);
  3858. if (ii !== -1) {
  3859. return ii;
  3860. }
  3861. ii = indexOf.call(this._weekdaysParse, llc);
  3862. if (ii !== -1) {
  3863. return ii;
  3864. }
  3865. ii = indexOf.call(this._shortWeekdaysParse, llc);
  3866. return ii !== -1 ? ii : null;
  3867. }
  3868. }
  3869. }
  3870. function localeWeekdaysParse (weekdayName, format, strict) {
  3871. var i, mom, regex;
  3872. if (this._weekdaysParseExact) {
  3873. return day_of_week__handleStrictParse.call(this, weekdayName, format, strict);
  3874. }
  3875. if (!this._weekdaysParse) {
  3876. this._weekdaysParse = [];
  3877. this._minWeekdaysParse = [];
  3878. this._shortWeekdaysParse = [];
  3879. this._fullWeekdaysParse = [];
  3880. }
  3881. for (i = 0; i < 7; i++) {
  3882. // make the regex if we don't have it already
  3883. mom = create_utc__createUTC([2000, 1]).day(i);
  3884. if (strict && !this._fullWeekdaysParse[i]) {
  3885. this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
  3886. this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
  3887. this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
  3888. }
  3889. if (!this._weekdaysParse[i]) {
  3890. regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
  3891. this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
  3892. }
  3893. // test the regex
  3894. if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
  3895. return i;
  3896. } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
  3897. return i;
  3898. } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
  3899. return i;
  3900. } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
  3901. return i;
  3902. }
  3903. }
  3904. }
  3905. // MOMENTS
  3906. function getSetDayOfWeek (input) {
  3907. if (!this.isValid()) {
  3908. return input != null ? this : NaN;
  3909. }
  3910. var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
  3911. if (input != null) {
  3912. input = parseWeekday(input, this.localeData());
  3913. return this.add(input - day, 'd');
  3914. } else {
  3915. return day;
  3916. }
  3917. }
  3918. function getSetLocaleDayOfWeek (input) {
  3919. if (!this.isValid()) {
  3920. return input != null ? this : NaN;
  3921. }
  3922. var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
  3923. return input == null ? weekday : this.add(input - weekday, 'd');
  3924. }
  3925. function getSetISODayOfWeek (input) {
  3926. if (!this.isValid()) {
  3927. return input != null ? this : NaN;
  3928. }
  3929. // behaves the same as moment#day except
  3930. // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
  3931. // as a setter, sunday should belong to the previous week.
  3932. return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
  3933. }
  3934. var defaultWeekdaysRegex = matchWord;
  3935. function weekdaysRegex (isStrict) {
  3936. if (this._weekdaysParseExact) {
  3937. if (!hasOwnProp(this, '_weekdaysRegex')) {
  3938. computeWeekdaysParse.call(this);
  3939. }
  3940. if (isStrict) {
  3941. return this._weekdaysStrictRegex;
  3942. } else {
  3943. return this._weekdaysRegex;
  3944. }
  3945. } else {
  3946. return this._weekdaysStrictRegex && isStrict ?
  3947. this._weekdaysStrictRegex : this._weekdaysRegex;
  3948. }
  3949. }
  3950. var defaultWeekdaysShortRegex = matchWord;
  3951. function weekdaysShortRegex (isStrict) {
  3952. if (this._weekdaysParseExact) {
  3953. if (!hasOwnProp(this, '_weekdaysRegex')) {
  3954. computeWeekdaysParse.call(this);
  3955. }
  3956. if (isStrict) {
  3957. return this._weekdaysShortStrictRegex;
  3958. } else {
  3959. return this._weekdaysShortRegex;
  3960. }
  3961. } else {
  3962. return this._weekdaysShortStrictRegex && isStrict ?
  3963. this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
  3964. }
  3965. }
  3966. var defaultWeekdaysMinRegex = matchWord;
  3967. function weekdaysMinRegex (isStrict) {
  3968. if (this._weekdaysParseExact) {
  3969. if (!hasOwnProp(this, '_weekdaysRegex')) {
  3970. computeWeekdaysParse.call(this);
  3971. }
  3972. if (isStrict) {
  3973. return this._weekdaysMinStrictRegex;
  3974. } else {
  3975. return this._weekdaysMinRegex;
  3976. }
  3977. } else {
  3978. return this._weekdaysMinStrictRegex && isStrict ?
  3979. this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
  3980. }
  3981. }
  3982. function computeWeekdaysParse () {
  3983. function cmpLenRev(a, b) {
  3984. return b.length - a.length;
  3985. }
  3986. var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
  3987. i, mom, minp, shortp, longp;
  3988. for (i = 0; i < 7; i++) {
  3989. // make the regex if we don't have it already
  3990. mom = create_utc__createUTC([2000, 1]).day(i);
  3991. minp = this.weekdaysMin(mom, '');
  3992. shortp = this.weekdaysShort(mom, '');
  3993. longp = this.weekdays(mom, '');
  3994. minPieces.push(minp);
  3995. shortPieces.push(shortp);
  3996. longPieces.push(longp);
  3997. mixedPieces.push(minp);
  3998. mixedPieces.push(shortp);
  3999. mixedPieces.push(longp);
  4000. }
  4001. // Sorting makes sure if one weekday (or abbr) is a prefix of another it
  4002. // will match the longer piece.
  4003. minPieces.sort(cmpLenRev);
  4004. shortPieces.sort(cmpLenRev);
  4005. longPieces.sort(cmpLenRev);
  4006. mixedPieces.sort(cmpLenRev);
  4007. for (i = 0; i < 7; i++) {
  4008. shortPieces[i] = regexEscape(shortPieces[i]);
  4009. longPieces[i] = regexEscape(longPieces[i]);
  4010. mixedPieces[i] = regexEscape(mixedPieces[i]);
  4011. }
  4012. this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
  4013. this._weekdaysShortRegex = this._weekdaysRegex;
  4014. this._weekdaysMinRegex = this._weekdaysRegex;
  4015. this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
  4016. this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
  4017. this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
  4018. }
  4019. // FORMATTING
  4020. addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
  4021. // ALIASES
  4022. addUnitAlias('dayOfYear', 'DDD');
  4023. // PARSING
  4024. addRegexToken('DDD', match1to3);
  4025. addRegexToken('DDDD', match3);
  4026. addParseToken(['DDD', 'DDDD'], function (input, array, config) {
  4027. config._dayOfYear = toInt(input);
  4028. });
  4029. // HELPERS
  4030. // MOMENTS
  4031. function getSetDayOfYear (input) {
  4032. var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
  4033. return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
  4034. }
  4035. // FORMATTING
  4036. function hFormat() {
  4037. return this.hours() % 12 || 12;
  4038. }
  4039. function kFormat() {
  4040. return this.hours() || 24;
  4041. }
  4042. addFormatToken('H', ['HH', 2], 0, 'hour');
  4043. addFormatToken('h', ['hh', 2], 0, hFormat);
  4044. addFormatToken('k', ['kk', 2], 0, kFormat);
  4045. addFormatToken('hmm', 0, 0, function () {
  4046. return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
  4047. });
  4048. addFormatToken('hmmss', 0, 0, function () {
  4049. return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
  4050. zeroFill(this.seconds(), 2);
  4051. });
  4052. addFormatToken('Hmm', 0, 0, function () {
  4053. return '' + this.hours() + zeroFill(this.minutes(), 2);
  4054. });
  4055. addFormatToken('Hmmss', 0, 0, function () {
  4056. return '' + this.hours() + zeroFill(this.minutes(), 2) +
  4057. zeroFill(this.seconds(), 2);
  4058. });
  4059. function meridiem (token, lowercase) {
  4060. addFormatToken(token, 0, 0, function () {
  4061. return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
  4062. });
  4063. }
  4064. meridiem('a', true);
  4065. meridiem('A', false);
  4066. // ALIASES
  4067. addUnitAlias('hour', 'h');
  4068. // PARSING
  4069. function matchMeridiem (isStrict, locale) {
  4070. return locale._meridiemParse;
  4071. }
  4072. addRegexToken('a', matchMeridiem);
  4073. addRegexToken('A', matchMeridiem);
  4074. addRegexToken('H', match1to2);
  4075. addRegexToken('h', match1to2);
  4076. addRegexToken('HH', match1to2, match2);
  4077. addRegexToken('hh', match1to2, match2);
  4078. addRegexToken('hmm', match3to4);
  4079. addRegexToken('hmmss', match5to6);
  4080. addRegexToken('Hmm', match3to4);
  4081. addRegexToken('Hmmss', match5to6);
  4082. addParseToken(['H', 'HH'], HOUR);
  4083. addParseToken(['a', 'A'], function (input, array, config) {
  4084. config._isPm = config._locale.isPM(input);
  4085. config._meridiem = input;
  4086. });
  4087. addParseToken(['h', 'hh'], function (input, array, config) {
  4088. array[HOUR] = toInt(input);
  4089. getParsingFlags(config).bigHour = true;
  4090. });
  4091. addParseToken('hmm', function (input, array, config) {
  4092. var pos = input.length - 2;
  4093. array[HOUR] = toInt(input.substr(0, pos));
  4094. array[MINUTE] = toInt(input.substr(pos));
  4095. getParsingFlags(config).bigHour = true;
  4096. });
  4097. addParseToken('hmmss', function (input, array, config) {
  4098. var pos1 = input.length - 4;
  4099. var pos2 = input.length - 2;
  4100. array[HOUR] = toInt(input.substr(0, pos1));
  4101. array[MINUTE] = toInt(input.substr(pos1, 2));
  4102. array[SECOND] = toInt(input.substr(pos2));
  4103. getParsingFlags(config).bigHour = true;
  4104. });
  4105. addParseToken('Hmm', function (input, array, config) {
  4106. var pos = input.length - 2;
  4107. array[HOUR] = toInt(input.substr(0, pos));
  4108. array[MINUTE] = toInt(input.substr(pos));
  4109. });
  4110. addParseToken('Hmmss', function (input, array, config) {
  4111. var pos1 = input.length - 4;
  4112. var pos2 = input.length - 2;
  4113. array[HOUR] = toInt(input.substr(0, pos1));
  4114. array[MINUTE] = toInt(input.substr(pos1, 2));
  4115. array[SECOND] = toInt(input.substr(pos2));
  4116. });
  4117. // LOCALES
  4118. function localeIsPM (input) {
  4119. // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
  4120. // Using charAt should be more compatible.
  4121. return ((input + '').toLowerCase().charAt(0) === 'p');
  4122. }
  4123. var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
  4124. function localeMeridiem (hours, minutes, isLower) {
  4125. if (hours > 11) {
  4126. return isLower ? 'pm' : 'PM';
  4127. } else {
  4128. return isLower ? 'am' : 'AM';
  4129. }
  4130. }
  4131. // MOMENTS
  4132. // Setting the hour should keep the time, because the user explicitly
  4133. // specified which hour he wants. So trying to maintain the same hour (in
  4134. // a new timezone) makes sense. Adding/subtracting hours does not follow
  4135. // this rule.
  4136. var getSetHour = makeGetSet('Hours', true);
  4137. // FORMATTING
  4138. addFormatToken('m', ['mm', 2], 0, 'minute');
  4139. // ALIASES
  4140. addUnitAlias('minute', 'm');
  4141. // PARSING
  4142. addRegexToken('m', match1to2);
  4143. addRegexToken('mm', match1to2, match2);
  4144. addParseToken(['m', 'mm'], MINUTE);
  4145. // MOMENTS
  4146. var getSetMinute = makeGetSet('Minutes', false);
  4147. // FORMATTING
  4148. addFormatToken('s', ['ss', 2], 0, 'second');
  4149. // ALIASES
  4150. addUnitAlias('second', 's');
  4151. // PARSING
  4152. addRegexToken('s', match1to2);
  4153. addRegexToken('ss', match1to2, match2);
  4154. addParseToken(['s', 'ss'], SECOND);
  4155. // MOMENTS
  4156. var getSetSecond = makeGetSet('Seconds', false);
  4157. // FORMATTING
  4158. addFormatToken('S', 0, 0, function () {
  4159. return ~~(this.millisecond() / 100);
  4160. });
  4161. addFormatToken(0, ['SS', 2], 0, function () {
  4162. return ~~(this.millisecond() / 10);
  4163. });
  4164. addFormatToken(0, ['SSS', 3], 0, 'millisecond');
  4165. addFormatToken(0, ['SSSS', 4], 0, function () {
  4166. return this.millisecond() * 10;
  4167. });
  4168. addFormatToken(0, ['SSSSS', 5], 0, function () {
  4169. return this.millisecond() * 100;
  4170. });
  4171. addFormatToken(0, ['SSSSSS', 6], 0, function () {
  4172. return this.millisecond() * 1000;
  4173. });
  4174. addFormatToken(0, ['SSSSSSS', 7], 0, function () {
  4175. return this.millisecond() * 10000;
  4176. });
  4177. addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
  4178. return this.millisecond() * 100000;
  4179. });
  4180. addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
  4181. return this.millisecond() * 1000000;
  4182. });
  4183. // ALIASES
  4184. addUnitAlias('millisecond', 'ms');
  4185. // PARSING
  4186. addRegexToken('S', match1to3, match1);
  4187. addRegexToken('SS', match1to3, match2);
  4188. addRegexToken('SSS', match1to3, match3);
  4189. var token;
  4190. for (token = 'SSSS'; token.length <= 9; token += 'S') {
  4191. addRegexToken(token, matchUnsigned);
  4192. }
  4193. function parseMs(input, array) {
  4194. array[MILLISECOND] = toInt(('0.' + input) * 1000);
  4195. }
  4196. for (token = 'S'; token.length <= 9; token += 'S') {
  4197. addParseToken(token, parseMs);
  4198. }
  4199. // MOMENTS
  4200. var getSetMillisecond = makeGetSet('Milliseconds', false);
  4201. // FORMATTING
  4202. addFormatToken('z', 0, 0, 'zoneAbbr');
  4203. addFormatToken('zz', 0, 0, 'zoneName');
  4204. // MOMENTS
  4205. function getZoneAbbr () {
  4206. return this._isUTC ? 'UTC' : '';
  4207. }
  4208. function getZoneName () {
  4209. return this._isUTC ? 'Coordinated Universal Time' : '';
  4210. }
  4211. var momentPrototype__proto = Moment.prototype;
  4212. momentPrototype__proto.add = add_subtract__add;
  4213. momentPrototype__proto.calendar = moment_calendar__calendar;
  4214. momentPrototype__proto.clone = clone;
  4215. momentPrototype__proto.diff = diff;
  4216. momentPrototype__proto.endOf = endOf;
  4217. momentPrototype__proto.format = format;
  4218. momentPrototype__proto.from = from;
  4219. momentPrototype__proto.fromNow = fromNow;
  4220. momentPrototype__proto.to = to;
  4221. momentPrototype__proto.toNow = toNow;
  4222. momentPrototype__proto.get = getSet;
  4223. momentPrototype__proto.invalidAt = invalidAt;
  4224. momentPrototype__proto.isAfter = isAfter;
  4225. momentPrototype__proto.isBefore = isBefore;
  4226. momentPrototype__proto.isBetween = isBetween;
  4227. momentPrototype__proto.isSame = isSame;
  4228. momentPrototype__proto.isSameOrAfter = isSameOrAfter;
  4229. momentPrototype__proto.isSameOrBefore = isSameOrBefore;
  4230. momentPrototype__proto.isValid = moment_valid__isValid;
  4231. momentPrototype__proto.lang = lang;
  4232. momentPrototype__proto.locale = locale;
  4233. momentPrototype__proto.localeData = localeData;
  4234. momentPrototype__proto.max = prototypeMax;
  4235. momentPrototype__proto.min = prototypeMin;
  4236. momentPrototype__proto.parsingFlags = parsingFlags;
  4237. momentPrototype__proto.set = getSet;
  4238. momentPrototype__proto.startOf = startOf;
  4239. momentPrototype__proto.subtract = add_subtract__subtract;
  4240. momentPrototype__proto.toArray = toArray;
  4241. momentPrototype__proto.toObject = toObject;
  4242. momentPrototype__proto.toDate = toDate;
  4243. momentPrototype__proto.toISOString = moment_format__toISOString;
  4244. momentPrototype__proto.toJSON = toJSON;
  4245. momentPrototype__proto.toString = toString;
  4246. momentPrototype__proto.unix = unix;
  4247. momentPrototype__proto.valueOf = to_type__valueOf;
  4248. momentPrototype__proto.creationData = creationData;
  4249. // Year
  4250. momentPrototype__proto.year = getSetYear;
  4251. momentPrototype__proto.isLeapYear = getIsLeapYear;
  4252. // Week Year
  4253. momentPrototype__proto.weekYear = getSetWeekYear;
  4254. momentPrototype__proto.isoWeekYear = getSetISOWeekYear;
  4255. // Quarter
  4256. momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter;
  4257. // Month
  4258. momentPrototype__proto.month = getSetMonth;
  4259. momentPrototype__proto.daysInMonth = getDaysInMonth;
  4260. // Week
  4261. momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek;
  4262. momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek;
  4263. momentPrototype__proto.weeksInYear = getWeeksInYear;
  4264. momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear;
  4265. // Day
  4266. momentPrototype__proto.date = getSetDayOfMonth;
  4267. momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek;
  4268. momentPrototype__proto.weekday = getSetLocaleDayOfWeek;
  4269. momentPrototype__proto.isoWeekday = getSetISODayOfWeek;
  4270. momentPrototype__proto.dayOfYear = getSetDayOfYear;
  4271. // Hour
  4272. momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour;
  4273. // Minute
  4274. momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute;
  4275. // Second
  4276. momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond;
  4277. // Millisecond
  4278. momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond;
  4279. // Offset
  4280. momentPrototype__proto.utcOffset = getSetOffset;
  4281. momentPrototype__proto.utc = setOffsetToUTC;
  4282. momentPrototype__proto.local = setOffsetToLocal;
  4283. momentPrototype__proto.parseZone = setOffsetToParsedOffset;
  4284. momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset;
  4285. momentPrototype__proto.isDST = isDaylightSavingTime;
  4286. momentPrototype__proto.isDSTShifted = isDaylightSavingTimeShifted;
  4287. momentPrototype__proto.isLocal = isLocal;
  4288. momentPrototype__proto.isUtcOffset = isUtcOffset;
  4289. momentPrototype__proto.isUtc = isUtc;
  4290. momentPrototype__proto.isUTC = isUtc;
  4291. // Timezone
  4292. momentPrototype__proto.zoneAbbr = getZoneAbbr;
  4293. momentPrototype__proto.zoneName = getZoneName;
  4294. // Deprecations
  4295. momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
  4296. momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
  4297. momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
  4298. momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone);
  4299. var momentPrototype = momentPrototype__proto;
  4300. function moment__createUnix (input) {
  4301. return local__createLocal(input * 1000);
  4302. }
  4303. function moment__createInZone () {
  4304. return local__createLocal.apply(null, arguments).parseZone();
  4305. }
  4306. var defaultCalendar = {
  4307. sameDay : '[Today at] LT',
  4308. nextDay : '[Tomorrow at] LT',
  4309. nextWeek : 'dddd [at] LT',
  4310. lastDay : '[Yesterday at] LT',
  4311. lastWeek : '[Last] dddd [at] LT',
  4312. sameElse : 'L'
  4313. };
  4314. function locale_calendar__calendar (key, mom, now) {
  4315. var output = this._calendar[key];
  4316. return isFunction(output) ? output.call(mom, now) : output;
  4317. }
  4318. var defaultLongDateFormat = {
  4319. LTS : 'h:mm:ss A',
  4320. LT : 'h:mm A',
  4321. L : 'MM/DD/YYYY',
  4322. LL : 'MMMM D, YYYY',
  4323. LLL : 'MMMM D, YYYY h:mm A',
  4324. LLLL : 'dddd, MMMM D, YYYY h:mm A'
  4325. };
  4326. function longDateFormat (key) {
  4327. var format = this._longDateFormat[key],
  4328. formatUpper = this._longDateFormat[key.toUpperCase()];
  4329. if (format || !formatUpper) {
  4330. return format;
  4331. }
  4332. this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
  4333. return val.slice(1);
  4334. });
  4335. return this._longDateFormat[key];
  4336. }
  4337. var defaultInvalidDate = 'Invalid date';
  4338. function invalidDate () {
  4339. return this._invalidDate;
  4340. }
  4341. var defaultOrdinal = '%d';
  4342. var defaultOrdinalParse = /\d{1,2}/;
  4343. function ordinal (number) {
  4344. return this._ordinal.replace('%d', number);
  4345. }
  4346. function preParsePostFormat (string) {
  4347. return string;
  4348. }
  4349. var defaultRelativeTime = {
  4350. future : 'in %s',
  4351. past : '%s ago',
  4352. s : 'a few seconds',
  4353. m : 'a minute',
  4354. mm : '%d minutes',
  4355. h : 'an hour',
  4356. hh : '%d hours',
  4357. d : 'a day',
  4358. dd : '%d days',
  4359. M : 'a month',
  4360. MM : '%d months',
  4361. y : 'a year',
  4362. yy : '%d years'
  4363. };
  4364. function relative__relativeTime (number, withoutSuffix, string, isFuture) {
  4365. var output = this._relativeTime[string];
  4366. return (isFunction(output)) ?
  4367. output(number, withoutSuffix, string, isFuture) :
  4368. output.replace(/%d/i, number);
  4369. }
  4370. function pastFuture (diff, output) {
  4371. var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
  4372. return isFunction(format) ? format(output) : format.replace(/%s/i, output);
  4373. }
  4374. var prototype__proto = Locale.prototype;
  4375. prototype__proto._calendar = defaultCalendar;
  4376. prototype__proto.calendar = locale_calendar__calendar;
  4377. prototype__proto._longDateFormat = defaultLongDateFormat;
  4378. prototype__proto.longDateFormat = longDateFormat;
  4379. prototype__proto._invalidDate = defaultInvalidDate;
  4380. prototype__proto.invalidDate = invalidDate;
  4381. prototype__proto._ordinal = defaultOrdinal;
  4382. prototype__proto.ordinal = ordinal;
  4383. prototype__proto._ordinalParse = defaultOrdinalParse;
  4384. prototype__proto.preparse = preParsePostFormat;
  4385. prototype__proto.postformat = preParsePostFormat;
  4386. prototype__proto._relativeTime = defaultRelativeTime;
  4387. prototype__proto.relativeTime = relative__relativeTime;
  4388. prototype__proto.pastFuture = pastFuture;
  4389. prototype__proto.set = locale_set__set;
  4390. // Month
  4391. prototype__proto.months = localeMonths;
  4392. prototype__proto._months = defaultLocaleMonths;
  4393. prototype__proto.monthsShort = localeMonthsShort;
  4394. prototype__proto._monthsShort = defaultLocaleMonthsShort;
  4395. prototype__proto.monthsParse = localeMonthsParse;
  4396. prototype__proto._monthsRegex = defaultMonthsRegex;
  4397. prototype__proto.monthsRegex = monthsRegex;
  4398. prototype__proto._monthsShortRegex = defaultMonthsShortRegex;
  4399. prototype__proto.monthsShortRegex = monthsShortRegex;
  4400. // Week
  4401. prototype__proto.week = localeWeek;
  4402. prototype__proto._week = defaultLocaleWeek;
  4403. prototype__proto.firstDayOfYear = localeFirstDayOfYear;
  4404. prototype__proto.firstDayOfWeek = localeFirstDayOfWeek;
  4405. // Day of Week
  4406. prototype__proto.weekdays = localeWeekdays;
  4407. prototype__proto._weekdays = defaultLocaleWeekdays;
  4408. prototype__proto.weekdaysMin = localeWeekdaysMin;
  4409. prototype__proto._weekdaysMin = defaultLocaleWeekdaysMin;
  4410. prototype__proto.weekdaysShort = localeWeekdaysShort;
  4411. prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort;
  4412. prototype__proto.weekdaysParse = localeWeekdaysParse;
  4413. prototype__proto._weekdaysRegex = defaultWeekdaysRegex;
  4414. prototype__proto.weekdaysRegex = weekdaysRegex;
  4415. prototype__proto._weekdaysShortRegex = defaultWeekdaysShortRegex;
  4416. prototype__proto.weekdaysShortRegex = weekdaysShortRegex;
  4417. prototype__proto._weekdaysMinRegex = defaultWeekdaysMinRegex;
  4418. prototype__proto.weekdaysMinRegex = weekdaysMinRegex;
  4419. // Hours
  4420. prototype__proto.isPM = localeIsPM;
  4421. prototype__proto._meridiemParse = defaultLocaleMeridiemParse;
  4422. prototype__proto.meridiem = localeMeridiem;
  4423. function lists__get (format, index, field, setter) {
  4424. var locale = locale_locales__getLocale();
  4425. var utc = create_utc__createUTC().set(setter, index);
  4426. return locale[field](utc, format);
  4427. }
  4428. function listMonthsImpl (format, index, field) {
  4429. if (typeof format === 'number') {
  4430. index = format;
  4431. format = undefined;
  4432. }
  4433. format = format || '';
  4434. if (index != null) {
  4435. return lists__get(format, index, field, 'month');
  4436. }
  4437. var i;
  4438. var out = [];
  4439. for (i = 0; i < 12; i++) {
  4440. out[i] = lists__get(format, i, field, 'month');
  4441. }
  4442. return out;
  4443. }
  4444. // ()
  4445. // (5)
  4446. // (fmt, 5)
  4447. // (fmt)
  4448. // (true)
  4449. // (true, 5)
  4450. // (true, fmt, 5)
  4451. // (true, fmt)
  4452. function listWeekdaysImpl (localeSorted, format, index, field) {
  4453. if (typeof localeSorted === 'boolean') {
  4454. if (typeof format === 'number') {
  4455. index = format;
  4456. format = undefined;
  4457. }
  4458. format = format || '';
  4459. } else {
  4460. format = localeSorted;
  4461. index = format;
  4462. localeSorted = false;
  4463. if (typeof format === 'number') {
  4464. index = format;
  4465. format = undefined;
  4466. }
  4467. format = format || '';
  4468. }
  4469. var locale = locale_locales__getLocale(),
  4470. shift = localeSorted ? locale._week.dow : 0;
  4471. if (index != null) {
  4472. return lists__get(format, (index + shift) % 7, field, 'day');
  4473. }
  4474. var i;
  4475. var out = [];
  4476. for (i = 0; i < 7; i++) {
  4477. out[i] = lists__get(format, (i + shift) % 7, field, 'day');
  4478. }
  4479. return out;
  4480. }
  4481. function lists__listMonths (format, index) {
  4482. return listMonthsImpl(format, index, 'months');
  4483. }
  4484. function lists__listMonthsShort (format, index) {
  4485. return listMonthsImpl(format, index, 'monthsShort');
  4486. }
  4487. function lists__listWeekdays (localeSorted, format, index) {
  4488. return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
  4489. }
  4490. function lists__listWeekdaysShort (localeSorted, format, index) {
  4491. return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
  4492. }
  4493. function lists__listWeekdaysMin (localeSorted, format, index) {
  4494. return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
  4495. }
  4496. locale_locales__getSetGlobalLocale('en', {
  4497. ordinalParse: /\d{1,2}(th|st|nd|rd)/,
  4498. ordinal : function (number) {
  4499. var b = number % 10,
  4500. output = (toInt(number % 100 / 10) === 1) ? 'th' :
  4501. (b === 1) ? 'st' :
  4502. (b === 2) ? 'nd' :
  4503. (b === 3) ? 'rd' : 'th';
  4504. return number + output;
  4505. }
  4506. });
  4507. // Side effect imports
  4508. utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale);
  4509. utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale);
  4510. var mathAbs = Math.abs;
  4511. function duration_abs__abs () {
  4512. var data = this._data;
  4513. this._milliseconds = mathAbs(this._milliseconds);
  4514. this._days = mathAbs(this._days);
  4515. this._months = mathAbs(this._months);
  4516. data.milliseconds = mathAbs(data.milliseconds);
  4517. data.seconds = mathAbs(data.seconds);
  4518. data.minutes = mathAbs(data.minutes);
  4519. data.hours = mathAbs(data.hours);
  4520. data.months = mathAbs(data.months);
  4521. data.years = mathAbs(data.years);
  4522. return this;
  4523. }
  4524. function duration_add_subtract__addSubtract (duration, input, value, direction) {
  4525. var other = create__createDuration(input, value);
  4526. duration._milliseconds += direction * other._milliseconds;
  4527. duration._days += direction * other._days;
  4528. duration._months += direction * other._months;
  4529. return duration._bubble();
  4530. }
  4531. // supports only 2.0-style add(1, 's') or add(duration)
  4532. function duration_add_subtract__add (input, value) {
  4533. return duration_add_subtract__addSubtract(this, input, value, 1);
  4534. }
  4535. // supports only 2.0-style subtract(1, 's') or subtract(duration)
  4536. function duration_add_subtract__subtract (input, value) {
  4537. return duration_add_subtract__addSubtract(this, input, value, -1);
  4538. }
  4539. function absCeil (number) {
  4540. if (number < 0) {
  4541. return Math.floor(number);
  4542. } else {
  4543. return Math.ceil(number);
  4544. }
  4545. }
  4546. function bubble () {
  4547. var milliseconds = this._milliseconds;
  4548. var days = this._days;
  4549. var months = this._months;
  4550. var data = this._data;
  4551. var seconds, minutes, hours, years, monthsFromDays;
  4552. // if we have a mix of positive and negative values, bubble down first
  4553. // check: https://github.com/moment/moment/issues/2166
  4554. if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
  4555. (milliseconds <= 0 && days <= 0 && months <= 0))) {
  4556. milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
  4557. days = 0;
  4558. months = 0;
  4559. }
  4560. // The following code bubbles up values, see the tests for
  4561. // examples of what that means.
  4562. data.milliseconds = milliseconds % 1000;
  4563. seconds = absFloor(milliseconds / 1000);
  4564. data.seconds = seconds % 60;
  4565. minutes = absFloor(seconds / 60);
  4566. data.minutes = minutes % 60;
  4567. hours = absFloor(minutes / 60);
  4568. data.hours = hours % 24;
  4569. days += absFloor(hours / 24);
  4570. // convert days to months
  4571. monthsFromDays = absFloor(daysToMonths(days));
  4572. months += monthsFromDays;
  4573. days -= absCeil(monthsToDays(monthsFromDays));
  4574. // 12 months -> 1 year
  4575. years = absFloor(months / 12);
  4576. months %= 12;
  4577. data.days = days;
  4578. data.months = months;
  4579. data.years = years;
  4580. return this;
  4581. }
  4582. function daysToMonths (days) {
  4583. // 400 years have 146097 days (taking into account leap year rules)
  4584. // 400 years have 12 months === 4800
  4585. return days * 4800 / 146097;
  4586. }
  4587. function monthsToDays (months) {
  4588. // the reverse of daysToMonths
  4589. return months * 146097 / 4800;
  4590. }
  4591. function as (units) {
  4592. var days;
  4593. var months;
  4594. var milliseconds = this._milliseconds;
  4595. units = normalizeUnits(units);
  4596. if (units === 'month' || units === 'year') {
  4597. days = this._days + milliseconds / 864e5;
  4598. months = this._months + daysToMonths(days);
  4599. return units === 'month' ? months : months / 12;
  4600. } else {
  4601. // handle milliseconds separately because of floating point math errors (issue #1867)
  4602. days = this._days + Math.round(monthsToDays(this._months));
  4603. switch (units) {
  4604. case 'week' : return days / 7 + milliseconds / 6048e5;
  4605. case 'day' : return days + milliseconds / 864e5;
  4606. case 'hour' : return days * 24 + milliseconds / 36e5;
  4607. case 'minute' : return days * 1440 + milliseconds / 6e4;
  4608. case 'second' : return days * 86400 + milliseconds / 1000;
  4609. // Math.floor prevents floating point math errors here
  4610. case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
  4611. default: throw new Error('Unknown unit ' + units);
  4612. }
  4613. }
  4614. }
  4615. // TODO: Use this.as('ms')?
  4616. function duration_as__valueOf () {
  4617. return (
  4618. this._milliseconds +
  4619. this._days * 864e5 +
  4620. (this._months % 12) * 2592e6 +
  4621. toInt(this._months / 12) * 31536e6
  4622. );
  4623. }
  4624. function makeAs (alias) {
  4625. return function () {
  4626. return this.as(alias);
  4627. };
  4628. }
  4629. var asMilliseconds = makeAs('ms');
  4630. var asSeconds = makeAs('s');
  4631. var asMinutes = makeAs('m');
  4632. var asHours = makeAs('h');
  4633. var asDays = makeAs('d');
  4634. var asWeeks = makeAs('w');
  4635. var asMonths = makeAs('M');
  4636. var asYears = makeAs('y');
  4637. function duration_get__get (units) {
  4638. units = normalizeUnits(units);
  4639. return this[units + 's']();
  4640. }
  4641. function makeGetter(name) {
  4642. return function () {
  4643. return this._data[name];
  4644. };
  4645. }
  4646. var milliseconds = makeGetter('milliseconds');
  4647. var seconds = makeGetter('seconds');
  4648. var minutes = makeGetter('minutes');
  4649. var hours = makeGetter('hours');
  4650. var days = makeGetter('days');
  4651. var months = makeGetter('months');
  4652. var years = makeGetter('years');
  4653. function weeks () {
  4654. return absFloor(this.days() / 7);
  4655. }
  4656. var round = Math.round;
  4657. var thresholds = {
  4658. s: 45, // seconds to minute
  4659. m: 45, // minutes to hour
  4660. h: 22, // hours to day
  4661. d: 26, // days to month
  4662. M: 11 // months to year
  4663. };
  4664. // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
  4665. function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
  4666. return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
  4667. }
  4668. function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) {
  4669. var duration = create__createDuration(posNegDuration).abs();
  4670. var seconds = round(duration.as('s'));
  4671. var minutes = round(duration.as('m'));
  4672. var hours = round(duration.as('h'));
  4673. var days = round(duration.as('d'));
  4674. var months = round(duration.as('M'));
  4675. var years = round(duration.as('y'));
  4676. var a = seconds < thresholds.s && ['s', seconds] ||
  4677. minutes <= 1 && ['m'] ||
  4678. minutes < thresholds.m && ['mm', minutes] ||
  4679. hours <= 1 && ['h'] ||
  4680. hours < thresholds.h && ['hh', hours] ||
  4681. days <= 1 && ['d'] ||
  4682. days < thresholds.d && ['dd', days] ||
  4683. months <= 1 && ['M'] ||
  4684. months < thresholds.M && ['MM', months] ||
  4685. years <= 1 && ['y'] || ['yy', years];
  4686. a[2] = withoutSuffix;
  4687. a[3] = +posNegDuration > 0;
  4688. a[4] = locale;
  4689. return substituteTimeAgo.apply(null, a);
  4690. }
  4691. // This function allows you to set a threshold for relative time strings
  4692. function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) {
  4693. if (thresholds[threshold] === undefined) {
  4694. return false;
  4695. }
  4696. if (limit === undefined) {
  4697. return thresholds[threshold];
  4698. }
  4699. thresholds[threshold] = limit;
  4700. return true;
  4701. }
  4702. function humanize (withSuffix) {
  4703. var locale = this.localeData();
  4704. var output = duration_humanize__relativeTime(this, !withSuffix, locale);
  4705. if (withSuffix) {
  4706. output = locale.pastFuture(+this, output);
  4707. }
  4708. return locale.postformat(output);
  4709. }
  4710. var iso_string__abs = Math.abs;
  4711. function iso_string__toISOString() {
  4712. // for ISO strings we do not use the normal bubbling rules:
  4713. // * milliseconds bubble up until they become hours
  4714. // * days do not bubble at all
  4715. // * months bubble up until they become years
  4716. // This is because there is no context-free conversion between hours and days
  4717. // (think of clock changes)
  4718. // and also not between days and months (28-31 days per month)
  4719. var seconds = iso_string__abs(this._milliseconds) / 1000;
  4720. var days = iso_string__abs(this._days);
  4721. var months = iso_string__abs(this._months);
  4722. var minutes, hours, years;
  4723. // 3600 seconds -> 60 minutes -> 1 hour
  4724. minutes = absFloor(seconds / 60);
  4725. hours = absFloor(minutes / 60);
  4726. seconds %= 60;
  4727. minutes %= 60;
  4728. // 12 months -> 1 year
  4729. years = absFloor(months / 12);
  4730. months %= 12;
  4731. // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
  4732. var Y = years;
  4733. var M = months;
  4734. var D = days;
  4735. var h = hours;
  4736. var m = minutes;
  4737. var s = seconds;
  4738. var total = this.asSeconds();
  4739. if (!total) {
  4740. // this is the same as C#'s (Noda) and python (isodate)...
  4741. // but not other JS (goog.date)
  4742. return 'P0D';
  4743. }
  4744. return (total < 0 ? '-' : '') +
  4745. 'P' +
  4746. (Y ? Y + 'Y' : '') +
  4747. (M ? M + 'M' : '') +
  4748. (D ? D + 'D' : '') +
  4749. ((h || m || s) ? 'T' : '') +
  4750. (h ? h + 'H' : '') +
  4751. (m ? m + 'M' : '') +
  4752. (s ? s + 'S' : '');
  4753. }
  4754. var duration_prototype__proto = Duration.prototype;
  4755. duration_prototype__proto.abs = duration_abs__abs;
  4756. duration_prototype__proto.add = duration_add_subtract__add;
  4757. duration_prototype__proto.subtract = duration_add_subtract__subtract;
  4758. duration_prototype__proto.as = as;
  4759. duration_prototype__proto.asMilliseconds = asMilliseconds;
  4760. duration_prototype__proto.asSeconds = asSeconds;
  4761. duration_prototype__proto.asMinutes = asMinutes;
  4762. duration_prototype__proto.asHours = asHours;
  4763. duration_prototype__proto.asDays = asDays;
  4764. duration_prototype__proto.asWeeks = asWeeks;
  4765. duration_prototype__proto.asMonths = asMonths;
  4766. duration_prototype__proto.asYears = asYears;
  4767. duration_prototype__proto.valueOf = duration_as__valueOf;
  4768. duration_prototype__proto._bubble = bubble;
  4769. duration_prototype__proto.get = duration_get__get;
  4770. duration_prototype__proto.milliseconds = milliseconds;
  4771. duration_prototype__proto.seconds = seconds;
  4772. duration_prototype__proto.minutes = minutes;
  4773. duration_prototype__proto.hours = hours;
  4774. duration_prototype__proto.days = days;
  4775. duration_prototype__proto.weeks = weeks;
  4776. duration_prototype__proto.months = months;
  4777. duration_prototype__proto.years = years;
  4778. duration_prototype__proto.humanize = humanize;
  4779. duration_prototype__proto.toISOString = iso_string__toISOString;
  4780. duration_prototype__proto.toString = iso_string__toISOString;
  4781. duration_prototype__proto.toJSON = iso_string__toISOString;
  4782. duration_prototype__proto.locale = locale;
  4783. duration_prototype__proto.localeData = localeData;
  4784. // Deprecations
  4785. duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString);
  4786. duration_prototype__proto.lang = lang;
  4787. // Side effect imports
  4788. // FORMATTING
  4789. addFormatToken('X', 0, 0, 'unix');
  4790. addFormatToken('x', 0, 0, 'valueOf');
  4791. // PARSING
  4792. addRegexToken('x', matchSigned);
  4793. addRegexToken('X', matchTimestamp);
  4794. addParseToken('X', function (input, array, config) {
  4795. config._d = new Date(parseFloat(input, 10) * 1000);
  4796. });
  4797. addParseToken('x', function (input, array, config) {
  4798. config._d = new Date(toInt(input));
  4799. });
  4800. // Side effect imports
  4801. utils_hooks__hooks.version = '2.13.0';
  4802. setHookCallback(local__createLocal);
  4803. utils_hooks__hooks.fn = momentPrototype;
  4804. utils_hooks__hooks.min = min;
  4805. utils_hooks__hooks.max = max;
  4806. utils_hooks__hooks.now = now;
  4807. utils_hooks__hooks.utc = create_utc__createUTC;
  4808. utils_hooks__hooks.unix = moment__createUnix;
  4809. utils_hooks__hooks.months = lists__listMonths;
  4810. utils_hooks__hooks.isDate = isDate;
  4811. utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale;
  4812. utils_hooks__hooks.invalid = valid__createInvalid;
  4813. utils_hooks__hooks.duration = create__createDuration;
  4814. utils_hooks__hooks.isMoment = isMoment;
  4815. utils_hooks__hooks.weekdays = lists__listWeekdays;
  4816. utils_hooks__hooks.parseZone = moment__createInZone;
  4817. utils_hooks__hooks.localeData = locale_locales__getLocale;
  4818. utils_hooks__hooks.isDuration = isDuration;
  4819. utils_hooks__hooks.monthsShort = lists__listMonthsShort;
  4820. utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin;
  4821. utils_hooks__hooks.defineLocale = defineLocale;
  4822. utils_hooks__hooks.updateLocale = updateLocale;
  4823. utils_hooks__hooks.locales = locale_locales__listLocales;
  4824. utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort;
  4825. utils_hooks__hooks.normalizeUnits = normalizeUnits;
  4826. utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold;
  4827. utils_hooks__hooks.prototype = momentPrototype;
  4828. var _moment = utils_hooks__hooks;
  4829. return _moment;
  4830. }));
  4831. },{}],7:[function(require,module,exports){
  4832. /**
  4833. * @namespace Chart
  4834. */
  4835. var Chart = require(26)();
  4836. require(25)(Chart);
  4837. require(24)(Chart);
  4838. require(21)(Chart);
  4839. require(22)(Chart);
  4840. require(23)(Chart);
  4841. require(27)(Chart);
  4842. require(31)(Chart);
  4843. require(29)(Chart);
  4844. require(30)(Chart);
  4845. require(32)(Chart);
  4846. require(28)(Chart);
  4847. require(33)(Chart);
  4848. require(34)(Chart);
  4849. require(35)(Chart);
  4850. require(36)(Chart);
  4851. require(37)(Chart);
  4852. require(40)(Chart);
  4853. require(38)(Chart);
  4854. require(39)(Chart);
  4855. require(41)(Chart);
  4856. require(42)(Chart);
  4857. require(43)(Chart);
  4858. // Controllers must be loaded after elements
  4859. // See Chart.core.datasetController.dataElementType
  4860. require(15)(Chart);
  4861. require(16)(Chart);
  4862. require(17)(Chart);
  4863. require(18)(Chart);
  4864. require(19)(Chart);
  4865. require(20)(Chart);
  4866. require(8)(Chart);
  4867. require(9)(Chart);
  4868. require(10)(Chart);
  4869. require(11)(Chart);
  4870. require(12)(Chart);
  4871. require(13)(Chart);
  4872. require(14)(Chart);
  4873. window.Chart = module.exports = Chart;
  4874. },{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"8":8,"9":9}],8:[function(require,module,exports){
  4875. "use strict";
  4876. module.exports = function(Chart) {
  4877. Chart.Bar = function(context, config) {
  4878. config.type = 'bar';
  4879. return new Chart(context, config);
  4880. };
  4881. };
  4882. },{}],9:[function(require,module,exports){
  4883. "use strict";
  4884. module.exports = function(Chart) {
  4885. Chart.Bubble = function(context, config) {
  4886. config.type = 'bubble';
  4887. return new Chart(context, config);
  4888. };
  4889. };
  4890. },{}],10:[function(require,module,exports){
  4891. "use strict";
  4892. module.exports = function(Chart) {
  4893. Chart.Doughnut = function(context, config) {
  4894. config.type = 'doughnut';
  4895. return new Chart(context, config);
  4896. };
  4897. };
  4898. },{}],11:[function(require,module,exports){
  4899. "use strict";
  4900. module.exports = function(Chart) {
  4901. Chart.Line = function(context, config) {
  4902. config.type = 'line';
  4903. return new Chart(context, config);
  4904. };
  4905. };
  4906. },{}],12:[function(require,module,exports){
  4907. "use strict";
  4908. module.exports = function(Chart) {
  4909. Chart.PolarArea = function(context, config) {
  4910. config.type = 'polarArea';
  4911. return new Chart(context, config);
  4912. };
  4913. };
  4914. },{}],13:[function(require,module,exports){
  4915. "use strict";
  4916. module.exports = function(Chart) {
  4917. Chart.Radar = function(context, config) {
  4918. config.options = Chart.helpers.configMerge({ aspectRatio: 1 }, config.options);
  4919. config.type = 'radar';
  4920. return new Chart(context, config);
  4921. };
  4922. };
  4923. },{}],14:[function(require,module,exports){
  4924. "use strict";
  4925. module.exports = function(Chart) {
  4926. var defaultConfig = {
  4927. hover: {
  4928. mode: 'single'
  4929. },
  4930. scales: {
  4931. xAxes: [{
  4932. type: "linear", // scatter should not use a category axis
  4933. position: "bottom",
  4934. id: "x-axis-1" // need an ID so datasets can reference the scale
  4935. }],
  4936. yAxes: [{
  4937. type: "linear",
  4938. position: "left",
  4939. id: "y-axis-1"
  4940. }]
  4941. },
  4942. tooltips: {
  4943. callbacks: {
  4944. title: function(tooltipItems, data) {
  4945. // Title doesn't make sense for scatter since we format the data as a point
  4946. return '';
  4947. },
  4948. label: function(tooltipItem, data) {
  4949. return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
  4950. }
  4951. }
  4952. }
  4953. };
  4954. // Register the default config for this type
  4955. Chart.defaults.scatter = defaultConfig;
  4956. // Scatter charts use line controllers
  4957. Chart.controllers.scatter = Chart.controllers.line;
  4958. Chart.Scatter = function(context, config) {
  4959. config.type = 'scatter';
  4960. return new Chart(context, config);
  4961. };
  4962. };
  4963. },{}],15:[function(require,module,exports){
  4964. "use strict";
  4965. module.exports = function(Chart) {
  4966. var helpers = Chart.helpers;
  4967. Chart.defaults.bar = {
  4968. hover: {
  4969. mode: "label"
  4970. },
  4971. scales: {
  4972. xAxes: [{
  4973. type: "category",
  4974. // Specific to Bar Controller
  4975. categoryPercentage: 0.8,
  4976. barPercentage: 0.9,
  4977. // grid line settings
  4978. gridLines: {
  4979. offsetGridLines: true
  4980. }
  4981. }],
  4982. yAxes: [{
  4983. type: "linear"
  4984. }]
  4985. }
  4986. };
  4987. Chart.controllers.bar = Chart.DatasetController.extend({
  4988. dataElementType: Chart.elements.Rectangle,
  4989. initialize: function(chart, datasetIndex) {
  4990. Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
  4991. // Use this to indicate that this is a bar dataset.
  4992. this.getMeta().bar = true;
  4993. },
  4994. // Get the number of datasets that display bars. We use this to correctly calculate the bar width
  4995. getBarCount: function getBarCount() {
  4996. var me = this;
  4997. var barCount = 0;
  4998. helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
  4999. var meta = me.chart.getDatasetMeta(datasetIndex);
  5000. if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) {
  5001. ++barCount;
  5002. }
  5003. }, me);
  5004. return barCount;
  5005. },
  5006. update: function update(reset) {
  5007. var me = this;
  5008. helpers.each(me.getMeta().data, function(rectangle, index) {
  5009. me.updateElement(rectangle, index, reset);
  5010. }, me);
  5011. },
  5012. updateElement: function updateElement(rectangle, index, reset) {
  5013. var me = this;
  5014. var meta = me.getMeta();
  5015. var xScale = me.getScaleForId(meta.xAxisID);
  5016. var yScale = me.getScaleForId(meta.yAxisID);
  5017. var scaleBase = yScale.getBasePixel();
  5018. var rectangleElementOptions = me.chart.options.elements.rectangle;
  5019. var custom = rectangle.custom || {};
  5020. var dataset = me.getDataset();
  5021. helpers.extend(rectangle, {
  5022. // Utility
  5023. _xScale: xScale,
  5024. _yScale: yScale,
  5025. _datasetIndex: me.index,
  5026. _index: index,
  5027. // Desired view properties
  5028. _model: {
  5029. x: me.calculateBarX(index, me.index),
  5030. y: reset ? scaleBase : me.calculateBarY(index, me.index),
  5031. // Tooltip
  5032. label: me.chart.data.labels[index],
  5033. datasetLabel: dataset.label,
  5034. // Appearance
  5035. base: reset ? scaleBase : me.calculateBarBase(me.index, index),
  5036. width: me.calculateBarWidth(index),
  5037. backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
  5038. borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
  5039. borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
  5040. borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
  5041. }
  5042. });
  5043. rectangle.pivot();
  5044. },
  5045. calculateBarBase: function(datasetIndex, index) {
  5046. var me = this;
  5047. var meta = me.getMeta();
  5048. var yScale = me.getScaleForId(meta.yAxisID);
  5049. var base = 0;
  5050. if (yScale.options.stacked) {
  5051. var chart = me.chart;
  5052. var datasets = chart.data.datasets;
  5053. var value = datasets[datasetIndex].data[index];
  5054. if (value < 0) {
  5055. for (var i = 0; i < datasetIndex; i++) {
  5056. var negDS = datasets[i];
  5057. var negDSMeta = chart.getDatasetMeta(i);
  5058. if (negDSMeta.bar && negDSMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
  5059. base += negDS.data[index] < 0 ? negDS.data[index] : 0;
  5060. }
  5061. }
  5062. } else {
  5063. for (var j = 0; j < datasetIndex; j++) {
  5064. var posDS = datasets[j];
  5065. var posDSMeta = chart.getDatasetMeta(j);
  5066. if (posDSMeta.bar && posDSMeta.yAxisID === yScale.id && chart.isDatasetVisible(j)) {
  5067. base += posDS.data[index] > 0 ? posDS.data[index] : 0;
  5068. }
  5069. }
  5070. }
  5071. return yScale.getPixelForValue(base);
  5072. }
  5073. return yScale.getBasePixel();
  5074. },
  5075. getRuler: function(index) {
  5076. var me = this;
  5077. var meta = me.getMeta();
  5078. var xScale = me.getScaleForId(meta.xAxisID);
  5079. var datasetCount = me.getBarCount();
  5080. var tickWidth;
  5081. if (xScale.options.type === 'category') {
  5082. tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index);
  5083. } else {
  5084. // Average width
  5085. tickWidth = xScale.width / xScale.ticks.length;
  5086. }
  5087. var categoryWidth = tickWidth * xScale.options.categoryPercentage;
  5088. var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
  5089. var fullBarWidth = categoryWidth / datasetCount;
  5090. if (xScale.ticks.length !== me.chart.data.labels.length) {
  5091. var perc = xScale.ticks.length / me.chart.data.labels.length;
  5092. fullBarWidth = fullBarWidth * perc;
  5093. }
  5094. var barWidth = fullBarWidth * xScale.options.barPercentage;
  5095. var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
  5096. return {
  5097. datasetCount: datasetCount,
  5098. tickWidth: tickWidth,
  5099. categoryWidth: categoryWidth,
  5100. categorySpacing: categorySpacing,
  5101. fullBarWidth: fullBarWidth,
  5102. barWidth: barWidth,
  5103. barSpacing: barSpacing
  5104. };
  5105. },
  5106. calculateBarWidth: function(index) {
  5107. var xScale = this.getScaleForId(this.getMeta().xAxisID);
  5108. var ruler = this.getRuler(index);
  5109. return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth;
  5110. },
  5111. // Get bar index from the given dataset index accounting for the fact that not all bars are visible
  5112. getBarIndex: function(datasetIndex) {
  5113. var barIndex = 0;
  5114. var meta, j;
  5115. for (j = 0; j < datasetIndex; ++j) {
  5116. meta = this.chart.getDatasetMeta(j);
  5117. if (meta.bar && this.chart.isDatasetVisible(j)) {
  5118. ++barIndex;
  5119. }
  5120. }
  5121. return barIndex;
  5122. },
  5123. calculateBarX: function(index, datasetIndex) {
  5124. var me = this;
  5125. var meta = me.getMeta();
  5126. var xScale = me.getScaleForId(meta.xAxisID);
  5127. var barIndex = me.getBarIndex(datasetIndex);
  5128. var ruler = me.getRuler(index);
  5129. var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
  5130. leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0;
  5131. if (xScale.options.stacked) {
  5132. return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
  5133. }
  5134. return leftTick +
  5135. (ruler.barWidth / 2) +
  5136. ruler.categorySpacing +
  5137. (ruler.barWidth * barIndex) +
  5138. (ruler.barSpacing / 2) +
  5139. (ruler.barSpacing * barIndex);
  5140. },
  5141. calculateBarY: function(index, datasetIndex) {
  5142. var me = this;
  5143. var meta = me.getMeta();
  5144. var yScale = me.getScaleForId(meta.yAxisID);
  5145. var value = me.getDataset().data[index];
  5146. if (yScale.options.stacked) {
  5147. var sumPos = 0,
  5148. sumNeg = 0;
  5149. for (var i = 0; i < datasetIndex; i++) {
  5150. var ds = me.chart.data.datasets[i];
  5151. var dsMeta = me.chart.getDatasetMeta(i);
  5152. if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) {
  5153. if (ds.data[index] < 0) {
  5154. sumNeg += ds.data[index] || 0;
  5155. } else {
  5156. sumPos += ds.data[index] || 0;
  5157. }
  5158. }
  5159. }
  5160. if (value < 0) {
  5161. return yScale.getPixelForValue(sumNeg + value);
  5162. } else {
  5163. return yScale.getPixelForValue(sumPos + value);
  5164. }
  5165. }
  5166. return yScale.getPixelForValue(value);
  5167. },
  5168. draw: function(ease) {
  5169. var me = this;
  5170. var easingDecimal = ease || 1;
  5171. helpers.each(me.getMeta().data, function(rectangle, index) {
  5172. var d = me.getDataset().data[index];
  5173. if (d !== null && d !== undefined && !isNaN(d)) {
  5174. rectangle.transition(easingDecimal).draw();
  5175. }
  5176. }, me);
  5177. },
  5178. setHoverStyle: function(rectangle) {
  5179. var dataset = this.chart.data.datasets[rectangle._datasetIndex];
  5180. var index = rectangle._index;
  5181. var custom = rectangle.custom || {};
  5182. var model = rectangle._model;
  5183. model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
  5184. model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
  5185. model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
  5186. },
  5187. removeHoverStyle: function(rectangle) {
  5188. var dataset = this.chart.data.datasets[rectangle._datasetIndex];
  5189. var index = rectangle._index;
  5190. var custom = rectangle.custom || {};
  5191. var model = rectangle._model;
  5192. var rectangleElementOptions = this.chart.options.elements.rectangle;
  5193. model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
  5194. model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
  5195. model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
  5196. }
  5197. });
  5198. // including horizontalBar in the bar file, instead of a file of its own
  5199. // it extends bar (like pie extends doughnut)
  5200. Chart.defaults.horizontalBar = {
  5201. hover: {
  5202. mode: "label"
  5203. },
  5204. scales: {
  5205. xAxes: [{
  5206. type: "linear",
  5207. position: "bottom"
  5208. }],
  5209. yAxes: [{
  5210. position: "left",
  5211. type: "category",
  5212. // Specific to Horizontal Bar Controller
  5213. categoryPercentage: 0.8,
  5214. barPercentage: 0.9,
  5215. // grid line settings
  5216. gridLines: {
  5217. offsetGridLines: true
  5218. }
  5219. }]
  5220. },
  5221. elements: {
  5222. rectangle: {
  5223. borderSkipped: 'left'
  5224. }
  5225. },
  5226. tooltips: {
  5227. callbacks: {
  5228. title: function(tooltipItems, data) {
  5229. // Pick first xLabel for now
  5230. var title = '';
  5231. if (tooltipItems.length > 0) {
  5232. if (tooltipItems[0].yLabel) {
  5233. title = tooltipItems[0].yLabel;
  5234. } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) {
  5235. title = data.labels[tooltipItems[0].index];
  5236. }
  5237. }
  5238. return title;
  5239. },
  5240. label: function(tooltipItem, data) {
  5241. var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
  5242. return datasetLabel + ': ' + tooltipItem.xLabel;
  5243. }
  5244. }
  5245. }
  5246. };
  5247. Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
  5248. updateElement: function updateElement(rectangle, index, reset, numBars) {
  5249. var me = this;
  5250. var meta = me.getMeta();
  5251. var xScale = me.getScaleForId(meta.xAxisID);
  5252. var yScale = me.getScaleForId(meta.yAxisID);
  5253. var scaleBase = xScale.getBasePixel();
  5254. var custom = rectangle.custom || {};
  5255. var dataset = me.getDataset();
  5256. var rectangleElementOptions = me.chart.options.elements.rectangle;
  5257. helpers.extend(rectangle, {
  5258. // Utility
  5259. _xScale: xScale,
  5260. _yScale: yScale,
  5261. _datasetIndex: me.index,
  5262. _index: index,
  5263. // Desired view properties
  5264. _model: {
  5265. x: reset ? scaleBase : me.calculateBarX(index, me.index),
  5266. y: me.calculateBarY(index, me.index),
  5267. // Tooltip
  5268. label: me.chart.data.labels[index],
  5269. datasetLabel: dataset.label,
  5270. // Appearance
  5271. base: reset ? scaleBase : me.calculateBarBase(me.index, index),
  5272. height: me.calculateBarHeight(index),
  5273. backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
  5274. borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
  5275. borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
  5276. borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
  5277. },
  5278. draw: function () {
  5279. var ctx = this._chart.ctx;
  5280. var vm = this._view;
  5281. var halfHeight = vm.height / 2,
  5282. topY = vm.y - halfHeight,
  5283. bottomY = vm.y + halfHeight,
  5284. right = vm.base - (vm.base - vm.x),
  5285. halfStroke = vm.borderWidth / 2;
  5286. // Canvas doesn't allow us to stroke inside the width so we can
  5287. // adjust the sizes to fit if we're setting a stroke on the line
  5288. if (vm.borderWidth) {
  5289. topY += halfStroke;
  5290. bottomY -= halfStroke;
  5291. right += halfStroke;
  5292. }
  5293. ctx.beginPath();
  5294. ctx.fillStyle = vm.backgroundColor;
  5295. ctx.strokeStyle = vm.borderColor;
  5296. ctx.lineWidth = vm.borderWidth;
  5297. // Corner points, from bottom-left to bottom-right clockwise
  5298. // | 1 2 |
  5299. // | 0 3 |
  5300. var corners = [
  5301. [vm.base, bottomY],
  5302. [vm.base, topY],
  5303. [right, topY],
  5304. [right, bottomY]
  5305. ];
  5306. // Find first (starting) corner with fallback to 'bottom'
  5307. var borders = ['bottom', 'left', 'top', 'right'];
  5308. var startCorner = borders.indexOf(vm.borderSkipped, 0);
  5309. if (startCorner === -1)
  5310. startCorner = 0;
  5311. function cornerAt(index) {
  5312. return corners[(startCorner + index) % 4];
  5313. }
  5314. // Draw rectangle from 'startCorner'
  5315. ctx.moveTo.apply(ctx, cornerAt(0));
  5316. for (var i = 1; i < 4; i++)
  5317. ctx.lineTo.apply(ctx, cornerAt(i));
  5318. ctx.fill();
  5319. if (vm.borderWidth) {
  5320. ctx.stroke();
  5321. }
  5322. },
  5323. inRange: function (mouseX, mouseY) {
  5324. var vm = this._view;
  5325. var inRange = false;
  5326. if (vm) {
  5327. if (vm.x < vm.base) {
  5328. inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base);
  5329. } else {
  5330. inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x);
  5331. }
  5332. }
  5333. return inRange;
  5334. }
  5335. });
  5336. rectangle.pivot();
  5337. },
  5338. calculateBarBase: function (datasetIndex, index) {
  5339. var me = this;
  5340. var meta = me.getMeta();
  5341. var xScale = me.getScaleForId(meta.xAxisID);
  5342. var base = 0;
  5343. if (xScale.options.stacked) {
  5344. var value = me.chart.data.datasets[datasetIndex].data[index];
  5345. if (value < 0) {
  5346. for (var i = 0; i < datasetIndex; i++) {
  5347. var negDS = me.chart.data.datasets[i];
  5348. var negDSMeta = me.chart.getDatasetMeta(i);
  5349. if (negDSMeta.bar && negDSMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) {
  5350. base += negDS.data[index] < 0 ? negDS.data[index] : 0;
  5351. }
  5352. }
  5353. } else {
  5354. for (var j = 0; j < datasetIndex; j++) {
  5355. var posDS = me.chart.data.datasets[j];
  5356. var posDSMeta = me.chart.getDatasetMeta(j);
  5357. if (posDSMeta.bar && posDSMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(j)) {
  5358. base += posDS.data[index] > 0 ? posDS.data[index] : 0;
  5359. }
  5360. }
  5361. }
  5362. return xScale.getPixelForValue(base);
  5363. }
  5364. return xScale.getBasePixel();
  5365. },
  5366. getRuler: function (index) {
  5367. var me = this;
  5368. var meta = me.getMeta();
  5369. var yScale = me.getScaleForId(meta.yAxisID);
  5370. var datasetCount = me.getBarCount();
  5371. var tickHeight;
  5372. if (yScale.options.type === 'category') {
  5373. tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index);
  5374. } else {
  5375. // Average width
  5376. tickHeight = yScale.width / yScale.ticks.length;
  5377. }
  5378. var categoryHeight = tickHeight * yScale.options.categoryPercentage;
  5379. var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
  5380. var fullBarHeight = categoryHeight / datasetCount;
  5381. if (yScale.ticks.length !== me.chart.data.labels.length) {
  5382. var perc = yScale.ticks.length / me.chart.data.labels.length;
  5383. fullBarHeight = fullBarHeight * perc;
  5384. }
  5385. var barHeight = fullBarHeight * yScale.options.barPercentage;
  5386. var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);
  5387. return {
  5388. datasetCount: datasetCount,
  5389. tickHeight: tickHeight,
  5390. categoryHeight: categoryHeight,
  5391. categorySpacing: categorySpacing,
  5392. fullBarHeight: fullBarHeight,
  5393. barHeight: barHeight,
  5394. barSpacing: barSpacing,
  5395. };
  5396. },
  5397. calculateBarHeight: function (index) {
  5398. var me = this;
  5399. var yScale = me.getScaleForId(me.getMeta().yAxisID);
  5400. var ruler = me.getRuler(index);
  5401. return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight;
  5402. },
  5403. calculateBarX: function (index, datasetIndex) {
  5404. var me = this;
  5405. var meta = me.getMeta();
  5406. var xScale = me.getScaleForId(meta.xAxisID);
  5407. var value = me.getDataset().data[index];
  5408. if (xScale.options.stacked) {
  5409. var sumPos = 0,
  5410. sumNeg = 0;
  5411. for (var i = 0; i < datasetIndex; i++) {
  5412. var ds = me.chart.data.datasets[i];
  5413. var dsMeta = me.chart.getDatasetMeta(i);
  5414. if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) {
  5415. if (ds.data[index] < 0) {
  5416. sumNeg += ds.data[index] || 0;
  5417. } else {
  5418. sumPos += ds.data[index] || 0;
  5419. }
  5420. }
  5421. }
  5422. if (value < 0) {
  5423. return xScale.getPixelForValue(sumNeg + value);
  5424. } else {
  5425. return xScale.getPixelForValue(sumPos + value);
  5426. }
  5427. }
  5428. return xScale.getPixelForValue(value);
  5429. },
  5430. calculateBarY: function (index, datasetIndex) {
  5431. var me = this;
  5432. var meta = me.getMeta();
  5433. var yScale = me.getScaleForId(meta.yAxisID);
  5434. var barIndex = me.getBarIndex(datasetIndex);
  5435. var ruler = me.getRuler(index);
  5436. var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
  5437. topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0;
  5438. if (yScale.options.stacked) {
  5439. return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
  5440. }
  5441. return topTick +
  5442. (ruler.barHeight / 2) +
  5443. ruler.categorySpacing +
  5444. (ruler.barHeight * barIndex) +
  5445. (ruler.barSpacing / 2) +
  5446. (ruler.barSpacing * barIndex);
  5447. }
  5448. });
  5449. };
  5450. },{}],16:[function(require,module,exports){
  5451. "use strict";
  5452. module.exports = function(Chart) {
  5453. var helpers = Chart.helpers;
  5454. Chart.defaults.bubble = {
  5455. hover: {
  5456. mode: "single"
  5457. },
  5458. scales: {
  5459. xAxes: [{
  5460. type: "linear", // bubble should probably use a linear scale by default
  5461. position: "bottom",
  5462. id: "x-axis-0" // need an ID so datasets can reference the scale
  5463. }],
  5464. yAxes: [{
  5465. type: "linear",
  5466. position: "left",
  5467. id: "y-axis-0"
  5468. }]
  5469. },
  5470. tooltips: {
  5471. callbacks: {
  5472. title: function(tooltipItems, data) {
  5473. // Title doesn't make sense for scatter since we format the data as a point
  5474. return '';
  5475. },
  5476. label: function(tooltipItem, data) {
  5477. var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
  5478. var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
  5479. return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')';
  5480. }
  5481. }
  5482. }
  5483. };
  5484. Chart.controllers.bubble = Chart.DatasetController.extend({
  5485. dataElementType: Chart.elements.Point,
  5486. update: function update(reset) {
  5487. var me = this;
  5488. var meta = me.getMeta();
  5489. var points = meta.data;
  5490. // Update Points
  5491. helpers.each(points, function(point, index) {
  5492. me.updateElement(point, index, reset);
  5493. });
  5494. },
  5495. updateElement: function(point, index, reset) {
  5496. var me = this;
  5497. var meta = me.getMeta();
  5498. var xScale = me.getScaleForId(meta.xAxisID);
  5499. var yScale = me.getScaleForId(meta.yAxisID);
  5500. var custom = point.custom || {};
  5501. var dataset = me.getDataset();
  5502. var data = dataset.data[index];
  5503. var pointElementOptions = me.chart.options.elements.point;
  5504. var dsIndex = me.index;
  5505. helpers.extend(point, {
  5506. // Utility
  5507. _xScale: xScale,
  5508. _yScale: yScale,
  5509. _datasetIndex: dsIndex,
  5510. _index: index,
  5511. // Desired view properties
  5512. _model: {
  5513. x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(data, index, dsIndex, me.chart.isCombo),
  5514. y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex),
  5515. // Appearance
  5516. radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data),
  5517. // Tooltip
  5518. hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius)
  5519. }
  5520. });
  5521. // Trick to reset the styles of the point
  5522. Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions);
  5523. var model = point._model;
  5524. model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y));
  5525. point.pivot();
  5526. },
  5527. getRadius: function(value) {
  5528. return value.r || this.chart.options.elements.point.radius;
  5529. },
  5530. setHoverStyle: function(point) {
  5531. var me = this;
  5532. Chart.DatasetController.prototype.setHoverStyle.call(me, point);
  5533. // Radius
  5534. var dataset = me.chart.data.datasets[point._datasetIndex];
  5535. var index = point._index;
  5536. var custom = point.custom || {};
  5537. var model = point._model;
  5538. model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]);
  5539. },
  5540. removeHoverStyle: function(point) {
  5541. var me = this;
  5542. Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point);
  5543. var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index];
  5544. var custom = point.custom || {};
  5545. var model = point._model;
  5546. model.radius = custom.radius ? custom.radius : me.getRadius(dataVal);
  5547. }
  5548. });
  5549. };
  5550. },{}],17:[function(require,module,exports){
  5551. "use strict";
  5552. module.exports = function(Chart) {
  5553. var helpers = Chart.helpers,
  5554. defaults = Chart.defaults;
  5555. defaults.doughnut = {
  5556. animation: {
  5557. //Boolean - Whether we animate the rotation of the Doughnut
  5558. animateRotate: true,
  5559. //Boolean - Whether we animate scaling the Doughnut from the centre
  5560. animateScale: false
  5561. },
  5562. aspectRatio: 1,
  5563. hover: {
  5564. mode: 'single'
  5565. },
  5566. legendCallback: function(chart) {
  5567. var text = [];
  5568. text.push('<ul class="' + chart.id + '-legend">');
  5569. var data = chart.data;
  5570. var datasets = data.datasets;
  5571. var labels = data.labels;
  5572. if (datasets.length) {
  5573. for (var i = 0; i < datasets[0].data.length; ++i) {
  5574. text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
  5575. if (labels[i]) {
  5576. text.push(labels[i]);
  5577. }
  5578. text.push('</li>');
  5579. }
  5580. }
  5581. text.push('</ul>');
  5582. return text.join("");
  5583. },
  5584. legend: {
  5585. labels: {
  5586. generateLabels: function(chart) {
  5587. var data = chart.data;
  5588. if (data.labels.length && data.datasets.length) {
  5589. return data.labels.map(function(label, i) {
  5590. var meta = chart.getDatasetMeta(0);
  5591. var ds = data.datasets[0];
  5592. var arc = meta.data[i];
  5593. var custom = arc.custom || {};
  5594. var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
  5595. var arcOpts = chart.options.elements.arc;
  5596. var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
  5597. var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
  5598. var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
  5599. return {
  5600. text: label,
  5601. fillStyle: fill,
  5602. strokeStyle: stroke,
  5603. lineWidth: bw,
  5604. hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
  5605. // Extra data used for toggling the correct item
  5606. index: i
  5607. };
  5608. });
  5609. } else {
  5610. return [];
  5611. }
  5612. }
  5613. },
  5614. onClick: function(e, legendItem) {
  5615. var index = legendItem.index;
  5616. var chart = this.chart;
  5617. var i, ilen, meta;
  5618. for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
  5619. meta = chart.getDatasetMeta(i);
  5620. meta.data[index].hidden = !meta.data[index].hidden;
  5621. }
  5622. chart.update();
  5623. }
  5624. },
  5625. //The percentage of the chart that we cut out of the middle.
  5626. cutoutPercentage: 50,
  5627. //The rotation of the chart, where the first data arc begins.
  5628. rotation: Math.PI * -0.5,
  5629. //The total circumference of the chart.
  5630. circumference: Math.PI * 2.0,
  5631. // Need to override these to give a nice default
  5632. tooltips: {
  5633. callbacks: {
  5634. title: function() {
  5635. return '';
  5636. },
  5637. label: function(tooltipItem, data) {
  5638. return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
  5639. }
  5640. }
  5641. }
  5642. };
  5643. defaults.pie = helpers.clone(defaults.doughnut);
  5644. helpers.extend(defaults.pie, {
  5645. cutoutPercentage: 0
  5646. });
  5647. Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
  5648. dataElementType: Chart.elements.Arc,
  5649. linkScales: helpers.noop,
  5650. // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
  5651. getRingIndex: function getRingIndex(datasetIndex) {
  5652. var ringIndex = 0;
  5653. for (var j = 0; j < datasetIndex; ++j) {
  5654. if (this.chart.isDatasetVisible(j)) {
  5655. ++ringIndex;
  5656. }
  5657. }
  5658. return ringIndex;
  5659. },
  5660. update: function update(reset) {
  5661. var me = this;
  5662. var chart = me.chart,
  5663. chartArea = chart.chartArea,
  5664. opts = chart.options,
  5665. arcOpts = opts.elements.arc,
  5666. availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth,
  5667. availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth,
  5668. minSize = Math.min(availableWidth, availableHeight),
  5669. offset = {
  5670. x: 0,
  5671. y: 0
  5672. },
  5673. meta = me.getMeta(),
  5674. cutoutPercentage = opts.cutoutPercentage,
  5675. circumference = opts.circumference;
  5676. // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
  5677. if (circumference < Math.PI * 2.0) {
  5678. var startAngle = opts.rotation % (Math.PI * 2.0);
  5679. startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
  5680. var endAngle = startAngle + circumference;
  5681. var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
  5682. var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
  5683. var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
  5684. var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
  5685. var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
  5686. var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
  5687. var cutout = cutoutPercentage / 100.0;
  5688. 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))};
  5689. 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))};
  5690. var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
  5691. minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
  5692. offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
  5693. }
  5694. chart.outerRadius = Math.max(minSize / 2, 0);
  5695. chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0);
  5696. chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
  5697. chart.offsetX = offset.x * chart.outerRadius;
  5698. chart.offsetY = offset.y * chart.outerRadius;
  5699. meta.total = me.calculateTotal();
  5700. me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
  5701. me.innerRadius = me.outerRadius - chart.radiusLength;
  5702. helpers.each(meta.data, function(arc, index) {
  5703. me.updateElement(arc, index, reset);
  5704. });
  5705. },
  5706. updateElement: function(arc, index, reset) {
  5707. var me = this;
  5708. var chart = me.chart,
  5709. chartArea = chart.chartArea,
  5710. opts = chart.options,
  5711. animationOpts = opts.animation,
  5712. arcOpts = opts.elements.arc,
  5713. centerX = (chartArea.left + chartArea.right) / 2,
  5714. centerY = (chartArea.top + chartArea.bottom) / 2,
  5715. startAngle = opts.rotation, // non reset case handled later
  5716. endAngle = opts.rotation, // non reset case handled later
  5717. dataset = me.getDataset(),
  5718. circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)),
  5719. innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius,
  5720. outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius,
  5721. custom = arc.custom || {},
  5722. valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
  5723. helpers.extend(arc, {
  5724. // Utility
  5725. _datasetIndex: me.index,
  5726. _index: index,
  5727. // Desired view properties
  5728. _model: {
  5729. x: centerX + chart.offsetX,
  5730. y: centerY + chart.offsetY,
  5731. startAngle: startAngle,
  5732. endAngle: endAngle,
  5733. circumference: circumference,
  5734. outerRadius: outerRadius,
  5735. innerRadius: innerRadius,
  5736. label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
  5737. }
  5738. });
  5739. var model = arc._model;
  5740. // Resets the visual styles
  5741. this.removeHoverStyle(arc);
  5742. // Set correct angles if not resetting
  5743. if (!reset || !animationOpts.animateRotate) {
  5744. if (index === 0) {
  5745. model.startAngle = opts.rotation;
  5746. } else {
  5747. model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
  5748. }
  5749. model.endAngle = model.startAngle + model.circumference;
  5750. }
  5751. arc.pivot();
  5752. },
  5753. removeHoverStyle: function(arc) {
  5754. Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
  5755. },
  5756. calculateTotal: function() {
  5757. var dataset = this.getDataset();
  5758. var meta = this.getMeta();
  5759. var total = 0;
  5760. var value;
  5761. helpers.each(meta.data, function(element, index) {
  5762. value = dataset.data[index];
  5763. if (!isNaN(value) && !element.hidden) {
  5764. total += Math.abs(value);
  5765. }
  5766. });
  5767. return total;
  5768. },
  5769. calculateCircumference: function(value) {
  5770. var total = this.getMeta().total;
  5771. if (total > 0 && !isNaN(value)) {
  5772. return (Math.PI * 2.0) * (value / total);
  5773. } else {
  5774. return 0;
  5775. }
  5776. }
  5777. });
  5778. };
  5779. },{}],18:[function(require,module,exports){
  5780. "use strict";
  5781. module.exports = function(Chart) {
  5782. var helpers = Chart.helpers;
  5783. Chart.defaults.line = {
  5784. showLines: true,
  5785. hover: {
  5786. mode: "label"
  5787. },
  5788. scales: {
  5789. xAxes: [{
  5790. type: "category",
  5791. id: 'x-axis-0'
  5792. }],
  5793. yAxes: [{
  5794. type: "linear",
  5795. id: 'y-axis-0'
  5796. }]
  5797. }
  5798. };
  5799. function lineEnabled(dataset, options) {
  5800. return helpers.getValueOrDefault(dataset.showLine, options.showLines);
  5801. }
  5802. Chart.controllers.line = Chart.DatasetController.extend({
  5803. datasetElementType: Chart.elements.Line,
  5804. dataElementType: Chart.elements.Point,
  5805. addElementAndReset: function(index) {
  5806. var me = this;
  5807. var options = me.chart.options;
  5808. var meta = me.getMeta();
  5809. Chart.DatasetController.prototype.addElementAndReset.call(me, index);
  5810. // Make sure bezier control points are updated
  5811. if (lineEnabled(me.getDataset(), options) && meta.dataset._model.tension !== 0) {
  5812. me.updateBezierControlPoints();
  5813. }
  5814. },
  5815. update: function update(reset) {
  5816. var me = this;
  5817. var meta = me.getMeta();
  5818. var line = meta.dataset;
  5819. var points = meta.data || [];
  5820. var options = me.chart.options;
  5821. var lineElementOptions = options.elements.line;
  5822. var scale = me.getScaleForId(meta.yAxisID);
  5823. var i, ilen, custom;
  5824. var dataset = me.getDataset();
  5825. var showLine = lineEnabled(dataset, options);
  5826. // Update Line
  5827. if (showLine) {
  5828. custom = line.custom || {};
  5829. // Compatibility: If the properties are defined with only the old name, use those values
  5830. if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
  5831. dataset.lineTension = dataset.tension;
  5832. }
  5833. // Utility
  5834. line._scale = scale;
  5835. line._datasetIndex = me.index;
  5836. // Data
  5837. line._children = points;
  5838. // Model
  5839. line._model = {
  5840. // Appearance
  5841. // The default behavior of lines is to break at null values, according
  5842. // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
  5843. // This option gives linse the ability to span gaps
  5844. spanGaps: dataset.spanGaps ? dataset.spanGaps : false,
  5845. tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
  5846. backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
  5847. borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
  5848. borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
  5849. borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
  5850. borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
  5851. borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
  5852. borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
  5853. fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
  5854. // Scale
  5855. scaleTop: scale.top,
  5856. scaleBottom: scale.bottom,
  5857. scaleZero: scale.getBasePixel()
  5858. };
  5859. line.pivot();
  5860. }
  5861. // Update Points
  5862. for (i=0, ilen=points.length; i<ilen; ++i) {
  5863. me.updateElement(points[i], i, reset);
  5864. }
  5865. if (showLine && line._model.tension !== 0) {
  5866. me.updateBezierControlPoints();
  5867. }
  5868. // Now pivot the point for animation
  5869. for (i=0, ilen=points.length; i<ilen; ++i) {
  5870. points[i].pivot();
  5871. }
  5872. },
  5873. getPointBackgroundColor: function(point, index) {
  5874. var backgroundColor = this.chart.options.elements.point.backgroundColor;
  5875. var dataset = this.getDataset();
  5876. var custom = point.custom || {};
  5877. if (custom.backgroundColor) {
  5878. backgroundColor = custom.backgroundColor;
  5879. } else if (dataset.pointBackgroundColor) {
  5880. backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
  5881. } else if (dataset.backgroundColor) {
  5882. backgroundColor = dataset.backgroundColor;
  5883. }
  5884. return backgroundColor;
  5885. },
  5886. getPointBorderColor: function(point, index) {
  5887. var borderColor = this.chart.options.elements.point.borderColor;
  5888. var dataset = this.getDataset();
  5889. var custom = point.custom || {};
  5890. if (custom.borderColor) {
  5891. borderColor = custom.borderColor;
  5892. } else if (dataset.pointBorderColor) {
  5893. borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
  5894. } else if (dataset.borderColor) {
  5895. borderColor = dataset.borderColor;
  5896. }
  5897. return borderColor;
  5898. },
  5899. getPointBorderWidth: function(point, index) {
  5900. var borderWidth = this.chart.options.elements.point.borderWidth;
  5901. var dataset = this.getDataset();
  5902. var custom = point.custom || {};
  5903. if (custom.borderWidth) {
  5904. borderWidth = custom.borderWidth;
  5905. } else if (dataset.pointBorderWidth) {
  5906. borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
  5907. } else if (dataset.borderWidth) {
  5908. borderWidth = dataset.borderWidth;
  5909. }
  5910. return borderWidth;
  5911. },
  5912. updateElement: function(point, index, reset) {
  5913. var me = this;
  5914. var meta = me.getMeta();
  5915. var custom = point.custom || {};
  5916. var dataset = me.getDataset();
  5917. var datasetIndex = me.index;
  5918. var value = dataset.data[index];
  5919. var yScale = me.getScaleForId(meta.yAxisID);
  5920. var xScale = me.getScaleForId(meta.xAxisID);
  5921. var pointOptions = me.chart.options.elements.point;
  5922. var x, y;
  5923. // Compatibility: If the properties are defined with only the old name, use those values
  5924. if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
  5925. dataset.pointRadius = dataset.radius;
  5926. }
  5927. if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
  5928. dataset.pointHitRadius = dataset.hitRadius;
  5929. }
  5930. x = xScale.getPixelForValue(value, index, datasetIndex, me.chart.isCombo);
  5931. y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex, me.chart.isCombo);
  5932. // Utility
  5933. point._xScale = xScale;
  5934. point._yScale = yScale;
  5935. point._datasetIndex = datasetIndex;
  5936. point._index = index;
  5937. // Desired view properties
  5938. point._model = {
  5939. x: x,
  5940. y: y,
  5941. skip: custom.skip || isNaN(x) || isNaN(y),
  5942. // Appearance
  5943. radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
  5944. pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
  5945. backgroundColor: me.getPointBackgroundColor(point, index),
  5946. borderColor: me.getPointBorderColor(point, index),
  5947. borderWidth: me.getPointBorderWidth(point, index),
  5948. tension: meta.dataset._model ? meta.dataset._model.tension : 0,
  5949. // Tooltip
  5950. hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
  5951. };
  5952. },
  5953. calculatePointY: function(value, index, datasetIndex, isCombo) {
  5954. var me = this;
  5955. var chart = me.chart;
  5956. var meta = me.getMeta();
  5957. var yScale = me.getScaleForId(meta.yAxisID);
  5958. var sumPos = 0;
  5959. var sumNeg = 0;
  5960. var i, ds, dsMeta;
  5961. if (yScale.options.stacked) {
  5962. for (i = 0; i < datasetIndex; i++) {
  5963. ds = chart.data.datasets[i];
  5964. dsMeta = chart.getDatasetMeta(i);
  5965. if (dsMeta.type === 'line' && chart.isDatasetVisible(i)) {
  5966. if (ds.data[index] < 0) {
  5967. sumNeg += ds.data[index] || 0;
  5968. } else {
  5969. sumPos += ds.data[index] || 0;
  5970. }
  5971. }
  5972. }
  5973. if (value < 0) {
  5974. return yScale.getPixelForValue(sumNeg + value);
  5975. } else {
  5976. return yScale.getPixelForValue(sumPos + value);
  5977. }
  5978. }
  5979. return yScale.getPixelForValue(value);
  5980. },
  5981. updateBezierControlPoints: function() {
  5982. var meta = this.getMeta();
  5983. var area = this.chart.chartArea;
  5984. var points = meta.data || [];
  5985. var i, ilen, point, model, controlPoints;
  5986. for (i=0, ilen=points.length; i<ilen; ++i) {
  5987. point = points[i];
  5988. model = point._model;
  5989. controlPoints = helpers.splineCurve(
  5990. helpers.previousItem(points, i)._model,
  5991. model,
  5992. helpers.nextItem(points, i)._model,
  5993. meta.dataset._model.tension
  5994. );
  5995. model.controlPointPreviousX = controlPoints.previous.x;
  5996. model.controlPointPreviousY = controlPoints.previous.y;
  5997. model.controlPointNextX = controlPoints.next.x;
  5998. model.controlPointNextY = controlPoints.next.y;
  5999. }
  6000. },
  6001. draw: function(ease) {
  6002. var me = this;
  6003. var meta = me.getMeta();
  6004. var points = meta.data || [];
  6005. var easingDecimal = ease || 1;
  6006. var i, ilen;
  6007. // Transition Point Locations
  6008. for (i=0, ilen=points.length; i<ilen; ++i) {
  6009. points[i].transition(easingDecimal);
  6010. }
  6011. // Transition and Draw the line
  6012. if (lineEnabled(me.getDataset(), me.chart.options)) {
  6013. meta.dataset.transition(easingDecimal).draw();
  6014. }
  6015. // Draw the points
  6016. for (i=0, ilen=points.length; i<ilen; ++i) {
  6017. points[i].draw();
  6018. }
  6019. },
  6020. setHoverStyle: function(point) {
  6021. // Point
  6022. var dataset = this.chart.data.datasets[point._datasetIndex];
  6023. var index = point._index;
  6024. var custom = point.custom || {};
  6025. var model = point._model;
  6026. model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
  6027. model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
  6028. model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
  6029. model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
  6030. },
  6031. removeHoverStyle: function(point) {
  6032. var me = this;
  6033. var dataset = me.chart.data.datasets[point._datasetIndex];
  6034. var index = point._index;
  6035. var custom = point.custom || {};
  6036. var model = point._model;
  6037. // Compatibility: If the properties are defined with only the old name, use those values
  6038. if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
  6039. dataset.pointRadius = dataset.radius;
  6040. }
  6041. model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
  6042. model.backgroundColor = me.getPointBackgroundColor(point, index);
  6043. model.borderColor = me.getPointBorderColor(point, index);
  6044. model.borderWidth = me.getPointBorderWidth(point, index);
  6045. }
  6046. });
  6047. };
  6048. },{}],19:[function(require,module,exports){
  6049. "use strict";
  6050. module.exports = function(Chart) {
  6051. var helpers = Chart.helpers;
  6052. Chart.defaults.polarArea = {
  6053. scale: {
  6054. type: "radialLinear",
  6055. lineArc: true // so that lines are circular
  6056. },
  6057. //Boolean - Whether to animate the rotation of the chart
  6058. animation: {
  6059. animateRotate: true,
  6060. animateScale: true
  6061. },
  6062. aspectRatio: 1,
  6063. legendCallback: function(chart) {
  6064. var text = [];
  6065. text.push('<ul class="' + chart.id + '-legend">');
  6066. var data = chart.data;
  6067. var datasets = data.datasets;
  6068. var labels = data.labels;
  6069. if (datasets.length) {
  6070. for (var i = 0; i < datasets[0].data.length; ++i) {
  6071. text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '">');
  6072. if (labels[i]) {
  6073. text.push(labels[i]);
  6074. }
  6075. text.push('</span></li>');
  6076. }
  6077. }
  6078. text.push('</ul>');
  6079. return text.join("");
  6080. },
  6081. legend: {
  6082. labels: {
  6083. generateLabels: function(chart) {
  6084. var data = chart.data;
  6085. if (data.labels.length && data.datasets.length) {
  6086. return data.labels.map(function(label, i) {
  6087. var meta = chart.getDatasetMeta(0);
  6088. var ds = data.datasets[0];
  6089. var arc = meta.data[i];
  6090. var custom = arc.custom || {};
  6091. var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
  6092. var arcOpts = chart.options.elements.arc;
  6093. var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
  6094. var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
  6095. var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
  6096. return {
  6097. text: label,
  6098. fillStyle: fill,
  6099. strokeStyle: stroke,
  6100. lineWidth: bw,
  6101. hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
  6102. // Extra data used for toggling the correct item
  6103. index: i
  6104. };
  6105. });
  6106. } else {
  6107. return [];
  6108. }
  6109. }
  6110. },
  6111. onClick: function(e, legendItem) {
  6112. var index = legendItem.index;
  6113. var chart = this.chart;
  6114. var i, ilen, meta;
  6115. for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
  6116. meta = chart.getDatasetMeta(i);
  6117. meta.data[index].hidden = !meta.data[index].hidden;
  6118. }
  6119. chart.update();
  6120. }
  6121. },
  6122. // Need to override these to give a nice default
  6123. tooltips: {
  6124. callbacks: {
  6125. title: function() {
  6126. return '';
  6127. },
  6128. label: function(tooltipItem, data) {
  6129. return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel;
  6130. }
  6131. }
  6132. }
  6133. };
  6134. Chart.controllers.polarArea = Chart.DatasetController.extend({
  6135. dataElementType: Chart.elements.Arc,
  6136. linkScales: helpers.noop,
  6137. update: function update(reset) {
  6138. var me = this;
  6139. var chart = me.chart;
  6140. var chartArea = chart.chartArea;
  6141. var meta = me.getMeta();
  6142. var opts = chart.options;
  6143. var arcOpts = opts.elements.arc;
  6144. var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
  6145. chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
  6146. chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
  6147. chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
  6148. me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
  6149. me.innerRadius = me.outerRadius - chart.radiusLength;
  6150. meta.count = me.countVisibleElements();
  6151. helpers.each(meta.data, function(arc, index) {
  6152. me.updateElement(arc, index, reset);
  6153. });
  6154. },
  6155. updateElement: function(arc, index, reset) {
  6156. var me = this;
  6157. var chart = me.chart;
  6158. var chartArea = chart.chartArea;
  6159. var dataset = me.getDataset();
  6160. var opts = chart.options;
  6161. var animationOpts = opts.animation;
  6162. var arcOpts = opts.elements.arc;
  6163. var custom = arc.custom || {};
  6164. var scale = chart.scale;
  6165. var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
  6166. var labels = chart.data.labels;
  6167. var circumference = me.calculateCircumference(dataset.data[index]);
  6168. var centerX = (chartArea.left + chartArea.right) / 2;
  6169. var centerY = (chartArea.top + chartArea.bottom) / 2;
  6170. // If there is NaN data before us, we need to calculate the starting angle correctly.
  6171. // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
  6172. var visibleCount = 0;
  6173. var meta = me.getMeta();
  6174. for (var i = 0; i < index; ++i) {
  6175. if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
  6176. ++visibleCount;
  6177. }
  6178. }
  6179. var negHalfPI = -0.5 * Math.PI;
  6180. var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
  6181. var startAngle = (negHalfPI) + (circumference * visibleCount);
  6182. var endAngle = startAngle + (arc.hidden ? 0 : circumference);
  6183. var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
  6184. helpers.extend(arc, {
  6185. // Utility
  6186. _datasetIndex: me.index,
  6187. _index: index,
  6188. _scale: scale,
  6189. // Desired view properties
  6190. _model: {
  6191. x: centerX,
  6192. y: centerY,
  6193. innerRadius: 0,
  6194. outerRadius: reset ? resetRadius : distance,
  6195. startAngle: reset && animationOpts.animateRotate ? negHalfPI : startAngle,
  6196. endAngle: reset && animationOpts.animateRotate ? negHalfPI : endAngle,
  6197. label: getValueAtIndexOrDefault(labels, index, labels[index])
  6198. }
  6199. });
  6200. // Apply border and fill style
  6201. me.removeHoverStyle(arc);
  6202. arc.pivot();
  6203. },
  6204. removeHoverStyle: function(arc) {
  6205. Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
  6206. },
  6207. countVisibleElements: function() {
  6208. var dataset = this.getDataset();
  6209. var meta = this.getMeta();
  6210. var count = 0;
  6211. helpers.each(meta.data, function(element, index) {
  6212. if (!isNaN(dataset.data[index]) && !element.hidden) {
  6213. count++;
  6214. }
  6215. });
  6216. return count;
  6217. },
  6218. calculateCircumference: function(value) {
  6219. var count = this.getMeta().count;
  6220. if (count > 0 && !isNaN(value)) {
  6221. return (2 * Math.PI) / count;
  6222. } else {
  6223. return 0;
  6224. }
  6225. }
  6226. });
  6227. };
  6228. },{}],20:[function(require,module,exports){
  6229. "use strict";
  6230. module.exports = function(Chart) {
  6231. var helpers = Chart.helpers;
  6232. Chart.defaults.radar = {
  6233. scale: {
  6234. type: "radialLinear"
  6235. },
  6236. elements: {
  6237. line: {
  6238. tension: 0 // no bezier in radar
  6239. }
  6240. }
  6241. };
  6242. Chart.controllers.radar = Chart.DatasetController.extend({
  6243. datasetElementType: Chart.elements.Line,
  6244. dataElementType: Chart.elements.Point,
  6245. linkScales: helpers.noop,
  6246. addElementAndReset: function(index) {
  6247. Chart.DatasetController.prototype.addElementAndReset.call(this, index);
  6248. // Make sure bezier control points are updated
  6249. this.updateBezierControlPoints();
  6250. },
  6251. update: function update(reset) {
  6252. var me = this;
  6253. var meta = me.getMeta();
  6254. var line = meta.dataset;
  6255. var points = meta.data;
  6256. var custom = line.custom || {};
  6257. var dataset = me.getDataset();
  6258. var lineElementOptions = me.chart.options.elements.line;
  6259. var scale = me.chart.scale;
  6260. // Compatibility: If the properties are defined with only the old name, use those values
  6261. if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
  6262. dataset.lineTension = dataset.tension;
  6263. }
  6264. helpers.extend(meta.dataset, {
  6265. // Utility
  6266. _datasetIndex: me.index,
  6267. // Data
  6268. _children: points,
  6269. _loop: true,
  6270. // Model
  6271. _model: {
  6272. // Appearance
  6273. tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
  6274. backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
  6275. borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
  6276. borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
  6277. fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
  6278. borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
  6279. borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
  6280. borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
  6281. borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
  6282. // Scale
  6283. scaleTop: scale.top,
  6284. scaleBottom: scale.bottom,
  6285. scaleZero: scale.getBasePosition()
  6286. }
  6287. });
  6288. meta.dataset.pivot();
  6289. // Update Points
  6290. helpers.each(points, function(point, index) {
  6291. me.updateElement(point, index, reset);
  6292. }, me);
  6293. // Update bezier control points
  6294. me.updateBezierControlPoints();
  6295. },
  6296. updateElement: function(point, index, reset) {
  6297. var me = this;
  6298. var custom = point.custom || {};
  6299. var dataset = me.getDataset();
  6300. var scale = me.chart.scale;
  6301. var pointElementOptions = me.chart.options.elements.point;
  6302. var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
  6303. helpers.extend(point, {
  6304. // Utility
  6305. _datasetIndex: me.index,
  6306. _index: index,
  6307. _scale: scale,
  6308. // Desired view properties
  6309. _model: {
  6310. x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
  6311. y: reset ? scale.yCenter : pointPosition.y,
  6312. // Appearance
  6313. tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension),
  6314. radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
  6315. backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
  6316. borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
  6317. borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
  6318. pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
  6319. // Tooltip
  6320. hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius)
  6321. }
  6322. });
  6323. point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
  6324. },
  6325. updateBezierControlPoints: function() {
  6326. var chartArea = this.chart.chartArea;
  6327. var meta = this.getMeta();
  6328. helpers.each(meta.data, function(point, index) {
  6329. var model = point._model;
  6330. var controlPoints = helpers.splineCurve(
  6331. helpers.previousItem(meta.data, index, true)._model,
  6332. model,
  6333. helpers.nextItem(meta.data, index, true)._model,
  6334. model.tension
  6335. );
  6336. // Prevent the bezier going outside of the bounds of the graph
  6337. model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
  6338. model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
  6339. model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
  6340. model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
  6341. // Now pivot the point for animation
  6342. point.pivot();
  6343. });
  6344. },
  6345. draw: function(ease) {
  6346. var meta = this.getMeta();
  6347. var easingDecimal = ease || 1;
  6348. // Transition Point Locations
  6349. helpers.each(meta.data, function(point, index) {
  6350. point.transition(easingDecimal);
  6351. });
  6352. // Transition and Draw the line
  6353. meta.dataset.transition(easingDecimal).draw();
  6354. // Draw the points
  6355. helpers.each(meta.data, function(point) {
  6356. point.draw();
  6357. });
  6358. },
  6359. setHoverStyle: function(point) {
  6360. // Point
  6361. var dataset = this.chart.data.datasets[point._datasetIndex];
  6362. var custom = point.custom || {};
  6363. var index = point._index;
  6364. var model = point._model;
  6365. model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
  6366. model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
  6367. model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
  6368. model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
  6369. },
  6370. removeHoverStyle: function(point) {
  6371. var dataset = this.chart.data.datasets[point._datasetIndex];
  6372. var custom = point.custom || {};
  6373. var index = point._index;
  6374. var model = point._model;
  6375. var pointElementOptions = this.chart.options.elements.point;
  6376. model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius);
  6377. model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);
  6378. model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);
  6379. model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);
  6380. }
  6381. });
  6382. };
  6383. },{}],21:[function(require,module,exports){
  6384. /*global window: false */
  6385. "use strict";
  6386. module.exports = function(Chart) {
  6387. var helpers = Chart.helpers;
  6388. Chart.defaults.global.animation = {
  6389. duration: 1000,
  6390. easing: "easeOutQuart",
  6391. onProgress: helpers.noop,
  6392. onComplete: helpers.noop
  6393. };
  6394. Chart.Animation = Chart.Element.extend({
  6395. currentStep: null, // the current animation step
  6396. numSteps: 60, // default number of steps
  6397. easing: "", // the easing to use for this animation
  6398. render: null, // render function used by the animation service
  6399. onAnimationProgress: null, // user specified callback to fire on each step of the animation
  6400. onAnimationComplete: null // user specified callback to fire when the animation finishes
  6401. });
  6402. Chart.animationService = {
  6403. frameDuration: 17,
  6404. animations: [],
  6405. dropFrames: 0,
  6406. request: null,
  6407. addAnimation: function(chartInstance, animationObject, duration, lazy) {
  6408. var me = this;
  6409. if (!lazy) {
  6410. chartInstance.animating = true;
  6411. }
  6412. for (var index = 0; index < me.animations.length; ++index) {
  6413. if (me.animations[index].chartInstance === chartInstance) {
  6414. // replacing an in progress animation
  6415. me.animations[index].animationObject = animationObject;
  6416. return;
  6417. }
  6418. }
  6419. me.animations.push({
  6420. chartInstance: chartInstance,
  6421. animationObject: animationObject
  6422. });
  6423. // If there are no animations queued, manually kickstart a digest, for lack of a better word
  6424. if (me.animations.length === 1) {
  6425. me.requestAnimationFrame();
  6426. }
  6427. },
  6428. // Cancel the animation for a given chart instance
  6429. cancelAnimation: function(chartInstance) {
  6430. var index = helpers.findIndex(this.animations, function(animationWrapper) {
  6431. return animationWrapper.chartInstance === chartInstance;
  6432. });
  6433. if (index !== -1) {
  6434. this.animations.splice(index, 1);
  6435. chartInstance.animating = false;
  6436. }
  6437. },
  6438. requestAnimationFrame: function() {
  6439. var me = this;
  6440. if (me.request === null) {
  6441. // Skip animation frame requests until the active one is executed.
  6442. // This can happen when processing mouse events, e.g. 'mousemove'
  6443. // and 'mouseout' events will trigger multiple renders.
  6444. me.request = helpers.requestAnimFrame.call(window, function() {
  6445. me.request = null;
  6446. me.startDigest();
  6447. });
  6448. }
  6449. },
  6450. startDigest: function() {
  6451. var me = this;
  6452. var startTime = Date.now();
  6453. var framesToDrop = 0;
  6454. if (me.dropFrames > 1) {
  6455. framesToDrop = Math.floor(me.dropFrames);
  6456. me.dropFrames = me.dropFrames % 1;
  6457. }
  6458. var i = 0;
  6459. while (i < me.animations.length) {
  6460. if (me.animations[i].animationObject.currentStep === null) {
  6461. me.animations[i].animationObject.currentStep = 0;
  6462. }
  6463. me.animations[i].animationObject.currentStep += 1 + framesToDrop;
  6464. if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) {
  6465. me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps;
  6466. }
  6467. me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject);
  6468. if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) {
  6469. me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]);
  6470. }
  6471. if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) {
  6472. if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) {
  6473. me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]);
  6474. }
  6475. // executed the last frame. Remove the animation.
  6476. me.animations[i].chartInstance.animating = false;
  6477. me.animations.splice(i, 1);
  6478. } else {
  6479. ++i;
  6480. }
  6481. }
  6482. var endTime = Date.now();
  6483. var dropFrames = (endTime - startTime) / me.frameDuration;
  6484. me.dropFrames += dropFrames;
  6485. // Do we have more stuff to animate?
  6486. if (me.animations.length > 0) {
  6487. me.requestAnimationFrame();
  6488. }
  6489. }
  6490. };
  6491. };
  6492. },{}],22:[function(require,module,exports){
  6493. "use strict";
  6494. module.exports = function(Chart) {
  6495. var helpers = Chart.helpers;
  6496. //Create a dictionary of chart types, to allow for extension of existing types
  6497. Chart.types = {};
  6498. //Store a reference to each instance - allowing us to globally resize chart instances on window resize.
  6499. //Destroy method on the chart will remove the instance of the chart from this reference.
  6500. Chart.instances = {};
  6501. // Controllers available for dataset visualization eg. bar, line, slice, etc.
  6502. Chart.controllers = {};
  6503. /**
  6504. * @class Chart.Controller
  6505. * The main controller of a chart.
  6506. */
  6507. Chart.Controller = function(instance) {
  6508. this.chart = instance;
  6509. this.config = instance.config;
  6510. this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
  6511. this.id = helpers.uid();
  6512. Object.defineProperty(this, 'data', {
  6513. get: function() {
  6514. return this.config.data;
  6515. }
  6516. });
  6517. //Add the chart instance to the global namespace
  6518. Chart.instances[this.id] = this;
  6519. if (this.options.responsive) {
  6520. // Silent resize before chart draws
  6521. this.resize(true);
  6522. }
  6523. this.initialize();
  6524. return this;
  6525. };
  6526. helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ {
  6527. initialize: function initialize() {
  6528. var me = this;
  6529. // Before init plugin notification
  6530. Chart.plugins.notify('beforeInit', [me]);
  6531. me.bindEvents();
  6532. // Make sure controllers are built first so that each dataset is bound to an axis before the scales
  6533. // are built
  6534. me.ensureScalesHaveIDs();
  6535. me.buildOrUpdateControllers();
  6536. me.buildScales();
  6537. me.updateLayout();
  6538. me.resetElements();
  6539. me.initToolTip();
  6540. me.update();
  6541. // After init plugin notification
  6542. Chart.plugins.notify('afterInit', [me]);
  6543. return me;
  6544. },
  6545. clear: function clear() {
  6546. helpers.clear(this.chart);
  6547. return this;
  6548. },
  6549. stop: function stop() {
  6550. // Stops any current animation loop occuring
  6551. Chart.animationService.cancelAnimation(this);
  6552. return this;
  6553. },
  6554. resize: function resize(silent) {
  6555. var me = this;
  6556. var chart = me.chart;
  6557. var canvas = chart.canvas;
  6558. var newWidth = helpers.getMaximumWidth(canvas);
  6559. var aspectRatio = chart.aspectRatio;
  6560. var newHeight = (me.options.maintainAspectRatio && isNaN(aspectRatio) === false && isFinite(aspectRatio) && aspectRatio !== 0) ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas);
  6561. var sizeChanged = chart.width !== newWidth || chart.height !== newHeight;
  6562. if (!sizeChanged) {
  6563. return me;
  6564. }
  6565. canvas.width = chart.width = newWidth;
  6566. canvas.height = chart.height = newHeight;
  6567. helpers.retinaScale(chart);
  6568. // Notify any plugins about the resize
  6569. var newSize = { width: newWidth, height: newHeight };
  6570. Chart.plugins.notify('resize', [me, newSize]);
  6571. // Notify of resize
  6572. if (me.options.onResize) {
  6573. me.options.onResize(me, newSize);
  6574. }
  6575. if (!silent) {
  6576. me.stop();
  6577. me.update(me.options.responsiveAnimationDuration);
  6578. }
  6579. return me;
  6580. },
  6581. ensureScalesHaveIDs: function ensureScalesHaveIDs() {
  6582. var options = this.options;
  6583. var scalesOptions = options.scales || {};
  6584. var scaleOptions = options.scale;
  6585. helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
  6586. xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
  6587. });
  6588. helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
  6589. yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
  6590. });
  6591. if (scaleOptions) {
  6592. scaleOptions.id = scaleOptions.id || 'scale';
  6593. }
  6594. },
  6595. /**
  6596. * Builds a map of scale ID to scale object for future lookup.
  6597. */
  6598. buildScales: function buildScales() {
  6599. var me = this;
  6600. var options = me.options;
  6601. var scales = me.scales = {};
  6602. var items = [];
  6603. if (options.scales) {
  6604. items = items.concat(
  6605. (options.scales.xAxes || []).map(function(xAxisOptions) {
  6606. return { options: xAxisOptions, dtype: 'category' }; }),
  6607. (options.scales.yAxes || []).map(function(yAxisOptions) {
  6608. return { options: yAxisOptions, dtype: 'linear' }; }));
  6609. }
  6610. if (options.scale) {
  6611. items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true });
  6612. }
  6613. helpers.each(items, function(item, index) {
  6614. var scaleOptions = item.options;
  6615. var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype);
  6616. var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
  6617. if (!scaleClass) {
  6618. return;
  6619. }
  6620. var scale = new scaleClass({
  6621. id: scaleOptions.id,
  6622. options: scaleOptions,
  6623. ctx: me.chart.ctx,
  6624. chart: me
  6625. });
  6626. scales[scale.id] = scale;
  6627. // TODO(SB): I think we should be able to remove this custom case (options.scale)
  6628. // and consider it as a regular scale part of the "scales"" map only! This would
  6629. // make the logic easier and remove some useless? custom code.
  6630. if (item.isDefault) {
  6631. me.scale = scale;
  6632. }
  6633. });
  6634. Chart.scaleService.addScalesToLayout(this);
  6635. },
  6636. updateLayout: function() {
  6637. Chart.layoutService.update(this, this.chart.width, this.chart.height);
  6638. },
  6639. buildOrUpdateControllers: function buildOrUpdateControllers() {
  6640. var me = this;
  6641. var types = [];
  6642. var newControllers = [];
  6643. helpers.each(me.data.datasets, function(dataset, datasetIndex) {
  6644. var meta = me.getDatasetMeta(datasetIndex);
  6645. if (!meta.type) {
  6646. meta.type = dataset.type || me.config.type;
  6647. }
  6648. types.push(meta.type);
  6649. if (meta.controller) {
  6650. meta.controller.updateIndex(datasetIndex);
  6651. } else {
  6652. meta.controller = new Chart.controllers[meta.type](me, datasetIndex);
  6653. newControllers.push(meta.controller);
  6654. }
  6655. }, me);
  6656. if (types.length > 1) {
  6657. for (var i = 1; i < types.length; i++) {
  6658. if (types[i] !== types[i - 1]) {
  6659. me.isCombo = true;
  6660. break;
  6661. }
  6662. }
  6663. }
  6664. return newControllers;
  6665. },
  6666. resetElements: function resetElements() {
  6667. var me = this;
  6668. helpers.each(me.data.datasets, function(dataset, datasetIndex) {
  6669. me.getDatasetMeta(datasetIndex).controller.reset();
  6670. }, me);
  6671. },
  6672. update: function update(animationDuration, lazy) {
  6673. var me = this;
  6674. Chart.plugins.notify('beforeUpdate', [me]);
  6675. // In case the entire data object changed
  6676. me.tooltip._data = me.data;
  6677. // Make sure dataset controllers are updated and new controllers are reset
  6678. var newControllers = me.buildOrUpdateControllers();
  6679. // Make sure all dataset controllers have correct meta data counts
  6680. helpers.each(me.data.datasets, function(dataset, datasetIndex) {
  6681. me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
  6682. }, me);
  6683. Chart.layoutService.update(me, me.chart.width, me.chart.height);
  6684. // Apply changes to the dataets that require the scales to have been calculated i.e BorderColor chages
  6685. Chart.plugins.notify('afterScaleUpdate', [me]);
  6686. // Can only reset the new controllers after the scales have been updated
  6687. helpers.each(newControllers, function(controller) {
  6688. controller.reset();
  6689. });
  6690. me.updateDatasets();
  6691. // Do this before render so that any plugins that need final scale updates can use it
  6692. Chart.plugins.notify('afterUpdate', [me]);
  6693. me.render(animationDuration, lazy);
  6694. },
  6695. /**
  6696. * @method beforeDatasetsUpdate
  6697. * @description Called before all datasets are updated. If a plugin returns false,
  6698. * the datasets update will be cancelled until another chart update is triggered.
  6699. * @param {Object} instance the chart instance being updated.
  6700. * @returns {Boolean} false to cancel the datasets update.
  6701. * @memberof Chart.PluginBase
  6702. * @since version 2.1.5
  6703. * @instance
  6704. */
  6705. /**
  6706. * @method afterDatasetsUpdate
  6707. * @description Called after all datasets have been updated. Note that this
  6708. * extension will not be called if the datasets update has been cancelled.
  6709. * @param {Object} instance the chart instance being updated.
  6710. * @memberof Chart.PluginBase
  6711. * @since version 2.1.5
  6712. * @instance
  6713. */
  6714. /**
  6715. * Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate
  6716. * extension, in which case no datasets will be updated and the afterDatasetsUpdate
  6717. * notification will be skipped.
  6718. * @protected
  6719. * @instance
  6720. */
  6721. updateDatasets: function() {
  6722. var me = this;
  6723. var i, ilen;
  6724. if (Chart.plugins.notify('beforeDatasetsUpdate', [ me ])) {
  6725. for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
  6726. me.getDatasetMeta(i).controller.update();
  6727. }
  6728. Chart.plugins.notify('afterDatasetsUpdate', [ me ]);
  6729. }
  6730. },
  6731. render: function render(duration, lazy) {
  6732. var me = this;
  6733. Chart.plugins.notify('beforeRender', [me]);
  6734. var animationOptions = me.options.animation;
  6735. if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
  6736. var animation = new Chart.Animation();
  6737. animation.numSteps = (duration || animationOptions.duration) / 16.66; //60 fps
  6738. animation.easing = animationOptions.easing;
  6739. // render function
  6740. animation.render = function(chartInstance, animationObject) {
  6741. var easingFunction = helpers.easingEffects[animationObject.easing];
  6742. var stepDecimal = animationObject.currentStep / animationObject.numSteps;
  6743. var easeDecimal = easingFunction(stepDecimal);
  6744. chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
  6745. };
  6746. // user events
  6747. animation.onAnimationProgress = animationOptions.onProgress;
  6748. animation.onAnimationComplete = animationOptions.onComplete;
  6749. Chart.animationService.addAnimation(me, animation, duration, lazy);
  6750. } else {
  6751. me.draw();
  6752. if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) {
  6753. animationOptions.onComplete.call(me);
  6754. }
  6755. }
  6756. return me;
  6757. },
  6758. draw: function(ease) {
  6759. var me = this;
  6760. var easingDecimal = ease || 1;
  6761. me.clear();
  6762. Chart.plugins.notify('beforeDraw', [me, easingDecimal]);
  6763. // Draw all the scales
  6764. helpers.each(me.boxes, function(box) {
  6765. box.draw(me.chartArea);
  6766. }, me);
  6767. if (me.scale) {
  6768. me.scale.draw();
  6769. }
  6770. Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]);
  6771. // Draw each dataset via its respective controller (reversed to support proper line stacking)
  6772. helpers.each(me.data.datasets, function(dataset, datasetIndex) {
  6773. if (me.isDatasetVisible(datasetIndex)) {
  6774. me.getDatasetMeta(datasetIndex).controller.draw(ease);
  6775. }
  6776. }, me, true);
  6777. Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]);
  6778. // Finally draw the tooltip
  6779. me.tooltip.transition(easingDecimal).draw();
  6780. Chart.plugins.notify('afterDraw', [me, easingDecimal]);
  6781. },
  6782. // Get the single element that was clicked on
  6783. // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
  6784. getElementAtEvent: function(e) {
  6785. var me = this;
  6786. var eventPosition = helpers.getRelativePosition(e, me.chart);
  6787. var elementsArray = [];
  6788. helpers.each(me.data.datasets, function(dataset, datasetIndex) {
  6789. if (me.isDatasetVisible(datasetIndex)) {
  6790. var meta = me.getDatasetMeta(datasetIndex);
  6791. helpers.each(meta.data, function(element, index) {
  6792. if (element.inRange(eventPosition.x, eventPosition.y)) {
  6793. elementsArray.push(element);
  6794. return elementsArray;
  6795. }
  6796. });
  6797. }
  6798. });
  6799. return elementsArray;
  6800. },
  6801. getElementsAtEvent: function(e) {
  6802. var me = this;
  6803. var eventPosition = helpers.getRelativePosition(e, me.chart);
  6804. var elementsArray = [];
  6805. var found = (function() {
  6806. if (me.data.datasets) {
  6807. for (var i = 0; i < me.data.datasets.length; i++) {
  6808. var meta = me.getDatasetMeta(i);
  6809. if (me.isDatasetVisible(i)) {
  6810. for (var j = 0; j < meta.data.length; j++) {
  6811. if (meta.data[j].inRange(eventPosition.x, eventPosition.y)) {
  6812. return meta.data[j];
  6813. }
  6814. }
  6815. }
  6816. }
  6817. }
  6818. }).call(me);
  6819. if (!found) {
  6820. return elementsArray;
  6821. }
  6822. helpers.each(me.data.datasets, function(dataset, datasetIndex) {
  6823. if (me.isDatasetVisible(datasetIndex)) {
  6824. var meta = me.getDatasetMeta(datasetIndex);
  6825. elementsArray.push(meta.data[found._index]);
  6826. }
  6827. }, me);
  6828. return elementsArray;
  6829. },
  6830. getElementsAtEventForMode: function(e, mode) {
  6831. var me = this;
  6832. switch (mode) {
  6833. case 'single':
  6834. return me.getElementAtEvent(e);
  6835. case 'label':
  6836. return me.getElementsAtEvent(e);
  6837. case 'dataset':
  6838. return me.getDatasetAtEvent(e);
  6839. default:
  6840. return e;
  6841. }
  6842. },
  6843. getDatasetAtEvent: function(e) {
  6844. var elementsArray = this.getElementAtEvent(e);
  6845. if (elementsArray.length > 0) {
  6846. elementsArray = this.getDatasetMeta(elementsArray[0]._datasetIndex).data;
  6847. }
  6848. return elementsArray;
  6849. },
  6850. getDatasetMeta: function(datasetIndex) {
  6851. var me = this;
  6852. var dataset = me.data.datasets[datasetIndex];
  6853. if (!dataset._meta) {
  6854. dataset._meta = {};
  6855. }
  6856. var meta = dataset._meta[me.id];
  6857. if (!meta) {
  6858. meta = dataset._meta[me.id] = {
  6859. type: null,
  6860. data: [],
  6861. dataset: null,
  6862. controller: null,
  6863. hidden: null, // See isDatasetVisible() comment
  6864. xAxisID: null,
  6865. yAxisID: null
  6866. };
  6867. }
  6868. return meta;
  6869. },
  6870. getVisibleDatasetCount: function() {
  6871. var count = 0;
  6872. for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) {
  6873. if (this.isDatasetVisible(i)) {
  6874. count++;
  6875. }
  6876. }
  6877. return count;
  6878. },
  6879. isDatasetVisible: function(datasetIndex) {
  6880. var meta = this.getDatasetMeta(datasetIndex);
  6881. // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
  6882. // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
  6883. return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
  6884. },
  6885. generateLegend: function generateLegend() {
  6886. return this.options.legendCallback(this);
  6887. },
  6888. destroy: function destroy() {
  6889. var me = this;
  6890. me.stop();
  6891. me.clear();
  6892. helpers.unbindEvents(me, me.events);
  6893. helpers.removeResizeListener(me.chart.canvas.parentNode);
  6894. // Reset canvas height/width attributes
  6895. var canvas = me.chart.canvas;
  6896. canvas.width = me.chart.width;
  6897. canvas.height = me.chart.height;
  6898. // if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here
  6899. if (me.chart.originalDevicePixelRatio !== undefined) {
  6900. me.chart.ctx.scale(1 / me.chart.originalDevicePixelRatio, 1 / me.chart.originalDevicePixelRatio);
  6901. }
  6902. // Reset to the old style since it may have been changed by the device pixel ratio changes
  6903. canvas.style.width = me.chart.originalCanvasStyleWidth;
  6904. canvas.style.height = me.chart.originalCanvasStyleHeight;
  6905. Chart.plugins.notify('destroy', [me]);
  6906. delete Chart.instances[me.id];
  6907. },
  6908. toBase64Image: function toBase64Image() {
  6909. return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
  6910. },
  6911. initToolTip: function initToolTip() {
  6912. var me = this;
  6913. me.tooltip = new Chart.Tooltip({
  6914. _chart: me.chart,
  6915. _chartInstance: me,
  6916. _data: me.data,
  6917. _options: me.options.tooltips
  6918. }, me);
  6919. },
  6920. bindEvents: function bindEvents() {
  6921. var me = this;
  6922. helpers.bindEvents(me, me.options.events, function(evt) {
  6923. me.eventHandler(evt);
  6924. });
  6925. },
  6926. updateHoverStyle: function(elements, mode, enabled) {
  6927. var method = enabled? 'setHoverStyle' : 'removeHoverStyle';
  6928. var element, i, ilen;
  6929. switch (mode) {
  6930. case 'single':
  6931. elements = [ elements[0] ];
  6932. break;
  6933. case 'label':
  6934. case 'dataset':
  6935. // elements = elements;
  6936. break;
  6937. default:
  6938. // unsupported mode
  6939. return;
  6940. }
  6941. for (i=0, ilen=elements.length; i<ilen; ++i) {
  6942. element = elements[i];
  6943. if (element) {
  6944. this.getDatasetMeta(element._datasetIndex).controller[method](element);
  6945. }
  6946. }
  6947. },
  6948. eventHandler: function eventHandler(e) {
  6949. var me = this;
  6950. var tooltip = me.tooltip;
  6951. var options = me.options || {};
  6952. var hoverOptions = options.hover;
  6953. var tooltipsOptions = options.tooltips;
  6954. me.lastActive = me.lastActive || [];
  6955. me.lastTooltipActive = me.lastTooltipActive || [];
  6956. // Find Active Elements for hover and tooltips
  6957. if (e.type === 'mouseout') {
  6958. me.active = [];
  6959. me.tooltipActive = [];
  6960. } else {
  6961. me.active = me.getElementsAtEventForMode(e, hoverOptions.mode);
  6962. me.tooltipActive = me.getElementsAtEventForMode(e, tooltipsOptions.mode);
  6963. }
  6964. // On Hover hook
  6965. if (hoverOptions.onHover) {
  6966. hoverOptions.onHover.call(me, me.active);
  6967. }
  6968. if (e.type === 'mouseup' || e.type === 'click') {
  6969. if (options.onClick) {
  6970. options.onClick.call(me, e, me.active);
  6971. }
  6972. if (me.legend && me.legend.handleEvent) {
  6973. me.legend.handleEvent(e);
  6974. }
  6975. }
  6976. // Remove styling for last active (even if it may still be active)
  6977. if (me.lastActive.length) {
  6978. me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
  6979. }
  6980. // Built in hover styling
  6981. if (me.active.length && hoverOptions.mode) {
  6982. me.updateHoverStyle(me.active, hoverOptions.mode, true);
  6983. }
  6984. // Built in Tooltips
  6985. if (tooltipsOptions.enabled || tooltipsOptions.custom) {
  6986. tooltip.initialize();
  6987. tooltip._active = me.tooltipActive;
  6988. tooltip.update(true);
  6989. }
  6990. // Hover animations
  6991. tooltip.pivot();
  6992. if (!me.animating) {
  6993. // If entering, leaving, or changing elements, animate the change via pivot
  6994. if (!helpers.arrayEquals(me.active, me.lastActive) ||
  6995. !helpers.arrayEquals(me.tooltipActive, me.lastTooltipActive)) {
  6996. me.stop();
  6997. if (tooltipsOptions.enabled || tooltipsOptions.custom) {
  6998. tooltip.update(true);
  6999. }
  7000. // We only need to render at this point. Updating will cause scales to be
  7001. // recomputed generating flicker & using more memory than necessary.
  7002. me.render(hoverOptions.animationDuration, true);
  7003. }
  7004. }
  7005. // Remember Last Actives
  7006. me.lastActive = me.active;
  7007. me.lastTooltipActive = me.tooltipActive;
  7008. return me;
  7009. }
  7010. });
  7011. };
  7012. },{}],23:[function(require,module,exports){
  7013. "use strict";
  7014. module.exports = function(Chart) {
  7015. var helpers = Chart.helpers;
  7016. var noop = helpers.noop;
  7017. // Base class for all dataset controllers (line, bar, etc)
  7018. Chart.DatasetController = function(chart, datasetIndex) {
  7019. this.initialize.call(this, chart, datasetIndex);
  7020. };
  7021. helpers.extend(Chart.DatasetController.prototype, {
  7022. /**
  7023. * Element type used to generate a meta dataset (e.g. Chart.element.Line).
  7024. * @type {Chart.core.element}
  7025. */
  7026. datasetElementType: null,
  7027. /**
  7028. * Element type used to generate a meta data (e.g. Chart.element.Point).
  7029. * @type {Chart.core.element}
  7030. */
  7031. dataElementType: null,
  7032. initialize: function(chart, datasetIndex) {
  7033. var me = this;
  7034. me.chart = chart;
  7035. me.index = datasetIndex;
  7036. me.linkScales();
  7037. me.addElements();
  7038. },
  7039. updateIndex: function(datasetIndex) {
  7040. this.index = datasetIndex;
  7041. },
  7042. linkScales: function() {
  7043. var me = this;
  7044. var meta = me.getMeta();
  7045. var dataset = me.getDataset();
  7046. if (meta.xAxisID === null) {
  7047. meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
  7048. }
  7049. if (meta.yAxisID === null) {
  7050. meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
  7051. }
  7052. },
  7053. getDataset: function() {
  7054. return this.chart.data.datasets[this.index];
  7055. },
  7056. getMeta: function() {
  7057. return this.chart.getDatasetMeta(this.index);
  7058. },
  7059. getScaleForId: function(scaleID) {
  7060. return this.chart.scales[scaleID];
  7061. },
  7062. reset: function() {
  7063. this.update(true);
  7064. },
  7065. createMetaDataset: function() {
  7066. var me = this;
  7067. var type = me.datasetElementType;
  7068. return type && new type({
  7069. _chart: me.chart.chart,
  7070. _datasetIndex: me.index
  7071. });
  7072. },
  7073. createMetaData: function(index) {
  7074. var me = this;
  7075. var type = me.dataElementType;
  7076. return type && new type({
  7077. _chart: me.chart.chart,
  7078. _datasetIndex: me.index,
  7079. _index: index
  7080. });
  7081. },
  7082. addElements: function() {
  7083. var me = this;
  7084. var meta = me.getMeta();
  7085. var data = me.getDataset().data || [];
  7086. var metaData = meta.data;
  7087. var i, ilen;
  7088. for (i=0, ilen=data.length; i<ilen; ++i) {
  7089. metaData[i] = metaData[i] || me.createMetaData(meta, i);
  7090. }
  7091. meta.dataset = meta.dataset || me.createMetaDataset();
  7092. },
  7093. addElementAndReset: function(index) {
  7094. var me = this;
  7095. var element = me.createMetaData(index);
  7096. me.getMeta().data.splice(index, 0, element);
  7097. me.updateElement(element, index, true);
  7098. },
  7099. buildOrUpdateElements: function buildOrUpdateElements() {
  7100. // Handle the number of data points changing
  7101. var meta = this.getMeta(),
  7102. md = meta.data,
  7103. numData = this.getDataset().data.length,
  7104. numMetaData = md.length;
  7105. // Make sure that we handle number of datapoints changing
  7106. if (numData < numMetaData) {
  7107. // Remove excess bars for data points that have been removed
  7108. md.splice(numData, numMetaData - numData);
  7109. } else if (numData > numMetaData) {
  7110. // Add new elements
  7111. for (var index = numMetaData; index < numData; ++index) {
  7112. this.addElementAndReset(index);
  7113. }
  7114. }
  7115. },
  7116. update: noop,
  7117. draw: function(ease) {
  7118. var easingDecimal = ease || 1;
  7119. helpers.each(this.getMeta().data, function(element, index) {
  7120. element.transition(easingDecimal).draw();
  7121. });
  7122. },
  7123. removeHoverStyle: function(element, elementOpts) {
  7124. var dataset = this.chart.data.datasets[element._datasetIndex],
  7125. index = element._index,
  7126. custom = element.custom || {},
  7127. valueOrDefault = helpers.getValueAtIndexOrDefault,
  7128. color = helpers.color,
  7129. model = element._model;
  7130. model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
  7131. model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
  7132. model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
  7133. },
  7134. setHoverStyle: function(element) {
  7135. var dataset = this.chart.data.datasets[element._datasetIndex],
  7136. index = element._index,
  7137. custom = element.custom || {},
  7138. valueOrDefault = helpers.getValueAtIndexOrDefault,
  7139. color = helpers.color,
  7140. getHoverColor = helpers.getHoverColor,
  7141. model = element._model;
  7142. model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
  7143. model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
  7144. model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
  7145. }
  7146. });
  7147. Chart.DatasetController.extend = helpers.inherits;
  7148. };
  7149. },{}],24:[function(require,module,exports){
  7150. "use strict";
  7151. module.exports = function(Chart) {
  7152. var helpers = Chart.helpers;
  7153. Chart.elements = {};
  7154. Chart.Element = function(configuration) {
  7155. helpers.extend(this, configuration);
  7156. this.initialize.apply(this, arguments);
  7157. };
  7158. helpers.extend(Chart.Element.prototype, {
  7159. initialize: function() {
  7160. this.hidden = false;
  7161. },
  7162. pivot: function() {
  7163. var me = this;
  7164. if (!me._view) {
  7165. me._view = helpers.clone(me._model);
  7166. }
  7167. me._start = helpers.clone(me._view);
  7168. return me;
  7169. },
  7170. transition: function(ease) {
  7171. var me = this;
  7172. if (!me._view) {
  7173. me._view = helpers.clone(me._model);
  7174. }
  7175. // No animation -> No Transition
  7176. if (ease === 1) {
  7177. me._view = me._model;
  7178. me._start = null;
  7179. return me;
  7180. }
  7181. if (!me._start) {
  7182. me.pivot();
  7183. }
  7184. helpers.each(me._model, function(value, key) {
  7185. if (key[0] === '_') {
  7186. // Only non-underscored properties
  7187. }
  7188. // Init if doesn't exist
  7189. else if (!me._view.hasOwnProperty(key)) {
  7190. if (typeof value === 'number' && !isNaN(me._view[key])) {
  7191. me._view[key] = value * ease;
  7192. } else {
  7193. me._view[key] = value;
  7194. }
  7195. }
  7196. // No unnecessary computations
  7197. else if (value === me._view[key]) {
  7198. // It's the same! Woohoo!
  7199. }
  7200. // Color transitions if possible
  7201. else if (typeof value === 'string') {
  7202. try {
  7203. var color = helpers.color(me._model[key]).mix(helpers.color(me._start[key]), ease);
  7204. me._view[key] = color.rgbString();
  7205. } catch (err) {
  7206. me._view[key] = value;
  7207. }
  7208. }
  7209. // Number transitions
  7210. else if (typeof value === 'number') {
  7211. var startVal = me._start[key] !== undefined && isNaN(me._start[key]) === false ? me._start[key] : 0;
  7212. me._view[key] = ((me._model[key] - startVal) * ease) + startVal;
  7213. }
  7214. // Everything else
  7215. else {
  7216. me._view[key] = value;
  7217. }
  7218. }, me);
  7219. return me;
  7220. },
  7221. tooltipPosition: function() {
  7222. return {
  7223. x: this._model.x,
  7224. y: this._model.y
  7225. };
  7226. },
  7227. hasValue: function() {
  7228. return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
  7229. }
  7230. });
  7231. Chart.Element.extend = helpers.inherits;
  7232. };
  7233. },{}],25:[function(require,module,exports){
  7234. /*global window: false */
  7235. /*global document: false */
  7236. "use strict";
  7237. var color = require(2);
  7238. module.exports = function(Chart) {
  7239. //Global Chart helpers object for utility methods and classes
  7240. var helpers = Chart.helpers = {};
  7241. //-- Basic js utility methods
  7242. helpers.each = function(loopable, callback, self, reverse) {
  7243. // Check to see if null or undefined firstly.
  7244. var i, len;
  7245. if (helpers.isArray(loopable)) {
  7246. len = loopable.length;
  7247. if (reverse) {
  7248. for (i = len - 1; i >= 0; i--) {
  7249. callback.call(self, loopable[i], i);
  7250. }
  7251. } else {
  7252. for (i = 0; i < len; i++) {
  7253. callback.call(self, loopable[i], i);
  7254. }
  7255. }
  7256. } else if (typeof loopable === 'object') {
  7257. var keys = Object.keys(loopable);
  7258. len = keys.length;
  7259. for (i = 0; i < len; i++) {
  7260. callback.call(self, loopable[keys[i]], keys[i]);
  7261. }
  7262. }
  7263. };
  7264. helpers.clone = function(obj) {
  7265. var objClone = {};
  7266. helpers.each(obj, function(value, key) {
  7267. if (helpers.isArray(value)) {
  7268. objClone[key] = value.slice(0);
  7269. } else if (typeof value === 'object' && value !== null) {
  7270. objClone[key] = helpers.clone(value);
  7271. } else {
  7272. objClone[key] = value;
  7273. }
  7274. });
  7275. return objClone;
  7276. };
  7277. helpers.extend = function(base) {
  7278. var setFn = function(value, key) { base[key] = value; };
  7279. for (var i = 1, ilen = arguments.length; i < ilen; i++) {
  7280. helpers.each(arguments[i], setFn);
  7281. }
  7282. return base;
  7283. };
  7284. // Need a special merge function to chart configs since they are now grouped
  7285. helpers.configMerge = function(_base) {
  7286. var base = helpers.clone(_base);
  7287. helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
  7288. helpers.each(extension, function(value, key) {
  7289. if (key === 'scales') {
  7290. // Scale config merging is complex. Add out own function here for that
  7291. base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value);
  7292. } else if (key === 'scale') {
  7293. // Used in polar area & radar charts since there is only one scale
  7294. base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value);
  7295. } else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) {
  7296. // In this case we have an array of objects replacing another array. Rather than doing a strict replace,
  7297. // merge. This allows easy scale option merging
  7298. var baseArray = base[key];
  7299. helpers.each(value, function(valueObj, index) {
  7300. if (index < baseArray.length) {
  7301. if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) {
  7302. // Two objects are coming together. Do a merge of them.
  7303. baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
  7304. } else {
  7305. // Just overwrite in this case since there is nothing to merge
  7306. baseArray[index] = valueObj;
  7307. }
  7308. } else {
  7309. baseArray.push(valueObj); // nothing to merge
  7310. }
  7311. });
  7312. } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") {
  7313. // If we are overwriting an object with an object, do a merge of the properties.
  7314. base[key] = helpers.configMerge(base[key], value);
  7315. } else {
  7316. // can just overwrite the value in this case
  7317. base[key] = value;
  7318. }
  7319. });
  7320. });
  7321. return base;
  7322. };
  7323. helpers.scaleMerge = function(_base, extension) {
  7324. var base = helpers.clone(_base);
  7325. helpers.each(extension, function(value, key) {
  7326. if (key === 'xAxes' || key === 'yAxes') {
  7327. // These properties are arrays of items
  7328. if (base.hasOwnProperty(key)) {
  7329. helpers.each(value, function(valueObj, index) {
  7330. var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
  7331. var axisDefaults = Chart.scaleService.getScaleDefaults(axisType);
  7332. if (index >= base[key].length || !base[key][index].type) {
  7333. base[key].push(helpers.configMerge(axisDefaults, valueObj));
  7334. } else if (valueObj.type && valueObj.type !== base[key][index].type) {
  7335. // Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
  7336. base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj);
  7337. } else {
  7338. // Type is the same
  7339. base[key][index] = helpers.configMerge(base[key][index], valueObj);
  7340. }
  7341. });
  7342. } else {
  7343. base[key] = [];
  7344. helpers.each(value, function(valueObj) {
  7345. var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
  7346. base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj));
  7347. });
  7348. }
  7349. } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") {
  7350. // If we are overwriting an object with an object, do a merge of the properties.
  7351. base[key] = helpers.configMerge(base[key], value);
  7352. } else {
  7353. // can just overwrite the value in this case
  7354. base[key] = value;
  7355. }
  7356. });
  7357. return base;
  7358. };
  7359. helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
  7360. if (value === undefined || value === null) {
  7361. return defaultValue;
  7362. }
  7363. if (helpers.isArray(value)) {
  7364. return index < value.length ? value[index] : defaultValue;
  7365. }
  7366. return value;
  7367. };
  7368. helpers.getValueOrDefault = function(value, defaultValue) {
  7369. return value === undefined ? defaultValue : value;
  7370. };
  7371. helpers.indexOf = Array.prototype.indexOf?
  7372. function(array, item) { return array.indexOf(item); } :
  7373. function(array, item) {
  7374. for (var i = 0, ilen = array.length; i < ilen; ++i) {
  7375. if (array[i] === item) {
  7376. return i;
  7377. }
  7378. }
  7379. return -1;
  7380. };
  7381. helpers.where = function(collection, filterCallback) {
  7382. if (helpers.isArray(collection) && Array.prototype.filter) {
  7383. return collection.filter(filterCallback);
  7384. } else {
  7385. var filtered = [];
  7386. helpers.each(collection, function(item) {
  7387. if (filterCallback(item)) {
  7388. filtered.push(item);
  7389. }
  7390. });
  7391. return filtered;
  7392. }
  7393. };
  7394. helpers.findIndex = Array.prototype.findIndex?
  7395. function(array, callback, scope) { return array.findIndex(callback, scope); } :
  7396. function(array, callback, scope) {
  7397. scope = scope === undefined? array : scope;
  7398. for (var i = 0, ilen = array.length; i < ilen; ++i) {
  7399. if (callback.call(scope, array[i], i, array)) {
  7400. return i;
  7401. }
  7402. }
  7403. return -1;
  7404. };
  7405. helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
  7406. // Default to start of the array
  7407. if (startIndex === undefined || startIndex === null) {
  7408. startIndex = -1;
  7409. }
  7410. for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
  7411. var currentItem = arrayToSearch[i];
  7412. if (filterCallback(currentItem)) {
  7413. return currentItem;
  7414. }
  7415. }
  7416. };
  7417. helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
  7418. // Default to end of the array
  7419. if (startIndex === undefined || startIndex === null) {
  7420. startIndex = arrayToSearch.length;
  7421. }
  7422. for (var i = startIndex - 1; i >= 0; i--) {
  7423. var currentItem = arrayToSearch[i];
  7424. if (filterCallback(currentItem)) {
  7425. return currentItem;
  7426. }
  7427. }
  7428. };
  7429. helpers.inherits = function(extensions) {
  7430. //Basic javascript inheritance based on the model created in Backbone.js
  7431. var parent = this;
  7432. var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() {
  7433. return parent.apply(this, arguments);
  7434. };
  7435. var Surrogate = function() {
  7436. this.constructor = ChartElement;
  7437. };
  7438. Surrogate.prototype = parent.prototype;
  7439. ChartElement.prototype = new Surrogate();
  7440. ChartElement.extend = helpers.inherits;
  7441. if (extensions) {
  7442. helpers.extend(ChartElement.prototype, extensions);
  7443. }
  7444. ChartElement.__super__ = parent.prototype;
  7445. return ChartElement;
  7446. };
  7447. helpers.noop = function() {};
  7448. helpers.uid = (function() {
  7449. var id = 0;
  7450. return function() {
  7451. return id++;
  7452. };
  7453. })();
  7454. //-- Math methods
  7455. helpers.isNumber = function(n) {
  7456. return !isNaN(parseFloat(n)) && isFinite(n);
  7457. };
  7458. helpers.almostEquals = function(x, y, epsilon) {
  7459. return Math.abs(x - y) < epsilon;
  7460. };
  7461. helpers.max = function(array) {
  7462. return array.reduce(function(max, value) {
  7463. if (!isNaN(value)) {
  7464. return Math.max(max, value);
  7465. } else {
  7466. return max;
  7467. }
  7468. }, Number.NEGATIVE_INFINITY);
  7469. };
  7470. helpers.min = function(array) {
  7471. return array.reduce(function(min, value) {
  7472. if (!isNaN(value)) {
  7473. return Math.min(min, value);
  7474. } else {
  7475. return min;
  7476. }
  7477. }, Number.POSITIVE_INFINITY);
  7478. };
  7479. helpers.sign = Math.sign?
  7480. function(x) { return Math.sign(x); } :
  7481. function(x) {
  7482. x = +x; // convert to a number
  7483. if (x === 0 || isNaN(x)) {
  7484. return x;
  7485. }
  7486. return x > 0 ? 1 : -1;
  7487. };
  7488. helpers.log10 = Math.log10?
  7489. function(x) { return Math.log10(x); } :
  7490. function(x) {
  7491. return Math.log(x) / Math.LN10;
  7492. };
  7493. helpers.toRadians = function(degrees) {
  7494. return degrees * (Math.PI / 180);
  7495. };
  7496. helpers.toDegrees = function(radians) {
  7497. return radians * (180 / Math.PI);
  7498. };
  7499. // Gets the angle from vertical upright to the point about a centre.
  7500. helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
  7501. var distanceFromXCenter = anglePoint.x - centrePoint.x,
  7502. distanceFromYCenter = anglePoint.y - centrePoint.y,
  7503. radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
  7504. var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
  7505. if (angle < (-0.5 * Math.PI)) {
  7506. angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
  7507. }
  7508. return {
  7509. angle: angle,
  7510. distance: radialDistanceFromCenter
  7511. };
  7512. };
  7513. helpers.aliasPixel = function(pixelWidth) {
  7514. return (pixelWidth % 2 === 0) ? 0 : 0.5;
  7515. };
  7516. helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
  7517. //Props to Rob Spencer at scaled innovation for his post on splining between points
  7518. //http://scaledinnovation.com/analytics/splines/aboutSplines.html
  7519. // This function must also respect "skipped" points
  7520. var previous = firstPoint.skip ? middlePoint : firstPoint,
  7521. current = middlePoint,
  7522. next = afterPoint.skip ? middlePoint : afterPoint;
  7523. var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
  7524. var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
  7525. var s01 = d01 / (d01 + d12);
  7526. var s12 = d12 / (d01 + d12);
  7527. // If all points are the same, s01 & s02 will be inf
  7528. s01 = isNaN(s01) ? 0 : s01;
  7529. s12 = isNaN(s12) ? 0 : s12;
  7530. var fa = t * s01; // scaling factor for triangle Ta
  7531. var fb = t * s12;
  7532. return {
  7533. previous: {
  7534. x: current.x - fa * (next.x - previous.x),
  7535. y: current.y - fa * (next.y - previous.y)
  7536. },
  7537. next: {
  7538. x: current.x + fb * (next.x - previous.x),
  7539. y: current.y + fb * (next.y - previous.y)
  7540. }
  7541. };
  7542. };
  7543. helpers.nextItem = function(collection, index, loop) {
  7544. if (loop) {
  7545. return index >= collection.length - 1 ? collection[0] : collection[index + 1];
  7546. }
  7547. return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
  7548. };
  7549. helpers.previousItem = function(collection, index, loop) {
  7550. if (loop) {
  7551. return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
  7552. }
  7553. return index <= 0 ? collection[0] : collection[index - 1];
  7554. };
  7555. // Implementation of the nice number algorithm used in determining where axis labels will go
  7556. helpers.niceNum = function(range, round) {
  7557. var exponent = Math.floor(helpers.log10(range));
  7558. var fraction = range / Math.pow(10, exponent);
  7559. var niceFraction;
  7560. if (round) {
  7561. if (fraction < 1.5) {
  7562. niceFraction = 1;
  7563. } else if (fraction < 3) {
  7564. niceFraction = 2;
  7565. } else if (fraction < 7) {
  7566. niceFraction = 5;
  7567. } else {
  7568. niceFraction = 10;
  7569. }
  7570. } else {
  7571. if (fraction <= 1.0) {
  7572. niceFraction = 1;
  7573. } else if (fraction <= 2) {
  7574. niceFraction = 2;
  7575. } else if (fraction <= 5) {
  7576. niceFraction = 5;
  7577. } else {
  7578. niceFraction = 10;
  7579. }
  7580. }
  7581. return niceFraction * Math.pow(10, exponent);
  7582. };
  7583. //Easing functions adapted from Robert Penner's easing equations
  7584. //http://www.robertpenner.com/easing/
  7585. var easingEffects = helpers.easingEffects = {
  7586. linear: function(t) {
  7587. return t;
  7588. },
  7589. easeInQuad: function(t) {
  7590. return t * t;
  7591. },
  7592. easeOutQuad: function(t) {
  7593. return -1 * t * (t - 2);
  7594. },
  7595. easeInOutQuad: function(t) {
  7596. if ((t /= 1 / 2) < 1) {
  7597. return 1 / 2 * t * t;
  7598. }
  7599. return -1 / 2 * ((--t) * (t - 2) - 1);
  7600. },
  7601. easeInCubic: function(t) {
  7602. return t * t * t;
  7603. },
  7604. easeOutCubic: function(t) {
  7605. return 1 * ((t = t / 1 - 1) * t * t + 1);
  7606. },
  7607. easeInOutCubic: function(t) {
  7608. if ((t /= 1 / 2) < 1) {
  7609. return 1 / 2 * t * t * t;
  7610. }
  7611. return 1 / 2 * ((t -= 2) * t * t + 2);
  7612. },
  7613. easeInQuart: function(t) {
  7614. return t * t * t * t;
  7615. },
  7616. easeOutQuart: function(t) {
  7617. return -1 * ((t = t / 1 - 1) * t * t * t - 1);
  7618. },
  7619. easeInOutQuart: function(t) {
  7620. if ((t /= 1 / 2) < 1) {
  7621. return 1 / 2 * t * t * t * t;
  7622. }
  7623. return -1 / 2 * ((t -= 2) * t * t * t - 2);
  7624. },
  7625. easeInQuint: function(t) {
  7626. return 1 * (t /= 1) * t * t * t * t;
  7627. },
  7628. easeOutQuint: function(t) {
  7629. return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
  7630. },
  7631. easeInOutQuint: function(t) {
  7632. if ((t /= 1 / 2) < 1) {
  7633. return 1 / 2 * t * t * t * t * t;
  7634. }
  7635. return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
  7636. },
  7637. easeInSine: function(t) {
  7638. return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
  7639. },
  7640. easeOutSine: function(t) {
  7641. return 1 * Math.sin(t / 1 * (Math.PI / 2));
  7642. },
  7643. easeInOutSine: function(t) {
  7644. return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
  7645. },
  7646. easeInExpo: function(t) {
  7647. return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
  7648. },
  7649. easeOutExpo: function(t) {
  7650. return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
  7651. },
  7652. easeInOutExpo: function(t) {
  7653. if (t === 0) {
  7654. return 0;
  7655. }
  7656. if (t === 1) {
  7657. return 1;
  7658. }
  7659. if ((t /= 1 / 2) < 1) {
  7660. return 1 / 2 * Math.pow(2, 10 * (t - 1));
  7661. }
  7662. return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
  7663. },
  7664. easeInCirc: function(t) {
  7665. if (t >= 1) {
  7666. return t;
  7667. }
  7668. return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
  7669. },
  7670. easeOutCirc: function(t) {
  7671. return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
  7672. },
  7673. easeInOutCirc: function(t) {
  7674. if ((t /= 1 / 2) < 1) {
  7675. return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
  7676. }
  7677. return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
  7678. },
  7679. easeInElastic: function(t) {
  7680. var s = 1.70158;
  7681. var p = 0;
  7682. var a = 1;
  7683. if (t === 0) {
  7684. return 0;
  7685. }
  7686. if ((t /= 1) === 1) {
  7687. return 1;
  7688. }
  7689. if (!p) {
  7690. p = 1 * 0.3;
  7691. }
  7692. if (a < Math.abs(1)) {
  7693. a = 1;
  7694. s = p / 4;
  7695. } else {
  7696. s = p / (2 * Math.PI) * Math.asin(1 / a);
  7697. }
  7698. return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
  7699. },
  7700. easeOutElastic: function(t) {
  7701. var s = 1.70158;
  7702. var p = 0;
  7703. var a = 1;
  7704. if (t === 0) {
  7705. return 0;
  7706. }
  7707. if ((t /= 1) === 1) {
  7708. return 1;
  7709. }
  7710. if (!p) {
  7711. p = 1 * 0.3;
  7712. }
  7713. if (a < Math.abs(1)) {
  7714. a = 1;
  7715. s = p / 4;
  7716. } else {
  7717. s = p / (2 * Math.PI) * Math.asin(1 / a);
  7718. }
  7719. return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
  7720. },
  7721. easeInOutElastic: function(t) {
  7722. var s = 1.70158;
  7723. var p = 0;
  7724. var a = 1;
  7725. if (t === 0) {
  7726. return 0;
  7727. }
  7728. if ((t /= 1 / 2) === 2) {
  7729. return 1;
  7730. }
  7731. if (!p) {
  7732. p = 1 * (0.3 * 1.5);
  7733. }
  7734. if (a < Math.abs(1)) {
  7735. a = 1;
  7736. s = p / 4;
  7737. } else {
  7738. s = p / (2 * Math.PI) * Math.asin(1 / a);
  7739. }
  7740. if (t < 1) {
  7741. return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
  7742. }
  7743. return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
  7744. },
  7745. easeInBack: function(t) {
  7746. var s = 1.70158;
  7747. return 1 * (t /= 1) * t * ((s + 1) * t - s);
  7748. },
  7749. easeOutBack: function(t) {
  7750. var s = 1.70158;
  7751. return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
  7752. },
  7753. easeInOutBack: function(t) {
  7754. var s = 1.70158;
  7755. if ((t /= 1 / 2) < 1) {
  7756. return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
  7757. }
  7758. return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
  7759. },
  7760. easeInBounce: function(t) {
  7761. return 1 - easingEffects.easeOutBounce(1 - t);
  7762. },
  7763. easeOutBounce: function(t) {
  7764. if ((t /= 1) < (1 / 2.75)) {
  7765. return 1 * (7.5625 * t * t);
  7766. } else if (t < (2 / 2.75)) {
  7767. return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
  7768. } else if (t < (2.5 / 2.75)) {
  7769. return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
  7770. } else {
  7771. return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
  7772. }
  7773. },
  7774. easeInOutBounce: function(t) {
  7775. if (t < 1 / 2) {
  7776. return easingEffects.easeInBounce(t * 2) * 0.5;
  7777. }
  7778. return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
  7779. }
  7780. };
  7781. //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
  7782. helpers.requestAnimFrame = (function() {
  7783. return window.requestAnimationFrame ||
  7784. window.webkitRequestAnimationFrame ||
  7785. window.mozRequestAnimationFrame ||
  7786. window.oRequestAnimationFrame ||
  7787. window.msRequestAnimationFrame ||
  7788. function(callback) {
  7789. return window.setTimeout(callback, 1000 / 60);
  7790. };
  7791. })();
  7792. helpers.cancelAnimFrame = (function() {
  7793. return window.cancelAnimationFrame ||
  7794. window.webkitCancelAnimationFrame ||
  7795. window.mozCancelAnimationFrame ||
  7796. window.oCancelAnimationFrame ||
  7797. window.msCancelAnimationFrame ||
  7798. function(callback) {
  7799. return window.clearTimeout(callback, 1000 / 60);
  7800. };
  7801. })();
  7802. //-- DOM methods
  7803. helpers.getRelativePosition = function(evt, chart) {
  7804. var mouseX, mouseY;
  7805. var e = evt.originalEvent || evt,
  7806. canvas = evt.currentTarget || evt.srcElement,
  7807. boundingRect = canvas.getBoundingClientRect();
  7808. var touches = e.touches;
  7809. if (touches && touches.length > 0) {
  7810. mouseX = touches[0].clientX;
  7811. mouseY = touches[0].clientY;
  7812. } else {
  7813. mouseX = e.clientX;
  7814. mouseY = e.clientY;
  7815. }
  7816. // Scale mouse coordinates into canvas coordinates
  7817. // by following the pattern laid out by 'jerryj' in the comments of
  7818. // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
  7819. var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
  7820. var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
  7821. var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
  7822. var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
  7823. var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
  7824. var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
  7825. // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
  7826. // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
  7827. mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
  7828. mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
  7829. return {
  7830. x: mouseX,
  7831. y: mouseY
  7832. };
  7833. };
  7834. helpers.addEvent = function(node, eventType, method) {
  7835. if (node.addEventListener) {
  7836. node.addEventListener(eventType, method);
  7837. } else if (node.attachEvent) {
  7838. node.attachEvent("on" + eventType, method);
  7839. } else {
  7840. node["on" + eventType] = method;
  7841. }
  7842. };
  7843. helpers.removeEvent = function(node, eventType, handler) {
  7844. if (node.removeEventListener) {
  7845. node.removeEventListener(eventType, handler, false);
  7846. } else if (node.detachEvent) {
  7847. node.detachEvent("on" + eventType, handler);
  7848. } else {
  7849. node["on" + eventType] = helpers.noop;
  7850. }
  7851. };
  7852. helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) {
  7853. // Create the events object if it's not already present
  7854. var events = chartInstance.events = chartInstance.events || {};
  7855. helpers.each(arrayOfEvents, function(eventName) {
  7856. events[eventName] = function() {
  7857. handler.apply(chartInstance, arguments);
  7858. };
  7859. helpers.addEvent(chartInstance.chart.canvas, eventName, events[eventName]);
  7860. });
  7861. };
  7862. helpers.unbindEvents = function(chartInstance, arrayOfEvents) {
  7863. var canvas = chartInstance.chart.canvas;
  7864. helpers.each(arrayOfEvents, function(handler, eventName) {
  7865. helpers.removeEvent(canvas, eventName, handler);
  7866. });
  7867. };
  7868. // Private helper function to convert max-width/max-height values that may be percentages into a number
  7869. function parseMaxStyle(styleValue, node, parentProperty) {
  7870. var valueInPixels;
  7871. if (typeof(styleValue) === 'string') {
  7872. valueInPixels = parseInt(styleValue, 10);
  7873. if (styleValue.indexOf('%') != -1) {
  7874. // percentage * size in dimension
  7875. valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
  7876. }
  7877. } else {
  7878. valueInPixels = styleValue;
  7879. }
  7880. return valueInPixels;
  7881. }
  7882. /**
  7883. * Returns if the given value contains an effective constraint.
  7884. * @private
  7885. */
  7886. function isConstrainedValue(value) {
  7887. return value !== undefined && value !== null && value !== 'none';
  7888. }
  7889. // Private helper to get a constraint dimension
  7890. // @param domNode : the node to check the constraint on
  7891. // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
  7892. // @param percentageProperty : property of parent to use when calculating width as a percentage
  7893. // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
  7894. function getConstraintDimension(domNode, maxStyle, percentageProperty) {
  7895. var view = document.defaultView;
  7896. var parentNode = domNode.parentNode;
  7897. var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
  7898. var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
  7899. var hasCNode = isConstrainedValue(constrainedNode);
  7900. var hasCContainer = isConstrainedValue(constrainedContainer);
  7901. var infinity = Number.POSITIVE_INFINITY;
  7902. if (hasCNode || hasCContainer) {
  7903. return Math.min(
  7904. hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
  7905. hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
  7906. }
  7907. return 'none';
  7908. }
  7909. // returns Number or undefined if no constraint
  7910. helpers.getConstraintWidth = function(domNode) {
  7911. return getConstraintDimension(domNode, 'max-width', 'clientWidth');
  7912. };
  7913. // returns Number or undefined if no constraint
  7914. helpers.getConstraintHeight = function(domNode) {
  7915. return getConstraintDimension(domNode, 'max-height', 'clientHeight');
  7916. };
  7917. helpers.getMaximumWidth = function(domNode) {
  7918. var container = domNode.parentNode;
  7919. var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right'));
  7920. var w = container.clientWidth - padding;
  7921. var cw = helpers.getConstraintWidth(domNode);
  7922. return isNaN(cw)? w : Math.min(w, cw);
  7923. };
  7924. helpers.getMaximumHeight = function(domNode) {
  7925. var container = domNode.parentNode;
  7926. var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom'));
  7927. var h = container.clientHeight - padding;
  7928. var ch = helpers.getConstraintHeight(domNode);
  7929. return isNaN(ch)? h : Math.min(h, ch);
  7930. };
  7931. helpers.getStyle = function(el, property) {
  7932. return el.currentStyle ?
  7933. el.currentStyle[property] :
  7934. document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
  7935. };
  7936. helpers.retinaScale = function(chart) {
  7937. var ctx = chart.ctx;
  7938. var canvas = chart.canvas;
  7939. var width = canvas.width;
  7940. var height = canvas.height;
  7941. var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
  7942. if (pixelRatio !== 1) {
  7943. canvas.height = height * pixelRatio;
  7944. canvas.width = width * pixelRatio;
  7945. ctx.scale(pixelRatio, pixelRatio);
  7946. // Store the device pixel ratio so that we can go backwards in `destroy`.
  7947. // The devicePixelRatio changes with zoom, so there are no guarantees that it is the same
  7948. // when destroy is called
  7949. chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio;
  7950. }
  7951. canvas.style.width = width + 'px';
  7952. canvas.style.height = height + 'px';
  7953. };
  7954. //-- Canvas methods
  7955. helpers.clear = function(chart) {
  7956. chart.ctx.clearRect(0, 0, chart.width, chart.height);
  7957. };
  7958. helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
  7959. return fontStyle + " " + pixelSize + "px " + fontFamily;
  7960. };
  7961. helpers.longestText = function(ctx, font, arrayOfThings, cache) {
  7962. cache = cache || {};
  7963. var data = cache.data = cache.data || {};
  7964. var gc = cache.garbageCollect = cache.garbageCollect || [];
  7965. if (cache.font !== font) {
  7966. data = cache.data = {};
  7967. gc = cache.garbageCollect = [];
  7968. cache.font = font;
  7969. }
  7970. ctx.font = font;
  7971. var longest = 0;
  7972. helpers.each(arrayOfThings, function(thing) {
  7973. // Undefined strings and arrays should not be measured
  7974. if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
  7975. longest = helpers.measureText(ctx, data, gc, longest, thing);
  7976. } else if (helpers.isArray(thing)) {
  7977. // if it is an array lets measure each element
  7978. // to do maybe simplify this function a bit so we can do this more recursively?
  7979. helpers.each(thing, function(nestedThing) {
  7980. // Undefined strings and arrays should not be measured
  7981. if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
  7982. longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
  7983. }
  7984. });
  7985. }
  7986. });
  7987. var gcLen = gc.length / 2;
  7988. if (gcLen > arrayOfThings.length) {
  7989. for (var i = 0; i < gcLen; i++) {
  7990. delete data[gc[i]];
  7991. }
  7992. gc.splice(0, gcLen);
  7993. }
  7994. return longest;
  7995. };
  7996. helpers.measureText = function (ctx, data, gc, longest, string) {
  7997. var textWidth = data[string];
  7998. if (!textWidth) {
  7999. textWidth = data[string] = ctx.measureText(string).width;
  8000. gc.push(string);
  8001. }
  8002. if (textWidth > longest) {
  8003. longest = textWidth;
  8004. }
  8005. return longest;
  8006. };
  8007. helpers.numberOfLabelLines = function(arrayOfThings) {
  8008. var numberOfLines = 1;
  8009. helpers.each(arrayOfThings, function(thing) {
  8010. if (helpers.isArray(thing)) {
  8011. if (thing.length > numberOfLines) {
  8012. numberOfLines = thing.length;
  8013. }
  8014. }
  8015. });
  8016. return numberOfLines;
  8017. };
  8018. helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
  8019. ctx.beginPath();
  8020. ctx.moveTo(x + radius, y);
  8021. ctx.lineTo(x + width - radius, y);
  8022. ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  8023. ctx.lineTo(x + width, y + height - radius);
  8024. ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  8025. ctx.lineTo(x + radius, y + height);
  8026. ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  8027. ctx.lineTo(x, y + radius);
  8028. ctx.quadraticCurveTo(x, y, x + radius, y);
  8029. ctx.closePath();
  8030. };
  8031. helpers.color = function(c) {
  8032. if (!color) {
  8033. console.log('Color.js not found!');
  8034. return c;
  8035. }
  8036. /* global CanvasGradient */
  8037. if (c instanceof CanvasGradient) {
  8038. return color(Chart.defaults.global.defaultColor);
  8039. }
  8040. return color(c);
  8041. };
  8042. helpers.addResizeListener = function(node, callback) {
  8043. // Hide an iframe before the node
  8044. var hiddenIframe = document.createElement('iframe');
  8045. var hiddenIframeClass = 'chartjs-hidden-iframe';
  8046. if (hiddenIframe.classlist) {
  8047. // can use classlist
  8048. hiddenIframe.classlist.add(hiddenIframeClass);
  8049. } else {
  8050. hiddenIframe.setAttribute('class', hiddenIframeClass);
  8051. }
  8052. // Set the style
  8053. var style = hiddenIframe.style;
  8054. style.width = '100%';
  8055. style.display = 'block';
  8056. style.border = 0;
  8057. style.height = 0;
  8058. style.margin = 0;
  8059. style.position = 'absolute';
  8060. style.left = 0;
  8061. style.right = 0;
  8062. style.top = 0;
  8063. style.bottom = 0;
  8064. // Insert the iframe so that contentWindow is available
  8065. node.insertBefore(hiddenIframe, node.firstChild);
  8066. (hiddenIframe.contentWindow || hiddenIframe).onresize = function() {
  8067. if (callback) {
  8068. callback();
  8069. }
  8070. };
  8071. };
  8072. helpers.removeResizeListener = function(node) {
  8073. var hiddenIframe = node.querySelector('.chartjs-hidden-iframe');
  8074. // Remove the resize detect iframe
  8075. if (hiddenIframe) {
  8076. hiddenIframe.parentNode.removeChild(hiddenIframe);
  8077. }
  8078. };
  8079. helpers.isArray = Array.isArray?
  8080. function(obj) { return Array.isArray(obj); } :
  8081. function(obj) {
  8082. return Object.prototype.toString.call(obj) === '[object Array]';
  8083. };
  8084. //! @see http://stackoverflow.com/a/14853974
  8085. helpers.arrayEquals = function(a0, a1) {
  8086. var i, ilen, v0, v1;
  8087. if (!a0 || !a1 || a0.length != a1.length) {
  8088. return false;
  8089. }
  8090. for (i = 0, ilen=a0.length; i < ilen; ++i) {
  8091. v0 = a0[i];
  8092. v1 = a1[i];
  8093. if (v0 instanceof Array && v1 instanceof Array) {
  8094. if (!helpers.arrayEquals(v0, v1)) {
  8095. return false;
  8096. }
  8097. } else if (v0 != v1) {
  8098. // NOTE: two different object instances will never be equal: {x:20} != {x:20}
  8099. return false;
  8100. }
  8101. }
  8102. return true;
  8103. };
  8104. helpers.callCallback = function(fn, args, _tArg) {
  8105. if (fn && typeof fn.call === 'function') {
  8106. fn.apply(_tArg, args);
  8107. }
  8108. };
  8109. helpers.getHoverColor = function(color) {
  8110. /* global CanvasPattern */
  8111. return (color instanceof CanvasPattern) ?
  8112. color :
  8113. helpers.color(color).saturate(0.5).darken(0.1).rgbString();
  8114. };
  8115. };
  8116. },{"2":2}],26:[function(require,module,exports){
  8117. "use strict";
  8118. module.exports = function() {
  8119. //Occupy the global variable of Chart, and create a simple base class
  8120. var Chart = function(context, config) {
  8121. var me = this;
  8122. var helpers = Chart.helpers;
  8123. me.config = config;
  8124. // Support a jQuery'd canvas element
  8125. if (context.length && context[0].getContext) {
  8126. context = context[0];
  8127. }
  8128. // Support a canvas domnode
  8129. if (context.getContext) {
  8130. context = context.getContext("2d");
  8131. }
  8132. me.ctx = context;
  8133. me.canvas = context.canvas;
  8134. context.canvas.style.display = context.canvas.style.display || 'block';
  8135. // Figure out what the size of the chart will be.
  8136. // If the canvas has a specified width and height, we use those else
  8137. // we look to see if the canvas node has a CSS width and height.
  8138. // If there is still no height, fill the parent container
  8139. me.width = context.canvas.width || parseInt(helpers.getStyle(context.canvas, 'width'), 10) || helpers.getMaximumWidth(context.canvas);
  8140. me.height = context.canvas.height || parseInt(helpers.getStyle(context.canvas, 'height'), 10) || helpers.getMaximumHeight(context.canvas);
  8141. me.aspectRatio = me.width / me.height;
  8142. if (isNaN(me.aspectRatio) || isFinite(me.aspectRatio) === false) {
  8143. // If the canvas has no size, try and figure out what the aspect ratio will be.
  8144. // Some charts prefer square canvases (pie, radar, etc). If that is specified, use that
  8145. // else use the canvas default ratio of 2
  8146. me.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2;
  8147. }
  8148. // Store the original style of the element so we can set it back
  8149. me.originalCanvasStyleWidth = context.canvas.style.width;
  8150. me.originalCanvasStyleHeight = context.canvas.style.height;
  8151. // High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
  8152. helpers.retinaScale(me);
  8153. if (config) {
  8154. me.controller = new Chart.Controller(me);
  8155. }
  8156. // Always bind this so that if the responsive state changes we still work
  8157. helpers.addResizeListener(context.canvas.parentNode, function() {
  8158. if (me.controller && me.controller.config.options.responsive) {
  8159. me.controller.resize();
  8160. }
  8161. });
  8162. return me.controller ? me.controller : me;
  8163. };
  8164. //Globally expose the defaults to allow for user updating/changing
  8165. Chart.defaults = {
  8166. global: {
  8167. responsive: true,
  8168. responsiveAnimationDuration: 0,
  8169. maintainAspectRatio: true,
  8170. events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"],
  8171. hover: {
  8172. onHover: null,
  8173. mode: 'single',
  8174. animationDuration: 400
  8175. },
  8176. onClick: null,
  8177. defaultColor: 'rgba(0,0,0,0.1)',
  8178. defaultFontColor: '#666',
  8179. defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
  8180. defaultFontSize: 12,
  8181. defaultFontStyle: 'normal',
  8182. showLines: true,
  8183. // Element defaults defined in element extensions
  8184. elements: {},
  8185. // Legend callback string
  8186. legendCallback: function(chart) {
  8187. var text = [];
  8188. text.push('<ul class="' + chart.id + '-legend">');
  8189. for (var i = 0; i < chart.data.datasets.length; i++) {
  8190. text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
  8191. if (chart.data.datasets[i].label) {
  8192. text.push(chart.data.datasets[i].label);
  8193. }
  8194. text.push('</li>');
  8195. }
  8196. text.push('</ul>');
  8197. return text.join("");
  8198. }
  8199. }
  8200. };
  8201. Chart.Chart = Chart;
  8202. return Chart;
  8203. };
  8204. },{}],27:[function(require,module,exports){
  8205. "use strict";
  8206. module.exports = function(Chart) {
  8207. var helpers = Chart.helpers;
  8208. // The layout service is very self explanatory. It's responsible for the layout within a chart.
  8209. // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
  8210. // It is this service's responsibility of carrying out that layout.
  8211. Chart.layoutService = {
  8212. defaults: {},
  8213. // Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
  8214. addBox: function(chartInstance, box) {
  8215. if (!chartInstance.boxes) {
  8216. chartInstance.boxes = [];
  8217. }
  8218. chartInstance.boxes.push(box);
  8219. },
  8220. removeBox: function(chartInstance, box) {
  8221. if (!chartInstance.boxes) {
  8222. return;
  8223. }
  8224. chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1);
  8225. },
  8226. // The most important function
  8227. update: function(chartInstance, width, height) {
  8228. if (!chartInstance) {
  8229. return;
  8230. }
  8231. var xPadding = 0;
  8232. var yPadding = 0;
  8233. var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
  8234. return box.options.position === "left";
  8235. });
  8236. var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
  8237. return box.options.position === "right";
  8238. });
  8239. var topBoxes = helpers.where(chartInstance.boxes, function(box) {
  8240. return box.options.position === "top";
  8241. });
  8242. var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
  8243. return box.options.position === "bottom";
  8244. });
  8245. // Boxes that overlay the chartarea such as the radialLinear scale
  8246. var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
  8247. return box.options.position === "chartArea";
  8248. });
  8249. // Ensure that full width boxes are at the very top / bottom
  8250. topBoxes.sort(function(a, b) {
  8251. return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
  8252. });
  8253. bottomBoxes.sort(function(a, b) {
  8254. return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0);
  8255. });
  8256. // Essentially we now have any number of boxes on each of the 4 sides.
  8257. // Our canvas looks like the following.
  8258. // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
  8259. // B1 is the bottom axis
  8260. // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
  8261. // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
  8262. // an error will be thrown.
  8263. //
  8264. // |----------------------------------------------------|
  8265. // | T1 (Full Width) |
  8266. // |----------------------------------------------------|
  8267. // | | | T2 | |
  8268. // | |----|-------------------------------------|----|
  8269. // | | | C1 | | C2 | |
  8270. // | | |----| |----| |
  8271. // | | | | |
  8272. // | L1 | L2 | ChartArea (C0) | R1 |
  8273. // | | | | |
  8274. // | | |----| |----| |
  8275. // | | | C3 | | C4 | |
  8276. // | |----|-------------------------------------|----|
  8277. // | | | B1 | |
  8278. // |----------------------------------------------------|
  8279. // | B2 (Full Width) |
  8280. // |----------------------------------------------------|
  8281. //
  8282. // What we do to find the best sizing, we do the following
  8283. // 1. Determine the minimum size of the chart area.
  8284. // 2. Split the remaining width equally between each vertical axis
  8285. // 3. Split the remaining height equally between each horizontal axis
  8286. // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
  8287. // 5. Adjust the sizes of each axis based on it's minimum reported size.
  8288. // 6. Refit each axis
  8289. // 7. Position each axis in the final location
  8290. // 8. Tell the chart the final location of the chart area
  8291. // 9. Tell any axes that overlay the chart area the positions of the chart area
  8292. // Step 1
  8293. var chartWidth = width - (2 * xPadding);
  8294. var chartHeight = height - (2 * yPadding);
  8295. var chartAreaWidth = chartWidth / 2; // min 50%
  8296. var chartAreaHeight = chartHeight / 2; // min 50%
  8297. // Step 2
  8298. var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
  8299. // Step 3
  8300. var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
  8301. // Step 4
  8302. var maxChartAreaWidth = chartWidth;
  8303. var maxChartAreaHeight = chartHeight;
  8304. var minBoxSizes = [];
  8305. helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
  8306. function getMinimumBoxSize(box) {
  8307. var minSize;
  8308. var isHorizontal = box.isHorizontal();
  8309. if (isHorizontal) {
  8310. minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
  8311. maxChartAreaHeight -= minSize.height;
  8312. } else {
  8313. minSize = box.update(verticalBoxWidth, chartAreaHeight);
  8314. maxChartAreaWidth -= minSize.width;
  8315. }
  8316. minBoxSizes.push({
  8317. horizontal: isHorizontal,
  8318. minSize: minSize,
  8319. box: box
  8320. });
  8321. }
  8322. // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
  8323. // be if the axes are drawn at their minimum sizes.
  8324. // Steps 5 & 6
  8325. var totalLeftBoxesWidth = xPadding;
  8326. var totalRightBoxesWidth = xPadding;
  8327. var totalTopBoxesHeight = yPadding;
  8328. var totalBottomBoxesHeight = yPadding;
  8329. // Update, and calculate the left and right margins for the horizontal boxes
  8330. helpers.each(leftBoxes.concat(rightBoxes), fitBox);
  8331. helpers.each(leftBoxes, function(box) {
  8332. totalLeftBoxesWidth += box.width;
  8333. });
  8334. helpers.each(rightBoxes, function(box) {
  8335. totalRightBoxesWidth += box.width;
  8336. });
  8337. // Set the Left and Right margins for the horizontal boxes
  8338. helpers.each(topBoxes.concat(bottomBoxes), fitBox);
  8339. // Function to fit a box
  8340. function fitBox(box) {
  8341. var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
  8342. return minBoxSize.box === box;
  8343. });
  8344. if (minBoxSize) {
  8345. if (box.isHorizontal()) {
  8346. var scaleMargin = {
  8347. left: totalLeftBoxesWidth,
  8348. right: totalRightBoxesWidth,
  8349. top: 0,
  8350. bottom: 0
  8351. };
  8352. // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
  8353. // on the margin. Sometimes they need to increase in size slightly
  8354. box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
  8355. } else {
  8356. box.update(minBoxSize.minSize.width, maxChartAreaHeight);
  8357. }
  8358. }
  8359. }
  8360. // Figure out how much margin is on the top and bottom of the vertical boxes
  8361. helpers.each(topBoxes, function(box) {
  8362. totalTopBoxesHeight += box.height;
  8363. });
  8364. helpers.each(bottomBoxes, function(box) {
  8365. totalBottomBoxesHeight += box.height;
  8366. });
  8367. // Let the left layout know the final margin
  8368. helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
  8369. function finalFitVerticalBox(box) {
  8370. var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
  8371. return minBoxSize.box === box;
  8372. });
  8373. var scaleMargin = {
  8374. left: 0,
  8375. right: 0,
  8376. top: totalTopBoxesHeight,
  8377. bottom: totalBottomBoxesHeight
  8378. };
  8379. if (minBoxSize) {
  8380. box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
  8381. }
  8382. }
  8383. // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
  8384. totalLeftBoxesWidth = xPadding;
  8385. totalRightBoxesWidth = xPadding;
  8386. totalTopBoxesHeight = yPadding;
  8387. totalBottomBoxesHeight = yPadding;
  8388. helpers.each(leftBoxes, function(box) {
  8389. totalLeftBoxesWidth += box.width;
  8390. });
  8391. helpers.each(rightBoxes, function(box) {
  8392. totalRightBoxesWidth += box.width;
  8393. });
  8394. helpers.each(topBoxes, function(box) {
  8395. totalTopBoxesHeight += box.height;
  8396. });
  8397. helpers.each(bottomBoxes, function(box) {
  8398. totalBottomBoxesHeight += box.height;
  8399. });
  8400. // Figure out if our chart area changed. This would occur if the dataset layout label rotation
  8401. // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
  8402. // without calling `fit` again
  8403. var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
  8404. var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
  8405. if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
  8406. helpers.each(leftBoxes, function(box) {
  8407. box.height = newMaxChartAreaHeight;
  8408. });
  8409. helpers.each(rightBoxes, function(box) {
  8410. box.height = newMaxChartAreaHeight;
  8411. });
  8412. helpers.each(topBoxes, function(box) {
  8413. if (!box.options.fullWidth) {
  8414. box.width = newMaxChartAreaWidth;
  8415. }
  8416. });
  8417. helpers.each(bottomBoxes, function(box) {
  8418. if (!box.options.fullWidth) {
  8419. box.width = newMaxChartAreaWidth;
  8420. }
  8421. });
  8422. maxChartAreaHeight = newMaxChartAreaHeight;
  8423. maxChartAreaWidth = newMaxChartAreaWidth;
  8424. }
  8425. // Step 7 - Position the boxes
  8426. var left = xPadding;
  8427. var top = yPadding;
  8428. var right = 0;
  8429. var bottom = 0;
  8430. helpers.each(leftBoxes.concat(topBoxes), placeBox);
  8431. // Account for chart width and height
  8432. left += maxChartAreaWidth;
  8433. top += maxChartAreaHeight;
  8434. helpers.each(rightBoxes, placeBox);
  8435. helpers.each(bottomBoxes, placeBox);
  8436. function placeBox(box) {
  8437. if (box.isHorizontal()) {
  8438. box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth;
  8439. box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth;
  8440. box.top = top;
  8441. box.bottom = top + box.height;
  8442. // Move to next point
  8443. top = box.bottom;
  8444. } else {
  8445. box.left = left;
  8446. box.right = left + box.width;
  8447. box.top = totalTopBoxesHeight;
  8448. box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
  8449. // Move to next point
  8450. left = box.right;
  8451. }
  8452. }
  8453. // Step 8
  8454. chartInstance.chartArea = {
  8455. left: totalLeftBoxesWidth,
  8456. top: totalTopBoxesHeight,
  8457. right: totalLeftBoxesWidth + maxChartAreaWidth,
  8458. bottom: totalTopBoxesHeight + maxChartAreaHeight
  8459. };
  8460. // Step 9
  8461. helpers.each(chartAreaBoxes, function(box) {
  8462. box.left = chartInstance.chartArea.left;
  8463. box.top = chartInstance.chartArea.top;
  8464. box.right = chartInstance.chartArea.right;
  8465. box.bottom = chartInstance.chartArea.bottom;
  8466. box.update(maxChartAreaWidth, maxChartAreaHeight);
  8467. });
  8468. }
  8469. };
  8470. };
  8471. },{}],28:[function(require,module,exports){
  8472. "use strict";
  8473. module.exports = function(Chart) {
  8474. var helpers = Chart.helpers;
  8475. var noop = helpers.noop;
  8476. Chart.defaults.global.legend = {
  8477. display: true,
  8478. position: 'top',
  8479. fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
  8480. reverse: false,
  8481. // a callback that will handle
  8482. onClick: function(e, legendItem) {
  8483. var index = legendItem.datasetIndex;
  8484. var ci = this.chart;
  8485. var meta = ci.getDatasetMeta(index);
  8486. // See controller.isDatasetVisible comment
  8487. meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
  8488. // We hid a dataset ... rerender the chart
  8489. ci.update();
  8490. },
  8491. labels: {
  8492. boxWidth: 40,
  8493. padding: 10,
  8494. // Generates labels shown in the legend
  8495. // Valid properties to return:
  8496. // text : text to display
  8497. // fillStyle : fill of coloured box
  8498. // strokeStyle: stroke of coloured box
  8499. // hidden : if this legend item refers to a hidden item
  8500. // lineCap : cap style for line
  8501. // lineDash
  8502. // lineDashOffset :
  8503. // lineJoin :
  8504. // lineWidth :
  8505. generateLabels: function(chart) {
  8506. var data = chart.data;
  8507. return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
  8508. return {
  8509. text: dataset.label,
  8510. fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
  8511. hidden: !chart.isDatasetVisible(i),
  8512. lineCap: dataset.borderCapStyle,
  8513. lineDash: dataset.borderDash,
  8514. lineDashOffset: dataset.borderDashOffset,
  8515. lineJoin: dataset.borderJoinStyle,
  8516. lineWidth: dataset.borderWidth,
  8517. strokeStyle: dataset.borderColor,
  8518. // Below is extra data used for toggling the datasets
  8519. datasetIndex: i
  8520. };
  8521. }, this) : [];
  8522. }
  8523. }
  8524. };
  8525. Chart.Legend = Chart.Element.extend({
  8526. initialize: function(config) {
  8527. helpers.extend(this, config);
  8528. // Contains hit boxes for each dataset (in dataset order)
  8529. this.legendHitBoxes = [];
  8530. // Are we in doughnut mode which has a different data type
  8531. this.doughnutMode = false;
  8532. },
  8533. // These methods are ordered by lifecyle. Utilities then follow.
  8534. // Any function defined here is inherited by all legend types.
  8535. // Any function can be extended by the legend type
  8536. beforeUpdate: noop,
  8537. update: function(maxWidth, maxHeight, margins) {
  8538. var me = this;
  8539. // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
  8540. me.beforeUpdate();
  8541. // Absorb the master measurements
  8542. me.maxWidth = maxWidth;
  8543. me.maxHeight = maxHeight;
  8544. me.margins = margins;
  8545. // Dimensions
  8546. me.beforeSetDimensions();
  8547. me.setDimensions();
  8548. me.afterSetDimensions();
  8549. // Labels
  8550. me.beforeBuildLabels();
  8551. me.buildLabels();
  8552. me.afterBuildLabels();
  8553. // Fit
  8554. me.beforeFit();
  8555. me.fit();
  8556. me.afterFit();
  8557. //
  8558. me.afterUpdate();
  8559. return me.minSize;
  8560. },
  8561. afterUpdate: noop,
  8562. //
  8563. beforeSetDimensions: noop,
  8564. setDimensions: function() {
  8565. var me = this;
  8566. // Set the unconstrained dimension before label rotation
  8567. if (me.isHorizontal()) {
  8568. // Reset position before calculating rotation
  8569. me.width = me.maxWidth;
  8570. me.left = 0;
  8571. me.right = me.width;
  8572. } else {
  8573. me.height = me.maxHeight;
  8574. // Reset position before calculating rotation
  8575. me.top = 0;
  8576. me.bottom = me.height;
  8577. }
  8578. // Reset padding
  8579. me.paddingLeft = 0;
  8580. me.paddingTop = 0;
  8581. me.paddingRight = 0;
  8582. me.paddingBottom = 0;
  8583. // Reset minSize
  8584. me.minSize = {
  8585. width: 0,
  8586. height: 0
  8587. };
  8588. },
  8589. afterSetDimensions: noop,
  8590. //
  8591. beforeBuildLabels: noop,
  8592. buildLabels: function() {
  8593. var me = this;
  8594. me.legendItems = me.options.labels.generateLabels.call(me, me.chart);
  8595. if(me.options.reverse){
  8596. me.legendItems.reverse();
  8597. }
  8598. },
  8599. afterBuildLabels: noop,
  8600. //
  8601. beforeFit: noop,
  8602. fit: function() {
  8603. var me = this;
  8604. var opts = me.options;
  8605. var labelOpts = opts.labels;
  8606. var display = opts.display;
  8607. var ctx = me.ctx;
  8608. var globalDefault = Chart.defaults.global,
  8609. itemOrDefault = helpers.getValueOrDefault,
  8610. fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
  8611. fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
  8612. fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
  8613. labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
  8614. // Reset hit boxes
  8615. var hitboxes = me.legendHitBoxes = [];
  8616. var minSize = me.minSize;
  8617. var isHorizontal = me.isHorizontal();
  8618. if (isHorizontal) {
  8619. minSize.width = me.maxWidth; // fill all the width
  8620. minSize.height = display ? 10 : 0;
  8621. } else {
  8622. minSize.width = display ? 10 : 0;
  8623. minSize.height = me.maxHeight; // fill all the height
  8624. }
  8625. // Increase sizes here
  8626. if (display) {
  8627. ctx.font = labelFont;
  8628. if (isHorizontal) {
  8629. // Labels
  8630. // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
  8631. var lineWidths = me.lineWidths = [0];
  8632. var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
  8633. ctx.textAlign = "left";
  8634. ctx.textBaseline = 'top';
  8635. helpers.each(me.legendItems, function(legendItem, i) {
  8636. var width = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
  8637. if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
  8638. totalHeight += fontSize + (labelOpts.padding);
  8639. lineWidths[lineWidths.length] = me.left;
  8640. }
  8641. // Store the hitbox width and height here. Final position will be updated in `draw`
  8642. hitboxes[i] = {
  8643. left: 0,
  8644. top: 0,
  8645. width: width,
  8646. height: fontSize
  8647. };
  8648. lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
  8649. });
  8650. minSize.height += totalHeight;
  8651. } else {
  8652. var vPadding = labelOpts.padding;
  8653. var columnWidths = me.columnWidths = [];
  8654. var totalWidth = labelOpts.padding;
  8655. var currentColWidth = 0;
  8656. var currentColHeight = 0;
  8657. var itemHeight = fontSize + vPadding;
  8658. helpers.each(me.legendItems, function(legendItem, i) {
  8659. var itemWidth = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
  8660. // If too tall, go to new column
  8661. if (currentColHeight + itemHeight > minSize.height) {
  8662. totalWidth += currentColWidth + labelOpts.padding;
  8663. columnWidths.push(currentColWidth); // previous column width
  8664. currentColWidth = 0;
  8665. currentColHeight = 0;
  8666. }
  8667. // Get max width
  8668. currentColWidth = Math.max(currentColWidth, itemWidth);
  8669. currentColHeight += itemHeight;
  8670. // Store the hitbox width and height here. Final position will be updated in `draw`
  8671. hitboxes[i] = {
  8672. left: 0,
  8673. top: 0,
  8674. width: itemWidth,
  8675. height: fontSize
  8676. };
  8677. });
  8678. totalWidth += currentColWidth;
  8679. columnWidths.push(currentColWidth);
  8680. minSize.width += totalWidth;
  8681. }
  8682. }
  8683. me.width = minSize.width;
  8684. me.height = minSize.height;
  8685. },
  8686. afterFit: noop,
  8687. // Shared Methods
  8688. isHorizontal: function() {
  8689. return this.options.position === "top" || this.options.position === "bottom";
  8690. },
  8691. // Actualy draw the legend on the canvas
  8692. draw: function() {
  8693. var me = this;
  8694. var opts = me.options;
  8695. var labelOpts = opts.labels;
  8696. var globalDefault = Chart.defaults.global,
  8697. lineDefault = globalDefault.elements.line,
  8698. legendWidth = me.width,
  8699. legendHeight = me.height,
  8700. lineWidths = me.lineWidths;
  8701. if (opts.display) {
  8702. var ctx = me.ctx,
  8703. cursor,
  8704. itemOrDefault = helpers.getValueOrDefault,
  8705. fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor),
  8706. fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
  8707. fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
  8708. fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
  8709. labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
  8710. // Canvas setup
  8711. ctx.textAlign = "left";
  8712. ctx.textBaseline = 'top';
  8713. ctx.lineWidth = 0.5;
  8714. ctx.strokeStyle = fontColor; // for strikethrough effect
  8715. ctx.fillStyle = fontColor; // render in correct colour
  8716. ctx.font = labelFont;
  8717. var boxWidth = labelOpts.boxWidth,
  8718. hitboxes = me.legendHitBoxes;
  8719. // current position
  8720. var drawLegendBox = function(x, y, legendItem) {
  8721. // Set the ctx for the box
  8722. ctx.save();
  8723. ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
  8724. ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
  8725. ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
  8726. ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
  8727. ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
  8728. ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
  8729. if (ctx.setLineDash) {
  8730. // IE 9 and 10 do not support line dash
  8731. ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
  8732. }
  8733. // Draw the box
  8734. ctx.strokeRect(x, y, boxWidth, fontSize);
  8735. ctx.fillRect(x, y, boxWidth, fontSize);
  8736. ctx.restore();
  8737. };
  8738. var fillText = function(x, y, legendItem, textWidth) {
  8739. ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y);
  8740. if (legendItem.hidden) {
  8741. // Strikethrough the text if hidden
  8742. ctx.beginPath();
  8743. ctx.lineWidth = 2;
  8744. ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2));
  8745. ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2));
  8746. ctx.stroke();
  8747. }
  8748. };
  8749. // Horizontal
  8750. var isHorizontal = me.isHorizontal();
  8751. if (isHorizontal) {
  8752. cursor = {
  8753. x: me.left + ((legendWidth - lineWidths[0]) / 2),
  8754. y: me.top + labelOpts.padding,
  8755. line: 0
  8756. };
  8757. } else {
  8758. cursor = {
  8759. x: me.left + labelOpts.padding,
  8760. y: me.top,
  8761. line: 0
  8762. };
  8763. }
  8764. var itemHeight = fontSize + labelOpts.padding;
  8765. helpers.each(me.legendItems, function(legendItem, i) {
  8766. var textWidth = ctx.measureText(legendItem.text).width,
  8767. width = boxWidth + (fontSize / 2) + textWidth,
  8768. x = cursor.x,
  8769. y = cursor.y;
  8770. if (isHorizontal) {
  8771. if (x + width >= legendWidth) {
  8772. y = cursor.y += fontSize + (labelOpts.padding);
  8773. cursor.line++;
  8774. x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
  8775. }
  8776. } else {
  8777. if (y + itemHeight > me.bottom) {
  8778. x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
  8779. y = cursor.y = me.top;
  8780. cursor.line++;
  8781. }
  8782. }
  8783. drawLegendBox(x, y, legendItem);
  8784. hitboxes[i].left = x;
  8785. hitboxes[i].top = y;
  8786. // Fill the actual label
  8787. fillText(x, y, legendItem, textWidth);
  8788. if (isHorizontal) {
  8789. cursor.x += width + (labelOpts.padding);
  8790. } else {
  8791. cursor.y += itemHeight;
  8792. }
  8793. });
  8794. }
  8795. },
  8796. // Handle an event
  8797. handleEvent: function(e) {
  8798. var me = this;
  8799. var position = helpers.getRelativePosition(e, me.chart.chart),
  8800. x = position.x,
  8801. y = position.y,
  8802. opts = me.options;
  8803. if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
  8804. // See if we are touching one of the dataset boxes
  8805. var lh = me.legendHitBoxes;
  8806. for (var i = 0; i < lh.length; ++i) {
  8807. var hitBox = lh[i];
  8808. if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
  8809. // Touching an element
  8810. if (opts.onClick) {
  8811. opts.onClick.call(me, e, me.legendItems[i]);
  8812. }
  8813. break;
  8814. }
  8815. }
  8816. }
  8817. }
  8818. });
  8819. // Register the legend plugin
  8820. Chart.plugins.register({
  8821. beforeInit: function(chartInstance) {
  8822. var opts = chartInstance.options;
  8823. var legendOpts = opts.legend;
  8824. if (legendOpts) {
  8825. chartInstance.legend = new Chart.Legend({
  8826. ctx: chartInstance.chart.ctx,
  8827. options: legendOpts,
  8828. chart: chartInstance
  8829. });
  8830. Chart.layoutService.addBox(chartInstance, chartInstance.legend);
  8831. }
  8832. }
  8833. });
  8834. };
  8835. },{}],29:[function(require,module,exports){
  8836. "use strict";
  8837. module.exports = function(Chart) {
  8838. var noop = Chart.helpers.noop;
  8839. /**
  8840. * The plugin service singleton
  8841. * @namespace Chart.plugins
  8842. * @since 2.1.0
  8843. */
  8844. Chart.plugins = {
  8845. _plugins: [],
  8846. /**
  8847. * Registers the given plugin(s) if not already registered.
  8848. * @param {Array|Object} plugins plugin instance(s).
  8849. */
  8850. register: function(plugins) {
  8851. var p = this._plugins;
  8852. ([]).concat(plugins).forEach(function(plugin) {
  8853. if (p.indexOf(plugin) === -1) {
  8854. p.push(plugin);
  8855. }
  8856. });
  8857. },
  8858. /**
  8859. * Unregisters the given plugin(s) only if registered.
  8860. * @param {Array|Object} plugins plugin instance(s).
  8861. */
  8862. unregister: function(plugins) {
  8863. var p = this._plugins;
  8864. ([]).concat(plugins).forEach(function(plugin) {
  8865. var idx = p.indexOf(plugin);
  8866. if (idx !== -1) {
  8867. p.splice(idx, 1);
  8868. }
  8869. });
  8870. },
  8871. /**
  8872. * Remove all registered p^lugins.
  8873. * @since 2.1.5
  8874. */
  8875. clear: function() {
  8876. this._plugins = [];
  8877. },
  8878. /**
  8879. * Returns the number of registered plugins?
  8880. * @returns {Number}
  8881. * @since 2.1.5
  8882. */
  8883. count: function() {
  8884. return this._plugins.length;
  8885. },
  8886. /**
  8887. * Returns all registered plugin intances.
  8888. * @returns {Array} array of plugin objects.
  8889. * @since 2.1.5
  8890. */
  8891. getAll: function() {
  8892. return this._plugins;
  8893. },
  8894. /**
  8895. * Calls registered plugins on the specified extension, with the given args. This
  8896. * method immediately returns as soon as a plugin explicitly returns false. The
  8897. * returned value can be used, for instance, to interrupt the current action.
  8898. * @param {String} extension the name of the plugin method to call (e.g. 'beforeUpdate').
  8899. * @param {Array} [args] extra arguments to apply to the extension call.
  8900. * @returns {Boolean} false if any of the plugins return false, else returns true.
  8901. */
  8902. notify: function(extension, args) {
  8903. var plugins = this._plugins;
  8904. var ilen = plugins.length;
  8905. var i, plugin;
  8906. for (i=0; i<ilen; ++i) {
  8907. plugin = plugins[i];
  8908. if (typeof plugin[extension] === 'function') {
  8909. if (plugin[extension].apply(plugin, args || []) === false) {
  8910. return false;
  8911. }
  8912. }
  8913. }
  8914. return true;
  8915. }
  8916. };
  8917. /**
  8918. * Plugin extension methods.
  8919. * @interface Chart.PluginBase
  8920. * @since 2.1.0
  8921. */
  8922. Chart.PluginBase = Chart.Element.extend({
  8923. // Called at start of chart init
  8924. beforeInit: noop,
  8925. // Called at end of chart init
  8926. afterInit: noop,
  8927. // Called at start of update
  8928. beforeUpdate: noop,
  8929. // Called at end of update
  8930. afterUpdate: noop,
  8931. // Called at start of draw
  8932. beforeDraw: noop,
  8933. // Called at end of draw
  8934. afterDraw: noop,
  8935. // Called during destroy
  8936. destroy: noop
  8937. });
  8938. /**
  8939. * Provided for backward compatibility, use Chart.plugins instead
  8940. * @namespace Chart.pluginService
  8941. * @deprecated since version 2.1.5
  8942. * @todo remove me at version 3
  8943. */
  8944. Chart.pluginService = Chart.plugins;
  8945. };
  8946. },{}],30:[function(require,module,exports){
  8947. "use strict";
  8948. module.exports = function(Chart) {
  8949. var helpers = Chart.helpers;
  8950. Chart.defaults.scale = {
  8951. display: true,
  8952. position: "left",
  8953. // grid line settings
  8954. gridLines: {
  8955. display: true,
  8956. color: "rgba(0, 0, 0, 0.1)",
  8957. lineWidth: 1,
  8958. drawBorder: true,
  8959. drawOnChartArea: true,
  8960. drawTicks: true,
  8961. tickMarkLength: 10,
  8962. zeroLineWidth: 1,
  8963. zeroLineColor: "rgba(0,0,0,0.25)",
  8964. offsetGridLines: false
  8965. },
  8966. // scale label
  8967. scaleLabel: {
  8968. // actual label
  8969. labelString: '',
  8970. // display property
  8971. display: false
  8972. },
  8973. // label settings
  8974. ticks: {
  8975. beginAtZero: false,
  8976. minRotation: 0,
  8977. maxRotation: 50,
  8978. mirror: false,
  8979. padding: 10,
  8980. reverse: false,
  8981. display: true,
  8982. autoSkip: true,
  8983. autoSkipPadding: 0,
  8984. labelOffset: 0,
  8985. // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
  8986. callback: function(value) {
  8987. return helpers.isArray(value) ? value : '' + value;
  8988. }
  8989. }
  8990. };
  8991. Chart.Scale = Chart.Element.extend({
  8992. // These methods are ordered by lifecyle. Utilities then follow.
  8993. // Any function defined here is inherited by all scale types.
  8994. // Any function can be extended by the scale type
  8995. beforeUpdate: function() {
  8996. helpers.callCallback(this.options.beforeUpdate, [this]);
  8997. },
  8998. update: function(maxWidth, maxHeight, margins) {
  8999. var me = this;
  9000. // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
  9001. me.beforeUpdate();
  9002. // Absorb the master measurements
  9003. me.maxWidth = maxWidth;
  9004. me.maxHeight = maxHeight;
  9005. me.margins = helpers.extend({
  9006. left: 0,
  9007. right: 0,
  9008. top: 0,
  9009. bottom: 0
  9010. }, margins);
  9011. // Dimensions
  9012. me.beforeSetDimensions();
  9013. me.setDimensions();
  9014. me.afterSetDimensions();
  9015. // Data min/max
  9016. me.beforeDataLimits();
  9017. me.determineDataLimits();
  9018. me.afterDataLimits();
  9019. // Ticks
  9020. me.beforeBuildTicks();
  9021. me.buildTicks();
  9022. me.afterBuildTicks();
  9023. me.beforeTickToLabelConversion();
  9024. me.convertTicksToLabels();
  9025. me.afterTickToLabelConversion();
  9026. // Tick Rotation
  9027. me.beforeCalculateTickRotation();
  9028. me.calculateTickRotation();
  9029. me.afterCalculateTickRotation();
  9030. // Fit
  9031. me.beforeFit();
  9032. me.fit();
  9033. me.afterFit();
  9034. //
  9035. me.afterUpdate();
  9036. return me.minSize;
  9037. },
  9038. afterUpdate: function() {
  9039. helpers.callCallback(this.options.afterUpdate, [this]);
  9040. },
  9041. //
  9042. beforeSetDimensions: function() {
  9043. helpers.callCallback(this.options.beforeSetDimensions, [this]);
  9044. },
  9045. setDimensions: function() {
  9046. var me = this;
  9047. // Set the unconstrained dimension before label rotation
  9048. if (me.isHorizontal()) {
  9049. // Reset position before calculating rotation
  9050. me.width = me.maxWidth;
  9051. me.left = 0;
  9052. me.right = me.width;
  9053. } else {
  9054. me.height = me.maxHeight;
  9055. // Reset position before calculating rotation
  9056. me.top = 0;
  9057. me.bottom = me.height;
  9058. }
  9059. // Reset padding
  9060. me.paddingLeft = 0;
  9061. me.paddingTop = 0;
  9062. me.paddingRight = 0;
  9063. me.paddingBottom = 0;
  9064. },
  9065. afterSetDimensions: function() {
  9066. helpers.callCallback(this.options.afterSetDimensions, [this]);
  9067. },
  9068. // Data limits
  9069. beforeDataLimits: function() {
  9070. helpers.callCallback(this.options.beforeDataLimits, [this]);
  9071. },
  9072. determineDataLimits: helpers.noop,
  9073. afterDataLimits: function() {
  9074. helpers.callCallback(this.options.afterDataLimits, [this]);
  9075. },
  9076. //
  9077. beforeBuildTicks: function() {
  9078. helpers.callCallback(this.options.beforeBuildTicks, [this]);
  9079. },
  9080. buildTicks: helpers.noop,
  9081. afterBuildTicks: function() {
  9082. helpers.callCallback(this.options.afterBuildTicks, [this]);
  9083. },
  9084. beforeTickToLabelConversion: function() {
  9085. helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
  9086. },
  9087. convertTicksToLabels: function() {
  9088. var me = this;
  9089. // Convert ticks to strings
  9090. me.ticks = me.ticks.map(function(numericalTick, index, ticks) {
  9091. if (me.options.ticks.userCallback) {
  9092. return me.options.ticks.userCallback(numericalTick, index, ticks);
  9093. }
  9094. return me.options.ticks.callback(numericalTick, index, ticks);
  9095. },
  9096. me);
  9097. },
  9098. afterTickToLabelConversion: function() {
  9099. helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
  9100. },
  9101. //
  9102. beforeCalculateTickRotation: function() {
  9103. helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
  9104. },
  9105. calculateTickRotation: function() {
  9106. var me = this;
  9107. var context = me.ctx;
  9108. var globalDefaults = Chart.defaults.global;
  9109. var optionTicks = me.options.ticks;
  9110. //Get the width of each grid by calculating the difference
  9111. //between x offsets between 0 and 1.
  9112. var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize);
  9113. var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle);
  9114. var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
  9115. var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
  9116. context.font = tickLabelFont;
  9117. var firstWidth = context.measureText(me.ticks[0]).width;
  9118. var lastWidth = context.measureText(me.ticks[me.ticks.length - 1]).width;
  9119. var firstRotated;
  9120. me.labelRotation = optionTicks.minRotation || 0;
  9121. me.paddingRight = 0;
  9122. me.paddingLeft = 0;
  9123. if (me.options.display) {
  9124. if (me.isHorizontal()) {
  9125. me.paddingRight = lastWidth / 2 + 3;
  9126. me.paddingLeft = firstWidth / 2 + 3;
  9127. if (!me.longestTextCache) {
  9128. me.longestTextCache = {};
  9129. }
  9130. var originalLabelWidth = helpers.longestText(context, tickLabelFont, me.ticks, me.longestTextCache);
  9131. var labelWidth = originalLabelWidth;
  9132. var cosRotation;
  9133. var sinRotation;
  9134. // Allow 3 pixels x2 padding either side for label readability
  9135. // only the index matters for a dataset scale, but we want a consistent interface between scales
  9136. var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
  9137. //Max label rotation can be set or default to 90 - also act as a loop counter
  9138. while (labelWidth > tickWidth && me.labelRotation < optionTicks.maxRotation) {
  9139. cosRotation = Math.cos(helpers.toRadians(me.labelRotation));
  9140. sinRotation = Math.sin(helpers.toRadians(me.labelRotation));
  9141. firstRotated = cosRotation * firstWidth;
  9142. // We're right aligning the text now.
  9143. if (firstRotated + tickFontSize / 2 > me.yLabelWidth) {
  9144. me.paddingLeft = firstRotated + tickFontSize / 2;
  9145. }
  9146. me.paddingRight = tickFontSize / 2;
  9147. if (sinRotation * originalLabelWidth > me.maxHeight) {
  9148. // go back one step
  9149. me.labelRotation--;
  9150. break;
  9151. }
  9152. me.labelRotation++;
  9153. labelWidth = cosRotation * originalLabelWidth;
  9154. }
  9155. }
  9156. }
  9157. if (me.margins) {
  9158. me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
  9159. me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
  9160. }
  9161. },
  9162. afterCalculateTickRotation: function() {
  9163. helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
  9164. },
  9165. //
  9166. beforeFit: function() {
  9167. helpers.callCallback(this.options.beforeFit, [this]);
  9168. },
  9169. fit: function() {
  9170. var me = this;
  9171. // Reset
  9172. var minSize = me.minSize = {
  9173. width: 0,
  9174. height: 0
  9175. };
  9176. var opts = me.options;
  9177. var globalDefaults = Chart.defaults.global;
  9178. var tickOpts = opts.ticks;
  9179. var scaleLabelOpts = opts.scaleLabel;
  9180. var display = opts.display;
  9181. var isHorizontal = me.isHorizontal();
  9182. var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
  9183. var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
  9184. var tickFontFamily = helpers.getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
  9185. var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
  9186. var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabelOpts.fontSize, globalDefaults.defaultFontSize);
  9187. var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabelOpts.fontStyle, globalDefaults.defaultFontStyle);
  9188. var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabelOpts.fontFamily, globalDefaults.defaultFontFamily);
  9189. var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily);
  9190. var tickMarkLength = opts.gridLines.tickMarkLength;
  9191. // Width
  9192. if (isHorizontal) {
  9193. // subtract the margins to line up with the chartArea if we are a full width scale
  9194. minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
  9195. } else {
  9196. minSize.width = display ? tickMarkLength : 0;
  9197. }
  9198. // height
  9199. if (isHorizontal) {
  9200. minSize.height = display ? tickMarkLength : 0;
  9201. } else {
  9202. minSize.height = me.maxHeight; // fill all the height
  9203. }
  9204. // Are we showing a title for the scale?
  9205. if (scaleLabelOpts.display && display) {
  9206. if (isHorizontal) {
  9207. minSize.height += (scaleLabelFontSize * 1.5);
  9208. } else {
  9209. minSize.width += (scaleLabelFontSize * 1.5);
  9210. }
  9211. }
  9212. if (tickOpts.display && display) {
  9213. // Don't bother fitting the ticks if we are not showing them
  9214. if (!me.longestTextCache) {
  9215. me.longestTextCache = {};
  9216. }
  9217. var largestTextWidth = helpers.longestText(me.ctx, tickLabelFont, me.ticks, me.longestTextCache);
  9218. var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks);
  9219. var lineSpace = tickFontSize * 0.5;
  9220. if (isHorizontal) {
  9221. // A horizontal axis is more constrained by the height.
  9222. me.longestLabelWidth = largestTextWidth;
  9223. // TODO - improve this calculation
  9224. var labelHeight = (Math.sin(helpers.toRadians(me.labelRotation)) * me.longestLabelWidth) + (tickFontSize * tallestLabelHeightInLines) + (lineSpace * tallestLabelHeightInLines);
  9225. minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight);
  9226. me.ctx.font = tickLabelFont;
  9227. var firstLabelWidth = me.ctx.measureText(me.ticks[0]).width;
  9228. var lastLabelWidth = me.ctx.measureText(me.ticks[me.ticks.length - 1]).width;
  9229. // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
  9230. // by the font height
  9231. var cosRotation = Math.cos(helpers.toRadians(me.labelRotation));
  9232. var sinRotation = Math.sin(helpers.toRadians(me.labelRotation));
  9233. me.paddingLeft = me.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
  9234. me.paddingRight = me.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated
  9235. } else {
  9236. // A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
  9237. var maxLabelWidth = me.maxWidth - minSize.width;
  9238. // Account for padding
  9239. var mirror = tickOpts.mirror;
  9240. if (!mirror) {
  9241. largestTextWidth += me.options.ticks.padding;
  9242. } else {
  9243. // If mirrored text is on the inside so don't expand
  9244. largestTextWidth = 0;
  9245. }
  9246. if (largestTextWidth < maxLabelWidth) {
  9247. // We don't need all the room
  9248. minSize.width += largestTextWidth;
  9249. } else {
  9250. // Expand to max size
  9251. minSize.width = me.maxWidth;
  9252. }
  9253. me.paddingTop = tickFontSize / 2;
  9254. me.paddingBottom = tickFontSize / 2;
  9255. }
  9256. }
  9257. if (me.margins) {
  9258. me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
  9259. me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
  9260. me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
  9261. me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
  9262. }
  9263. me.width = minSize.width;
  9264. me.height = minSize.height;
  9265. },
  9266. afterFit: function() {
  9267. helpers.callCallback(this.options.afterFit, [this]);
  9268. },
  9269. // Shared Methods
  9270. isHorizontal: function() {
  9271. return this.options.position === "top" || this.options.position === "bottom";
  9272. },
  9273. isFullWidth: function() {
  9274. return (this.options.fullWidth);
  9275. },
  9276. // 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
  9277. getRightValue: function getRightValue(rawValue) {
  9278. // Null and undefined values first
  9279. if (rawValue === null || typeof(rawValue) === 'undefined') {
  9280. return NaN;
  9281. }
  9282. // isNaN(object) returns true, so make sure NaN is checking for a number
  9283. if (typeof(rawValue) === 'number' && isNaN(rawValue)) {
  9284. return NaN;
  9285. }
  9286. // If it is in fact an object, dive in one more level
  9287. if (typeof(rawValue) === "object") {
  9288. if ((rawValue instanceof Date) || (rawValue.isValid)) {
  9289. return rawValue;
  9290. } else {
  9291. return getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
  9292. }
  9293. }
  9294. // Value is good, return it
  9295. return rawValue;
  9296. },
  9297. // Used to get the value to display in the tooltip for the data at the given index
  9298. // function getLabelForIndex(index, datasetIndex)
  9299. getLabelForIndex: helpers.noop,
  9300. // Used to get data value locations. Value can either be an index or a numerical value
  9301. getPixelForValue: helpers.noop,
  9302. // Used to get the data value from a given pixel. This is the inverse of getPixelForValue
  9303. getValueForPixel: helpers.noop,
  9304. // Used for tick location, should
  9305. getPixelForTick: function(index, includeOffset) {
  9306. var me = this;
  9307. if (me.isHorizontal()) {
  9308. var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
  9309. var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
  9310. var pixel = (tickWidth * index) + me.paddingLeft;
  9311. if (includeOffset) {
  9312. pixel += tickWidth / 2;
  9313. }
  9314. var finalVal = me.left + Math.round(pixel);
  9315. finalVal += me.isFullWidth() ? me.margins.left : 0;
  9316. return finalVal;
  9317. } else {
  9318. var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
  9319. return me.top + (index * (innerHeight / (me.ticks.length - 1)));
  9320. }
  9321. },
  9322. // Utility for getting the pixel location of a percentage of scale
  9323. getPixelForDecimal: function(decimal /*, includeOffset*/ ) {
  9324. var me = this;
  9325. if (me.isHorizontal()) {
  9326. var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
  9327. var valueOffset = (innerWidth * decimal) + me.paddingLeft;
  9328. var finalVal = me.left + Math.round(valueOffset);
  9329. finalVal += me.isFullWidth() ? me.margins.left : 0;
  9330. return finalVal;
  9331. } else {
  9332. return me.top + (decimal * me.height);
  9333. }
  9334. },
  9335. getBasePixel: function() {
  9336. var me = this;
  9337. var min = me.min;
  9338. var max = me.max;
  9339. return me.getPixelForValue(
  9340. me.beginAtZero? 0:
  9341. min < 0 && max < 0? max :
  9342. min > 0 && max > 0? min :
  9343. 0);
  9344. },
  9345. // Actualy draw the scale on the canvas
  9346. // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
  9347. draw: function(chartArea) {
  9348. var me = this;
  9349. var options = me.options;
  9350. if (!options.display) {
  9351. return;
  9352. }
  9353. var context = me.ctx;
  9354. var globalDefaults = Chart.defaults.global;
  9355. var optionTicks = options.ticks;
  9356. var gridLines = options.gridLines;
  9357. var scaleLabel = options.scaleLabel;
  9358. var isRotated = me.labelRotation !== 0;
  9359. var skipRatio;
  9360. var useAutoskipper = optionTicks.autoSkip;
  9361. var isHorizontal = me.isHorizontal();
  9362. // figure out the maximum number of gridlines to show
  9363. var maxTicks;
  9364. if (optionTicks.maxTicksLimit) {
  9365. maxTicks = optionTicks.maxTicksLimit;
  9366. }
  9367. var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
  9368. var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize);
  9369. var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle);
  9370. var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
  9371. var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
  9372. var tl = gridLines.tickMarkLength;
  9373. var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
  9374. var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize);
  9375. var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabel.fontStyle, globalDefaults.defaultFontStyle);
  9376. var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily);
  9377. var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily);
  9378. var labelRotationRadians = helpers.toRadians(me.labelRotation);
  9379. var cosRotation = Math.cos(labelRotationRadians);
  9380. var sinRotation = Math.sin(labelRotationRadians);
  9381. var longestRotatedLabel = me.longestLabelWidth * cosRotation;
  9382. var rotatedLabelHeight = tickFontSize * sinRotation;
  9383. // Make sure we draw text in the correct color and font
  9384. context.fillStyle = tickFontColor;
  9385. var itemsToDraw = [];
  9386. if (isHorizontal) {
  9387. skipRatio = false;
  9388. // Only calculate the skip ratio with the half width of longestRotateLabel if we got an actual rotation
  9389. // See #2584
  9390. if (isRotated) {
  9391. longestRotatedLabel /= 2;
  9392. }
  9393. if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) {
  9394. skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight)));
  9395. }
  9396. // if they defined a max number of optionTicks,
  9397. // increase skipRatio until that number is met
  9398. if (maxTicks && me.ticks.length > maxTicks) {
  9399. while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) {
  9400. if (!skipRatio) {
  9401. skipRatio = 1;
  9402. }
  9403. skipRatio += 1;
  9404. }
  9405. }
  9406. if (!useAutoskipper) {
  9407. skipRatio = false;
  9408. }
  9409. }
  9410. var xTickStart = options.position === "right" ? me.left : me.right - tl;
  9411. var xTickEnd = options.position === "right" ? me.left + tl : me.right;
  9412. var yTickStart = options.position === "bottom" ? me.top : me.bottom - tl;
  9413. var yTickEnd = options.position === "bottom" ? me.top + tl : me.bottom;
  9414. helpers.each(me.ticks, function(label, index) {
  9415. // If the callback returned a null or undefined value, do not draw this line
  9416. if (label === undefined || label === null) {
  9417. return;
  9418. }
  9419. var isLastTick = me.ticks.length === index + 1;
  9420. // Since we always show the last tick,we need may need to hide the last shown one before
  9421. var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length);
  9422. if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
  9423. return;
  9424. }
  9425. var lineWidth, lineColor;
  9426. if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) {
  9427. // Draw the first index specially
  9428. lineWidth = gridLines.zeroLineWidth;
  9429. lineColor = gridLines.zeroLineColor;
  9430. } else {
  9431. lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index);
  9432. lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index);
  9433. }
  9434. // Common properties
  9435. var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
  9436. var textAlign, textBaseline = 'middle';
  9437. if (isHorizontal) {
  9438. if (!isRotated) {
  9439. textBaseline = options.position === 'top' ? 'bottom' : 'top';
  9440. }
  9441. textAlign = isRotated ? 'right' : 'center';
  9442. var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines
  9443. labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
  9444. labelY = (isRotated) ? me.top + 12 : options.position === 'top' ? me.bottom - tl : me.top + tl;
  9445. tx1 = tx2 = x1 = x2 = xLineValue;
  9446. ty1 = yTickStart;
  9447. ty2 = yTickEnd;
  9448. y1 = chartArea.top;
  9449. y2 = chartArea.bottom;
  9450. } else {
  9451. if (options.position === 'left') {
  9452. if (optionTicks.mirror) {
  9453. labelX = me.right + optionTicks.padding;
  9454. textAlign = 'left';
  9455. } else {
  9456. labelX = me.right - optionTicks.padding;
  9457. textAlign = 'right';
  9458. }
  9459. } else {
  9460. // right side
  9461. if (optionTicks.mirror) {
  9462. labelX = me.left - optionTicks.padding;
  9463. textAlign = 'right';
  9464. } else {
  9465. labelX = me.left + optionTicks.padding;
  9466. textAlign = 'left';
  9467. }
  9468. }
  9469. var yLineValue = me.getPixelForTick(index); // xvalues for grid lines
  9470. yLineValue += helpers.aliasPixel(lineWidth);
  9471. labelY = me.getPixelForTick(index, gridLines.offsetGridLines);
  9472. tx1 = xTickStart;
  9473. tx2 = xTickEnd;
  9474. x1 = chartArea.left;
  9475. x2 = chartArea.right;
  9476. ty1 = ty2 = y1 = y2 = yLineValue;
  9477. }
  9478. itemsToDraw.push({
  9479. tx1: tx1,
  9480. ty1: ty1,
  9481. tx2: tx2,
  9482. ty2: ty2,
  9483. x1: x1,
  9484. y1: y1,
  9485. x2: x2,
  9486. y2: y2,
  9487. labelX: labelX,
  9488. labelY: labelY,
  9489. glWidth: lineWidth,
  9490. glColor: lineColor,
  9491. rotation: -1 * labelRotationRadians,
  9492. label: label,
  9493. textBaseline: textBaseline,
  9494. textAlign: textAlign
  9495. });
  9496. });
  9497. // Draw all of the tick labels, tick marks, and grid lines at the correct places
  9498. helpers.each(itemsToDraw, function(itemToDraw) {
  9499. if (gridLines.display) {
  9500. context.lineWidth = itemToDraw.glWidth;
  9501. context.strokeStyle = itemToDraw.glColor;
  9502. context.beginPath();
  9503. if (gridLines.drawTicks) {
  9504. context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
  9505. context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
  9506. }
  9507. if (gridLines.drawOnChartArea) {
  9508. context.moveTo(itemToDraw.x1, itemToDraw.y1);
  9509. context.lineTo(itemToDraw.x2, itemToDraw.y2);
  9510. }
  9511. context.stroke();
  9512. }
  9513. if (optionTicks.display) {
  9514. context.save();
  9515. context.translate(itemToDraw.labelX, itemToDraw.labelY);
  9516. context.rotate(itemToDraw.rotation);
  9517. context.font = tickLabelFont;
  9518. context.textBaseline = itemToDraw.textBaseline;
  9519. context.textAlign = itemToDraw.textAlign;
  9520. var label = itemToDraw.label;
  9521. if (helpers.isArray(label)) {
  9522. for (var i = 0, y = 0; i < label.length; ++i) {
  9523. // We just make sure the multiline element is a string here..
  9524. context.fillText('' + label[i], 0, y);
  9525. // apply same lineSpacing as calculated @ L#320
  9526. y += (tickFontSize * 1.5);
  9527. }
  9528. } else {
  9529. context.fillText(label, 0, 0);
  9530. }
  9531. context.restore();
  9532. }
  9533. });
  9534. if (scaleLabel.display) {
  9535. // Draw the scale label
  9536. var scaleLabelX;
  9537. var scaleLabelY;
  9538. var rotation = 0;
  9539. if (isHorizontal) {
  9540. scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
  9541. scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFontSize / 2) : me.top + (scaleLabelFontSize / 2);
  9542. } else {
  9543. var isLeft = options.position === 'left';
  9544. scaleLabelX = isLeft ? me.left + (scaleLabelFontSize / 2) : me.right - (scaleLabelFontSize / 2);
  9545. scaleLabelY = me.top + ((me.bottom - me.top) / 2);
  9546. rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
  9547. }
  9548. context.save();
  9549. context.translate(scaleLabelX, scaleLabelY);
  9550. context.rotate(rotation);
  9551. context.textAlign = 'center';
  9552. context.textBaseline = 'middle';
  9553. context.fillStyle = scaleLabelFontColor; // render in correct colour
  9554. context.font = scaleLabelFont;
  9555. context.fillText(scaleLabel.labelString, 0, 0);
  9556. context.restore();
  9557. }
  9558. if (gridLines.drawBorder) {
  9559. // Draw the line at the edge of the axis
  9560. context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0);
  9561. context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0);
  9562. var x1 = me.left,
  9563. x2 = me.right,
  9564. y1 = me.top,
  9565. y2 = me.bottom;
  9566. var aliasPixel = helpers.aliasPixel(context.lineWidth);
  9567. if (isHorizontal) {
  9568. y1 = y2 = options.position === 'top' ? me.bottom : me.top;
  9569. y1 += aliasPixel;
  9570. y2 += aliasPixel;
  9571. } else {
  9572. x1 = x2 = options.position === 'left' ? me.right : me.left;
  9573. x1 += aliasPixel;
  9574. x2 += aliasPixel;
  9575. }
  9576. context.beginPath();
  9577. context.moveTo(x1, y1);
  9578. context.lineTo(x2, y2);
  9579. context.stroke();
  9580. }
  9581. }
  9582. });
  9583. };
  9584. },{}],31:[function(require,module,exports){
  9585. "use strict";
  9586. module.exports = function(Chart) {
  9587. var helpers = Chart.helpers;
  9588. Chart.scaleService = {
  9589. // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
  9590. // use the new chart options to grab the correct scale
  9591. constructors: {},
  9592. // Use a registration function so that we can move to an ES6 map when we no longer need to support
  9593. // old browsers
  9594. // Scale config defaults
  9595. defaults: {},
  9596. registerScaleType: function(type, scaleConstructor, defaults) {
  9597. this.constructors[type] = scaleConstructor;
  9598. this.defaults[type] = helpers.clone(defaults);
  9599. },
  9600. getScaleConstructor: function(type) {
  9601. return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
  9602. },
  9603. getScaleDefaults: function(type) {
  9604. // Return the scale defaults merged with the global settings so that we always use the latest ones
  9605. return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
  9606. },
  9607. updateScaleDefaults: function(type, additions) {
  9608. var defaults = this.defaults;
  9609. if (defaults.hasOwnProperty(type)) {
  9610. defaults[type] = helpers.extend(defaults[type], additions);
  9611. }
  9612. },
  9613. addScalesToLayout: function(chartInstance) {
  9614. // Adds each scale to the chart.boxes array to be sized accordingly
  9615. helpers.each(chartInstance.scales, function(scale) {
  9616. Chart.layoutService.addBox(chartInstance, scale);
  9617. });
  9618. }
  9619. };
  9620. };
  9621. },{}],32:[function(require,module,exports){
  9622. "use strict";
  9623. module.exports = function(Chart) {
  9624. var helpers = Chart.helpers;
  9625. Chart.defaults.global.title = {
  9626. display: false,
  9627. position: 'top',
  9628. fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
  9629. fontStyle: 'bold',
  9630. padding: 10,
  9631. // actual title
  9632. text: ''
  9633. };
  9634. var noop = helpers.noop;
  9635. Chart.Title = Chart.Element.extend({
  9636. initialize: function(config) {
  9637. var me = this;
  9638. helpers.extend(me, config);
  9639. me.options = helpers.configMerge(Chart.defaults.global.title, config.options);
  9640. // Contains hit boxes for each dataset (in dataset order)
  9641. me.legendHitBoxes = [];
  9642. },
  9643. // These methods are ordered by lifecyle. Utilities then follow.
  9644. beforeUpdate: function () {
  9645. var chartOpts = this.chart.options;
  9646. if (chartOpts && chartOpts.title) {
  9647. this.options = helpers.configMerge(Chart.defaults.global.title, chartOpts.title);
  9648. }
  9649. },
  9650. update: function(maxWidth, maxHeight, margins) {
  9651. var me = this;
  9652. // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
  9653. me.beforeUpdate();
  9654. // Absorb the master measurements
  9655. me.maxWidth = maxWidth;
  9656. me.maxHeight = maxHeight;
  9657. me.margins = margins;
  9658. // Dimensions
  9659. me.beforeSetDimensions();
  9660. me.setDimensions();
  9661. me.afterSetDimensions();
  9662. // Labels
  9663. me.beforeBuildLabels();
  9664. me.buildLabels();
  9665. me.afterBuildLabels();
  9666. // Fit
  9667. me.beforeFit();
  9668. me.fit();
  9669. me.afterFit();
  9670. //
  9671. me.afterUpdate();
  9672. return me.minSize;
  9673. },
  9674. afterUpdate: noop,
  9675. //
  9676. beforeSetDimensions: noop,
  9677. setDimensions: function() {
  9678. var me = this;
  9679. // Set the unconstrained dimension before label rotation
  9680. if (me.isHorizontal()) {
  9681. // Reset position before calculating rotation
  9682. me.width = me.maxWidth;
  9683. me.left = 0;
  9684. me.right = me.width;
  9685. } else {
  9686. me.height = me.maxHeight;
  9687. // Reset position before calculating rotation
  9688. me.top = 0;
  9689. me.bottom = me.height;
  9690. }
  9691. // Reset padding
  9692. me.paddingLeft = 0;
  9693. me.paddingTop = 0;
  9694. me.paddingRight = 0;
  9695. me.paddingBottom = 0;
  9696. // Reset minSize
  9697. me.minSize = {
  9698. width: 0,
  9699. height: 0
  9700. };
  9701. },
  9702. afterSetDimensions: noop,
  9703. //
  9704. beforeBuildLabels: noop,
  9705. buildLabels: noop,
  9706. afterBuildLabels: noop,
  9707. //
  9708. beforeFit: noop,
  9709. fit: function() {
  9710. var me = this,
  9711. ctx = me.ctx,
  9712. valueOrDefault = helpers.getValueOrDefault,
  9713. opts = me.options,
  9714. globalDefaults = Chart.defaults.global,
  9715. display = opts.display,
  9716. fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
  9717. minSize = me.minSize;
  9718. if (me.isHorizontal()) {
  9719. minSize.width = me.maxWidth; // fill all the width
  9720. minSize.height = display ? fontSize + (opts.padding * 2) : 0;
  9721. } else {
  9722. minSize.width = display ? fontSize + (opts.padding * 2) : 0;
  9723. minSize.height = me.maxHeight; // fill all the height
  9724. }
  9725. me.width = minSize.width;
  9726. me.height = minSize.height;
  9727. },
  9728. afterFit: noop,
  9729. // Shared Methods
  9730. isHorizontal: function() {
  9731. var pos = this.options.position;
  9732. return pos === "top" || pos === "bottom";
  9733. },
  9734. // Actualy draw the title block on the canvas
  9735. draw: function() {
  9736. var me = this,
  9737. ctx = me.ctx,
  9738. valueOrDefault = helpers.getValueOrDefault,
  9739. opts = me.options,
  9740. globalDefaults = Chart.defaults.global;
  9741. if (opts.display) {
  9742. var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
  9743. fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle),
  9744. fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily),
  9745. titleFont = helpers.fontString(fontSize, fontStyle, fontFamily),
  9746. rotation = 0,
  9747. titleX,
  9748. titleY,
  9749. top = me.top,
  9750. left = me.left,
  9751. bottom = me.bottom,
  9752. right = me.right;
  9753. ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
  9754. ctx.font = titleFont;
  9755. // Horizontal
  9756. if (me.isHorizontal()) {
  9757. titleX = left + ((right - left) / 2); // midpoint of the width
  9758. titleY = top + ((bottom - top) / 2); // midpoint of the height
  9759. } else {
  9760. titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2);
  9761. titleY = top + ((bottom - top) / 2);
  9762. rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
  9763. }
  9764. ctx.save();
  9765. ctx.translate(titleX, titleY);
  9766. ctx.rotate(rotation);
  9767. ctx.textAlign = 'center';
  9768. ctx.textBaseline = 'middle';
  9769. ctx.fillText(opts.text, 0, 0);
  9770. ctx.restore();
  9771. }
  9772. }
  9773. });
  9774. // Register the title plugin
  9775. Chart.plugins.register({
  9776. beforeInit: function(chartInstance) {
  9777. var opts = chartInstance.options;
  9778. var titleOpts = opts.title;
  9779. if (titleOpts) {
  9780. chartInstance.titleBlock = new Chart.Title({
  9781. ctx: chartInstance.chart.ctx,
  9782. options: titleOpts,
  9783. chart: chartInstance
  9784. });
  9785. Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock);
  9786. }
  9787. }
  9788. });
  9789. };
  9790. },{}],33:[function(require,module,exports){
  9791. "use strict";
  9792. module.exports = function(Chart) {
  9793. var helpers = Chart.helpers;
  9794. Chart.defaults.global.tooltips = {
  9795. enabled: true,
  9796. custom: null,
  9797. mode: 'single',
  9798. backgroundColor: "rgba(0,0,0,0.8)",
  9799. titleFontStyle: "bold",
  9800. titleSpacing: 2,
  9801. titleMarginBottom: 6,
  9802. titleFontColor: "#fff",
  9803. titleAlign: "left",
  9804. bodySpacing: 2,
  9805. bodyFontColor: "#fff",
  9806. bodyAlign: "left",
  9807. footerFontStyle: "bold",
  9808. footerSpacing: 2,
  9809. footerMarginTop: 6,
  9810. footerFontColor: "#fff",
  9811. footerAlign: "left",
  9812. yPadding: 6,
  9813. xPadding: 6,
  9814. yAlign : 'center',
  9815. xAlign : 'center',
  9816. caretSize: 5,
  9817. cornerRadius: 6,
  9818. multiKeyBackground: '#fff',
  9819. callbacks: {
  9820. // Args are: (tooltipItems, data)
  9821. beforeTitle: helpers.noop,
  9822. title: function(tooltipItems, data) {
  9823. // Pick first xLabel for now
  9824. var title = '';
  9825. var labels = data.labels;
  9826. var labelCount = labels ? labels.length : 0;
  9827. if (tooltipItems.length > 0) {
  9828. var item = tooltipItems[0];
  9829. if (item.xLabel) {
  9830. title = item.xLabel;
  9831. } else if (labelCount > 0 && item.index < labelCount) {
  9832. title = labels[item.index];
  9833. }
  9834. }
  9835. return title;
  9836. },
  9837. afterTitle: helpers.noop,
  9838. // Args are: (tooltipItems, data)
  9839. beforeBody: helpers.noop,
  9840. // Args are: (tooltipItem, data)
  9841. beforeLabel: helpers.noop,
  9842. label: function(tooltipItem, data) {
  9843. var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
  9844. return datasetLabel + ': ' + tooltipItem.yLabel;
  9845. },
  9846. labelColor: function(tooltipItem, chartInstance) {
  9847. var meta = chartInstance.getDatasetMeta(tooltipItem.datasetIndex);
  9848. var activeElement = meta.data[tooltipItem.index];
  9849. var view = activeElement._view;
  9850. return {
  9851. borderColor: view.borderColor,
  9852. backgroundColor: view.backgroundColor
  9853. };
  9854. },
  9855. afterLabel: helpers.noop,
  9856. // Args are: (tooltipItems, data)
  9857. afterBody: helpers.noop,
  9858. // Args are: (tooltipItems, data)
  9859. beforeFooter: helpers.noop,
  9860. footer: helpers.noop,
  9861. afterFooter: helpers.noop
  9862. }
  9863. };
  9864. // Helper to push or concat based on if the 2nd parameter is an array or not
  9865. function pushOrConcat(base, toPush) {
  9866. if (toPush) {
  9867. if (helpers.isArray(toPush)) {
  9868. //base = base.concat(toPush);
  9869. Array.prototype.push.apply(base, toPush);
  9870. } else {
  9871. base.push(toPush);
  9872. }
  9873. }
  9874. return base;
  9875. }
  9876. function getAveragePosition(elements) {
  9877. if (!elements.length) {
  9878. return false;
  9879. }
  9880. var i, len;
  9881. var xPositions = [];
  9882. var yPositions = [];
  9883. for (i = 0, len = elements.length; i < len; ++i) {
  9884. var el = elements[i];
  9885. if (el && el.hasValue()){
  9886. var pos = el.tooltipPosition();
  9887. xPositions.push(pos.x);
  9888. yPositions.push(pos.y);
  9889. }
  9890. }
  9891. var x = 0,
  9892. y = 0;
  9893. for (i = 0, len - xPositions.length; i < len; ++i) {
  9894. x += xPositions[i];
  9895. y += yPositions[i];
  9896. }
  9897. return {
  9898. x: Math.round(x / xPositions.length),
  9899. y: Math.round(y / xPositions.length)
  9900. };
  9901. }
  9902. // Private helper to create a tooltip iteam model
  9903. // @param element : the chart element (point, arc, bar) to create the tooltip item for
  9904. // @return : new tooltip item
  9905. function createTooltipItem(element) {
  9906. var xScale = element._xScale;
  9907. var yScale = element._yScale || element._scale; // handle radar || polarArea charts
  9908. var index = element._index,
  9909. datasetIndex = element._datasetIndex;
  9910. return {
  9911. xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
  9912. yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
  9913. index: index,
  9914. datasetIndex: datasetIndex
  9915. };
  9916. }
  9917. Chart.Tooltip = Chart.Element.extend({
  9918. initialize: function() {
  9919. var me = this;
  9920. var globalDefaults = Chart.defaults.global;
  9921. var tooltipOpts = me._options;
  9922. var getValueOrDefault = helpers.getValueOrDefault;
  9923. helpers.extend(me, {
  9924. _model: {
  9925. // Positioning
  9926. xPadding: tooltipOpts.xPadding,
  9927. yPadding: tooltipOpts.yPadding,
  9928. xAlign : tooltipOpts.yAlign,
  9929. yAlign : tooltipOpts.xAlign,
  9930. // Body
  9931. bodyFontColor: tooltipOpts.bodyFontColor,
  9932. _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
  9933. _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
  9934. _bodyAlign: tooltipOpts.bodyAlign,
  9935. bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
  9936. bodySpacing: tooltipOpts.bodySpacing,
  9937. // Title
  9938. titleFontColor: tooltipOpts.titleFontColor,
  9939. _titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
  9940. _titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
  9941. titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
  9942. _titleAlign: tooltipOpts.titleAlign,
  9943. titleSpacing: tooltipOpts.titleSpacing,
  9944. titleMarginBottom: tooltipOpts.titleMarginBottom,
  9945. // Footer
  9946. footerFontColor: tooltipOpts.footerFontColor,
  9947. _footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
  9948. _footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
  9949. footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
  9950. _footerAlign: tooltipOpts.footerAlign,
  9951. footerSpacing: tooltipOpts.footerSpacing,
  9952. footerMarginTop: tooltipOpts.footerMarginTop,
  9953. // Appearance
  9954. caretSize: tooltipOpts.caretSize,
  9955. cornerRadius: tooltipOpts.cornerRadius,
  9956. backgroundColor: tooltipOpts.backgroundColor,
  9957. opacity: 0,
  9958. legendColorBackground: tooltipOpts.multiKeyBackground
  9959. }
  9960. });
  9961. },
  9962. // Get the title
  9963. // Args are: (tooltipItem, data)
  9964. getTitle: function() {
  9965. var me = this;
  9966. var opts = me._options;
  9967. var callbacks = opts.callbacks;
  9968. var beforeTitle = callbacks.beforeTitle.apply(me, arguments),
  9969. title = callbacks.title.apply(me, arguments),
  9970. afterTitle = callbacks.afterTitle.apply(me, arguments);
  9971. var lines = [];
  9972. lines = pushOrConcat(lines, beforeTitle);
  9973. lines = pushOrConcat(lines, title);
  9974. lines = pushOrConcat(lines, afterTitle);
  9975. return lines;
  9976. },
  9977. // Args are: (tooltipItem, data)
  9978. getBeforeBody: function() {
  9979. var lines = this._options.callbacks.beforeBody.apply(this, arguments);
  9980. return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
  9981. },
  9982. // Args are: (tooltipItem, data)
  9983. getBody: function(tooltipItems, data) {
  9984. var me = this;
  9985. var callbacks = me._options.callbacks;
  9986. var bodyItems = [];
  9987. helpers.each(tooltipItems, function(tooltipItem) {
  9988. var bodyItem = {
  9989. before: [],
  9990. lines: [],
  9991. after: []
  9992. };
  9993. pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
  9994. pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
  9995. pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));
  9996. bodyItems.push(bodyItem);
  9997. });
  9998. return bodyItems;
  9999. },
  10000. // Args are: (tooltipItem, data)
  10001. getAfterBody: function() {
  10002. var lines = this._options.callbacks.afterBody.apply(this, arguments);
  10003. return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
  10004. },
  10005. // Get the footer and beforeFooter and afterFooter lines
  10006. // Args are: (tooltipItem, data)
  10007. getFooter: function() {
  10008. var me = this;
  10009. var callbacks = me._options.callbacks;
  10010. var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
  10011. var footer = callbacks.footer.apply(me, arguments);
  10012. var afterFooter = callbacks.afterFooter.apply(me, arguments);
  10013. var lines = [];
  10014. lines = pushOrConcat(lines, beforeFooter);
  10015. lines = pushOrConcat(lines, footer);
  10016. lines = pushOrConcat(lines, afterFooter);
  10017. return lines;
  10018. },
  10019. update: function(changed) {
  10020. var me = this;
  10021. var opts = me._options;
  10022. var model = me._model;
  10023. var active = me._active;
  10024. var data = me._data;
  10025. var chartInstance = me._chartInstance;
  10026. var i, len;
  10027. if (active.length) {
  10028. model.opacity = 1;
  10029. var labelColors = [],
  10030. tooltipPosition = getAveragePosition(active);
  10031. var tooltipItems = [];
  10032. for (i = 0, len = active.length; i < len; ++i) {
  10033. tooltipItems.push(createTooltipItem(active[i]));
  10034. }
  10035. // If the user provided a sorting function, use it to modify the tooltip items
  10036. if (opts.itemSort) {
  10037. tooltipItems = tooltipItems.sort(opts.itemSort);
  10038. }
  10039. // If there is more than one item, show color items
  10040. if (active.length > 1) {
  10041. helpers.each(tooltipItems, function(tooltipItem) {
  10042. labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance));
  10043. });
  10044. }
  10045. // Build the Text Lines
  10046. helpers.extend(model, {
  10047. title: me.getTitle(tooltipItems, data),
  10048. beforeBody: me.getBeforeBody(tooltipItems, data),
  10049. body: me.getBody(tooltipItems, data),
  10050. afterBody: me.getAfterBody(tooltipItems, data),
  10051. footer: me.getFooter(tooltipItems, data),
  10052. x: Math.round(tooltipPosition.x),
  10053. y: Math.round(tooltipPosition.y),
  10054. caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2),
  10055. labelColors: labelColors
  10056. });
  10057. // We need to determine alignment of
  10058. var tooltipSize = me.getTooltipSize(model);
  10059. me.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas
  10060. helpers.extend(model, me.getBackgroundPoint(model, tooltipSize));
  10061. } else {
  10062. me._model.opacity = 0;
  10063. }
  10064. if (changed && opts.custom) {
  10065. opts.custom.call(me, model);
  10066. }
  10067. return me;
  10068. },
  10069. getTooltipSize: function getTooltipSize(vm) {
  10070. var ctx = this._chart.ctx;
  10071. var size = {
  10072. height: vm.yPadding * 2, // Tooltip Padding
  10073. width: 0
  10074. };
  10075. // Count of all lines in the body
  10076. var body = vm.body;
  10077. var combinedBodyLength = body.reduce(function(count, bodyItem) {
  10078. return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
  10079. }, 0);
  10080. combinedBodyLength += vm.beforeBody.length + vm.afterBody.length;
  10081. var titleLineCount = vm.title.length;
  10082. var footerLineCount = vm.footer.length;
  10083. var titleFontSize = vm.titleFontSize,
  10084. bodyFontSize = vm.bodyFontSize,
  10085. footerFontSize = vm.footerFontSize;
  10086. size.height += titleLineCount * titleFontSize; // Title Lines
  10087. size.height += (titleLineCount - 1) * vm.titleSpacing; // Title Line Spacing
  10088. size.height += titleLineCount ? vm.titleMarginBottom : 0; // Title's bottom Margin
  10089. size.height += combinedBodyLength * bodyFontSize; // Body Lines
  10090. size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
  10091. size.height += footerLineCount ? vm.footerMarginTop : 0; // Footer Margin
  10092. size.height += footerLineCount * (footerFontSize); // Footer Lines
  10093. size.height += footerLineCount ? (footerLineCount - 1) * vm.footerSpacing : 0; // Footer Line Spacing
  10094. // Title width
  10095. var widthPadding = 0;
  10096. var maxLineWidth = function(line) {
  10097. size.width = Math.max(size.width, ctx.measureText(line).width + widthPadding);
  10098. };
  10099. ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
  10100. helpers.each(vm.title, maxLineWidth);
  10101. // Body width
  10102. ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
  10103. helpers.each(vm.beforeBody.concat(vm.afterBody), maxLineWidth);
  10104. // Body lines may include some extra width due to the color box
  10105. widthPadding = body.length > 1 ? (bodyFontSize + 2) : 0;
  10106. helpers.each(body, function(bodyItem) {
  10107. helpers.each(bodyItem.before, maxLineWidth);
  10108. helpers.each(bodyItem.lines, maxLineWidth);
  10109. helpers.each(bodyItem.after, maxLineWidth);
  10110. });
  10111. // Reset back to 0
  10112. widthPadding = 0;
  10113. // Footer width
  10114. ctx.font = helpers.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
  10115. helpers.each(vm.footer, maxLineWidth);
  10116. // Add padding
  10117. size.width += 2 * vm.xPadding;
  10118. return size;
  10119. },
  10120. determineAlignment: function determineAlignment(size) {
  10121. var me = this;
  10122. var model = me._model;
  10123. var chart = me._chart;
  10124. var chartArea = me._chartInstance.chartArea;
  10125. if (model.y < size.height) {
  10126. model.yAlign = 'top';
  10127. } else if (model.y > (chart.height - size.height)) {
  10128. model.yAlign = 'bottom';
  10129. }
  10130. var lf, rf; // functions to determine left, right alignment
  10131. var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
  10132. var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
  10133. var midX = (chartArea.left + chartArea.right) / 2;
  10134. var midY = (chartArea.top + chartArea.bottom) / 2;
  10135. if (model.yAlign === 'center') {
  10136. lf = function(x) {
  10137. return x <= midX;
  10138. };
  10139. rf = function(x) {
  10140. return x > midX;
  10141. };
  10142. } else {
  10143. lf = function(x) {
  10144. return x <= (size.width / 2);
  10145. };
  10146. rf = function(x) {
  10147. return x >= (chart.width - (size.width / 2));
  10148. };
  10149. }
  10150. olf = function(x) {
  10151. return x + size.width > chart.width;
  10152. };
  10153. orf = function(x) {
  10154. return x - size.width < 0;
  10155. };
  10156. yf = function(y) {
  10157. return y <= midY ? 'top' : 'bottom';
  10158. };
  10159. if (lf(model.x)) {
  10160. model.xAlign = 'left';
  10161. // Is tooltip too wide and goes over the right side of the chart.?
  10162. if (olf(model.x)) {
  10163. model.xAlign = 'center';
  10164. model.yAlign = yf(model.y);
  10165. }
  10166. } else if (rf(model.x)) {
  10167. model.xAlign = 'right';
  10168. // Is tooltip too wide and goes outside left edge of canvas?
  10169. if (orf(model.x)) {
  10170. model.xAlign = 'center';
  10171. model.yAlign = yf(model.y);
  10172. }
  10173. }
  10174. },
  10175. getBackgroundPoint: function getBackgroundPoint(vm, size) {
  10176. // Background Position
  10177. var pt = {
  10178. x: vm.x,
  10179. y: vm.y
  10180. };
  10181. var caretSize = vm.caretSize,
  10182. caretPadding = vm.caretPadding,
  10183. cornerRadius = vm.cornerRadius,
  10184. xAlign = vm.xAlign,
  10185. yAlign = vm.yAlign,
  10186. paddingAndSize = caretSize + caretPadding,
  10187. radiusAndPadding = cornerRadius + caretPadding;
  10188. if (xAlign === 'right') {
  10189. pt.x -= size.width;
  10190. } else if (xAlign === 'center') {
  10191. pt.x -= (size.width / 2);
  10192. }
  10193. if (yAlign === 'top') {
  10194. pt.y += paddingAndSize;
  10195. } else if (yAlign === 'bottom') {
  10196. pt.y -= size.height + paddingAndSize;
  10197. } else {
  10198. pt.y -= (size.height / 2);
  10199. }
  10200. if (yAlign === 'center') {
  10201. if (xAlign === 'left') {
  10202. pt.x += paddingAndSize;
  10203. } else if (xAlign === 'right') {
  10204. pt.x -= paddingAndSize;
  10205. }
  10206. } else {
  10207. if (xAlign === 'left') {
  10208. pt.x -= radiusAndPadding;
  10209. } else if (xAlign === 'right') {
  10210. pt.x += radiusAndPadding;
  10211. }
  10212. }
  10213. return pt;
  10214. },
  10215. drawCaret: function drawCaret(tooltipPoint, size, opacity, caretPadding) {
  10216. var vm = this._view;
  10217. var ctx = this._chart.ctx;
  10218. var x1, x2, x3;
  10219. var y1, y2, y3;
  10220. var caretSize = vm.caretSize;
  10221. var cornerRadius = vm.cornerRadius;
  10222. var xAlign = vm.xAlign,
  10223. yAlign = vm.yAlign;
  10224. var ptX = tooltipPoint.x,
  10225. ptY = tooltipPoint.y;
  10226. var width = size.width,
  10227. height = size.height;
  10228. if (yAlign === 'center') {
  10229. // Left or right side
  10230. if (xAlign === 'left') {
  10231. x1 = ptX;
  10232. x2 = x1 - caretSize;
  10233. x3 = x1;
  10234. } else {
  10235. x1 = ptX + width;
  10236. x2 = x1 + caretSize;
  10237. x3 = x1;
  10238. }
  10239. y2 = ptY + (height / 2);
  10240. y1 = y2 - caretSize;
  10241. y3 = y2 + caretSize;
  10242. } else {
  10243. if (xAlign === 'left') {
  10244. x1 = ptX + cornerRadius;
  10245. x2 = x1 + caretSize;
  10246. x3 = x2 + caretSize;
  10247. } else if (xAlign === 'right') {
  10248. x1 = ptX + width - cornerRadius;
  10249. x2 = x1 - caretSize;
  10250. x3 = x2 - caretSize;
  10251. } else {
  10252. x2 = ptX + (width / 2);
  10253. x1 = x2 - caretSize;
  10254. x3 = x2 + caretSize;
  10255. }
  10256. if (yAlign === 'top') {
  10257. y1 = ptY;
  10258. y2 = y1 - caretSize;
  10259. y3 = y1;
  10260. } else {
  10261. y1 = ptY + height;
  10262. y2 = y1 + caretSize;
  10263. y3 = y1;
  10264. }
  10265. }
  10266. var bgColor = helpers.color(vm.backgroundColor);
  10267. ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
  10268. ctx.beginPath();
  10269. ctx.moveTo(x1, y1);
  10270. ctx.lineTo(x2, y2);
  10271. ctx.lineTo(x3, y3);
  10272. ctx.closePath();
  10273. ctx.fill();
  10274. },
  10275. drawTitle: function drawTitle(pt, vm, ctx, opacity) {
  10276. var title = vm.title;
  10277. if (title.length) {
  10278. ctx.textAlign = vm._titleAlign;
  10279. ctx.textBaseline = "top";
  10280. var titleFontSize = vm.titleFontSize,
  10281. titleSpacing = vm.titleSpacing;
  10282. var titleFontColor = helpers.color(vm.titleFontColor);
  10283. ctx.fillStyle = titleFontColor.alpha(opacity * titleFontColor.alpha()).rgbString();
  10284. ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
  10285. var i, len;
  10286. for (i = 0, len = title.length; i < len; ++i) {
  10287. ctx.fillText(title[i], pt.x, pt.y);
  10288. pt.y += titleFontSize + titleSpacing; // Line Height and spacing
  10289. if (i + 1 === title.length) {
  10290. pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
  10291. }
  10292. }
  10293. }
  10294. },
  10295. drawBody: function drawBody(pt, vm, ctx, opacity) {
  10296. var bodyFontSize = vm.bodyFontSize;
  10297. var bodySpacing = vm.bodySpacing;
  10298. var body = vm.body;
  10299. ctx.textAlign = vm._bodyAlign;
  10300. ctx.textBaseline = "top";
  10301. var bodyFontColor = helpers.color(vm.bodyFontColor);
  10302. var textColor = bodyFontColor.alpha(opacity * bodyFontColor.alpha()).rgbString();
  10303. ctx.fillStyle = textColor;
  10304. ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
  10305. // Before Body
  10306. var xLinePadding = 0;
  10307. var fillLineOfText = function(line) {
  10308. ctx.fillText(line, pt.x + xLinePadding, pt.y);
  10309. pt.y += bodyFontSize + bodySpacing;
  10310. };
  10311. // Before body lines
  10312. helpers.each(vm.beforeBody, fillLineOfText);
  10313. var drawColorBoxes = body.length > 1;
  10314. xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
  10315. // Draw body lines now
  10316. helpers.each(body, function(bodyItem, i) {
  10317. helpers.each(bodyItem.before, fillLineOfText);
  10318. helpers.each(bodyItem.lines, function(line) {
  10319. // Draw Legend-like boxes if needed
  10320. if (drawColorBoxes) {
  10321. // Fill a white rect so that colours merge nicely if the opacity is < 1
  10322. ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
  10323. ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
  10324. // Border
  10325. ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
  10326. ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
  10327. // Inner square
  10328. ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
  10329. ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
  10330. ctx.fillStyle = textColor;
  10331. }
  10332. fillLineOfText(line);
  10333. });
  10334. helpers.each(bodyItem.after, fillLineOfText);
  10335. });
  10336. // Reset back to 0 for after body
  10337. xLinePadding = 0;
  10338. // After body lines
  10339. helpers.each(vm.afterBody, fillLineOfText);
  10340. pt.y -= bodySpacing; // Remove last body spacing
  10341. },
  10342. drawFooter: function drawFooter(pt, vm, ctx, opacity) {
  10343. var footer = vm.footer;
  10344. if (footer.length) {
  10345. pt.y += vm.footerMarginTop;
  10346. ctx.textAlign = vm._footerAlign;
  10347. ctx.textBaseline = "top";
  10348. var footerFontColor = helpers.color(vm.footerFontColor);
  10349. ctx.fillStyle = footerFontColor.alpha(opacity * footerFontColor.alpha()).rgbString();
  10350. ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
  10351. helpers.each(footer, function(line) {
  10352. ctx.fillText(line, pt.x, pt.y);
  10353. pt.y += vm.footerFontSize + vm.footerSpacing;
  10354. });
  10355. }
  10356. },
  10357. draw: function draw() {
  10358. var ctx = this._chart.ctx;
  10359. var vm = this._view;
  10360. if (vm.opacity === 0) {
  10361. return;
  10362. }
  10363. var tooltipSize = this.getTooltipSize(vm);
  10364. var pt = {
  10365. x: vm.x,
  10366. y: vm.y
  10367. };
  10368. // IE11/Edge does not like very small opacities, so snap to 0
  10369. var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
  10370. if (this._options.enabled) {
  10371. // Draw Background
  10372. var bgColor = helpers.color(vm.backgroundColor);
  10373. ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
  10374. helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
  10375. ctx.fill();
  10376. // Draw Caret
  10377. this.drawCaret(pt, tooltipSize, opacity, vm.caretPadding);
  10378. // Draw Title, Body, and Footer
  10379. pt.x += vm.xPadding;
  10380. pt.y += vm.yPadding;
  10381. // Titles
  10382. this.drawTitle(pt, vm, ctx, opacity);
  10383. // Body
  10384. this.drawBody(pt, vm, ctx, opacity);
  10385. // Footer
  10386. this.drawFooter(pt, vm, ctx, opacity);
  10387. }
  10388. }
  10389. });
  10390. };
  10391. },{}],34:[function(require,module,exports){
  10392. "use strict";
  10393. module.exports = function(Chart, moment) {
  10394. var helpers = Chart.helpers,
  10395. globalOpts = Chart.defaults.global;
  10396. globalOpts.elements.arc = {
  10397. backgroundColor: globalOpts.defaultColor,
  10398. borderColor: "#fff",
  10399. borderWidth: 2
  10400. };
  10401. Chart.elements.Arc = Chart.Element.extend({
  10402. inLabelRange: function(mouseX) {
  10403. var vm = this._view;
  10404. if (vm) {
  10405. return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
  10406. } else {
  10407. return false;
  10408. }
  10409. },
  10410. inRange: function(chartX, chartY) {
  10411. var vm = this._view;
  10412. if (vm) {
  10413. var pointRelativePosition = helpers.getAngleFromPoint(vm, {
  10414. x: chartX,
  10415. y: chartY
  10416. }),
  10417. angle = pointRelativePosition.angle,
  10418. distance = pointRelativePosition.distance;
  10419. //Sanitise angle range
  10420. var startAngle = vm.startAngle;
  10421. var endAngle = vm.endAngle;
  10422. while (endAngle < startAngle) {
  10423. endAngle += 2.0 * Math.PI;
  10424. }
  10425. while (angle > endAngle) {
  10426. angle -= 2.0 * Math.PI;
  10427. }
  10428. while (angle < startAngle) {
  10429. angle += 2.0 * Math.PI;
  10430. }
  10431. //Check if within the range of the open/close angle
  10432. var betweenAngles = (angle >= startAngle && angle <= endAngle),
  10433. withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
  10434. return (betweenAngles && withinRadius);
  10435. } else {
  10436. return false;
  10437. }
  10438. },
  10439. tooltipPosition: function() {
  10440. var vm = this._view;
  10441. var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
  10442. rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
  10443. return {
  10444. x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
  10445. y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
  10446. };
  10447. },
  10448. draw: function() {
  10449. var ctx = this._chart.ctx,
  10450. vm = this._view,
  10451. sA = vm.startAngle,
  10452. eA = vm.endAngle;
  10453. ctx.beginPath();
  10454. ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
  10455. ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
  10456. ctx.closePath();
  10457. ctx.strokeStyle = vm.borderColor;
  10458. ctx.lineWidth = vm.borderWidth;
  10459. ctx.fillStyle = vm.backgroundColor;
  10460. ctx.fill();
  10461. ctx.lineJoin = 'bevel';
  10462. if (vm.borderWidth) {
  10463. ctx.stroke();
  10464. }
  10465. }
  10466. });
  10467. };
  10468. },{}],35:[function(require,module,exports){
  10469. "use strict";
  10470. module.exports = function(Chart) {
  10471. var helpers = Chart.helpers;
  10472. var globalDefaults = Chart.defaults.global;
  10473. Chart.defaults.global.elements.line = {
  10474. tension: 0.4,
  10475. backgroundColor: globalDefaults.defaultColor,
  10476. borderWidth: 3,
  10477. borderColor: globalDefaults.defaultColor,
  10478. borderCapStyle: 'butt',
  10479. borderDash: [],
  10480. borderDashOffset: 0.0,
  10481. borderJoinStyle: 'miter',
  10482. fill: true // do we fill in the area between the line and its base axis
  10483. };
  10484. Chart.elements.Line = Chart.Element.extend({
  10485. lineToNextPoint: function(previousPoint, point, nextPoint, skipHandler, previousSkipHandler) {
  10486. var me = this;
  10487. var ctx = me._chart.ctx;
  10488. var spanGaps = me._view ? me._view.spanGaps : false;
  10489. if (point._view.skip && !spanGaps) {
  10490. skipHandler.call(me, previousPoint, point, nextPoint);
  10491. } else if (previousPoint._view.skip && !spanGaps) {
  10492. previousSkipHandler.call(me, previousPoint, point, nextPoint);
  10493. } else if (point._view.tension === 0) {
  10494. ctx.lineTo(point._view.x, point._view.y);
  10495. } else {
  10496. // Line between points
  10497. ctx.bezierCurveTo(
  10498. previousPoint._view.controlPointNextX,
  10499. previousPoint._view.controlPointNextY,
  10500. point._view.controlPointPreviousX,
  10501. point._view.controlPointPreviousY,
  10502. point._view.x,
  10503. point._view.y
  10504. );
  10505. }
  10506. },
  10507. draw: function() {
  10508. var me = this;
  10509. var vm = me._view;
  10510. var ctx = me._chart.ctx;
  10511. var first = me._children[0];
  10512. var last = me._children[me._children.length - 1];
  10513. function loopBackToStart(drawLineToCenter) {
  10514. if (!first._view.skip && !last._view.skip) {
  10515. // Draw a bezier line from last to first
  10516. ctx.bezierCurveTo(
  10517. last._view.controlPointNextX,
  10518. last._view.controlPointNextY,
  10519. first._view.controlPointPreviousX,
  10520. first._view.controlPointPreviousY,
  10521. first._view.x,
  10522. first._view.y
  10523. );
  10524. } else if (drawLineToCenter) {
  10525. // Go to center
  10526. ctx.lineTo(me._view.scaleZero.x, me._view.scaleZero.y);
  10527. }
  10528. }
  10529. ctx.save();
  10530. // If we had points and want to fill this line, do so.
  10531. if (me._children.length > 0 && vm.fill) {
  10532. // Draw the background first (so the border is always on top)
  10533. ctx.beginPath();
  10534. helpers.each(me._children, function(point, index) {
  10535. var previous = helpers.previousItem(me._children, index);
  10536. var next = helpers.nextItem(me._children, index);
  10537. // First point moves to it's starting position no matter what
  10538. if (index === 0) {
  10539. if (me._loop) {
  10540. ctx.moveTo(vm.scaleZero.x, vm.scaleZero.y);
  10541. } else {
  10542. ctx.moveTo(point._view.x, vm.scaleZero);
  10543. }
  10544. if (point._view.skip) {
  10545. if (!me._loop) {
  10546. ctx.moveTo(next._view.x, me._view.scaleZero);
  10547. }
  10548. } else {
  10549. ctx.lineTo(point._view.x, point._view.y);
  10550. }
  10551. } else {
  10552. me.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
  10553. if (me._loop) {
  10554. // Go to center
  10555. ctx.lineTo(me._view.scaleZero.x, me._view.scaleZero.y);
  10556. } else {
  10557. ctx.lineTo(previousPoint._view.x, me._view.scaleZero);
  10558. ctx.moveTo(nextPoint._view.x, me._view.scaleZero);
  10559. }
  10560. }, function(previousPoint, point) {
  10561. // If we skipped the last point, draw a line to ourselves so that the fill is nice
  10562. ctx.lineTo(point._view.x, point._view.y);
  10563. });
  10564. }
  10565. }, me);
  10566. // For radial scales, loop back around to the first point
  10567. if (me._loop) {
  10568. loopBackToStart(true);
  10569. } else {
  10570. //Round off the line by going to the base of the chart, back to the start, then fill.
  10571. ctx.lineTo(me._children[me._children.length - 1]._view.x, vm.scaleZero);
  10572. ctx.lineTo(me._children[0]._view.x, vm.scaleZero);
  10573. }
  10574. ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor;
  10575. ctx.closePath();
  10576. ctx.fill();
  10577. }
  10578. var globalOptionLineElements = globalDefaults.elements.line;
  10579. // Now draw the line between all the points with any borders
  10580. ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
  10581. // IE 9 and 10 do not support line dash
  10582. if (ctx.setLineDash) {
  10583. ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
  10584. }
  10585. ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
  10586. ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
  10587. ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
  10588. ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
  10589. ctx.beginPath();
  10590. helpers.each(me._children, function(point, index) {
  10591. var previous = helpers.previousItem(me._children, index);
  10592. var next = helpers.nextItem(me._children, index);
  10593. if (index === 0) {
  10594. ctx.moveTo(point._view.x, point._view.y);
  10595. } else {
  10596. me.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
  10597. ctx.moveTo(nextPoint._view.x, nextPoint._view.y);
  10598. }, function(previousPoint, point) {
  10599. // If we skipped the last point, move up to our point preventing a line from being drawn
  10600. ctx.moveTo(point._view.x, point._view.y);
  10601. });
  10602. }
  10603. }, me);
  10604. if (me._loop && me._children.length > 0) {
  10605. loopBackToStart();
  10606. }
  10607. ctx.stroke();
  10608. ctx.restore();
  10609. }
  10610. });
  10611. };
  10612. },{}],36:[function(require,module,exports){
  10613. "use strict";
  10614. module.exports = function(Chart) {
  10615. var helpers = Chart.helpers,
  10616. globalOpts = Chart.defaults.global,
  10617. defaultColor = globalOpts.defaultColor;
  10618. globalOpts.elements.point = {
  10619. radius: 3,
  10620. pointStyle: 'circle',
  10621. backgroundColor: defaultColor,
  10622. borderWidth: 1,
  10623. borderColor: defaultColor,
  10624. // Hover
  10625. hitRadius: 1,
  10626. hoverRadius: 4,
  10627. hoverBorderWidth: 1
  10628. };
  10629. Chart.elements.Point = Chart.Element.extend({
  10630. inRange: function(mouseX, mouseY) {
  10631. var vm = this._view;
  10632. return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
  10633. },
  10634. inLabelRange: function(mouseX) {
  10635. var vm = this._view;
  10636. return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
  10637. },
  10638. tooltipPosition: function() {
  10639. var vm = this._view;
  10640. return {
  10641. x: vm.x,
  10642. y: vm.y,
  10643. padding: vm.radius + vm.borderWidth
  10644. };
  10645. },
  10646. draw: function() {
  10647. var vm = this._view;
  10648. var ctx = this._chart.ctx;
  10649. var pointStyle = vm.pointStyle;
  10650. var radius = vm.radius;
  10651. var x = vm.x;
  10652. var y = vm.y;
  10653. var type, edgeLength, xOffset, yOffset, height, size;
  10654. if (vm.skip) {
  10655. return;
  10656. }
  10657. if (typeof pointStyle === 'object') {
  10658. type = pointStyle.toString();
  10659. if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
  10660. ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
  10661. return;
  10662. }
  10663. }
  10664. if (isNaN(radius) || radius <= 0) {
  10665. return;
  10666. }
  10667. ctx.strokeStyle = vm.borderColor || defaultColor;
  10668. ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
  10669. ctx.fillStyle = vm.backgroundColor || defaultColor;
  10670. switch (pointStyle) {
  10671. // Default includes circle
  10672. default:
  10673. ctx.beginPath();
  10674. ctx.arc(x, y, radius, 0, Math.PI * 2);
  10675. ctx.closePath();
  10676. ctx.fill();
  10677. break;
  10678. case 'triangle':
  10679. ctx.beginPath();
  10680. edgeLength = 3 * radius / Math.sqrt(3);
  10681. height = edgeLength * Math.sqrt(3) / 2;
  10682. ctx.moveTo(x - edgeLength / 2, y + height / 3);
  10683. ctx.lineTo(x + edgeLength / 2, y + height / 3);
  10684. ctx.lineTo(x, y - 2 * height / 3);
  10685. ctx.closePath();
  10686. ctx.fill();
  10687. break;
  10688. case 'rect':
  10689. size = 1 / Math.SQRT2 * radius;
  10690. ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
  10691. ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
  10692. break;
  10693. case 'rectRot':
  10694. size = 1 / Math.SQRT2 * radius;
  10695. ctx.beginPath();
  10696. ctx.moveTo(x - size, y);
  10697. ctx.lineTo(x, y + size);
  10698. ctx.lineTo(x + size, y);
  10699. ctx.lineTo(x, y - size);
  10700. ctx.closePath();
  10701. ctx.fill();
  10702. break;
  10703. case 'cross':
  10704. ctx.beginPath();
  10705. ctx.moveTo(x, y + radius);
  10706. ctx.lineTo(x, y - radius);
  10707. ctx.moveTo(x - radius, y);
  10708. ctx.lineTo(x + radius, y);
  10709. ctx.closePath();
  10710. break;
  10711. case 'crossRot':
  10712. ctx.beginPath();
  10713. xOffset = Math.cos(Math.PI / 4) * radius;
  10714. yOffset = Math.sin(Math.PI / 4) * radius;
  10715. ctx.moveTo(x - xOffset, y - yOffset);
  10716. ctx.lineTo(x + xOffset, y + yOffset);
  10717. ctx.moveTo(x - xOffset, y + yOffset);
  10718. ctx.lineTo(x + xOffset, y - yOffset);
  10719. ctx.closePath();
  10720. break;
  10721. case 'star':
  10722. ctx.beginPath();
  10723. ctx.moveTo(x, y + radius);
  10724. ctx.lineTo(x, y - radius);
  10725. ctx.moveTo(x - radius, y);
  10726. ctx.lineTo(x + radius, y);
  10727. xOffset = Math.cos(Math.PI / 4) * radius;
  10728. yOffset = Math.sin(Math.PI / 4) * radius;
  10729. ctx.moveTo(x - xOffset, y - yOffset);
  10730. ctx.lineTo(x + xOffset, y + yOffset);
  10731. ctx.moveTo(x - xOffset, y + yOffset);
  10732. ctx.lineTo(x + xOffset, y - yOffset);
  10733. ctx.closePath();
  10734. break;
  10735. case 'line':
  10736. ctx.beginPath();
  10737. ctx.moveTo(x - radius, y);
  10738. ctx.lineTo(x + radius, y);
  10739. ctx.closePath();
  10740. break;
  10741. case 'dash':
  10742. ctx.beginPath();
  10743. ctx.moveTo(x, y);
  10744. ctx.lineTo(x + radius, y);
  10745. ctx.closePath();
  10746. break;
  10747. }
  10748. ctx.stroke();
  10749. }
  10750. });
  10751. };
  10752. },{}],37:[function(require,module,exports){
  10753. "use strict";
  10754. module.exports = function(Chart) {
  10755. var helpers = Chart.helpers,
  10756. globalOpts = Chart.defaults.global;
  10757. globalOpts.elements.rectangle = {
  10758. backgroundColor: globalOpts.defaultColor,
  10759. borderWidth: 0,
  10760. borderColor: globalOpts.defaultColor,
  10761. borderSkipped: 'bottom'
  10762. };
  10763. Chart.elements.Rectangle = Chart.Element.extend({
  10764. draw: function() {
  10765. var ctx = this._chart.ctx;
  10766. var vm = this._view;
  10767. var halfWidth = vm.width / 2,
  10768. leftX = vm.x - halfWidth,
  10769. rightX = vm.x + halfWidth,
  10770. top = vm.base - (vm.base - vm.y),
  10771. halfStroke = vm.borderWidth / 2;
  10772. // Canvas doesn't allow us to stroke inside the width so we can
  10773. // adjust the sizes to fit if we're setting a stroke on the line
  10774. if (vm.borderWidth) {
  10775. leftX += halfStroke;
  10776. rightX -= halfStroke;
  10777. top += halfStroke;
  10778. }
  10779. ctx.beginPath();
  10780. ctx.fillStyle = vm.backgroundColor;
  10781. ctx.strokeStyle = vm.borderColor;
  10782. ctx.lineWidth = vm.borderWidth;
  10783. // Corner points, from bottom-left to bottom-right clockwise
  10784. // | 1 2 |
  10785. // | 0 3 |
  10786. var corners = [
  10787. [leftX, vm.base],
  10788. [leftX, top],
  10789. [rightX, top],
  10790. [rightX, vm.base]
  10791. ];
  10792. // Find first (starting) corner with fallback to 'bottom'
  10793. var borders = ['bottom', 'left', 'top', 'right'];
  10794. var startCorner = borders.indexOf(vm.borderSkipped, 0);
  10795. if (startCorner === -1)
  10796. startCorner = 0;
  10797. function cornerAt(index) {
  10798. return corners[(startCorner + index) % 4];
  10799. }
  10800. // Draw rectangle from 'startCorner'
  10801. ctx.moveTo.apply(ctx, cornerAt(0));
  10802. for (var i = 1; i < 4; i++)
  10803. ctx.lineTo.apply(ctx, cornerAt(i));
  10804. ctx.fill();
  10805. if (vm.borderWidth) {
  10806. ctx.stroke();
  10807. }
  10808. },
  10809. height: function() {
  10810. var vm = this._view;
  10811. return vm.base - vm.y;
  10812. },
  10813. inRange: function(mouseX, mouseY) {
  10814. var vm = this._view;
  10815. return vm ?
  10816. (vm.y < vm.base ?
  10817. (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base) :
  10818. (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y)) :
  10819. false;
  10820. },
  10821. inLabelRange: function(mouseX) {
  10822. var vm = this._view;
  10823. return vm ? (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) : false;
  10824. },
  10825. tooltipPosition: function() {
  10826. var vm = this._view;
  10827. return {
  10828. x: vm.x,
  10829. y: vm.y
  10830. };
  10831. }
  10832. });
  10833. };
  10834. },{}],38:[function(require,module,exports){
  10835. "use strict";
  10836. module.exports = function(Chart) {
  10837. var helpers = Chart.helpers;
  10838. // Default config for a category scale
  10839. var defaultConfig = {
  10840. position: "bottom"
  10841. };
  10842. var DatasetScale = Chart.Scale.extend({
  10843. // Implement this so that
  10844. determineDataLimits: function() {
  10845. var me = this;
  10846. me.minIndex = 0;
  10847. me.maxIndex = me.chart.data.labels.length - 1;
  10848. var findIndex;
  10849. if (me.options.ticks.min !== undefined) {
  10850. // user specified min value
  10851. findIndex = helpers.indexOf(me.chart.data.labels, me.options.ticks.min);
  10852. me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
  10853. }
  10854. if (me.options.ticks.max !== undefined) {
  10855. // user specified max value
  10856. findIndex = helpers.indexOf(me.chart.data.labels, me.options.ticks.max);
  10857. me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
  10858. }
  10859. me.min = me.chart.data.labels[me.minIndex];
  10860. me.max = me.chart.data.labels[me.maxIndex];
  10861. },
  10862. buildTicks: function(index) {
  10863. var me = this;
  10864. // If we are viewing some subset of labels, slice the original array
  10865. me.ticks = (me.minIndex === 0 && me.maxIndex === me.chart.data.labels.length - 1) ? me.chart.data.labels : me.chart.data.labels.slice(me.minIndex, me.maxIndex + 1);
  10866. },
  10867. getLabelForIndex: function(index, datasetIndex) {
  10868. return this.ticks[index];
  10869. },
  10870. // Used to get data value locations. Value can either be an index or a numerical value
  10871. getPixelForValue: function(value, index, datasetIndex, includeOffset) {
  10872. var me = this;
  10873. // 1 is added because we need the length but we have the indexes
  10874. var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
  10875. if (me.isHorizontal()) {
  10876. var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
  10877. var valueWidth = innerWidth / offsetAmt;
  10878. var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft;
  10879. if (me.options.gridLines.offsetGridLines && includeOffset) {
  10880. widthOffset += (valueWidth / 2);
  10881. }
  10882. return me.left + Math.round(widthOffset);
  10883. } else {
  10884. var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
  10885. var valueHeight = innerHeight / offsetAmt;
  10886. var heightOffset = (valueHeight * (index - me.minIndex)) + me.paddingTop;
  10887. if (me.options.gridLines.offsetGridLines && includeOffset) {
  10888. heightOffset += (valueHeight / 2);
  10889. }
  10890. return me.top + Math.round(heightOffset);
  10891. }
  10892. },
  10893. getPixelForTick: function(index, includeOffset) {
  10894. return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
  10895. },
  10896. getValueForPixel: function(pixel) {
  10897. var me = this;
  10898. var value;
  10899. var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
  10900. var horz = me.isHorizontal();
  10901. var innerDimension = horz ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
  10902. var valueDimension = innerDimension / offsetAmt;
  10903. if (me.options.gridLines.offsetGridLines) {
  10904. pixel -= (valueDimension / 2);
  10905. }
  10906. pixel -= horz ? me.paddingLeft : me.paddingTop;
  10907. if (pixel <= 0) {
  10908. value = 0;
  10909. } else {
  10910. value = Math.round(pixel / valueDimension);
  10911. }
  10912. return value;
  10913. }
  10914. });
  10915. Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
  10916. };
  10917. },{}],39:[function(require,module,exports){
  10918. "use strict";
  10919. module.exports = function(Chart) {
  10920. var helpers = Chart.helpers;
  10921. var defaultConfig = {
  10922. position: "left",
  10923. ticks: {
  10924. callback: function(tickValue, index, ticks) {
  10925. // If we have lots of ticks, don't use the ones
  10926. var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
  10927. // If we have a number like 2.5 as the delta, figure out how many decimal places we need
  10928. if (Math.abs(delta) > 1) {
  10929. if (tickValue !== Math.floor(tickValue)) {
  10930. // not an integer
  10931. delta = tickValue - Math.floor(tickValue);
  10932. }
  10933. }
  10934. var logDelta = helpers.log10(Math.abs(delta));
  10935. var tickString = '';
  10936. if (tickValue !== 0) {
  10937. var numDecimal = -1 * Math.floor(logDelta);
  10938. numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
  10939. tickString = tickValue.toFixed(numDecimal);
  10940. } else {
  10941. tickString = '0'; // never show decimal places for 0
  10942. }
  10943. return tickString;
  10944. }
  10945. }
  10946. };
  10947. var LinearScale = Chart.LinearScaleBase.extend({
  10948. determineDataLimits: function() {
  10949. var me = this;
  10950. var opts = me.options;
  10951. var tickOpts = opts.ticks;
  10952. var chart = me.chart;
  10953. var data = chart.data;
  10954. var datasets = data.datasets;
  10955. var isHorizontal = me.isHorizontal();
  10956. function IDMatches(meta) {
  10957. return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
  10958. }
  10959. // First Calculate the range
  10960. me.min = null;
  10961. me.max = null;
  10962. if (opts.stacked) {
  10963. var valuesPerType = {};
  10964. var hasPositiveValues = false;
  10965. var hasNegativeValues = false;
  10966. helpers.each(datasets, function(dataset, datasetIndex) {
  10967. var meta = chart.getDatasetMeta(datasetIndex);
  10968. if (valuesPerType[meta.type] === undefined) {
  10969. valuesPerType[meta.type] = {
  10970. positiveValues: [],
  10971. negativeValues: []
  10972. };
  10973. }
  10974. // Store these per type
  10975. var positiveValues = valuesPerType[meta.type].positiveValues;
  10976. var negativeValues = valuesPerType[meta.type].negativeValues;
  10977. if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
  10978. helpers.each(dataset.data, function(rawValue, index) {
  10979. var value = +me.getRightValue(rawValue);
  10980. if (isNaN(value) || meta.data[index].hidden) {
  10981. return;
  10982. }
  10983. positiveValues[index] = positiveValues[index] || 0;
  10984. negativeValues[index] = negativeValues[index] || 0;
  10985. if (opts.relativePoints) {
  10986. positiveValues[index] = 100;
  10987. } else {
  10988. if (value < 0) {
  10989. hasNegativeValues = true;
  10990. negativeValues[index] += value;
  10991. } else {
  10992. hasPositiveValues = true;
  10993. positiveValues[index] += value;
  10994. }
  10995. }
  10996. });
  10997. }
  10998. });
  10999. helpers.each(valuesPerType, function(valuesForType) {
  11000. var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
  11001. var minVal = helpers.min(values);
  11002. var maxVal = helpers.max(values);
  11003. me.min = me.min === null ? minVal : Math.min(me.min, minVal);
  11004. me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
  11005. });
  11006. } else {
  11007. helpers.each(datasets, function(dataset, datasetIndex) {
  11008. var meta = chart.getDatasetMeta(datasetIndex);
  11009. if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
  11010. helpers.each(dataset.data, function(rawValue, index) {
  11011. var value = +me.getRightValue(rawValue);
  11012. if (isNaN(value) || meta.data[index].hidden) {
  11013. return;
  11014. }
  11015. if (me.min === null) {
  11016. me.min = value;
  11017. } else if (value < me.min) {
  11018. me.min = value;
  11019. }
  11020. if (me.max === null) {
  11021. me.max = value;
  11022. } else if (value > me.max) {
  11023. me.max = value;
  11024. }
  11025. });
  11026. }
  11027. });
  11028. }
  11029. // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
  11030. this.handleTickRangeOptions();
  11031. },
  11032. getTickLimit: function() {
  11033. var maxTicks;
  11034. var me = this;
  11035. var tickOpts = me.options.ticks;
  11036. if (me.isHorizontal()) {
  11037. maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
  11038. } else {
  11039. // The factor of 2 used to scale the font size has been experimentally determined.
  11040. var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize);
  11041. maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
  11042. }
  11043. return maxTicks;
  11044. },
  11045. // Called after the ticks are built. We need
  11046. handleDirectionalChanges: function() {
  11047. if (!this.isHorizontal()) {
  11048. // We are in a vertical orientation. The top value is the highest. So reverse the array
  11049. this.ticks.reverse();
  11050. }
  11051. },
  11052. getLabelForIndex: function(index, datasetIndex) {
  11053. return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
  11054. },
  11055. // Utils
  11056. getPixelForValue: function(value, index, datasetIndex, includeOffset) {
  11057. // This must be called after fit has been run so that
  11058. // this.left, this.top, this.right, and this.bottom have been defined
  11059. var me = this;
  11060. var paddingLeft = me.paddingLeft;
  11061. var paddingBottom = me.paddingBottom;
  11062. var start = me.start;
  11063. var rightValue = +me.getRightValue(value);
  11064. var pixel;
  11065. var innerDimension;
  11066. var range = me.end - start;
  11067. if (me.isHorizontal()) {
  11068. innerDimension = me.width - (paddingLeft + me.paddingRight);
  11069. pixel = me.left + (innerDimension / range * (rightValue - start));
  11070. return Math.round(pixel + paddingLeft);
  11071. } else {
  11072. innerDimension = me.height - (me.paddingTop + paddingBottom);
  11073. pixel = (me.bottom - paddingBottom) - (innerDimension / range * (rightValue - start));
  11074. return Math.round(pixel);
  11075. }
  11076. },
  11077. getValueForPixel: function(pixel) {
  11078. var me = this;
  11079. var isHorizontal = me.isHorizontal();
  11080. var paddingLeft = me.paddingLeft;
  11081. var paddingBottom = me.paddingBottom;
  11082. var innerDimension = isHorizontal ? me.width - (paddingLeft + me.paddingRight) : me.height - (me.paddingTop + paddingBottom);
  11083. var offset = (isHorizontal ? pixel - me.left - paddingLeft : me.bottom - paddingBottom - pixel) / innerDimension;
  11084. return me.start + ((me.end - me.start) * offset);
  11085. },
  11086. getPixelForTick: function(index, includeOffset) {
  11087. return this.getPixelForValue(this.ticksAsNumbers[index], null, null, includeOffset);
  11088. }
  11089. });
  11090. Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
  11091. };
  11092. },{}],40:[function(require,module,exports){
  11093. "use strict";
  11094. module.exports = function(Chart) {
  11095. var helpers = Chart.helpers,
  11096. noop = helpers.noop;
  11097. Chart.LinearScaleBase = Chart.Scale.extend({
  11098. handleTickRangeOptions: function() {
  11099. var me = this;
  11100. var opts = me.options;
  11101. var tickOpts = opts.ticks;
  11102. // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
  11103. // do nothing since that would make the chart weird. If the user really wants a weird chart
  11104. // axis, they can manually override it
  11105. if (tickOpts.beginAtZero) {
  11106. var minSign = helpers.sign(me.min);
  11107. var maxSign = helpers.sign(me.max);
  11108. if (minSign < 0 && maxSign < 0) {
  11109. // move the top up to 0
  11110. me.max = 0;
  11111. } else if (minSign > 0 && maxSign > 0) {
  11112. // move the botttom down to 0
  11113. me.min = 0;
  11114. }
  11115. }
  11116. if (tickOpts.min !== undefined) {
  11117. me.min = tickOpts.min;
  11118. } else if (tickOpts.suggestedMin !== undefined) {
  11119. me.min = Math.min(me.min, tickOpts.suggestedMin);
  11120. }
  11121. if (tickOpts.max !== undefined) {
  11122. me.max = tickOpts.max;
  11123. } else if (tickOpts.suggestedMax !== undefined) {
  11124. me.max = Math.max(me.max, tickOpts.suggestedMax);
  11125. }
  11126. if (me.min === me.max) {
  11127. me.max++;
  11128. if (!tickOpts.beginAtZero) {
  11129. me.min--;
  11130. }
  11131. }
  11132. },
  11133. getTickLimit: noop,
  11134. handleDirectionalChanges: noop,
  11135. buildTicks: function() {
  11136. var me = this;
  11137. var opts = me.options;
  11138. var tickOpts = opts.ticks;
  11139. var getValueOrDefault = helpers.getValueOrDefault;
  11140. var isHorizontal = me.isHorizontal();
  11141. var ticks = me.ticks = [];
  11142. // Figure out what the max number of ticks we can support it is based on the size of
  11143. // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
  11144. // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
  11145. // the graph
  11146. var maxTicks = me.getTickLimit();
  11147. // Make sure we always have at least 2 ticks
  11148. maxTicks = Math.max(2, maxTicks);
  11149. // To get a "nice" value for the tick spacing, we will use the appropriately named
  11150. // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
  11151. // for details.
  11152. var spacing;
  11153. var fixedStepSizeSet = (tickOpts.fixedStepSize && tickOpts.fixedStepSize > 0) || (tickOpts.stepSize && tickOpts.stepSize > 0);
  11154. if (fixedStepSizeSet) {
  11155. spacing = getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize);
  11156. } else {
  11157. var niceRange = helpers.niceNum(me.max - me.min, false);
  11158. spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
  11159. }
  11160. var niceMin = Math.floor(me.min / spacing) * spacing;
  11161. var niceMax = Math.ceil(me.max / spacing) * spacing;
  11162. var numSpaces = (niceMax - niceMin) / spacing;
  11163. // If very close to our rounded value, use it.
  11164. if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
  11165. numSpaces = Math.round(numSpaces);
  11166. } else {
  11167. numSpaces = Math.ceil(numSpaces);
  11168. }
  11169. // Put the values into the ticks array
  11170. ticks.push(tickOpts.min !== undefined ? tickOpts.min : niceMin);
  11171. for (var j = 1; j < numSpaces; ++j) {
  11172. ticks.push(niceMin + (j * spacing));
  11173. }
  11174. ticks.push(tickOpts.max !== undefined ? tickOpts.max : niceMax);
  11175. me.handleDirectionalChanges();
  11176. // At this point, we need to update our max and min given the tick values since we have expanded the
  11177. // range of the scale
  11178. me.max = helpers.max(ticks);
  11179. me.min = helpers.min(ticks);
  11180. if (tickOpts.reverse) {
  11181. ticks.reverse();
  11182. me.start = me.max;
  11183. me.end = me.min;
  11184. } else {
  11185. me.start = me.min;
  11186. me.end = me.max;
  11187. }
  11188. },
  11189. convertTicksToLabels: function() {
  11190. var me = this;
  11191. me.ticksAsNumbers = me.ticks.slice();
  11192. me.zeroLineIndex = me.ticks.indexOf(0);
  11193. Chart.Scale.prototype.convertTicksToLabels.call(me);
  11194. },
  11195. });
  11196. };
  11197. },{}],41:[function(require,module,exports){
  11198. "use strict";
  11199. module.exports = function(Chart) {
  11200. var helpers = Chart.helpers;
  11201. var defaultConfig = {
  11202. position: "left",
  11203. // label settings
  11204. ticks: {
  11205. callback: function(value, index, arr) {
  11206. var remain = value / (Math.pow(10, Math.floor(helpers.log10(value))));
  11207. if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
  11208. return value.toExponential();
  11209. } else {
  11210. return '';
  11211. }
  11212. }
  11213. }
  11214. };
  11215. var LogarithmicScale = Chart.Scale.extend({
  11216. determineDataLimits: function() {
  11217. var me = this;
  11218. var opts = me.options;
  11219. var tickOpts = opts.ticks;
  11220. var chart = me.chart;
  11221. var data = chart.data;
  11222. var datasets = data.datasets;
  11223. var getValueOrDefault = helpers.getValueOrDefault;
  11224. var isHorizontal = me.isHorizontal();
  11225. function IDMatches(meta) {
  11226. return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
  11227. }
  11228. // Calculate Range
  11229. me.min = null;
  11230. me.max = null;
  11231. if (opts.stacked) {
  11232. var valuesPerType = {};
  11233. helpers.each(datasets, function(dataset, datasetIndex) {
  11234. var meta = chart.getDatasetMeta(datasetIndex);
  11235. if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
  11236. if (valuesPerType[meta.type] === undefined) {
  11237. valuesPerType[meta.type] = [];
  11238. }
  11239. helpers.each(dataset.data, function(rawValue, index) {
  11240. var values = valuesPerType[meta.type];
  11241. var value = +me.getRightValue(rawValue);
  11242. if (isNaN(value) || meta.data[index].hidden) {
  11243. return;
  11244. }
  11245. values[index] = values[index] || 0;
  11246. if (opts.relativePoints) {
  11247. values[index] = 100;
  11248. } else {
  11249. // Don't need to split positive and negative since the log scale can't handle a 0 crossing
  11250. values[index] += value;
  11251. }
  11252. });
  11253. }
  11254. });
  11255. helpers.each(valuesPerType, function(valuesForType) {
  11256. var minVal = helpers.min(valuesForType);
  11257. var maxVal = helpers.max(valuesForType);
  11258. me.min = me.min === null ? minVal : Math.min(me.min, minVal);
  11259. me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
  11260. });
  11261. } else {
  11262. helpers.each(datasets, function(dataset, datasetIndex) {
  11263. var meta = chart.getDatasetMeta(datasetIndex);
  11264. if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
  11265. helpers.each(dataset.data, function(rawValue, index) {
  11266. var value = +me.getRightValue(rawValue);
  11267. if (isNaN(value) || meta.data[index].hidden) {
  11268. return;
  11269. }
  11270. if (me.min === null) {
  11271. me.min = value;
  11272. } else if (value < me.min) {
  11273. me.min = value;
  11274. }
  11275. if (me.max === null) {
  11276. me.max = value;
  11277. } else if (value > me.max) {
  11278. me.max = value;
  11279. }
  11280. });
  11281. }
  11282. });
  11283. }
  11284. me.min = getValueOrDefault(tickOpts.min, me.min);
  11285. me.max = getValueOrDefault(tickOpts.max, me.max);
  11286. if (me.min === me.max) {
  11287. if (me.min !== 0 && me.min !== null) {
  11288. me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
  11289. me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
  11290. } else {
  11291. me.min = 1;
  11292. me.max = 10;
  11293. }
  11294. }
  11295. },
  11296. buildTicks: function() {
  11297. var me = this;
  11298. var opts = me.options;
  11299. var tickOpts = opts.ticks;
  11300. var getValueOrDefault = helpers.getValueOrDefault;
  11301. // Reset the ticks array. Later on, we will draw a grid line at these positions
  11302. // The array simply contains the numerical value of the spots where ticks will be
  11303. var ticks = me.ticks = [];
  11304. // Figure out what the max number of ticks we can support it is based on the size of
  11305. // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
  11306. // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
  11307. // the graph
  11308. var tickVal = getValueOrDefault(tickOpts.min, Math.pow(10, Math.floor(helpers.log10(me.min))));
  11309. while (tickVal < me.max) {
  11310. ticks.push(tickVal);
  11311. var exp = Math.floor(helpers.log10(tickVal));
  11312. var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
  11313. if (significand === 10) {
  11314. significand = 1;
  11315. ++exp;
  11316. }
  11317. tickVal = significand * Math.pow(10, exp);
  11318. }
  11319. var lastTick = getValueOrDefault(tickOpts.max, tickVal);
  11320. ticks.push(lastTick);
  11321. if (!me.isHorizontal()) {
  11322. // We are in a vertical orientation. The top value is the highest. So reverse the array
  11323. ticks.reverse();
  11324. }
  11325. // At this point, we need to update our max and min given the tick values since we have expanded the
  11326. // range of the scale
  11327. me.max = helpers.max(ticks);
  11328. me.min = helpers.min(ticks);
  11329. if (tickOpts.reverse) {
  11330. ticks.reverse();
  11331. me.start = me.max;
  11332. me.end = me.min;
  11333. } else {
  11334. me.start = me.min;
  11335. me.end = me.max;
  11336. }
  11337. },
  11338. convertTicksToLabels: function() {
  11339. this.tickValues = this.ticks.slice();
  11340. Chart.Scale.prototype.convertTicksToLabels.call(this);
  11341. },
  11342. // Get the correct tooltip label
  11343. getLabelForIndex: function(index, datasetIndex) {
  11344. return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
  11345. },
  11346. getPixelForTick: function(index, includeOffset) {
  11347. return this.getPixelForValue(this.tickValues[index], null, null, includeOffset);
  11348. },
  11349. getPixelForValue: function(value, index, datasetIndex, includeOffset) {
  11350. var me = this;
  11351. var innerDimension;
  11352. var pixel;
  11353. var start = me.start;
  11354. var newVal = +me.getRightValue(value);
  11355. var range = helpers.log10(me.end) - helpers.log10(start);
  11356. var paddingTop = me.paddingTop;
  11357. var paddingBottom = me.paddingBottom;
  11358. var paddingLeft = me.paddingLeft;
  11359. if (me.isHorizontal()) {
  11360. if (newVal === 0) {
  11361. pixel = me.left + paddingLeft;
  11362. } else {
  11363. innerDimension = me.width - (paddingLeft + me.paddingRight);
  11364. pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
  11365. pixel += paddingLeft;
  11366. }
  11367. } else {
  11368. // Bottom - top since pixels increase downard on a screen
  11369. if (newVal === 0) {
  11370. pixel = me.top + paddingTop;
  11371. } else {
  11372. innerDimension = me.height - (paddingTop + paddingBottom);
  11373. pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
  11374. }
  11375. }
  11376. return pixel;
  11377. },
  11378. getValueForPixel: function(pixel) {
  11379. var me = this;
  11380. var offset;
  11381. var range = helpers.log10(me.end) - helpers.log10(me.start);
  11382. var value;
  11383. var innerDimension;
  11384. if (me.isHorizontal()) {
  11385. innerDimension = me.width - (me.paddingLeft + me.paddingRight);
  11386. value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension);
  11387. } else {
  11388. innerDimension = me.height - (me.paddingTop + me.paddingBottom);
  11389. value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start;
  11390. }
  11391. return value;
  11392. }
  11393. });
  11394. Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig);
  11395. };
  11396. },{}],42:[function(require,module,exports){
  11397. "use strict";
  11398. module.exports = function(Chart) {
  11399. var helpers = Chart.helpers;
  11400. var globalDefaults = Chart.defaults.global;
  11401. var defaultConfig = {
  11402. display: true,
  11403. //Boolean - Whether to animate scaling the chart from the centre
  11404. animate: true,
  11405. lineArc: false,
  11406. position: "chartArea",
  11407. angleLines: {
  11408. display: true,
  11409. color: "rgba(0, 0, 0, 0.1)",
  11410. lineWidth: 1
  11411. },
  11412. // label settings
  11413. ticks: {
  11414. //Boolean - Show a backdrop to the scale label
  11415. showLabelBackdrop: true,
  11416. //String - The colour of the label backdrop
  11417. backdropColor: "rgba(255,255,255,0.75)",
  11418. //Number - The backdrop padding above & below the label in pixels
  11419. backdropPaddingY: 2,
  11420. //Number - The backdrop padding to the side of the label in pixels
  11421. backdropPaddingX: 2
  11422. },
  11423. pointLabels: {
  11424. //Number - Point label font size in pixels
  11425. fontSize: 10,
  11426. //Function - Used to convert point labels
  11427. callback: function(label) {
  11428. return label;
  11429. }
  11430. }
  11431. };
  11432. var LinearRadialScale = Chart.LinearScaleBase.extend({
  11433. getValueCount: function() {
  11434. return this.chart.data.labels.length;
  11435. },
  11436. setDimensions: function() {
  11437. var me = this;
  11438. var opts = me.options;
  11439. var tickOpts = opts.ticks;
  11440. // Set the unconstrained dimension before label rotation
  11441. me.width = me.maxWidth;
  11442. me.height = me.maxHeight;
  11443. me.xCenter = Math.round(me.width / 2);
  11444. me.yCenter = Math.round(me.height / 2);
  11445. var minSize = helpers.min([me.height, me.width]);
  11446. var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
  11447. me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
  11448. },
  11449. determineDataLimits: function() {
  11450. var me = this;
  11451. var chart = me.chart;
  11452. me.min = null;
  11453. me.max = null;
  11454. helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
  11455. if (chart.isDatasetVisible(datasetIndex)) {
  11456. var meta = chart.getDatasetMeta(datasetIndex);
  11457. helpers.each(dataset.data, function(rawValue, index) {
  11458. var value = +me.getRightValue(rawValue);
  11459. if (isNaN(value) || meta.data[index].hidden) {
  11460. return;
  11461. }
  11462. if (me.min === null) {
  11463. me.min = value;
  11464. } else if (value < me.min) {
  11465. me.min = value;
  11466. }
  11467. if (me.max === null) {
  11468. me.max = value;
  11469. } else if (value > me.max) {
  11470. me.max = value;
  11471. }
  11472. });
  11473. }
  11474. });
  11475. // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
  11476. me.handleTickRangeOptions();
  11477. },
  11478. getTickLimit: function() {
  11479. var tickOpts = this.options.ticks;
  11480. var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
  11481. return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
  11482. },
  11483. convertTicksToLabels: function() {
  11484. var me = this;
  11485. Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
  11486. // Point labels
  11487. me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
  11488. },
  11489. getLabelForIndex: function(index, datasetIndex) {
  11490. return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
  11491. },
  11492. fit: function() {
  11493. /*
  11494. * Right, this is really confusing and there is a lot of maths going on here
  11495. * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
  11496. *
  11497. * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
  11498. *
  11499. * Solution:
  11500. *
  11501. * We assume the radius of the polygon is half the size of the canvas at first
  11502. * at each index we check if the text overlaps.
  11503. *
  11504. * Where it does, we store that angle and that index.
  11505. *
  11506. * After finding the largest index and angle we calculate how much we need to remove
  11507. * from the shape radius to move the point inwards by that x.
  11508. *
  11509. * We average the left and right distances to get the maximum shape radius that can fit in the box
  11510. * along with labels.
  11511. *
  11512. * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
  11513. * on each side, removing that from the size, halving it and adding the left x protrusion width.
  11514. *
  11515. * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
  11516. * and position it in the most space efficient manner
  11517. *
  11518. * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
  11519. */
  11520. var pointLabels = this.options.pointLabels;
  11521. var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize);
  11522. var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle);
  11523. var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily);
  11524. var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
  11525. // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
  11526. // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
  11527. var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]),
  11528. pointPosition,
  11529. i,
  11530. textWidth,
  11531. halfTextWidth,
  11532. furthestRight = this.width,
  11533. furthestRightIndex,
  11534. furthestRightAngle,
  11535. furthestLeft = 0,
  11536. furthestLeftIndex,
  11537. furthestLeftAngle,
  11538. xProtrusionLeft,
  11539. xProtrusionRight,
  11540. radiusReductionRight,
  11541. radiusReductionLeft,
  11542. maxWidthRadius;
  11543. this.ctx.font = pointLabeFont;
  11544. for (i = 0; i < this.getValueCount(); i++) {
  11545. // 5px to space the text slightly out - similar to what we do in the draw function.
  11546. pointPosition = this.getPointPosition(i, largestPossibleRadius);
  11547. textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5;
  11548. if (i === 0 || i === this.getValueCount() / 2) {
  11549. // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
  11550. // of the radar chart, so text will be aligned centrally, so we'll half it and compare
  11551. // w/left and right text sizes
  11552. halfTextWidth = textWidth / 2;
  11553. if (pointPosition.x + halfTextWidth > furthestRight) {
  11554. furthestRight = pointPosition.x + halfTextWidth;
  11555. furthestRightIndex = i;
  11556. }
  11557. if (pointPosition.x - halfTextWidth < furthestLeft) {
  11558. furthestLeft = pointPosition.x - halfTextWidth;
  11559. furthestLeftIndex = i;
  11560. }
  11561. } else if (i < this.getValueCount() / 2) {
  11562. // Less than half the values means we'll left align the text
  11563. if (pointPosition.x + textWidth > furthestRight) {
  11564. furthestRight = pointPosition.x + textWidth;
  11565. furthestRightIndex = i;
  11566. }
  11567. } else if (i > this.getValueCount() / 2) {
  11568. // More than half the values means we'll right align the text
  11569. if (pointPosition.x - textWidth < furthestLeft) {
  11570. furthestLeft = pointPosition.x - textWidth;
  11571. furthestLeftIndex = i;
  11572. }
  11573. }
  11574. }
  11575. xProtrusionLeft = furthestLeft;
  11576. xProtrusionRight = Math.ceil(furthestRight - this.width);
  11577. furthestRightAngle = this.getIndexAngle(furthestRightIndex);
  11578. furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
  11579. radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
  11580. radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
  11581. // Ensure we actually need to reduce the size of the chart
  11582. radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
  11583. radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
  11584. this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2);
  11585. this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
  11586. },
  11587. setCenterPoint: function(leftMovement, rightMovement) {
  11588. var me = this;
  11589. var maxRight = me.width - rightMovement - me.drawingArea,
  11590. maxLeft = leftMovement + me.drawingArea;
  11591. me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
  11592. // Always vertically in the centre as the text height doesn't change
  11593. me.yCenter = Math.round((me.height / 2) + me.top);
  11594. },
  11595. getIndexAngle: function(index) {
  11596. var angleMultiplier = (Math.PI * 2) / this.getValueCount();
  11597. // Start from the top instead of right, so remove a quarter of the circle
  11598. return index * angleMultiplier - (Math.PI / 2);
  11599. },
  11600. getDistanceFromCenterForValue: function(value) {
  11601. var me = this;
  11602. if (value === null) {
  11603. return 0; // null always in center
  11604. }
  11605. // Take into account half font size + the yPadding of the top value
  11606. var scalingFactor = me.drawingArea / (me.max - me.min);
  11607. if (me.options.reverse) {
  11608. return (me.max - value) * scalingFactor;
  11609. } else {
  11610. return (value - me.min) * scalingFactor;
  11611. }
  11612. },
  11613. getPointPosition: function(index, distanceFromCenter) {
  11614. var me = this;
  11615. var thisAngle = me.getIndexAngle(index);
  11616. return {
  11617. x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
  11618. y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
  11619. };
  11620. },
  11621. getPointPositionForValue: function(index, value) {
  11622. return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
  11623. },
  11624. getBasePosition: function() {
  11625. var me = this;
  11626. var min = me.min;
  11627. var max = me.max;
  11628. return me.getPointPositionForValue(0,
  11629. me.beginAtZero? 0:
  11630. min < 0 && max < 0? max :
  11631. min > 0 && max > 0? min :
  11632. 0);
  11633. },
  11634. draw: function() {
  11635. var me = this;
  11636. var opts = me.options;
  11637. var gridLineOpts = opts.gridLines;
  11638. var tickOpts = opts.ticks;
  11639. var angleLineOpts = opts.angleLines;
  11640. var pointLabelOpts = opts.pointLabels;
  11641. var getValueOrDefault = helpers.getValueOrDefault;
  11642. if (opts.display) {
  11643. var ctx = me.ctx;
  11644. // Tick Font
  11645. var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
  11646. var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
  11647. var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
  11648. var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
  11649. helpers.each(me.ticks, function(label, index) {
  11650. // Don't draw a centre value (if it is minimum)
  11651. if (index > 0 || opts.reverse) {
  11652. var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
  11653. var yHeight = me.yCenter - yCenterOffset;
  11654. // Draw circular lines around the scale
  11655. if (gridLineOpts.display && index !== 0) {
  11656. ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
  11657. ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
  11658. if (opts.lineArc) {
  11659. // Draw circular arcs between the points
  11660. ctx.beginPath();
  11661. ctx.arc(me.xCenter, me.yCenter, yCenterOffset, 0, Math.PI * 2);
  11662. ctx.closePath();
  11663. ctx.stroke();
  11664. } else {
  11665. // Draw straight lines connecting each index
  11666. ctx.beginPath();
  11667. for (var i = 0; i < me.getValueCount(); i++) {
  11668. var pointPosition = me.getPointPosition(i, yCenterOffset);
  11669. if (i === 0) {
  11670. ctx.moveTo(pointPosition.x, pointPosition.y);
  11671. } else {
  11672. ctx.lineTo(pointPosition.x, pointPosition.y);
  11673. }
  11674. }
  11675. ctx.closePath();
  11676. ctx.stroke();
  11677. }
  11678. }
  11679. if (tickOpts.display) {
  11680. var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
  11681. ctx.font = tickLabelFont;
  11682. if (tickOpts.showLabelBackdrop) {
  11683. var labelWidth = ctx.measureText(label).width;
  11684. ctx.fillStyle = tickOpts.backdropColor;
  11685. ctx.fillRect(
  11686. me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX,
  11687. yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY,
  11688. labelWidth + tickOpts.backdropPaddingX * 2,
  11689. tickFontSize + tickOpts.backdropPaddingY * 2
  11690. );
  11691. }
  11692. ctx.textAlign = 'center';
  11693. ctx.textBaseline = "middle";
  11694. ctx.fillStyle = tickFontColor;
  11695. ctx.fillText(label, me.xCenter, yHeight);
  11696. }
  11697. }
  11698. });
  11699. if (!opts.lineArc) {
  11700. ctx.lineWidth = angleLineOpts.lineWidth;
  11701. ctx.strokeStyle = angleLineOpts.color;
  11702. var outerDistance = me.getDistanceFromCenterForValue(opts.reverse ? me.min : me.max);
  11703. // Point Label Font
  11704. var pointLabelFontSize = getValueOrDefault(pointLabelOpts.fontSize, globalDefaults.defaultFontSize);
  11705. var pointLabeFontStyle = getValueOrDefault(pointLabelOpts.fontStyle, globalDefaults.defaultFontStyle);
  11706. var pointLabeFontFamily = getValueOrDefault(pointLabelOpts.fontFamily, globalDefaults.defaultFontFamily);
  11707. var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
  11708. for (var i = me.getValueCount() - 1; i >= 0; i--) {
  11709. if (angleLineOpts.display) {
  11710. var outerPosition = me.getPointPosition(i, outerDistance);
  11711. ctx.beginPath();
  11712. ctx.moveTo(me.xCenter, me.yCenter);
  11713. ctx.lineTo(outerPosition.x, outerPosition.y);
  11714. ctx.stroke();
  11715. ctx.closePath();
  11716. }
  11717. // Extra 3px out for some label spacing
  11718. var pointLabelPosition = me.getPointPosition(i, outerDistance + 5);
  11719. // Keep this in loop since we may support array properties here
  11720. var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
  11721. ctx.font = pointLabeFont;
  11722. ctx.fillStyle = pointLabelFontColor;
  11723. var pointLabels = me.pointLabels,
  11724. labelsCount = pointLabels.length,
  11725. halfLabelsCount = pointLabels.length / 2,
  11726. quarterLabelsCount = halfLabelsCount / 2,
  11727. upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
  11728. exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
  11729. if (i === 0) {
  11730. ctx.textAlign = 'center';
  11731. } else if (i === halfLabelsCount) {
  11732. ctx.textAlign = 'center';
  11733. } else if (i < halfLabelsCount) {
  11734. ctx.textAlign = 'left';
  11735. } else {
  11736. ctx.textAlign = 'right';
  11737. }
  11738. // Set the correct text baseline based on outer positioning
  11739. if (exactQuarter) {
  11740. ctx.textBaseline = 'middle';
  11741. } else if (upperHalf) {
  11742. ctx.textBaseline = 'bottom';
  11743. } else {
  11744. ctx.textBaseline = 'top';
  11745. }
  11746. ctx.fillText(pointLabels[i] ? pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y);
  11747. }
  11748. }
  11749. }
  11750. }
  11751. });
  11752. Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig);
  11753. };
  11754. },{}],43:[function(require,module,exports){
  11755. /*global window: false */
  11756. "use strict";
  11757. var moment = require(6);
  11758. moment = typeof(moment) === 'function' ? moment : window.moment;
  11759. module.exports = function(Chart) {
  11760. var helpers = Chart.helpers;
  11761. var time = {
  11762. units: [{
  11763. name: 'millisecond',
  11764. steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
  11765. }, {
  11766. name: 'second',
  11767. steps: [1, 2, 5, 10, 30]
  11768. }, {
  11769. name: 'minute',
  11770. steps: [1, 2, 5, 10, 30]
  11771. }, {
  11772. name: 'hour',
  11773. steps: [1, 2, 3, 6, 12]
  11774. }, {
  11775. name: 'day',
  11776. steps: [1, 2, 5]
  11777. }, {
  11778. name: 'week',
  11779. maxStep: 4
  11780. }, {
  11781. name: 'month',
  11782. maxStep: 3
  11783. }, {
  11784. name: 'quarter',
  11785. maxStep: 4
  11786. }, {
  11787. name: 'year',
  11788. maxStep: false
  11789. }]
  11790. };
  11791. var defaultConfig = {
  11792. position: "bottom",
  11793. time: {
  11794. 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
  11795. format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
  11796. unit: false, // false == automatic or override with week, month, year, etc.
  11797. round: false, // none, or override with week, month, year, etc.
  11798. displayFormat: false, // DEPRECATED
  11799. isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
  11800. // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
  11801. displayFormats: {
  11802. 'millisecond': 'h:mm:ss.SSS a', // 11:20:01.123 AM,
  11803. 'second': 'h:mm:ss a', // 11:20:01 AM
  11804. 'minute': 'h:mm:ss a', // 11:20:01 AM
  11805. 'hour': 'MMM D, hA', // Sept 4, 5PM
  11806. 'day': 'll', // Sep 4 2015
  11807. 'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
  11808. 'month': 'MMM YYYY', // Sept 2015
  11809. 'quarter': '[Q]Q - YYYY', // Q3
  11810. 'year': 'YYYY' // 2015
  11811. }
  11812. },
  11813. ticks: {
  11814. autoSkip: false
  11815. }
  11816. };
  11817. var TimeScale = Chart.Scale.extend({
  11818. initialize: function() {
  11819. if (!moment) {
  11820. 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');
  11821. }
  11822. Chart.Scale.prototype.initialize.call(this);
  11823. },
  11824. getLabelMoment: function(datasetIndex, index) {
  11825. return this.labelMoments[datasetIndex][index];
  11826. },
  11827. getMomentStartOf: function(tick) {
  11828. var me = this;
  11829. if (me.options.time.unit === 'week' && me.options.time.isoWeekday !== false) {
  11830. return tick.clone().startOf('isoWeek').isoWeekday(me.options.time.isoWeekday);
  11831. } else {
  11832. return tick.clone().startOf(me.tickUnit);
  11833. }
  11834. },
  11835. determineDataLimits: function() {
  11836. var me = this;
  11837. me.labelMoments = [];
  11838. // Only parse these once. If the dataset does not have data as x,y pairs, we will use
  11839. // these
  11840. var scaleLabelMoments = [];
  11841. if (me.chart.data.labels && me.chart.data.labels.length > 0) {
  11842. helpers.each(me.chart.data.labels, function(label, index) {
  11843. var labelMoment = me.parseTime(label);
  11844. if (labelMoment.isValid()) {
  11845. if (me.options.time.round) {
  11846. labelMoment.startOf(me.options.time.round);
  11847. }
  11848. scaleLabelMoments.push(labelMoment);
  11849. }
  11850. }, me);
  11851. me.firstTick = moment.min.call(me, scaleLabelMoments);
  11852. me.lastTick = moment.max.call(me, scaleLabelMoments);
  11853. } else {
  11854. me.firstTick = null;
  11855. me.lastTick = null;
  11856. }
  11857. helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
  11858. var momentsForDataset = [];
  11859. var datasetVisible = me.chart.isDatasetVisible(datasetIndex);
  11860. if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) {
  11861. helpers.each(dataset.data, function(value, index) {
  11862. var labelMoment = me.parseTime(me.getRightValue(value));
  11863. if (labelMoment.isValid()) {
  11864. if (me.options.time.round) {
  11865. labelMoment.startOf(me.options.time.round);
  11866. }
  11867. momentsForDataset.push(labelMoment);
  11868. if (datasetVisible) {
  11869. // May have gone outside the scale ranges, make sure we keep the first and last ticks updated
  11870. me.firstTick = me.firstTick !== null ? moment.min(me.firstTick, labelMoment) : labelMoment;
  11871. me.lastTick = me.lastTick !== null ? moment.max(me.lastTick, labelMoment) : labelMoment;
  11872. }
  11873. }
  11874. }, me);
  11875. } else {
  11876. // We have no labels. Use the ones from the scale
  11877. momentsForDataset = scaleLabelMoments;
  11878. }
  11879. me.labelMoments.push(momentsForDataset);
  11880. }, me);
  11881. // Set these after we've done all the data
  11882. if (me.options.time.min) {
  11883. me.firstTick = me.parseTime(me.options.time.min);
  11884. }
  11885. if (me.options.time.max) {
  11886. me.lastTick = me.parseTime(me.options.time.max);
  11887. }
  11888. // We will modify these, so clone for later
  11889. me.firstTick = (me.firstTick || moment()).clone();
  11890. me.lastTick = (me.lastTick || moment()).clone();
  11891. },
  11892. buildTicks: function(index) {
  11893. var me = this;
  11894. me.ctx.save();
  11895. var tickFontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
  11896. var tickFontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
  11897. var tickFontFamily = helpers.getValueOrDefault(me.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
  11898. var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
  11899. me.ctx.font = tickLabelFont;
  11900. me.ticks = [];
  11901. me.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step
  11902. me.scaleSizeInUnits = 0; // How large the scale is in the base unit (seconds, minutes, etc)
  11903. // Set unit override if applicable
  11904. if (me.options.time.unit) {
  11905. me.tickUnit = me.options.time.unit || 'day';
  11906. me.displayFormat = me.options.time.displayFormats[me.tickUnit];
  11907. me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
  11908. me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, 1);
  11909. } else {
  11910. // Determine the smallest needed unit of the time
  11911. var innerWidth = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
  11912. // Crude approximation of what the label length might be
  11913. var tempFirstLabel = me.tickFormatFunction(me.firstTick, 0, []);
  11914. var tickLabelWidth = me.ctx.measureText(tempFirstLabel).width;
  11915. var cosRotation = Math.cos(helpers.toRadians(me.options.ticks.maxRotation));
  11916. var sinRotation = Math.sin(helpers.toRadians(me.options.ticks.maxRotation));
  11917. tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
  11918. var labelCapacity = innerWidth / (tickLabelWidth);
  11919. // Start as small as possible
  11920. me.tickUnit = 'millisecond';
  11921. me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
  11922. me.displayFormat = me.options.time.displayFormats[me.tickUnit];
  11923. var unitDefinitionIndex = 0;
  11924. var unitDefinition = time.units[unitDefinitionIndex];
  11925. // While we aren't ideal and we don't have units left
  11926. while (unitDefinitionIndex < time.units.length) {
  11927. // Can we scale this unit. If `false` we can scale infinitely
  11928. me.unitScale = 1;
  11929. if (helpers.isArray(unitDefinition.steps) && Math.ceil(me.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) {
  11930. // Use one of the prefedined steps
  11931. for (var idx = 0; idx < unitDefinition.steps.length; ++idx) {
  11932. if (unitDefinition.steps[idx] >= Math.ceil(me.scaleSizeInUnits / labelCapacity)) {
  11933. me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, unitDefinition.steps[idx]);
  11934. break;
  11935. }
  11936. }
  11937. break;
  11938. } else if ((unitDefinition.maxStep === false) || (Math.ceil(me.scaleSizeInUnits / labelCapacity) < unitDefinition.maxStep)) {
  11939. // We have a max step. Scale this unit
  11940. me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, Math.ceil(me.scaleSizeInUnits / labelCapacity));
  11941. break;
  11942. } else {
  11943. // Move to the next unit up
  11944. ++unitDefinitionIndex;
  11945. unitDefinition = time.units[unitDefinitionIndex];
  11946. me.tickUnit = unitDefinition.name;
  11947. var leadingUnitBuffer = me.firstTick.diff(me.getMomentStartOf(me.firstTick), me.tickUnit, true);
  11948. var trailingUnitBuffer = me.getMomentStartOf(me.lastTick.clone().add(1, me.tickUnit)).diff(me.lastTick, me.tickUnit, true);
  11949. me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true) + leadingUnitBuffer + trailingUnitBuffer;
  11950. me.displayFormat = me.options.time.displayFormats[unitDefinition.name];
  11951. }
  11952. }
  11953. }
  11954. var roundedStart;
  11955. // Only round the first tick if we have no hard minimum
  11956. if (!me.options.time.min) {
  11957. me.firstTick = me.getMomentStartOf(me.firstTick);
  11958. roundedStart = me.firstTick;
  11959. } else {
  11960. roundedStart = me.getMomentStartOf(me.firstTick);
  11961. }
  11962. // Only round the last tick if we have no hard maximum
  11963. if (!me.options.time.max) {
  11964. var roundedEnd = me.getMomentStartOf(me.lastTick);
  11965. if (roundedEnd.diff(me.lastTick, me.tickUnit, true) !== 0) {
  11966. // Do not use end of because we need me to be in the next time unit
  11967. me.lastTick = me.getMomentStartOf(me.lastTick.add(1, me.tickUnit));
  11968. }
  11969. }
  11970. me.smallestLabelSeparation = me.width;
  11971. helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
  11972. for (var i = 1; i < me.labelMoments[datasetIndex].length; i++) {
  11973. me.smallestLabelSeparation = Math.min(me.smallestLabelSeparation, me.labelMoments[datasetIndex][i].diff(me.labelMoments[datasetIndex][i - 1], me.tickUnit, true));
  11974. }
  11975. }, me);
  11976. // Tick displayFormat override
  11977. if (me.options.time.displayFormat) {
  11978. me.displayFormat = me.options.time.displayFormat;
  11979. }
  11980. // first tick. will have been rounded correctly if options.time.min is not specified
  11981. me.ticks.push(me.firstTick.clone());
  11982. // For every unit in between the first and last moment, create a moment and add it to the ticks tick
  11983. for (var i = 1; i <= me.scaleSizeInUnits; ++i) {
  11984. var newTick = roundedStart.clone().add(i, me.tickUnit);
  11985. // Are we greater than the max time
  11986. if (me.options.time.max && newTick.diff(me.lastTick, me.tickUnit, true) >= 0) {
  11987. break;
  11988. }
  11989. if (i % me.unitScale === 0) {
  11990. me.ticks.push(newTick);
  11991. }
  11992. }
  11993. // Always show the right tick
  11994. var diff = me.ticks[me.ticks.length - 1].diff(me.lastTick, me.tickUnit);
  11995. if (diff !== 0 || me.scaleSizeInUnits === 0) {
  11996. // this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
  11997. // but the last tick was not rounded.
  11998. if (me.options.time.max) {
  11999. me.ticks.push(me.lastTick.clone());
  12000. me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true);
  12001. } else {
  12002. me.ticks.push(me.lastTick.clone());
  12003. me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
  12004. }
  12005. }
  12006. me.ctx.restore();
  12007. },
  12008. // Get tooltip label
  12009. getLabelForIndex: function(index, datasetIndex) {
  12010. var me = this;
  12011. var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : '';
  12012. if (typeof me.chart.data.datasets[datasetIndex].data[0] === 'object') {
  12013. label = me.getRightValue(me.chart.data.datasets[datasetIndex].data[index]);
  12014. }
  12015. // Format nicely
  12016. if (me.options.time.tooltipFormat) {
  12017. label = me.parseTime(label).format(me.options.time.tooltipFormat);
  12018. }
  12019. return label;
  12020. },
  12021. // Function to format an individual tick mark
  12022. tickFormatFunction: function tickFormatFunction(tick, index, ticks) {
  12023. var formattedTick = tick.format(this.displayFormat);
  12024. var tickOpts = this.options.ticks;
  12025. var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback);
  12026. if (callback) {
  12027. return callback(formattedTick, index, ticks);
  12028. } else {
  12029. return formattedTick;
  12030. }
  12031. },
  12032. convertTicksToLabels: function() {
  12033. var me = this;
  12034. me.tickMoments = me.ticks;
  12035. me.ticks = me.ticks.map(me.tickFormatFunction, me);
  12036. },
  12037. getPixelForValue: function(value, index, datasetIndex, includeOffset) {
  12038. var me = this;
  12039. var labelMoment = value && value.isValid && value.isValid() ? value : me.getLabelMoment(datasetIndex, index);
  12040. if (labelMoment) {
  12041. var offset = labelMoment.diff(me.firstTick, me.tickUnit, true);
  12042. var decimal = offset / me.scaleSizeInUnits;
  12043. if (me.isHorizontal()) {
  12044. var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
  12045. var valueWidth = innerWidth / Math.max(me.ticks.length - 1, 1);
  12046. var valueOffset = (innerWidth * decimal) + me.paddingLeft;
  12047. return me.left + Math.round(valueOffset);
  12048. } else {
  12049. var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
  12050. var valueHeight = innerHeight / Math.max(me.ticks.length - 1, 1);
  12051. var heightOffset = (innerHeight * decimal) + me.paddingTop;
  12052. return me.top + Math.round(heightOffset);
  12053. }
  12054. }
  12055. },
  12056. getPixelForTick: function(index, includeOffset) {
  12057. return this.getPixelForValue(this.tickMoments[index], null, null, includeOffset);
  12058. },
  12059. getValueForPixel: function(pixel) {
  12060. var me = this;
  12061. var innerDimension = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
  12062. var offset = (pixel - (me.isHorizontal() ? me.left + me.paddingLeft : me.top + me.paddingTop)) / innerDimension;
  12063. offset *= me.scaleSizeInUnits;
  12064. return me.firstTick.clone().add(moment.duration(offset, me.tickUnit).asSeconds(), 'seconds');
  12065. },
  12066. parseTime: function(label) {
  12067. var me = this;
  12068. if (typeof me.options.time.parser === 'string') {
  12069. return moment(label, me.options.time.parser);
  12070. }
  12071. if (typeof me.options.time.parser === 'function') {
  12072. return me.options.time.parser(label);
  12073. }
  12074. // Date objects
  12075. if (typeof label.getMonth === 'function' || typeof label === 'number') {
  12076. return moment(label);
  12077. }
  12078. // Moment support
  12079. if (label.isValid && label.isValid()) {
  12080. return label;
  12081. }
  12082. // Custom parsing (return an instance of moment)
  12083. if (typeof me.options.time.format !== 'string' && me.options.time.format.call) {
  12084. console.warn("options.time.format is deprecated and replaced by options.time.parser. See http://nnnick.github.io/Chart.js/docs-v2/#scales-time-scale");
  12085. return me.options.time.format(label);
  12086. }
  12087. // Moment format parsing
  12088. return moment(label, me.options.time.format);
  12089. }
  12090. });
  12091. Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);
  12092. };
  12093. },{"6":6}]},{},[7])(7)
  12094. });