app/assets/javascripts/pghero/Chart.bundle.js in pghero-1.7.0 vs app/assets/javascripts/pghero/Chart.bundle.js in pghero-2.0.0
- old
+ new
@@ -1,11 +1,11 @@
/*!
* Chart.js
* http://chartjs.org/
- * Version: 2.2.2
+ * Version: 2.5.0
*
- * Copyright 2016 Nick Downie
+ * Copyright 2017 Nick Downie
* Released under the MIT license
* https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
*/
(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){
/* MIT license */
@@ -1659,4235 +1659,4345 @@
"yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50]
};
},{}],6:[function(require,module,exports){
//! moment.js
-//! version : 2.14.1
+//! version : 2.17.1
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.moment = factory()
-}(this, function () { 'use strict';
+}(this, (function () { 'use strict';
- var hookCallback;
+var hookCallback;
- function utils_hooks__hooks () {
- return hookCallback.apply(null, arguments);
- }
+function hooks () {
+ return hookCallback.apply(null, arguments);
+}
- // This is done to register the method called with moment()
- // without creating circular dependencies.
- function setHookCallback (callback) {
- hookCallback = callback;
- }
+// This is done to register the method called with moment()
+// without creating circular dependencies.
+function setHookCallback (callback) {
+ hookCallback = callback;
+}
- function isArray(input) {
- return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
- }
+function isArray(input) {
+ return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
+}
- function isObject(input) {
- return Object.prototype.toString.call(input) === '[object Object]';
- }
+function isObject(input) {
+ // IE8 will treat undefined and null as object if it wasn't for
+ // input != null
+ return input != null && Object.prototype.toString.call(input) === '[object Object]';
+}
- function isObjectEmpty(obj) {
- var k;
- for (k in obj) {
- // even if its not own property I'd still call it non-empty
- return false;
- }
- return true;
+function isObjectEmpty(obj) {
+ var k;
+ for (k in obj) {
+ // even if its not own property I'd still call it non-empty
+ return false;
}
+ return true;
+}
- function isDate(input) {
- return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
- }
+function isNumber(input) {
+ return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
+}
- function map(arr, fn) {
- var res = [], i;
- for (i = 0; i < arr.length; ++i) {
- res.push(fn(arr[i], i));
- }
- return res;
- }
+function isDate(input) {
+ return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
+}
- function hasOwnProp(a, b) {
- return Object.prototype.hasOwnProperty.call(a, b);
+function map(arr, fn) {
+ var res = [], i;
+ for (i = 0; i < arr.length; ++i) {
+ res.push(fn(arr[i], i));
}
+ return res;
+}
- function extend(a, b) {
- for (var i in b) {
- if (hasOwnProp(b, i)) {
- a[i] = b[i];
- }
- }
+function hasOwnProp(a, b) {
+ return Object.prototype.hasOwnProperty.call(a, b);
+}
- if (hasOwnProp(b, 'toString')) {
- a.toString = b.toString;
+function extend(a, b) {
+ for (var i in b) {
+ if (hasOwnProp(b, i)) {
+ a[i] = b[i];
}
-
- if (hasOwnProp(b, 'valueOf')) {
- a.valueOf = b.valueOf;
- }
-
- return a;
}
- function create_utc__createUTC (input, format, locale, strict) {
- return createLocalOrUTC(input, format, locale, strict, true).utc();
+ if (hasOwnProp(b, 'toString')) {
+ a.toString = b.toString;
}
- function defaultParsingFlags() {
- // We need to deep clone this object.
- return {
- empty : false,
- unusedTokens : [],
- unusedInput : [],
- overflow : -2,
- charsLeftOver : 0,
- nullInput : false,
- invalidMonth : null,
- invalidFormat : false,
- userInvalidated : false,
- iso : false,
- parsedDateParts : [],
- meridiem : null
- };
+ if (hasOwnProp(b, 'valueOf')) {
+ a.valueOf = b.valueOf;
}
- function getParsingFlags(m) {
- if (m._pf == null) {
- m._pf = defaultParsingFlags();
- }
- return m._pf;
- }
+ return a;
+}
- var some;
- if (Array.prototype.some) {
- some = Array.prototype.some;
- } else {
- some = function (fun) {
- var t = Object(this);
- var len = t.length >>> 0;
+function createUTC (input, format, locale, strict) {
+ return createLocalOrUTC(input, format, locale, strict, true).utc();
+}
- for (var i = 0; i < len; i++) {
- if (i in t && fun.call(this, t[i], i, t)) {
- return true;
- }
- }
+function defaultParsingFlags() {
+ // We need to deep clone this object.
+ return {
+ empty : false,
+ unusedTokens : [],
+ unusedInput : [],
+ overflow : -2,
+ charsLeftOver : 0,
+ nullInput : false,
+ invalidMonth : null,
+ invalidFormat : false,
+ userInvalidated : false,
+ iso : false,
+ parsedDateParts : [],
+ meridiem : null
+ };
+}
- return false;
- };
+function getParsingFlags(m) {
+ if (m._pf == null) {
+ m._pf = defaultParsingFlags();
}
+ return m._pf;
+}
- function valid__isValid(m) {
- if (m._isValid == null) {
- var flags = getParsingFlags(m);
- var parsedParts = some.call(flags.parsedDateParts, function (i) {
- return i != null;
- });
- m._isValid = !isNaN(m._d.getTime()) &&
- flags.overflow < 0 &&
- !flags.empty &&
- !flags.invalidMonth &&
- !flags.invalidWeekday &&
- !flags.nullInput &&
- !flags.invalidFormat &&
- !flags.userInvalidated &&
- (!flags.meridiem || (flags.meridiem && parsedParts));
+var some;
+if (Array.prototype.some) {
+ some = Array.prototype.some;
+} else {
+ some = function (fun) {
+ var t = Object(this);
+ var len = t.length >>> 0;
- if (m._strict) {
- m._isValid = m._isValid &&
- flags.charsLeftOver === 0 &&
- flags.unusedTokens.length === 0 &&
- flags.bigHour === undefined;
+ for (var i = 0; i < len; i++) {
+ if (i in t && fun.call(this, t[i], i, t)) {
+ return true;
}
}
- return m._isValid;
- }
- function valid__createInvalid (flags) {
- var m = create_utc__createUTC(NaN);
- if (flags != null) {
- extend(getParsingFlags(m), flags);
+ return false;
+ };
+}
+
+var some$1 = some;
+
+function isValid(m) {
+ if (m._isValid == null) {
+ var flags = getParsingFlags(m);
+ var parsedParts = some$1.call(flags.parsedDateParts, function (i) {
+ return i != null;
+ });
+ var isNowValid = !isNaN(m._d.getTime()) &&
+ flags.overflow < 0 &&
+ !flags.empty &&
+ !flags.invalidMonth &&
+ !flags.invalidWeekday &&
+ !flags.nullInput &&
+ !flags.invalidFormat &&
+ !flags.userInvalidated &&
+ (!flags.meridiem || (flags.meridiem && parsedParts));
+
+ if (m._strict) {
+ isNowValid = isNowValid &&
+ flags.charsLeftOver === 0 &&
+ flags.unusedTokens.length === 0 &&
+ flags.bigHour === undefined;
}
+
+ if (Object.isFrozen == null || !Object.isFrozen(m)) {
+ m._isValid = isNowValid;
+ }
else {
- getParsingFlags(m).userInvalidated = true;
+ return isNowValid;
}
-
- return m;
}
+ return m._isValid;
+}
- function isUndefined(input) {
- return input === void 0;
+function createInvalid (flags) {
+ var m = createUTC(NaN);
+ if (flags != null) {
+ extend(getParsingFlags(m), flags);
}
+ else {
+ getParsingFlags(m).userInvalidated = true;
+ }
- // Plugins that add properties should also add the key here (null value),
- // so we can properly clone ourselves.
- var momentProperties = utils_hooks__hooks.momentProperties = [];
+ return m;
+}
- function copyConfig(to, from) {
- var i, prop, val;
+function isUndefined(input) {
+ return input === void 0;
+}
- if (!isUndefined(from._isAMomentObject)) {
- to._isAMomentObject = from._isAMomentObject;
- }
- if (!isUndefined(from._i)) {
- to._i = from._i;
- }
- if (!isUndefined(from._f)) {
- to._f = from._f;
- }
- if (!isUndefined(from._l)) {
- to._l = from._l;
- }
- if (!isUndefined(from._strict)) {
- to._strict = from._strict;
- }
- if (!isUndefined(from._tzm)) {
- to._tzm = from._tzm;
- }
- if (!isUndefined(from._isUTC)) {
- to._isUTC = from._isUTC;
- }
- if (!isUndefined(from._offset)) {
- to._offset = from._offset;
- }
- if (!isUndefined(from._pf)) {
- to._pf = getParsingFlags(from);
- }
- if (!isUndefined(from._locale)) {
- to._locale = from._locale;
- }
+// Plugins that add properties should also add the key here (null value),
+// so we can properly clone ourselves.
+var momentProperties = hooks.momentProperties = [];
- if (momentProperties.length > 0) {
- for (i in momentProperties) {
- prop = momentProperties[i];
- val = from[prop];
- if (!isUndefined(val)) {
- to[prop] = val;
- }
- }
- }
+function copyConfig(to, from) {
+ var i, prop, val;
- return to;
+ if (!isUndefined(from._isAMomentObject)) {
+ to._isAMomentObject = from._isAMomentObject;
}
-
- var updateInProgress = false;
-
- // Moment prototype object
- function Moment(config) {
- copyConfig(this, config);
- this._d = new Date(config._d != null ? config._d.getTime() : NaN);
- // Prevent infinite loop in case updateOffset creates new moment
- // objects.
- if (updateInProgress === false) {
- updateInProgress = true;
- utils_hooks__hooks.updateOffset(this);
- updateInProgress = false;
- }
+ if (!isUndefined(from._i)) {
+ to._i = from._i;
}
-
- function isMoment (obj) {
- return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
+ if (!isUndefined(from._f)) {
+ to._f = from._f;
}
+ if (!isUndefined(from._l)) {
+ to._l = from._l;
+ }
+ if (!isUndefined(from._strict)) {
+ to._strict = from._strict;
+ }
+ if (!isUndefined(from._tzm)) {
+ to._tzm = from._tzm;
+ }
+ if (!isUndefined(from._isUTC)) {
+ to._isUTC = from._isUTC;
+ }
+ if (!isUndefined(from._offset)) {
+ to._offset = from._offset;
+ }
+ if (!isUndefined(from._pf)) {
+ to._pf = getParsingFlags(from);
+ }
+ if (!isUndefined(from._locale)) {
+ to._locale = from._locale;
+ }
- function absFloor (number) {
- if (number < 0) {
- // -0 -> 0
- return Math.ceil(number) || 0;
- } else {
- return Math.floor(number);
+ if (momentProperties.length > 0) {
+ for (i in momentProperties) {
+ prop = momentProperties[i];
+ val = from[prop];
+ if (!isUndefined(val)) {
+ to[prop] = val;
+ }
}
}
- function toInt(argumentForCoercion) {
- var coercedNumber = +argumentForCoercion,
- value = 0;
+ return to;
+}
- if (coercedNumber !== 0 && isFinite(coercedNumber)) {
- value = absFloor(coercedNumber);
- }
+var updateInProgress = false;
- return value;
+// Moment prototype object
+function Moment(config) {
+ copyConfig(this, config);
+ this._d = new Date(config._d != null ? config._d.getTime() : NaN);
+ if (!this.isValid()) {
+ this._d = new Date(NaN);
}
+ // Prevent infinite loop in case updateOffset creates new moment
+ // objects.
+ if (updateInProgress === false) {
+ updateInProgress = true;
+ hooks.updateOffset(this);
+ updateInProgress = false;
+ }
+}
- // compare two arrays, return the number of differences
- function compareArrays(array1, array2, dontConvert) {
- var len = Math.min(array1.length, array2.length),
- lengthDiff = Math.abs(array1.length - array2.length),
- diffs = 0,
- i;
- for (i = 0; i < len; i++) {
- if ((dontConvert && array1[i] !== array2[i]) ||
- (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
- diffs++;
- }
- }
- return diffs + lengthDiff;
+function isMoment (obj) {
+ return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
+}
+
+function absFloor (number) {
+ if (number < 0) {
+ // -0 -> 0
+ return Math.ceil(number) || 0;
+ } else {
+ return Math.floor(number);
}
+}
- function warn(msg) {
- if (utils_hooks__hooks.suppressDeprecationWarnings === false &&
- (typeof console !== 'undefined') && console.warn) {
- console.warn('Deprecation warning: ' + msg);
- }
+function toInt(argumentForCoercion) {
+ var coercedNumber = +argumentForCoercion,
+ value = 0;
+
+ if (coercedNumber !== 0 && isFinite(coercedNumber)) {
+ value = absFloor(coercedNumber);
}
- function deprecate(msg, fn) {
- var firstTime = true;
+ return value;
+}
- return extend(function () {
- if (utils_hooks__hooks.deprecationHandler != null) {
- utils_hooks__hooks.deprecationHandler(null, msg);
- }
- if (firstTime) {
- warn(msg + '\nArguments: ' + Array.prototype.slice.call(arguments).join(', ') + '\n' + (new Error()).stack);
- firstTime = false;
- }
- return fn.apply(this, arguments);
- }, fn);
+// compare two arrays, return the number of differences
+function compareArrays(array1, array2, dontConvert) {
+ var len = Math.min(array1.length, array2.length),
+ lengthDiff = Math.abs(array1.length - array2.length),
+ diffs = 0,
+ i;
+ for (i = 0; i < len; i++) {
+ if ((dontConvert && array1[i] !== array2[i]) ||
+ (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
+ diffs++;
+ }
}
+ return diffs + lengthDiff;
+}
- var deprecations = {};
+function warn(msg) {
+ if (hooks.suppressDeprecationWarnings === false &&
+ (typeof console !== 'undefined') && console.warn) {
+ console.warn('Deprecation warning: ' + msg);
+ }
+}
- function deprecateSimple(name, msg) {
- if (utils_hooks__hooks.deprecationHandler != null) {
- utils_hooks__hooks.deprecationHandler(name, msg);
+function deprecate(msg, fn) {
+ var firstTime = true;
+
+ return extend(function () {
+ if (hooks.deprecationHandler != null) {
+ hooks.deprecationHandler(null, msg);
}
- if (!deprecations[name]) {
- warn(msg);
- deprecations[name] = true;
+ if (firstTime) {
+ var args = [];
+ var arg;
+ for (var i = 0; i < arguments.length; i++) {
+ arg = '';
+ if (typeof arguments[i] === 'object') {
+ arg += '\n[' + i + '] ';
+ for (var key in arguments[0]) {
+ arg += key + ': ' + arguments[0][key] + ', ';
+ }
+ arg = arg.slice(0, -2); // Remove trailing comma and space
+ } else {
+ arg = arguments[i];
+ }
+ args.push(arg);
+ }
+ warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
+ firstTime = false;
}
+ return fn.apply(this, arguments);
+ }, fn);
+}
+
+var deprecations = {};
+
+function deprecateSimple(name, msg) {
+ if (hooks.deprecationHandler != null) {
+ hooks.deprecationHandler(name, msg);
}
+ if (!deprecations[name]) {
+ warn(msg);
+ deprecations[name] = true;
+ }
+}
- utils_hooks__hooks.suppressDeprecationWarnings = false;
- utils_hooks__hooks.deprecationHandler = null;
+hooks.suppressDeprecationWarnings = false;
+hooks.deprecationHandler = null;
- function isFunction(input) {
- return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
+function isFunction(input) {
+ return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
+}
+
+function set (config) {
+ var prop, i;
+ for (i in config) {
+ prop = config[i];
+ if (isFunction(prop)) {
+ this[i] = prop;
+ } else {
+ this['_' + i] = prop;
+ }
}
+ this._config = config;
+ // Lenient ordinal parsing accepts just a number in addition to
+ // number + (possibly) stuff coming from _ordinalParseLenient.
+ this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
+}
- function locale_set__set (config) {
- var prop, i;
- for (i in config) {
- prop = config[i];
- if (isFunction(prop)) {
- this[i] = prop;
+function mergeConfigs(parentConfig, childConfig) {
+ var res = extend({}, parentConfig), prop;
+ for (prop in childConfig) {
+ if (hasOwnProp(childConfig, prop)) {
+ if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
+ res[prop] = {};
+ extend(res[prop], parentConfig[prop]);
+ extend(res[prop], childConfig[prop]);
+ } else if (childConfig[prop] != null) {
+ res[prop] = childConfig[prop];
} else {
- this['_' + i] = prop;
+ delete res[prop];
}
}
- this._config = config;
- // Lenient ordinal parsing accepts just a number in addition to
- // number + (possibly) stuff coming from _ordinalParseLenient.
- this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
}
-
- function mergeConfigs(parentConfig, childConfig) {
- var res = extend({}, parentConfig), prop;
- for (prop in childConfig) {
- if (hasOwnProp(childConfig, prop)) {
- if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
- res[prop] = {};
- extend(res[prop], parentConfig[prop]);
- extend(res[prop], childConfig[prop]);
- } else if (childConfig[prop] != null) {
- res[prop] = childConfig[prop];
- } else {
- delete res[prop];
- }
- }
+ for (prop in parentConfig) {
+ if (hasOwnProp(parentConfig, prop) &&
+ !hasOwnProp(childConfig, prop) &&
+ isObject(parentConfig[prop])) {
+ // make sure changes to properties don't modify parent config
+ res[prop] = extend({}, res[prop]);
}
- for (prop in parentConfig) {
- if (hasOwnProp(parentConfig, prop) &&
- !hasOwnProp(childConfig, prop) &&
- isObject(parentConfig[prop])) {
- // make sure changes to properties don't modify parent config
- res[prop] = extend({}, res[prop]);
- }
- }
- return res;
}
+ return res;
+}
- function Locale(config) {
- if (config != null) {
- this.set(config);
- }
+function Locale(config) {
+ if (config != null) {
+ this.set(config);
}
+}
- var keys;
+var keys;
- if (Object.keys) {
- keys = Object.keys;
- } else {
- keys = function (obj) {
- var i, res = [];
- for (i in obj) {
- if (hasOwnProp(obj, i)) {
- res.push(i);
- }
+if (Object.keys) {
+ keys = Object.keys;
+} else {
+ keys = function (obj) {
+ var i, res = [];
+ for (i in obj) {
+ if (hasOwnProp(obj, i)) {
+ res.push(i);
}
- return res;
- };
- }
-
- var defaultCalendar = {
- sameDay : '[Today at] LT',
- nextDay : '[Tomorrow at] LT',
- nextWeek : 'dddd [at] LT',
- lastDay : '[Yesterday at] LT',
- lastWeek : '[Last] dddd [at] LT',
- sameElse : 'L'
+ }
+ return res;
};
+}
- function locale_calendar__calendar (key, mom, now) {
- var output = this._calendar[key] || this._calendar['sameElse'];
- return isFunction(output) ? output.call(mom, now) : output;
- }
+var keys$1 = keys;
- var defaultLongDateFormat = {
- LTS : 'h:mm:ss A',
- LT : 'h:mm A',
- L : 'MM/DD/YYYY',
- LL : 'MMMM D, YYYY',
- LLL : 'MMMM D, YYYY h:mm A',
- LLLL : 'dddd, MMMM D, YYYY h:mm A'
- };
+var defaultCalendar = {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[Last] dddd [at] LT',
+ sameElse : 'L'
+};
- function longDateFormat (key) {
- var format = this._longDateFormat[key],
- formatUpper = this._longDateFormat[key.toUpperCase()];
+function calendar (key, mom, now) {
+ var output = this._calendar[key] || this._calendar['sameElse'];
+ return isFunction(output) ? output.call(mom, now) : output;
+}
- if (format || !formatUpper) {
- return format;
- }
+var defaultLongDateFormat = {
+ LTS : 'h:mm:ss A',
+ LT : 'h:mm A',
+ L : 'MM/DD/YYYY',
+ LL : 'MMMM D, YYYY',
+ LLL : 'MMMM D, YYYY h:mm A',
+ LLLL : 'dddd, MMMM D, YYYY h:mm A'
+};
- this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
- return val.slice(1);
- });
+function longDateFormat (key) {
+ var format = this._longDateFormat[key],
+ formatUpper = this._longDateFormat[key.toUpperCase()];
- return this._longDateFormat[key];
+ if (format || !formatUpper) {
+ return format;
}
- var defaultInvalidDate = 'Invalid date';
+ this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
+ return val.slice(1);
+ });
- function invalidDate () {
- return this._invalidDate;
- }
+ return this._longDateFormat[key];
+}
- var defaultOrdinal = '%d';
- var defaultOrdinalParse = /\d{1,2}/;
+var defaultInvalidDate = 'Invalid date';
- function ordinal (number) {
- return this._ordinal.replace('%d', number);
- }
+function invalidDate () {
+ return this._invalidDate;
+}
- var defaultRelativeTime = {
- future : 'in %s',
- past : '%s ago',
- s : 'a few seconds',
- m : 'a minute',
- mm : '%d minutes',
- h : 'an hour',
- hh : '%d hours',
- d : 'a day',
- dd : '%d days',
- M : 'a month',
- MM : '%d months',
- y : 'a year',
- yy : '%d years'
- };
+var defaultOrdinal = '%d';
+var defaultOrdinalParse = /\d{1,2}/;
- function relative__relativeTime (number, withoutSuffix, string, isFuture) {
- var output = this._relativeTime[string];
- return (isFunction(output)) ?
- output(number, withoutSuffix, string, isFuture) :
- output.replace(/%d/i, number);
- }
+function ordinal (number) {
+ return this._ordinal.replace('%d', number);
+}
- function pastFuture (diff, output) {
- var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
- return isFunction(format) ? format(output) : format.replace(/%s/i, output);
- }
+var defaultRelativeTime = {
+ future : 'in %s',
+ past : '%s ago',
+ s : 'a few seconds',
+ m : 'a minute',
+ mm : '%d minutes',
+ h : 'an hour',
+ hh : '%d hours',
+ d : 'a day',
+ dd : '%d days',
+ M : 'a month',
+ MM : '%d months',
+ y : 'a year',
+ yy : '%d years'
+};
- var aliases = {};
+function relativeTime (number, withoutSuffix, string, isFuture) {
+ var output = this._relativeTime[string];
+ return (isFunction(output)) ?
+ output(number, withoutSuffix, string, isFuture) :
+ output.replace(/%d/i, number);
+}
- function addUnitAlias (unit, shorthand) {
- var lowerCase = unit.toLowerCase();
- aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
- }
+function pastFuture (diff, output) {
+ var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+ return isFunction(format) ? format(output) : format.replace(/%s/i, output);
+}
- function normalizeUnits(units) {
- return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
- }
+var aliases = {};
- function normalizeObjectUnits(inputObject) {
- var normalizedInput = {},
- normalizedProp,
- prop;
+function addUnitAlias (unit, shorthand) {
+ var lowerCase = unit.toLowerCase();
+ aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
+}
- for (prop in inputObject) {
- if (hasOwnProp(inputObject, prop)) {
- normalizedProp = normalizeUnits(prop);
- if (normalizedProp) {
- normalizedInput[normalizedProp] = inputObject[prop];
- }
+function normalizeUnits(units) {
+ return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
+}
+
+function normalizeObjectUnits(inputObject) {
+ var normalizedInput = {},
+ normalizedProp,
+ prop;
+
+ for (prop in inputObject) {
+ if (hasOwnProp(inputObject, prop)) {
+ normalizedProp = normalizeUnits(prop);
+ if (normalizedProp) {
+ normalizedInput[normalizedProp] = inputObject[prop];
}
}
-
- return normalizedInput;
}
- var priorities = {};
+ return normalizedInput;
+}
- function addUnitPriority(unit, priority) {
- priorities[unit] = priority;
+var priorities = {};
+
+function addUnitPriority(unit, priority) {
+ priorities[unit] = priority;
+}
+
+function getPrioritizedUnits(unitsObj) {
+ var units = [];
+ for (var u in unitsObj) {
+ units.push({unit: u, priority: priorities[u]});
}
+ units.sort(function (a, b) {
+ return a.priority - b.priority;
+ });
+ return units;
+}
- function getPrioritizedUnits(unitsObj) {
- var units = [];
- for (var u in unitsObj) {
- units.push({unit: u, priority: priorities[u]});
+function makeGetSet (unit, keepTime) {
+ return function (value) {
+ if (value != null) {
+ set$1(this, unit, value);
+ hooks.updateOffset(this, keepTime);
+ return this;
+ } else {
+ return get(this, unit);
}
- units.sort(function (a, b) {
- return a.priority - b.priority;
- });
- return units;
- }
+ };
+}
- function makeGetSet (unit, keepTime) {
- return function (value) {
- if (value != null) {
- get_set__set(this, unit, value);
- utils_hooks__hooks.updateOffset(this, keepTime);
- return this;
- } else {
- return get_set__get(this, unit);
- }
- };
- }
+function get (mom, unit) {
+ return mom.isValid() ?
+ mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
+}
- function get_set__get (mom, unit) {
- return mom.isValid() ?
- mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
+function set$1 (mom, unit, value) {
+ if (mom.isValid()) {
+ mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
}
+}
- function get_set__set (mom, unit, value) {
- if (mom.isValid()) {
- mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
- }
+// MOMENTS
+
+function stringGet (units) {
+ units = normalizeUnits(units);
+ if (isFunction(this[units])) {
+ return this[units]();
}
+ return this;
+}
- // MOMENTS
- function stringGet (units) {
+function stringSet (units, value) {
+ if (typeof units === 'object') {
+ units = normalizeObjectUnits(units);
+ var prioritized = getPrioritizedUnits(units);
+ for (var i = 0; i < prioritized.length; i++) {
+ this[prioritized[i].unit](units[prioritized[i].unit]);
+ }
+ } else {
units = normalizeUnits(units);
if (isFunction(this[units])) {
- return this[units]();
+ return this[units](value);
}
- return this;
}
+ return this;
+}
+function zeroFill(number, targetLength, forceSign) {
+ var absNumber = '' + Math.abs(number),
+ zerosToFill = targetLength - absNumber.length,
+ sign = number >= 0;
+ return (sign ? (forceSign ? '+' : '') : '-') +
+ Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
+}
- function stringSet (units, value) {
- if (typeof units === 'object') {
- units = normalizeObjectUnits(units);
- var prioritized = getPrioritizedUnits(units);
- for (var i = 0; i < prioritized.length; i++) {
- this[prioritized[i].unit](units[prioritized[i].unit]);
- }
- } else {
- units = normalizeUnits(units);
- if (isFunction(this[units])) {
- return this[units](value);
- }
- }
- return this;
- }
+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;
- function zeroFill(number, targetLength, forceSign) {
- var absNumber = '' + Math.abs(number),
- zerosToFill = targetLength - absNumber.length,
- sign = number >= 0;
- return (sign ? (forceSign ? '+' : '') : '-') +
- Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
- }
+var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
- 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;
+var formatFunctions = {};
- var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
+var formatTokenFunctions = {};
- var formatFunctions = {};
-
- var formatTokenFunctions = {};
-
- // token: 'M'
- // padded: ['MM', 2]
- // ordinal: 'Mo'
- // callback: function () { this.month() + 1 }
- function addFormatToken (token, padded, ordinal, callback) {
- var func = callback;
- if (typeof callback === 'string') {
- func = function () {
- return this[callback]();
- };
- }
- if (token) {
- formatTokenFunctions[token] = func;
- }
- if (padded) {
- formatTokenFunctions[padded[0]] = function () {
- return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
- };
- }
- if (ordinal) {
- formatTokenFunctions[ordinal] = function () {
- return this.localeData().ordinal(func.apply(this, arguments), token);
- };
- }
+// token: 'M'
+// padded: ['MM', 2]
+// ordinal: 'Mo'
+// callback: function () { this.month() + 1 }
+function addFormatToken (token, padded, ordinal, callback) {
+ var func = callback;
+ if (typeof callback === 'string') {
+ func = function () {
+ return this[callback]();
+ };
}
+ if (token) {
+ formatTokenFunctions[token] = func;
+ }
+ if (padded) {
+ formatTokenFunctions[padded[0]] = function () {
+ return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
+ };
+ }
+ if (ordinal) {
+ formatTokenFunctions[ordinal] = function () {
+ return this.localeData().ordinal(func.apply(this, arguments), token);
+ };
+ }
+}
- function removeFormattingTokens(input) {
- if (input.match(/\[[\s\S]/)) {
- return input.replace(/^\[|\]$/g, '');
- }
- return input.replace(/\\/g, '');
+function removeFormattingTokens(input) {
+ if (input.match(/\[[\s\S]/)) {
+ return input.replace(/^\[|\]$/g, '');
}
+ return input.replace(/\\/g, '');
+}
- function makeFormatFunction(format) {
- var array = format.match(formattingTokens), i, length;
+function makeFormatFunction(format) {
+ var array = format.match(formattingTokens), i, length;
- for (i = 0, length = array.length; i < length; i++) {
- if (formatTokenFunctions[array[i]]) {
- array[i] = formatTokenFunctions[array[i]];
- } else {
- array[i] = removeFormattingTokens(array[i]);
- }
+ for (i = 0, length = array.length; i < length; i++) {
+ if (formatTokenFunctions[array[i]]) {
+ array[i] = formatTokenFunctions[array[i]];
+ } else {
+ array[i] = removeFormattingTokens(array[i]);
}
-
- return function (mom) {
- var output = '', i;
- for (i = 0; i < length; i++) {
- output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
- }
- return output;
- };
}
- // format date using native date object
- function formatMoment(m, format) {
- if (!m.isValid()) {
- return m.localeData().invalidDate();
+ return function (mom) {
+ var output = '', i;
+ for (i = 0; i < length; i++) {
+ output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
}
+ return output;
+ };
+}
- format = expandFormat(format, m.localeData());
- formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
-
- return formatFunctions[format](m);
+// format date using native date object
+function formatMoment(m, format) {
+ if (!m.isValid()) {
+ return m.localeData().invalidDate();
}
- function expandFormat(format, locale) {
- var i = 5;
+ format = expandFormat(format, m.localeData());
+ formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
- function replaceLongDateFormatTokens(input) {
- return locale.longDateFormat(input) || input;
- }
+ return formatFunctions[format](m);
+}
- localFormattingTokens.lastIndex = 0;
- while (i >= 0 && localFormattingTokens.test(format)) {
- format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
- localFormattingTokens.lastIndex = 0;
- i -= 1;
- }
+function expandFormat(format, locale) {
+ var i = 5;
- return format;
+ function replaceLongDateFormatTokens(input) {
+ return locale.longDateFormat(input) || input;
}
- var match1 = /\d/; // 0 - 9
- var match2 = /\d\d/; // 00 - 99
- var match3 = /\d{3}/; // 000 - 999
- var match4 = /\d{4}/; // 0000 - 9999
- var match6 = /[+-]?\d{6}/; // -999999 - 999999
- var match1to2 = /\d\d?/; // 0 - 99
- var match3to4 = /\d\d\d\d?/; // 999 - 9999
- var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
- var match1to3 = /\d{1,3}/; // 0 - 999
- var match1to4 = /\d{1,4}/; // 0 - 9999
- var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
+ localFormattingTokens.lastIndex = 0;
+ while (i >= 0 && localFormattingTokens.test(format)) {
+ format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
+ localFormattingTokens.lastIndex = 0;
+ i -= 1;
+ }
- var matchUnsigned = /\d+/; // 0 - inf
- var matchSigned = /[+-]?\d+/; // -inf - inf
+ return format;
+}
- var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
- var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
+var match1 = /\d/; // 0 - 9
+var match2 = /\d\d/; // 00 - 99
+var match3 = /\d{3}/; // 000 - 999
+var match4 = /\d{4}/; // 0000 - 9999
+var match6 = /[+-]?\d{6}/; // -999999 - 999999
+var match1to2 = /\d\d?/; // 0 - 99
+var match3to4 = /\d\d\d\d?/; // 999 - 9999
+var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
+var match1to3 = /\d{1,3}/; // 0 - 999
+var match1to4 = /\d{1,4}/; // 0 - 9999
+var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
- var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
+var matchUnsigned = /\d+/; // 0 - inf
+var matchSigned = /[+-]?\d+/; // -inf - inf
- // any word (or two) characters or numbers including two/three word month in arabic.
- // includes scottish gaelic two word and hyphenated months
- var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
+var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
+var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
+var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
- var regexes = {};
+// any word (or two) characters or numbers including two/three word month in arabic.
+// includes scottish gaelic two word and hyphenated months
+var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
- function addRegexToken (token, regex, strictRegex) {
- regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
- return (isStrict && strictRegex) ? strictRegex : regex;
- };
- }
- function getParseRegexForToken (token, config) {
- if (!hasOwnProp(regexes, token)) {
- return new RegExp(unescapeFormat(token));
- }
+var regexes = {};
- return regexes[token](config._strict, config._locale);
- }
+function addRegexToken (token, regex, strictRegex) {
+ regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
+ return (isStrict && strictRegex) ? strictRegex : regex;
+ };
+}
- // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
- function unescapeFormat(s) {
- return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
- return p1 || p2 || p3 || p4;
- }));
+function getParseRegexForToken (token, config) {
+ if (!hasOwnProp(regexes, token)) {
+ return new RegExp(unescapeFormat(token));
}
- function regexEscape(s) {
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
- }
+ return regexes[token](config._strict, config._locale);
+}
- var tokens = {};
+// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
+function unescapeFormat(s) {
+ return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
+ return p1 || p2 || p3 || p4;
+ }));
+}
- function addParseToken (token, callback) {
- var i, func = callback;
- if (typeof token === 'string') {
- token = [token];
- }
- if (typeof callback === 'number') {
- func = function (input, array) {
- array[callback] = toInt(input);
- };
- }
- for (i = 0; i < token.length; i++) {
- tokens[token[i]] = func;
- }
- }
+function regexEscape(s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+}
- function addWeekParseToken (token, callback) {
- addParseToken(token, function (input, array, config, token) {
- config._w = config._w || {};
- callback(input, config._w, config, token);
- });
+var tokens = {};
+
+function addParseToken (token, callback) {
+ var i, func = callback;
+ if (typeof token === 'string') {
+ token = [token];
}
+ if (isNumber(callback)) {
+ func = function (input, array) {
+ array[callback] = toInt(input);
+ };
+ }
+ for (i = 0; i < token.length; i++) {
+ tokens[token[i]] = func;
+ }
+}
- function addTimeToArrayFromToken(token, input, config) {
- if (input != null && hasOwnProp(tokens, token)) {
- tokens[token](input, config._a, config, token);
- }
+function addWeekParseToken (token, callback) {
+ addParseToken(token, function (input, array, config, token) {
+ config._w = config._w || {};
+ callback(input, config._w, config, token);
+ });
+}
+
+function addTimeToArrayFromToken(token, input, config) {
+ if (input != null && hasOwnProp(tokens, token)) {
+ tokens[token](input, config._a, config, token);
}
+}
- var YEAR = 0;
- var MONTH = 1;
- var DATE = 2;
- var HOUR = 3;
- var MINUTE = 4;
- var SECOND = 5;
- var MILLISECOND = 6;
- var WEEK = 7;
- var WEEKDAY = 8;
+var YEAR = 0;
+var MONTH = 1;
+var DATE = 2;
+var HOUR = 3;
+var MINUTE = 4;
+var SECOND = 5;
+var MILLISECOND = 6;
+var WEEK = 7;
+var WEEKDAY = 8;
- var indexOf;
+var indexOf;
- if (Array.prototype.indexOf) {
- indexOf = Array.prototype.indexOf;
- } else {
- indexOf = function (o) {
- // I know
- var i;
- for (i = 0; i < this.length; ++i) {
- if (this[i] === o) {
- return i;
- }
+if (Array.prototype.indexOf) {
+ indexOf = Array.prototype.indexOf;
+} else {
+ indexOf = function (o) {
+ // I know
+ var i;
+ for (i = 0; i < this.length; ++i) {
+ if (this[i] === o) {
+ return i;
}
- return -1;
- };
- }
+ }
+ return -1;
+ };
+}
- function daysInMonth(year, month) {
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
- }
+var indexOf$1 = indexOf;
- // FORMATTING
+function daysInMonth(year, month) {
+ return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
+}
- addFormatToken('M', ['MM', 2], 'Mo', function () {
- return this.month() + 1;
- });
+// FORMATTING
- addFormatToken('MMM', 0, 0, function (format) {
- return this.localeData().monthsShort(this, format);
- });
+addFormatToken('M', ['MM', 2], 'Mo', function () {
+ return this.month() + 1;
+});
- addFormatToken('MMMM', 0, 0, function (format) {
- return this.localeData().months(this, format);
- });
+addFormatToken('MMM', 0, 0, function (format) {
+ return this.localeData().monthsShort(this, format);
+});
- // ALIASES
+addFormatToken('MMMM', 0, 0, function (format) {
+ return this.localeData().months(this, format);
+});
- addUnitAlias('month', 'M');
+// ALIASES
- // PRIORITY
+addUnitAlias('month', 'M');
- addUnitPriority('month', 8);
+// PRIORITY
- // PARSING
+addUnitPriority('month', 8);
- addRegexToken('M', match1to2);
- addRegexToken('MM', match1to2, match2);
- addRegexToken('MMM', function (isStrict, locale) {
- return locale.monthsShortRegex(isStrict);
- });
- addRegexToken('MMMM', function (isStrict, locale) {
- return locale.monthsRegex(isStrict);
- });
+// PARSING
- addParseToken(['M', 'MM'], function (input, array) {
- array[MONTH] = toInt(input) - 1;
- });
+addRegexToken('M', match1to2);
+addRegexToken('MM', match1to2, match2);
+addRegexToken('MMM', function (isStrict, locale) {
+ return locale.monthsShortRegex(isStrict);
+});
+addRegexToken('MMMM', function (isStrict, locale) {
+ return locale.monthsRegex(isStrict);
+});
- addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
- var month = config._locale.monthsParse(input, token, config._strict);
- // if we didn't find a month name, mark the date as invalid.
- if (month != null) {
- array[MONTH] = month;
- } else {
- getParsingFlags(config).invalidMonth = input;
- }
- });
+addParseToken(['M', 'MM'], function (input, array) {
+ array[MONTH] = toInt(input) - 1;
+});
- // LOCALES
+addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
+ var month = config._locale.monthsParse(input, token, config._strict);
+ // if we didn't find a month name, mark the date as invalid.
+ if (month != null) {
+ array[MONTH] = month;
+ } else {
+ getParsingFlags(config).invalidMonth = input;
+ }
+});
- var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/;
- var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
- function localeMonths (m, format) {
- return isArray(this._months) ? this._months[m.month()] :
- this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
+// LOCALES
+
+var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
+var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
+function localeMonths (m, format) {
+ if (!m) {
+ return this._months;
}
+ return isArray(this._months) ? this._months[m.month()] :
+ this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
+}
- var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
- function localeMonthsShort (m, format) {
- return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
- this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
+var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
+function localeMonthsShort (m, format) {
+ if (!m) {
+ return this._monthsShort;
}
+ return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
+ this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
+}
- function units_month__handleStrictParse(monthName, format, strict) {
- var i, ii, mom, llc = monthName.toLocaleLowerCase();
- if (!this._monthsParse) {
- // this is not used
- this._monthsParse = [];
- this._longMonthsParse = [];
- this._shortMonthsParse = [];
- for (i = 0; i < 12; ++i) {
- mom = create_utc__createUTC([2000, i]);
- this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
- this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
- }
+function handleStrictParse(monthName, format, strict) {
+ var i, ii, mom, llc = monthName.toLocaleLowerCase();
+ if (!this._monthsParse) {
+ // this is not used
+ this._monthsParse = [];
+ this._longMonthsParse = [];
+ this._shortMonthsParse = [];
+ for (i = 0; i < 12; ++i) {
+ mom = createUTC([2000, i]);
+ this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
+ this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
}
+ }
- if (strict) {
- if (format === 'MMM') {
- ii = indexOf.call(this._shortMonthsParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf.call(this._longMonthsParse, llc);
- return ii !== -1 ? ii : null;
+ if (strict) {
+ if (format === 'MMM') {
+ ii = indexOf$1.call(this._shortMonthsParse, llc);
+ return ii !== -1 ? ii : null;
+ } else {
+ ii = indexOf$1.call(this._longMonthsParse, llc);
+ return ii !== -1 ? ii : null;
+ }
+ } else {
+ if (format === 'MMM') {
+ ii = indexOf$1.call(this._shortMonthsParse, llc);
+ if (ii !== -1) {
+ return ii;
}
+ ii = indexOf$1.call(this._longMonthsParse, llc);
+ return ii !== -1 ? ii : null;
} else {
- if (format === 'MMM') {
- ii = indexOf.call(this._shortMonthsParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._longMonthsParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf.call(this._longMonthsParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._shortMonthsParse, llc);
- return ii !== -1 ? ii : null;
+ ii = indexOf$1.call(this._longMonthsParse, llc);
+ if (ii !== -1) {
+ return ii;
}
+ ii = indexOf$1.call(this._shortMonthsParse, llc);
+ return ii !== -1 ? ii : null;
}
}
+}
- function localeMonthsParse (monthName, format, strict) {
- var i, mom, regex;
+function localeMonthsParse (monthName, format, strict) {
+ var i, mom, regex;
- if (this._monthsParseExact) {
- return units_month__handleStrictParse.call(this, monthName, format, strict);
- }
+ if (this._monthsParseExact) {
+ return handleStrictParse.call(this, monthName, format, strict);
+ }
- if (!this._monthsParse) {
- this._monthsParse = [];
- this._longMonthsParse = [];
- this._shortMonthsParse = [];
- }
+ if (!this._monthsParse) {
+ this._monthsParse = [];
+ this._longMonthsParse = [];
+ this._shortMonthsParse = [];
+ }
- // TODO: add sorting
- // Sorting makes sure if one month (or abbr) is a prefix of another
- // see sorting in computeMonthsParse
- for (i = 0; i < 12; i++) {
- // make the regex if we don't have it already
- mom = create_utc__createUTC([2000, i]);
- if (strict && !this._longMonthsParse[i]) {
- this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
- this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
- }
- if (!strict && !this._monthsParse[i]) {
- regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
- this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
- return i;
- } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
- return i;
- } else if (!strict && this._monthsParse[i].test(monthName)) {
- return i;
- }
+ // TODO: add sorting
+ // Sorting makes sure if one month (or abbr) is a prefix of another
+ // see sorting in computeMonthsParse
+ for (i = 0; i < 12; i++) {
+ // make the regex if we don't have it already
+ mom = createUTC([2000, i]);
+ if (strict && !this._longMonthsParse[i]) {
+ this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
+ this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
}
+ if (!strict && !this._monthsParse[i]) {
+ regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+ this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
+ return i;
+ } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
+ return i;
+ } else if (!strict && this._monthsParse[i].test(monthName)) {
+ return i;
+ }
}
+}
- // MOMENTS
+// MOMENTS
- function setMonth (mom, value) {
- var dayOfMonth;
+function setMonth (mom, value) {
+ var dayOfMonth;
- if (!mom.isValid()) {
- // No op
- return mom;
- }
-
- if (typeof value === 'string') {
- if (/^\d+$/.test(value)) {
- value = toInt(value);
- } else {
- value = mom.localeData().monthsParse(value);
- // TODO: Another silent failure?
- if (typeof value !== 'number') {
- return mom;
- }
- }
- }
-
- dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
- mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
+ if (!mom.isValid()) {
+ // No op
return mom;
}
- function getSetMonth (value) {
- if (value != null) {
- setMonth(this, value);
- utils_hooks__hooks.updateOffset(this, true);
- return this;
+ if (typeof value === 'string') {
+ if (/^\d+$/.test(value)) {
+ value = toInt(value);
} else {
- return get_set__get(this, 'Month');
+ value = mom.localeData().monthsParse(value);
+ // TODO: Another silent failure?
+ if (!isNumber(value)) {
+ return mom;
+ }
}
}
- function getDaysInMonth () {
- return daysInMonth(this.year(), this.month());
- }
+ dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
+ mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
+ return mom;
+}
- var defaultMonthsShortRegex = matchWord;
- function monthsShortRegex (isStrict) {
- if (this._monthsParseExact) {
- if (!hasOwnProp(this, '_monthsRegex')) {
- computeMonthsParse.call(this);
- }
- if (isStrict) {
- return this._monthsShortStrictRegex;
- } else {
- return this._monthsShortRegex;
- }
- } else {
- if (!hasOwnProp(this, '_monthsShortRegex')) {
- this._monthsShortRegex = defaultMonthsShortRegex;
- }
- return this._monthsShortStrictRegex && isStrict ?
- this._monthsShortStrictRegex : this._monthsShortRegex;
- }
+function getSetMonth (value) {
+ if (value != null) {
+ setMonth(this, value);
+ hooks.updateOffset(this, true);
+ return this;
+ } else {
+ return get(this, 'Month');
}
+}
- var defaultMonthsRegex = matchWord;
- function monthsRegex (isStrict) {
- if (this._monthsParseExact) {
- if (!hasOwnProp(this, '_monthsRegex')) {
- computeMonthsParse.call(this);
- }
- if (isStrict) {
- return this._monthsStrictRegex;
- } else {
- return this._monthsRegex;
- }
+function getDaysInMonth () {
+ return daysInMonth(this.year(), this.month());
+}
+
+var defaultMonthsShortRegex = matchWord;
+function monthsShortRegex (isStrict) {
+ if (this._monthsParseExact) {
+ if (!hasOwnProp(this, '_monthsRegex')) {
+ computeMonthsParse.call(this);
+ }
+ if (isStrict) {
+ return this._monthsShortStrictRegex;
} else {
- if (!hasOwnProp(this, '_monthsRegex')) {
- this._monthsRegex = defaultMonthsRegex;
- }
- return this._monthsStrictRegex && isStrict ?
- this._monthsStrictRegex : this._monthsRegex;
+ return this._monthsShortRegex;
}
+ } else {
+ if (!hasOwnProp(this, '_monthsShortRegex')) {
+ this._monthsShortRegex = defaultMonthsShortRegex;
+ }
+ return this._monthsShortStrictRegex && isStrict ?
+ this._monthsShortStrictRegex : this._monthsShortRegex;
}
+}
- function computeMonthsParse () {
- function cmpLenRev(a, b) {
- return b.length - a.length;
+var defaultMonthsRegex = matchWord;
+function monthsRegex (isStrict) {
+ if (this._monthsParseExact) {
+ if (!hasOwnProp(this, '_monthsRegex')) {
+ computeMonthsParse.call(this);
}
-
- var shortPieces = [], longPieces = [], mixedPieces = [],
- i, mom;
- for (i = 0; i < 12; i++) {
- // make the regex if we don't have it already
- mom = create_utc__createUTC([2000, i]);
- shortPieces.push(this.monthsShort(mom, ''));
- longPieces.push(this.months(mom, ''));
- mixedPieces.push(this.months(mom, ''));
- mixedPieces.push(this.monthsShort(mom, ''));
+ if (isStrict) {
+ return this._monthsStrictRegex;
+ } else {
+ return this._monthsRegex;
}
- // Sorting makes sure if one month (or abbr) is a prefix of another it
- // will match the longer piece.
- shortPieces.sort(cmpLenRev);
- longPieces.sort(cmpLenRev);
- mixedPieces.sort(cmpLenRev);
- for (i = 0; i < 12; i++) {
- shortPieces[i] = regexEscape(shortPieces[i]);
- longPieces[i] = regexEscape(longPieces[i]);
+ } else {
+ if (!hasOwnProp(this, '_monthsRegex')) {
+ this._monthsRegex = defaultMonthsRegex;
}
- for (i = 0; i < 24; i++) {
- mixedPieces[i] = regexEscape(mixedPieces[i]);
- }
+ return this._monthsStrictRegex && isStrict ?
+ this._monthsStrictRegex : this._monthsRegex;
+ }
+}
- this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
- this._monthsShortRegex = this._monthsRegex;
- this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
- this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
+function computeMonthsParse () {
+ function cmpLenRev(a, b) {
+ return b.length - a.length;
}
- // FORMATTING
+ var shortPieces = [], longPieces = [], mixedPieces = [],
+ i, mom;
+ for (i = 0; i < 12; i++) {
+ // make the regex if we don't have it already
+ mom = createUTC([2000, i]);
+ shortPieces.push(this.monthsShort(mom, ''));
+ longPieces.push(this.months(mom, ''));
+ mixedPieces.push(this.months(mom, ''));
+ mixedPieces.push(this.monthsShort(mom, ''));
+ }
+ // Sorting makes sure if one month (or abbr) is a prefix of another it
+ // will match the longer piece.
+ shortPieces.sort(cmpLenRev);
+ longPieces.sort(cmpLenRev);
+ mixedPieces.sort(cmpLenRev);
+ for (i = 0; i < 12; i++) {
+ shortPieces[i] = regexEscape(shortPieces[i]);
+ longPieces[i] = regexEscape(longPieces[i]);
+ }
+ for (i = 0; i < 24; i++) {
+ mixedPieces[i] = regexEscape(mixedPieces[i]);
+ }
- addFormatToken('Y', 0, 0, function () {
- var y = this.year();
- return y <= 9999 ? '' + y : '+' + y;
- });
+ this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
+ this._monthsShortRegex = this._monthsRegex;
+ this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
+ this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
+}
- addFormatToken(0, ['YY', 2], 0, function () {
- return this.year() % 100;
- });
+// FORMATTING
- addFormatToken(0, ['YYYY', 4], 0, 'year');
- addFormatToken(0, ['YYYYY', 5], 0, 'year');
- addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
+addFormatToken('Y', 0, 0, function () {
+ var y = this.year();
+ return y <= 9999 ? '' + y : '+' + y;
+});
- // ALIASES
+addFormatToken(0, ['YY', 2], 0, function () {
+ return this.year() % 100;
+});
- addUnitAlias('year', 'y');
+addFormatToken(0, ['YYYY', 4], 0, 'year');
+addFormatToken(0, ['YYYYY', 5], 0, 'year');
+addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
- // PRIORITIES
+// ALIASES
- addUnitPriority('year', 1);
+addUnitAlias('year', 'y');
- // PARSING
+// PRIORITIES
- addRegexToken('Y', matchSigned);
- addRegexToken('YY', match1to2, match2);
- addRegexToken('YYYY', match1to4, match4);
- addRegexToken('YYYYY', match1to6, match6);
- addRegexToken('YYYYYY', match1to6, match6);
+addUnitPriority('year', 1);
- addParseToken(['YYYYY', 'YYYYYY'], YEAR);
- addParseToken('YYYY', function (input, array) {
- array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input);
- });
- addParseToken('YY', function (input, array) {
- array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input);
- });
- addParseToken('Y', function (input, array) {
- array[YEAR] = parseInt(input, 10);
- });
+// PARSING
- // HELPERS
+addRegexToken('Y', matchSigned);
+addRegexToken('YY', match1to2, match2);
+addRegexToken('YYYY', match1to4, match4);
+addRegexToken('YYYYY', match1to6, match6);
+addRegexToken('YYYYYY', match1to6, match6);
- function daysInYear(year) {
- return isLeapYear(year) ? 366 : 365;
- }
+addParseToken(['YYYYY', 'YYYYYY'], YEAR);
+addParseToken('YYYY', function (input, array) {
+ array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
+});
+addParseToken('YY', function (input, array) {
+ array[YEAR] = hooks.parseTwoDigitYear(input);
+});
+addParseToken('Y', function (input, array) {
+ array[YEAR] = parseInt(input, 10);
+});
- function isLeapYear(year) {
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
- }
+// HELPERS
- // HOOKS
+function daysInYear(year) {
+ return isLeapYear(year) ? 366 : 365;
+}
- utils_hooks__hooks.parseTwoDigitYear = function (input) {
- return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
- };
+function isLeapYear(year) {
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+}
- // MOMENTS
+// HOOKS
- var getSetYear = makeGetSet('FullYear', true);
+hooks.parseTwoDigitYear = function (input) {
+ return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+};
- function getIsLeapYear () {
- return isLeapYear(this.year());
- }
+// MOMENTS
- function createDate (y, m, d, h, M, s, ms) {
- //can't just apply() to create a date:
- //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
- var date = new Date(y, m, d, h, M, s, ms);
+var getSetYear = makeGetSet('FullYear', true);
- //the date constructor remaps years 0-99 to 1900-1999
- if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
- date.setFullYear(y);
- }
- return date;
- }
+function getIsLeapYear () {
+ return isLeapYear(this.year());
+}
- function createUTCDate (y) {
- var date = new Date(Date.UTC.apply(null, arguments));
+function createDate (y, m, d, h, M, s, ms) {
+ //can't just apply() to create a date:
+ //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
+ var date = new Date(y, m, d, h, M, s, ms);
- //the Date.UTC function remaps years 0-99 to 1900-1999
- if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
- date.setUTCFullYear(y);
- }
- return date;
+ //the date constructor remaps years 0-99 to 1900-1999
+ if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
+ date.setFullYear(y);
}
+ return date;
+}
- // start-of-first-week - start-of-year
- function firstWeekOffset(year, dow, doy) {
- var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
- fwd = 7 + dow - doy,
- // first-week day local weekday -- which local weekday is fwd
- fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
+function createUTCDate (y) {
+ var date = new Date(Date.UTC.apply(null, arguments));
- return -fwdlw + fwd - 1;
+ //the Date.UTC function remaps years 0-99 to 1900-1999
+ if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
+ date.setUTCFullYear(y);
}
+ return date;
+}
- //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
- function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
- var localWeekday = (7 + weekday - dow) % 7,
- weekOffset = firstWeekOffset(year, dow, doy),
- dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
- resYear, resDayOfYear;
+// start-of-first-week - start-of-year
+function firstWeekOffset(year, dow, doy) {
+ var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
+ fwd = 7 + dow - doy,
+ // first-week day local weekday -- which local weekday is fwd
+ fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
- if (dayOfYear <= 0) {
- resYear = year - 1;
- resDayOfYear = daysInYear(resYear) + dayOfYear;
- } else if (dayOfYear > daysInYear(year)) {
- resYear = year + 1;
- resDayOfYear = dayOfYear - daysInYear(year);
- } else {
- resYear = year;
- resDayOfYear = dayOfYear;
- }
+ return -fwdlw + fwd - 1;
+}
- return {
- year: resYear,
- dayOfYear: resDayOfYear
- };
+//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
+function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
+ var localWeekday = (7 + weekday - dow) % 7,
+ weekOffset = firstWeekOffset(year, dow, doy),
+ dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
+ resYear, resDayOfYear;
+
+ if (dayOfYear <= 0) {
+ resYear = year - 1;
+ resDayOfYear = daysInYear(resYear) + dayOfYear;
+ } else if (dayOfYear > daysInYear(year)) {
+ resYear = year + 1;
+ resDayOfYear = dayOfYear - daysInYear(year);
+ } else {
+ resYear = year;
+ resDayOfYear = dayOfYear;
}
- function weekOfYear(mom, dow, doy) {
- var weekOffset = firstWeekOffset(mom.year(), dow, doy),
- week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
- resWeek, resYear;
+ return {
+ year: resYear,
+ dayOfYear: resDayOfYear
+ };
+}
- if (week < 1) {
- resYear = mom.year() - 1;
- resWeek = week + weeksInYear(resYear, dow, doy);
- } else if (week > weeksInYear(mom.year(), dow, doy)) {
- resWeek = week - weeksInYear(mom.year(), dow, doy);
- resYear = mom.year() + 1;
- } else {
- resYear = mom.year();
- resWeek = week;
- }
+function weekOfYear(mom, dow, doy) {
+ var weekOffset = firstWeekOffset(mom.year(), dow, doy),
+ week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
+ resWeek, resYear;
- return {
- week: resWeek,
- year: resYear
- };
+ if (week < 1) {
+ resYear = mom.year() - 1;
+ resWeek = week + weeksInYear(resYear, dow, doy);
+ } else if (week > weeksInYear(mom.year(), dow, doy)) {
+ resWeek = week - weeksInYear(mom.year(), dow, doy);
+ resYear = mom.year() + 1;
+ } else {
+ resYear = mom.year();
+ resWeek = week;
}
- function weeksInYear(year, dow, doy) {
- var weekOffset = firstWeekOffset(year, dow, doy),
- weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
- return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
- }
+ return {
+ week: resWeek,
+ year: resYear
+ };
+}
- // FORMATTING
+function weeksInYear(year, dow, doy) {
+ var weekOffset = firstWeekOffset(year, dow, doy),
+ weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
+ return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
+}
- addFormatToken('w', ['ww', 2], 'wo', 'week');
- addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
+// FORMATTING
- // ALIASES
+addFormatToken('w', ['ww', 2], 'wo', 'week');
+addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
- addUnitAlias('week', 'w');
- addUnitAlias('isoWeek', 'W');
+// ALIASES
- // PRIORITIES
+addUnitAlias('week', 'w');
+addUnitAlias('isoWeek', 'W');
- addUnitPriority('week', 5);
- addUnitPriority('isoWeek', 5);
+// PRIORITIES
- // PARSING
+addUnitPriority('week', 5);
+addUnitPriority('isoWeek', 5);
- addRegexToken('w', match1to2);
- addRegexToken('ww', match1to2, match2);
- addRegexToken('W', match1to2);
- addRegexToken('WW', match1to2, match2);
+// PARSING
- addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
- week[token.substr(0, 1)] = toInt(input);
- });
+addRegexToken('w', match1to2);
+addRegexToken('ww', match1to2, match2);
+addRegexToken('W', match1to2);
+addRegexToken('WW', match1to2, match2);
- // HELPERS
+addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
+ week[token.substr(0, 1)] = toInt(input);
+});
- // LOCALES
+// HELPERS
- function localeWeek (mom) {
- return weekOfYear(mom, this._week.dow, this._week.doy).week;
- }
+// LOCALES
- var defaultLocaleWeek = {
- dow : 0, // Sunday is the first day of the week.
- doy : 6 // The week that contains Jan 1st is the first week of the year.
- };
+function localeWeek (mom) {
+ return weekOfYear(mom, this._week.dow, this._week.doy).week;
+}
- function localeFirstDayOfWeek () {
- return this._week.dow;
- }
+var defaultLocaleWeek = {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+};
- function localeFirstDayOfYear () {
- return this._week.doy;
- }
+function localeFirstDayOfWeek () {
+ return this._week.dow;
+}
- // MOMENTS
+function localeFirstDayOfYear () {
+ return this._week.doy;
+}
- function getSetWeek (input) {
- var week = this.localeData().week(this);
- return input == null ? week : this.add((input - week) * 7, 'd');
- }
+// MOMENTS
- function getSetISOWeek (input) {
- var week = weekOfYear(this, 1, 4).week;
- return input == null ? week : this.add((input - week) * 7, 'd');
- }
+function getSetWeek (input) {
+ var week = this.localeData().week(this);
+ return input == null ? week : this.add((input - week) * 7, 'd');
+}
- // FORMATTING
+function getSetISOWeek (input) {
+ var week = weekOfYear(this, 1, 4).week;
+ return input == null ? week : this.add((input - week) * 7, 'd');
+}
- addFormatToken('d', 0, 'do', 'day');
+// FORMATTING
- addFormatToken('dd', 0, 0, function (format) {
- return this.localeData().weekdaysMin(this, format);
- });
+addFormatToken('d', 0, 'do', 'day');
- addFormatToken('ddd', 0, 0, function (format) {
- return this.localeData().weekdaysShort(this, format);
- });
+addFormatToken('dd', 0, 0, function (format) {
+ return this.localeData().weekdaysMin(this, format);
+});
- addFormatToken('dddd', 0, 0, function (format) {
- return this.localeData().weekdays(this, format);
- });
+addFormatToken('ddd', 0, 0, function (format) {
+ return this.localeData().weekdaysShort(this, format);
+});
- addFormatToken('e', 0, 0, 'weekday');
- addFormatToken('E', 0, 0, 'isoWeekday');
+addFormatToken('dddd', 0, 0, function (format) {
+ return this.localeData().weekdays(this, format);
+});
- // ALIASES
+addFormatToken('e', 0, 0, 'weekday');
+addFormatToken('E', 0, 0, 'isoWeekday');
- addUnitAlias('day', 'd');
- addUnitAlias('weekday', 'e');
- addUnitAlias('isoWeekday', 'E');
+// ALIASES
- // PRIORITY
- addUnitPriority('day', 11);
- addUnitPriority('weekday', 11);
- addUnitPriority('isoWeekday', 11);
+addUnitAlias('day', 'd');
+addUnitAlias('weekday', 'e');
+addUnitAlias('isoWeekday', 'E');
- // PARSING
+// PRIORITY
+addUnitPriority('day', 11);
+addUnitPriority('weekday', 11);
+addUnitPriority('isoWeekday', 11);
- addRegexToken('d', match1to2);
- addRegexToken('e', match1to2);
- addRegexToken('E', match1to2);
- addRegexToken('dd', function (isStrict, locale) {
- return locale.weekdaysMinRegex(isStrict);
- });
- addRegexToken('ddd', function (isStrict, locale) {
- return locale.weekdaysShortRegex(isStrict);
- });
- addRegexToken('dddd', function (isStrict, locale) {
- return locale.weekdaysRegex(isStrict);
- });
+// PARSING
- addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
- var weekday = config._locale.weekdaysParse(input, token, config._strict);
- // if we didn't get a weekday name, mark the date as invalid
- if (weekday != null) {
- week.d = weekday;
- } else {
- getParsingFlags(config).invalidWeekday = input;
- }
- });
+addRegexToken('d', match1to2);
+addRegexToken('e', match1to2);
+addRegexToken('E', match1to2);
+addRegexToken('dd', function (isStrict, locale) {
+ return locale.weekdaysMinRegex(isStrict);
+});
+addRegexToken('ddd', function (isStrict, locale) {
+ return locale.weekdaysShortRegex(isStrict);
+});
+addRegexToken('dddd', function (isStrict, locale) {
+ return locale.weekdaysRegex(isStrict);
+});
- addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
- week[token] = toInt(input);
- });
+addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
+ var weekday = config._locale.weekdaysParse(input, token, config._strict);
+ // if we didn't get a weekday name, mark the date as invalid
+ if (weekday != null) {
+ week.d = weekday;
+ } else {
+ getParsingFlags(config).invalidWeekday = input;
+ }
+});
- // HELPERS
+addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
+ week[token] = toInt(input);
+});
- function parseWeekday(input, locale) {
- if (typeof input !== 'string') {
- return input;
- }
+// HELPERS
- if (!isNaN(input)) {
- return parseInt(input, 10);
- }
+function parseWeekday(input, locale) {
+ if (typeof input !== 'string') {
+ return input;
+ }
- input = locale.weekdaysParse(input);
- if (typeof input === 'number') {
- return input;
- }
-
- return null;
+ if (!isNaN(input)) {
+ return parseInt(input, 10);
}
- function parseIsoWeekday(input, locale) {
- if (typeof input === 'string') {
- return locale.weekdaysParse(input) % 7 || 7;
- }
- return isNaN(input) ? null : input;
+ input = locale.weekdaysParse(input);
+ if (typeof input === 'number') {
+ return input;
}
- // LOCALES
+ return null;
+}
- var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
- function localeWeekdays (m, format) {
- return isArray(this._weekdays) ? this._weekdays[m.day()] :
- this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
+function parseIsoWeekday(input, locale) {
+ if (typeof input === 'string') {
+ return locale.weekdaysParse(input) % 7 || 7;
}
+ return isNaN(input) ? null : input;
+}
- var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
- function localeWeekdaysShort (m) {
- return this._weekdaysShort[m.day()];
- }
+// LOCALES
- var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
- function localeWeekdaysMin (m) {
- return this._weekdaysMin[m.day()];
+var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
+function localeWeekdays (m, format) {
+ if (!m) {
+ return this._weekdays;
}
+ return isArray(this._weekdays) ? this._weekdays[m.day()] :
+ this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
+}
- function day_of_week__handleStrictParse(weekdayName, format, strict) {
- var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
- if (!this._weekdaysParse) {
- this._weekdaysParse = [];
- this._shortWeekdaysParse = [];
- this._minWeekdaysParse = [];
+var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
+function localeWeekdaysShort (m) {
+ return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
+}
- for (i = 0; i < 7; ++i) {
- mom = create_utc__createUTC([2000, 1]).day(i);
- this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
- this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
- this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
- }
+var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
+function localeWeekdaysMin (m) {
+ return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
+}
+
+function handleStrictParse$1(weekdayName, format, strict) {
+ var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
+ if (!this._weekdaysParse) {
+ this._weekdaysParse = [];
+ this._shortWeekdaysParse = [];
+ this._minWeekdaysParse = [];
+
+ for (i = 0; i < 7; ++i) {
+ mom = createUTC([2000, 1]).day(i);
+ this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
+ this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
+ this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
}
+ }
- if (strict) {
- if (format === 'dddd') {
- ii = indexOf.call(this._weekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else if (format === 'ddd') {
- ii = indexOf.call(this._shortWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf.call(this._minWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
+ if (strict) {
+ if (format === 'dddd') {
+ ii = indexOf$1.call(this._weekdaysParse, llc);
+ return ii !== -1 ? ii : null;
+ } else if (format === 'ddd') {
+ ii = indexOf$1.call(this._shortWeekdaysParse, llc);
+ return ii !== -1 ? ii : null;
+ } else {
+ ii = indexOf$1.call(this._minWeekdaysParse, llc);
+ return ii !== -1 ? ii : null;
+ }
+ } else {
+ if (format === 'dddd') {
+ ii = indexOf$1.call(this._weekdaysParse, llc);
+ if (ii !== -1) {
+ return ii;
}
+ ii = indexOf$1.call(this._shortWeekdaysParse, llc);
+ if (ii !== -1) {
+ return ii;
+ }
+ ii = indexOf$1.call(this._minWeekdaysParse, llc);
+ return ii !== -1 ? ii : null;
+ } else if (format === 'ddd') {
+ ii = indexOf$1.call(this._shortWeekdaysParse, llc);
+ if (ii !== -1) {
+ return ii;
+ }
+ ii = indexOf$1.call(this._weekdaysParse, llc);
+ if (ii !== -1) {
+ return ii;
+ }
+ ii = indexOf$1.call(this._minWeekdaysParse, llc);
+ return ii !== -1 ? ii : null;
} else {
- if (format === 'dddd') {
- ii = indexOf.call(this._weekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._shortWeekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._minWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else if (format === 'ddd') {
- ii = indexOf.call(this._shortWeekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._weekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._minWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf.call(this._minWeekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._weekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf.call(this._shortWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
+ ii = indexOf$1.call(this._minWeekdaysParse, llc);
+ if (ii !== -1) {
+ return ii;
}
+ ii = indexOf$1.call(this._weekdaysParse, llc);
+ if (ii !== -1) {
+ return ii;
+ }
+ ii = indexOf$1.call(this._shortWeekdaysParse, llc);
+ return ii !== -1 ? ii : null;
}
}
+}
- function localeWeekdaysParse (weekdayName, format, strict) {
- var i, mom, regex;
+function localeWeekdaysParse (weekdayName, format, strict) {
+ var i, mom, regex;
- if (this._weekdaysParseExact) {
- return day_of_week__handleStrictParse.call(this, weekdayName, format, strict);
- }
+ if (this._weekdaysParseExact) {
+ return handleStrictParse$1.call(this, weekdayName, format, strict);
+ }
- if (!this._weekdaysParse) {
- this._weekdaysParse = [];
- this._minWeekdaysParse = [];
- this._shortWeekdaysParse = [];
- this._fullWeekdaysParse = [];
- }
+ if (!this._weekdaysParse) {
+ this._weekdaysParse = [];
+ this._minWeekdaysParse = [];
+ this._shortWeekdaysParse = [];
+ this._fullWeekdaysParse = [];
+ }
- for (i = 0; i < 7; i++) {
- // make the regex if we don't have it already
+ for (i = 0; i < 7; i++) {
+ // make the regex if we don't have it already
- mom = create_utc__createUTC([2000, 1]).day(i);
- if (strict && !this._fullWeekdaysParse[i]) {
- this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
- this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
- this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
- }
- if (!this._weekdaysParse[i]) {
- regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
- this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
- return i;
- } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
- return i;
- } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
- return i;
- } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
- return i;
- }
+ mom = createUTC([2000, 1]).day(i);
+ if (strict && !this._fullWeekdaysParse[i]) {
+ this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
+ this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
+ this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
}
+ if (!this._weekdaysParse[i]) {
+ regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
+ this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
+ return i;
+ } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
+ return i;
+ } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
+ return i;
+ } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
+ return i;
+ }
}
+}
- // MOMENTS
+// MOMENTS
- function getSetDayOfWeek (input) {
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
- var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
- if (input != null) {
- input = parseWeekday(input, this.localeData());
- return this.add(input - day, 'd');
- } else {
- return day;
- }
+function getSetDayOfWeek (input) {
+ if (!this.isValid()) {
+ return input != null ? this : NaN;
}
+ var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
+ if (input != null) {
+ input = parseWeekday(input, this.localeData());
+ return this.add(input - day, 'd');
+ } else {
+ return day;
+ }
+}
- function getSetLocaleDayOfWeek (input) {
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
- var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
- return input == null ? weekday : this.add(input - weekday, 'd');
+function getSetLocaleDayOfWeek (input) {
+ if (!this.isValid()) {
+ return input != null ? this : NaN;
}
+ var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
+ return input == null ? weekday : this.add(input - weekday, 'd');
+}
- function getSetISODayOfWeek (input) {
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
+function getSetISODayOfWeek (input) {
+ if (!this.isValid()) {
+ return input != null ? this : NaN;
+ }
- // behaves the same as moment#day except
- // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
- // as a setter, sunday should belong to the previous week.
+ // behaves the same as moment#day except
+ // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
+ // as a setter, sunday should belong to the previous week.
- if (input != null) {
- var weekday = parseIsoWeekday(input, this.localeData());
- return this.day(this.day() % 7 ? weekday : weekday - 7);
- } else {
- return this.day() || 7;
- }
+ if (input != null) {
+ var weekday = parseIsoWeekday(input, this.localeData());
+ return this.day(this.day() % 7 ? weekday : weekday - 7);
+ } else {
+ return this.day() || 7;
}
+}
- var defaultWeekdaysRegex = matchWord;
- function weekdaysRegex (isStrict) {
- if (this._weekdaysParseExact) {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- computeWeekdaysParse.call(this);
- }
- if (isStrict) {
- return this._weekdaysStrictRegex;
- } else {
- return this._weekdaysRegex;
- }
+var defaultWeekdaysRegex = matchWord;
+function weekdaysRegex (isStrict) {
+ if (this._weekdaysParseExact) {
+ if (!hasOwnProp(this, '_weekdaysRegex')) {
+ computeWeekdaysParse.call(this);
+ }
+ if (isStrict) {
+ return this._weekdaysStrictRegex;
} else {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- this._weekdaysRegex = defaultWeekdaysRegex;
- }
- return this._weekdaysStrictRegex && isStrict ?
- this._weekdaysStrictRegex : this._weekdaysRegex;
+ return this._weekdaysRegex;
}
+ } else {
+ if (!hasOwnProp(this, '_weekdaysRegex')) {
+ this._weekdaysRegex = defaultWeekdaysRegex;
+ }
+ return this._weekdaysStrictRegex && isStrict ?
+ this._weekdaysStrictRegex : this._weekdaysRegex;
}
+}
- var defaultWeekdaysShortRegex = matchWord;
- function weekdaysShortRegex (isStrict) {
- if (this._weekdaysParseExact) {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- computeWeekdaysParse.call(this);
- }
- if (isStrict) {
- return this._weekdaysShortStrictRegex;
- } else {
- return this._weekdaysShortRegex;
- }
+var defaultWeekdaysShortRegex = matchWord;
+function weekdaysShortRegex (isStrict) {
+ if (this._weekdaysParseExact) {
+ if (!hasOwnProp(this, '_weekdaysRegex')) {
+ computeWeekdaysParse.call(this);
+ }
+ if (isStrict) {
+ return this._weekdaysShortStrictRegex;
} else {
- if (!hasOwnProp(this, '_weekdaysShortRegex')) {
- this._weekdaysShortRegex = defaultWeekdaysShortRegex;
- }
- return this._weekdaysShortStrictRegex && isStrict ?
- this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
+ return this._weekdaysShortRegex;
}
+ } else {
+ if (!hasOwnProp(this, '_weekdaysShortRegex')) {
+ this._weekdaysShortRegex = defaultWeekdaysShortRegex;
+ }
+ return this._weekdaysShortStrictRegex && isStrict ?
+ this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
}
+}
- var defaultWeekdaysMinRegex = matchWord;
- function weekdaysMinRegex (isStrict) {
- if (this._weekdaysParseExact) {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- computeWeekdaysParse.call(this);
- }
- if (isStrict) {
- return this._weekdaysMinStrictRegex;
- } else {
- return this._weekdaysMinRegex;
- }
+var defaultWeekdaysMinRegex = matchWord;
+function weekdaysMinRegex (isStrict) {
+ if (this._weekdaysParseExact) {
+ if (!hasOwnProp(this, '_weekdaysRegex')) {
+ computeWeekdaysParse.call(this);
+ }
+ if (isStrict) {
+ return this._weekdaysMinStrictRegex;
} else {
- if (!hasOwnProp(this, '_weekdaysMinRegex')) {
- this._weekdaysMinRegex = defaultWeekdaysMinRegex;
- }
- return this._weekdaysMinStrictRegex && isStrict ?
- this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
+ return this._weekdaysMinRegex;
}
+ } else {
+ if (!hasOwnProp(this, '_weekdaysMinRegex')) {
+ this._weekdaysMinRegex = defaultWeekdaysMinRegex;
+ }
+ return this._weekdaysMinStrictRegex && isStrict ?
+ this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
}
+}
- function computeWeekdaysParse () {
- function cmpLenRev(a, b) {
- return b.length - a.length;
- }
+function computeWeekdaysParse () {
+ function cmpLenRev(a, b) {
+ return b.length - a.length;
+ }
- var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
- i, mom, minp, shortp, longp;
- for (i = 0; i < 7; i++) {
- // make the regex if we don't have it already
- mom = create_utc__createUTC([2000, 1]).day(i);
- minp = this.weekdaysMin(mom, '');
- shortp = this.weekdaysShort(mom, '');
- longp = this.weekdays(mom, '');
- minPieces.push(minp);
- shortPieces.push(shortp);
- longPieces.push(longp);
- mixedPieces.push(minp);
- mixedPieces.push(shortp);
- mixedPieces.push(longp);
- }
- // Sorting makes sure if one weekday (or abbr) is a prefix of another it
- // will match the longer piece.
- minPieces.sort(cmpLenRev);
- shortPieces.sort(cmpLenRev);
- longPieces.sort(cmpLenRev);
- mixedPieces.sort(cmpLenRev);
- for (i = 0; i < 7; i++) {
- shortPieces[i] = regexEscape(shortPieces[i]);
- longPieces[i] = regexEscape(longPieces[i]);
- mixedPieces[i] = regexEscape(mixedPieces[i]);
- }
+ var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
+ i, mom, minp, shortp, longp;
+ for (i = 0; i < 7; i++) {
+ // make the regex if we don't have it already
+ mom = createUTC([2000, 1]).day(i);
+ minp = this.weekdaysMin(mom, '');
+ shortp = this.weekdaysShort(mom, '');
+ longp = this.weekdays(mom, '');
+ minPieces.push(minp);
+ shortPieces.push(shortp);
+ longPieces.push(longp);
+ mixedPieces.push(minp);
+ mixedPieces.push(shortp);
+ mixedPieces.push(longp);
+ }
+ // Sorting makes sure if one weekday (or abbr) is a prefix of another it
+ // will match the longer piece.
+ minPieces.sort(cmpLenRev);
+ shortPieces.sort(cmpLenRev);
+ longPieces.sort(cmpLenRev);
+ mixedPieces.sort(cmpLenRev);
+ for (i = 0; i < 7; i++) {
+ shortPieces[i] = regexEscape(shortPieces[i]);
+ longPieces[i] = regexEscape(longPieces[i]);
+ mixedPieces[i] = regexEscape(mixedPieces[i]);
+ }
- this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
- this._weekdaysShortRegex = this._weekdaysRegex;
- this._weekdaysMinRegex = this._weekdaysRegex;
+ this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
+ this._weekdaysShortRegex = this._weekdaysRegex;
+ this._weekdaysMinRegex = this._weekdaysRegex;
- this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
- this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
- this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
- }
+ this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
+ this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
+ this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
+}
- // FORMATTING
+// FORMATTING
- function hFormat() {
- return this.hours() % 12 || 12;
- }
+function hFormat() {
+ return this.hours() % 12 || 12;
+}
- function kFormat() {
- return this.hours() || 24;
- }
+function kFormat() {
+ return this.hours() || 24;
+}
- addFormatToken('H', ['HH', 2], 0, 'hour');
- addFormatToken('h', ['hh', 2], 0, hFormat);
- addFormatToken('k', ['kk', 2], 0, kFormat);
+addFormatToken('H', ['HH', 2], 0, 'hour');
+addFormatToken('h', ['hh', 2], 0, hFormat);
+addFormatToken('k', ['kk', 2], 0, kFormat);
- addFormatToken('hmm', 0, 0, function () {
- return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
- });
+addFormatToken('hmm', 0, 0, function () {
+ return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
+});
- addFormatToken('hmmss', 0, 0, function () {
- return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
- zeroFill(this.seconds(), 2);
- });
+addFormatToken('hmmss', 0, 0, function () {
+ return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
+ zeroFill(this.seconds(), 2);
+});
- addFormatToken('Hmm', 0, 0, function () {
- return '' + this.hours() + zeroFill(this.minutes(), 2);
- });
+addFormatToken('Hmm', 0, 0, function () {
+ return '' + this.hours() + zeroFill(this.minutes(), 2);
+});
- addFormatToken('Hmmss', 0, 0, function () {
- return '' + this.hours() + zeroFill(this.minutes(), 2) +
- zeroFill(this.seconds(), 2);
+addFormatToken('Hmmss', 0, 0, function () {
+ return '' + this.hours() + zeroFill(this.minutes(), 2) +
+ zeroFill(this.seconds(), 2);
+});
+
+function meridiem (token, lowercase) {
+ addFormatToken(token, 0, 0, function () {
+ return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
});
+}
- function meridiem (token, lowercase) {
- addFormatToken(token, 0, 0, function () {
- return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
- });
- }
+meridiem('a', true);
+meridiem('A', false);
- meridiem('a', true);
- meridiem('A', false);
+// ALIASES
- // ALIASES
+addUnitAlias('hour', 'h');
- addUnitAlias('hour', 'h');
+// PRIORITY
+addUnitPriority('hour', 13);
- // PRIORITY
- addUnitPriority('hour', 13);
+// PARSING
- // PARSING
+function matchMeridiem (isStrict, locale) {
+ return locale._meridiemParse;
+}
- function matchMeridiem (isStrict, locale) {
- return locale._meridiemParse;
- }
+addRegexToken('a', matchMeridiem);
+addRegexToken('A', matchMeridiem);
+addRegexToken('H', match1to2);
+addRegexToken('h', match1to2);
+addRegexToken('HH', match1to2, match2);
+addRegexToken('hh', match1to2, match2);
- addRegexToken('a', matchMeridiem);
- addRegexToken('A', matchMeridiem);
- addRegexToken('H', match1to2);
- addRegexToken('h', match1to2);
- addRegexToken('HH', match1to2, match2);
- addRegexToken('hh', match1to2, match2);
+addRegexToken('hmm', match3to4);
+addRegexToken('hmmss', match5to6);
+addRegexToken('Hmm', match3to4);
+addRegexToken('Hmmss', match5to6);
- addRegexToken('hmm', match3to4);
- addRegexToken('hmmss', match5to6);
- addRegexToken('Hmm', match3to4);
- addRegexToken('Hmmss', match5to6);
+addParseToken(['H', 'HH'], HOUR);
+addParseToken(['a', 'A'], function (input, array, config) {
+ config._isPm = config._locale.isPM(input);
+ config._meridiem = input;
+});
+addParseToken(['h', 'hh'], function (input, array, config) {
+ array[HOUR] = toInt(input);
+ getParsingFlags(config).bigHour = true;
+});
+addParseToken('hmm', function (input, array, config) {
+ var pos = input.length - 2;
+ array[HOUR] = toInt(input.substr(0, pos));
+ array[MINUTE] = toInt(input.substr(pos));
+ getParsingFlags(config).bigHour = true;
+});
+addParseToken('hmmss', function (input, array, config) {
+ var pos1 = input.length - 4;
+ var pos2 = input.length - 2;
+ array[HOUR] = toInt(input.substr(0, pos1));
+ array[MINUTE] = toInt(input.substr(pos1, 2));
+ array[SECOND] = toInt(input.substr(pos2));
+ getParsingFlags(config).bigHour = true;
+});
+addParseToken('Hmm', function (input, array, config) {
+ var pos = input.length - 2;
+ array[HOUR] = toInt(input.substr(0, pos));
+ array[MINUTE] = toInt(input.substr(pos));
+});
+addParseToken('Hmmss', function (input, array, config) {
+ var pos1 = input.length - 4;
+ var pos2 = input.length - 2;
+ array[HOUR] = toInt(input.substr(0, pos1));
+ array[MINUTE] = toInt(input.substr(pos1, 2));
+ array[SECOND] = toInt(input.substr(pos2));
+});
- addParseToken(['H', 'HH'], HOUR);
- addParseToken(['a', 'A'], function (input, array, config) {
- config._isPm = config._locale.isPM(input);
- config._meridiem = input;
- });
- addParseToken(['h', 'hh'], function (input, array, config) {
- array[HOUR] = toInt(input);
- getParsingFlags(config).bigHour = true;
- });
- addParseToken('hmm', function (input, array, config) {
- var pos = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos));
- array[MINUTE] = toInt(input.substr(pos));
- getParsingFlags(config).bigHour = true;
- });
- addParseToken('hmmss', function (input, array, config) {
- var pos1 = input.length - 4;
- var pos2 = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos1));
- array[MINUTE] = toInt(input.substr(pos1, 2));
- array[SECOND] = toInt(input.substr(pos2));
- getParsingFlags(config).bigHour = true;
- });
- addParseToken('Hmm', function (input, array, config) {
- var pos = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos));
- array[MINUTE] = toInt(input.substr(pos));
- });
- addParseToken('Hmmss', function (input, array, config) {
- var pos1 = input.length - 4;
- var pos2 = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos1));
- array[MINUTE] = toInt(input.substr(pos1, 2));
- array[SECOND] = toInt(input.substr(pos2));
- });
+// LOCALES
- // LOCALES
+function localeIsPM (input) {
+ // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
+ // Using charAt should be more compatible.
+ return ((input + '').toLowerCase().charAt(0) === 'p');
+}
- function localeIsPM (input) {
- // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
- // Using charAt should be more compatible.
- return ((input + '').toLowerCase().charAt(0) === 'p');
+var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
+function localeMeridiem (hours, minutes, isLower) {
+ if (hours > 11) {
+ return isLower ? 'pm' : 'PM';
+ } else {
+ return isLower ? 'am' : 'AM';
}
+}
- var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
- function localeMeridiem (hours, minutes, isLower) {
- if (hours > 11) {
- return isLower ? 'pm' : 'PM';
- } else {
- return isLower ? 'am' : 'AM';
- }
- }
+// MOMENTS
- // MOMENTS
+// Setting the hour should keep the time, because the user explicitly
+// specified which hour he wants. So trying to maintain the same hour (in
+// a new timezone) makes sense. Adding/subtracting hours does not follow
+// this rule.
+var getSetHour = makeGetSet('Hours', true);
- // Setting the hour should keep the time, because the user explicitly
- // specified which hour he wants. So trying to maintain the same hour (in
- // a new timezone) makes sense. Adding/subtracting hours does not follow
- // this rule.
- var getSetHour = makeGetSet('Hours', true);
+// months
+// week
+// weekdays
+// meridiem
+var baseConfig = {
+ calendar: defaultCalendar,
+ longDateFormat: defaultLongDateFormat,
+ invalidDate: defaultInvalidDate,
+ ordinal: defaultOrdinal,
+ ordinalParse: defaultOrdinalParse,
+ relativeTime: defaultRelativeTime,
- var baseConfig = {
- calendar: defaultCalendar,
- longDateFormat: defaultLongDateFormat,
- invalidDate: defaultInvalidDate,
- ordinal: defaultOrdinal,
- ordinalParse: defaultOrdinalParse,
- relativeTime: defaultRelativeTime,
+ months: defaultLocaleMonths,
+ monthsShort: defaultLocaleMonthsShort,
- months: defaultLocaleMonths,
- monthsShort: defaultLocaleMonthsShort,
+ week: defaultLocaleWeek,
- week: defaultLocaleWeek,
+ weekdays: defaultLocaleWeekdays,
+ weekdaysMin: defaultLocaleWeekdaysMin,
+ weekdaysShort: defaultLocaleWeekdaysShort,
- weekdays: defaultLocaleWeekdays,
- weekdaysMin: defaultLocaleWeekdaysMin,
- weekdaysShort: defaultLocaleWeekdaysShort,
+ meridiemParse: defaultLocaleMeridiemParse
+};
- meridiemParse: defaultLocaleMeridiemParse
- };
+// internal storage for locale config files
+var locales = {};
+var localeFamilies = {};
+var globalLocale;
- // internal storage for locale config files
- var locales = {};
- var globalLocale;
+function normalizeLocale(key) {
+ return key ? key.toLowerCase().replace('_', '-') : key;
+}
- function normalizeLocale(key) {
- return key ? key.toLowerCase().replace('_', '-') : key;
- }
+// pick the locale from the array
+// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
+// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
+function chooseLocale(names) {
+ var i = 0, j, next, locale, split;
- // pick the locale from the array
- // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
- // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
- function chooseLocale(names) {
- var i = 0, j, next, locale, split;
-
- while (i < names.length) {
- split = normalizeLocale(names[i]).split('-');
- j = split.length;
- next = normalizeLocale(names[i + 1]);
- next = next ? next.split('-') : null;
- while (j > 0) {
- locale = loadLocale(split.slice(0, j).join('-'));
- if (locale) {
- return locale;
- }
- if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
- //the next array item is better than a shallower substring of this one
- break;
- }
- j--;
+ while (i < names.length) {
+ split = normalizeLocale(names[i]).split('-');
+ j = split.length;
+ next = normalizeLocale(names[i + 1]);
+ next = next ? next.split('-') : null;
+ while (j > 0) {
+ locale = loadLocale(split.slice(0, j).join('-'));
+ if (locale) {
+ return locale;
}
- i++;
+ if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
+ //the next array item is better than a shallower substring of this one
+ break;
+ }
+ j--;
}
- return null;
+ i++;
}
+ return null;
+}
- function loadLocale(name) {
- var oldLocale = null;
- // TODO: Find a better way to register and load all the locales in Node
- if (!locales[name] && (typeof module !== 'undefined') &&
- module && module.exports) {
- try {
- oldLocale = globalLocale._abbr;
- require('./locale/' + name);
- // because defineLocale currently also sets the global locale, we
- // want to undo that for lazy loaded locales
- locale_locales__getSetGlobalLocale(oldLocale);
- } catch (e) { }
- }
- return locales[name];
+function loadLocale(name) {
+ var oldLocale = null;
+ // TODO: Find a better way to register and load all the locales in Node
+ if (!locales[name] && (typeof module !== 'undefined') &&
+ module && module.exports) {
+ try {
+ oldLocale = globalLocale._abbr;
+ require('./locale/' + name);
+ // because defineLocale currently also sets the global locale, we
+ // want to undo that for lazy loaded locales
+ getSetGlobalLocale(oldLocale);
+ } catch (e) { }
}
+ return locales[name];
+}
- // This function will load locale and then set the global locale. If
- // no arguments are passed in, it will simply return the current global
- // locale key.
- function locale_locales__getSetGlobalLocale (key, values) {
- var data;
- if (key) {
- if (isUndefined(values)) {
- data = locale_locales__getLocale(key);
- }
- else {
- data = defineLocale(key, values);
- }
-
- if (data) {
- // moment.duration._locale = moment._locale = data;
- globalLocale = data;
- }
+// This function will load locale and then set the global locale. If
+// no arguments are passed in, it will simply return the current global
+// locale key.
+function getSetGlobalLocale (key, values) {
+ var data;
+ if (key) {
+ if (isUndefined(values)) {
+ data = getLocale(key);
}
+ else {
+ data = defineLocale(key, values);
+ }
- return globalLocale._abbr;
+ if (data) {
+ // moment.duration._locale = moment._locale = data;
+ globalLocale = data;
+ }
}
- function defineLocale (name, config) {
- if (config !== null) {
- var parentConfig = baseConfig;
- config.abbr = name;
- if (locales[name] != null) {
- deprecateSimple('defineLocaleOverride',
- 'use moment.updateLocale(localeName, config) to change ' +
- 'an existing locale. moment.defineLocale(localeName, ' +
- 'config) should only be used for creating a new locale ' +
- 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
- parentConfig = locales[name]._config;
- } else if (config.parentLocale != null) {
- if (locales[config.parentLocale] != null) {
- parentConfig = locales[config.parentLocale]._config;
- } else {
- // treat as if there is no base config
- deprecateSimple('parentLocaleUndefined',
- 'specified parentLocale is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/');
+ return globalLocale._abbr;
+}
+
+function defineLocale (name, config) {
+ if (config !== null) {
+ var parentConfig = baseConfig;
+ config.abbr = name;
+ if (locales[name] != null) {
+ deprecateSimple('defineLocaleOverride',
+ 'use moment.updateLocale(localeName, config) to change ' +
+ 'an existing locale. moment.defineLocale(localeName, ' +
+ 'config) should only be used for creating a new locale ' +
+ 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
+ parentConfig = locales[name]._config;
+ } else if (config.parentLocale != null) {
+ if (locales[config.parentLocale] != null) {
+ parentConfig = locales[config.parentLocale]._config;
+ } else {
+ if (!localeFamilies[config.parentLocale]) {
+ localeFamilies[config.parentLocale] = [];
}
+ localeFamilies[config.parentLocale].push({
+ name: name,
+ config: config
+ });
+ return null;
}
- locales[name] = new Locale(mergeConfigs(parentConfig, config));
+ }
+ locales[name] = new Locale(mergeConfigs(parentConfig, config));
- // backwards compat for now: also set the locale
- locale_locales__getSetGlobalLocale(name);
-
- return locales[name];
- } else {
- // useful for testing
- delete locales[name];
- return null;
+ if (localeFamilies[name]) {
+ localeFamilies[name].forEach(function (x) {
+ defineLocale(x.name, x.config);
+ });
}
- }
- function updateLocale(name, config) {
- if (config != null) {
- var locale, parentConfig = baseConfig;
- // MERGE
- if (locales[name] != null) {
- parentConfig = locales[name]._config;
- }
- config = mergeConfigs(parentConfig, config);
- locale = new Locale(config);
- locale.parentLocale = locales[name];
- locales[name] = locale;
+ // backwards compat for now: also set the locale
+ // make sure we set the locale AFTER all child locales have been
+ // created, so we won't end up with the child locale set.
+ getSetGlobalLocale(name);
- // backwards compat for now: also set the locale
- locale_locales__getSetGlobalLocale(name);
- } else {
- // pass null for config to unupdate, useful for tests
- if (locales[name] != null) {
- if (locales[name].parentLocale != null) {
- locales[name] = locales[name].parentLocale;
- } else if (locales[name] != null) {
- delete locales[name];
- }
- }
- }
+
return locales[name];
+ } else {
+ // useful for testing
+ delete locales[name];
+ return null;
}
+}
- // returns locale data
- function locale_locales__getLocale (key) {
- var locale;
-
- if (key && key._locale && key._locale._abbr) {
- key = key._locale._abbr;
+function updateLocale(name, config) {
+ if (config != null) {
+ var locale, parentConfig = baseConfig;
+ // MERGE
+ if (locales[name] != null) {
+ parentConfig = locales[name]._config;
}
+ config = mergeConfigs(parentConfig, config);
+ locale = new Locale(config);
+ locale.parentLocale = locales[name];
+ locales[name] = locale;
- if (!key) {
- return globalLocale;
- }
-
- if (!isArray(key)) {
- //short-circuit everything else
- locale = loadLocale(key);
- if (locale) {
- return locale;
+ // backwards compat for now: also set the locale
+ getSetGlobalLocale(name);
+ } else {
+ // pass null for config to unupdate, useful for tests
+ if (locales[name] != null) {
+ if (locales[name].parentLocale != null) {
+ locales[name] = locales[name].parentLocale;
+ } else if (locales[name] != null) {
+ delete locales[name];
}
- key = [key];
}
+ }
+ return locales[name];
+}
- return chooseLocale(key);
+// returns locale data
+function getLocale (key) {
+ var locale;
+
+ if (key && key._locale && key._locale._abbr) {
+ key = key._locale._abbr;
}
- function locale_locales__listLocales() {
- return keys(locales);
+ if (!key) {
+ return globalLocale;
}
- function checkOverflow (m) {
- var overflow;
- var a = m._a;
+ if (!isArray(key)) {
+ //short-circuit everything else
+ locale = loadLocale(key);
+ if (locale) {
+ return locale;
+ }
+ key = [key];
+ }
- if (a && getParsingFlags(m).overflow === -2) {
- overflow =
- a[MONTH] < 0 || a[MONTH] > 11 ? MONTH :
- a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
- a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
- a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE :
- a[SECOND] < 0 || a[SECOND] > 59 ? SECOND :
- a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
- -1;
+ return chooseLocale(key);
+}
- if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
- overflow = DATE;
- }
- if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
- overflow = WEEK;
- }
- if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
- overflow = WEEKDAY;
- }
+function listLocales() {
+ return keys$1(locales);
+}
- getParsingFlags(m).overflow = overflow;
+function checkOverflow (m) {
+ var overflow;
+ var a = m._a;
+
+ if (a && getParsingFlags(m).overflow === -2) {
+ overflow =
+ a[MONTH] < 0 || a[MONTH] > 11 ? MONTH :
+ a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
+ a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
+ a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE :
+ a[SECOND] < 0 || a[SECOND] > 59 ? SECOND :
+ a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
+ -1;
+
+ if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
+ overflow = DATE;
}
+ if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
+ overflow = WEEK;
+ }
+ if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
+ overflow = WEEKDAY;
+ }
- return m;
+ getParsingFlags(m).overflow = overflow;
}
- // iso 8601 regex
- // 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)
- 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)?)?/;
- 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)?)?/;
+ return m;
+}
- var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
+// iso 8601 regex
+// 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)
+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)?)?$/;
+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)?)?$/;
- var isoDates = [
- ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
- ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
- ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
- ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
- ['YYYY-DDD', /\d{4}-\d{3}/],
- ['YYYY-MM', /\d{4}-\d\d/, false],
- ['YYYYYYMMDD', /[+-]\d{10}/],
- ['YYYYMMDD', /\d{8}/],
- // YYYYMM is NOT allowed by the standard
- ['GGGG[W]WWE', /\d{4}W\d{3}/],
- ['GGGG[W]WW', /\d{4}W\d{2}/, false],
- ['YYYYDDD', /\d{7}/]
- ];
+var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
- // iso time formats and regexes
- var isoTimes = [
- ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
- ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
- ['HH:mm:ss', /\d\d:\d\d:\d\d/],
- ['HH:mm', /\d\d:\d\d/],
- ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
- ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
- ['HHmmss', /\d\d\d\d\d\d/],
- ['HHmm', /\d\d\d\d/],
- ['HH', /\d\d/]
- ];
+var isoDates = [
+ ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
+ ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
+ ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
+ ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
+ ['YYYY-DDD', /\d{4}-\d{3}/],
+ ['YYYY-MM', /\d{4}-\d\d/, false],
+ ['YYYYYYMMDD', /[+-]\d{10}/],
+ ['YYYYMMDD', /\d{8}/],
+ // YYYYMM is NOT allowed by the standard
+ ['GGGG[W]WWE', /\d{4}W\d{3}/],
+ ['GGGG[W]WW', /\d{4}W\d{2}/, false],
+ ['YYYYDDD', /\d{7}/]
+];
- var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
+// iso time formats and regexes
+var isoTimes = [
+ ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
+ ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
+ ['HH:mm:ss', /\d\d:\d\d:\d\d/],
+ ['HH:mm', /\d\d:\d\d/],
+ ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
+ ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
+ ['HHmmss', /\d\d\d\d\d\d/],
+ ['HHmm', /\d\d\d\d/],
+ ['HH', /\d\d/]
+];
- // date from iso format
- function configFromISO(config) {
- var i, l,
- string = config._i,
- match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
- allowTime, dateFormat, timeFormat, tzFormat;
+var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
- if (match) {
- getParsingFlags(config).iso = true;
+// date from iso format
+function configFromISO(config) {
+ var i, l,
+ string = config._i,
+ match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
+ allowTime, dateFormat, timeFormat, tzFormat;
- for (i = 0, l = isoDates.length; i < l; i++) {
- if (isoDates[i][1].exec(match[1])) {
- dateFormat = isoDates[i][0];
- allowTime = isoDates[i][2] !== false;
+ if (match) {
+ getParsingFlags(config).iso = true;
+
+ for (i = 0, l = isoDates.length; i < l; i++) {
+ if (isoDates[i][1].exec(match[1])) {
+ dateFormat = isoDates[i][0];
+ allowTime = isoDates[i][2] !== false;
+ break;
+ }
+ }
+ if (dateFormat == null) {
+ config._isValid = false;
+ return;
+ }
+ if (match[3]) {
+ for (i = 0, l = isoTimes.length; i < l; i++) {
+ if (isoTimes[i][1].exec(match[3])) {
+ // match[2] should be 'T' or space
+ timeFormat = (match[2] || ' ') + isoTimes[i][0];
break;
}
}
- if (dateFormat == null) {
+ if (timeFormat == null) {
config._isValid = false;
return;
}
- if (match[3]) {
- for (i = 0, l = isoTimes.length; i < l; i++) {
- if (isoTimes[i][1].exec(match[3])) {
- // match[2] should be 'T' or space
- timeFormat = (match[2] || ' ') + isoTimes[i][0];
- break;
- }
- }
- if (timeFormat == null) {
- config._isValid = false;
- return;
- }
- }
- if (!allowTime && timeFormat != null) {
+ }
+ if (!allowTime && timeFormat != null) {
+ config._isValid = false;
+ return;
+ }
+ if (match[4]) {
+ if (tzRegex.exec(match[4])) {
+ tzFormat = 'Z';
+ } else {
config._isValid = false;
return;
}
- if (match[4]) {
- if (tzRegex.exec(match[4])) {
- tzFormat = 'Z';
- } else {
- config._isValid = false;
- return;
- }
- }
- config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
- configFromStringAndFormat(config);
- } else {
- config._isValid = false;
}
+ config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
+ configFromStringAndFormat(config);
+ } else {
+ config._isValid = false;
}
+}
- // date from iso format or fallback
- function configFromString(config) {
- var matched = aspNetJsonRegex.exec(config._i);
+// date from iso format or fallback
+function configFromString(config) {
+ var matched = aspNetJsonRegex.exec(config._i);
- if (matched !== null) {
- config._d = new Date(+matched[1]);
- return;
- }
+ if (matched !== null) {
+ config._d = new Date(+matched[1]);
+ return;
+ }
- configFromISO(config);
- if (config._isValid === false) {
- delete config._isValid;
- utils_hooks__hooks.createFromInputFallback(config);
- }
+ configFromISO(config);
+ if (config._isValid === false) {
+ delete config._isValid;
+ hooks.createFromInputFallback(config);
}
+}
- utils_hooks__hooks.createFromInputFallback = deprecate(
- 'moment construction falls back to js Date. This is ' +
- 'discouraged and will be removed in upcoming major ' +
- 'release. Please refer to ' +
- 'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
- function (config) {
- config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
- }
- );
+hooks.createFromInputFallback = deprecate(
+ 'value provided is not in a recognized ISO format. moment construction falls back to js Date(), ' +
+ 'which is not reliable across all browsers and versions. Non ISO date formats are ' +
+ 'discouraged and will be removed in an upcoming major release. Please refer to ' +
+ 'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
+ function (config) {
+ config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
+ }
+);
- // Pick the first defined of two or three arguments.
- function defaults(a, b, c) {
- if (a != null) {
- return a;
- }
- if (b != null) {
- return b;
- }
- return c;
+// Pick the first defined of two or three arguments.
+function defaults(a, b, c) {
+ if (a != null) {
+ return a;
}
+ if (b != null) {
+ return b;
+ }
+ return c;
+}
- function currentDateArray(config) {
- // hooks is actually the exported moment object
- var nowValue = new Date(utils_hooks__hooks.now());
- if (config._useUTC) {
- return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
- }
- return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
+function currentDateArray(config) {
+ // hooks is actually the exported moment object
+ var nowValue = new Date(hooks.now());
+ if (config._useUTC) {
+ return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
}
+ return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
+}
- // convert an array to a date.
- // the array should mirror the parameters below
- // note: all values past the year are optional and will default to the lowest possible value.
- // [year, month, day , hour, minute, second, millisecond]
- function configFromArray (config) {
- var i, date, input = [], currentDate, yearToUse;
+// convert an array to a date.
+// the array should mirror the parameters below
+// note: all values past the year are optional and will default to the lowest possible value.
+// [year, month, day , hour, minute, second, millisecond]
+function configFromArray (config) {
+ var i, date, input = [], currentDate, yearToUse;
- if (config._d) {
- return;
- }
+ if (config._d) {
+ return;
+ }
- currentDate = currentDateArray(config);
+ currentDate = currentDateArray(config);
- //compute day of the year from weeks and weekdays
- if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- dayOfYearFromWeekInfo(config);
+ //compute day of the year from weeks and weekdays
+ if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
+ dayOfYearFromWeekInfo(config);
+ }
+
+ //if the day of the year is set, figure out what it is
+ if (config._dayOfYear) {
+ yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
+
+ if (config._dayOfYear > daysInYear(yearToUse)) {
+ getParsingFlags(config)._overflowDayOfYear = true;
}
- //if the day of the year is set, figure out what it is
- if (config._dayOfYear) {
- yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
+ date = createUTCDate(yearToUse, 0, config._dayOfYear);
+ config._a[MONTH] = date.getUTCMonth();
+ config._a[DATE] = date.getUTCDate();
+ }
- if (config._dayOfYear > daysInYear(yearToUse)) {
- getParsingFlags(config)._overflowDayOfYear = true;
- }
+ // Default to current date.
+ // * if no year, month, day of month are given, default to today
+ // * if day of month is given, default month and year
+ // * if month is given, default only year
+ // * if year is given, don't default anything
+ for (i = 0; i < 3 && config._a[i] == null; ++i) {
+ config._a[i] = input[i] = currentDate[i];
+ }
- date = createUTCDate(yearToUse, 0, config._dayOfYear);
- config._a[MONTH] = date.getUTCMonth();
- config._a[DATE] = date.getUTCDate();
- }
+ // Zero out whatever was not defaulted, including time
+ for (; i < 7; i++) {
+ config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
+ }
- // Default to current date.
- // * if no year, month, day of month are given, default to today
- // * if day of month is given, default month and year
- // * if month is given, default only year
- // * if year is given, don't default anything
- for (i = 0; i < 3 && config._a[i] == null; ++i) {
- config._a[i] = input[i] = currentDate[i];
- }
+ // Check for 24:00:00.000
+ if (config._a[HOUR] === 24 &&
+ config._a[MINUTE] === 0 &&
+ config._a[SECOND] === 0 &&
+ config._a[MILLISECOND] === 0) {
+ config._nextDay = true;
+ config._a[HOUR] = 0;
+ }
- // Zero out whatever was not defaulted, including time
- for (; i < 7; i++) {
- config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
- }
+ config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
+ // Apply timezone offset from input. The actual utcOffset can be changed
+ // with parseZone.
+ if (config._tzm != null) {
+ config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
+ }
- // Check for 24:00:00.000
- if (config._a[HOUR] === 24 &&
- config._a[MINUTE] === 0 &&
- config._a[SECOND] === 0 &&
- config._a[MILLISECOND] === 0) {
- config._nextDay = true;
- config._a[HOUR] = 0;
- }
+ if (config._nextDay) {
+ config._a[HOUR] = 24;
+ }
+}
- config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
- // Apply timezone offset from input. The actual utcOffset can be changed
- // with parseZone.
- if (config._tzm != null) {
- config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
- }
+function dayOfYearFromWeekInfo(config) {
+ var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
- if (config._nextDay) {
- config._a[HOUR] = 24;
+ w = config._w;
+ if (w.GG != null || w.W != null || w.E != null) {
+ dow = 1;
+ doy = 4;
+
+ // TODO: We need to take the current isoWeekYear, but that depends on
+ // how we interpret now (local, utc, fixed offset). So create
+ // a now version of current config (take local/utc/offset flags, and
+ // create now).
+ weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
+ week = defaults(w.W, 1);
+ weekday = defaults(w.E, 1);
+ if (weekday < 1 || weekday > 7) {
+ weekdayOverflow = true;
}
- }
+ } else {
+ dow = config._locale._week.dow;
+ doy = config._locale._week.doy;
- function dayOfYearFromWeekInfo(config) {
- var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
+ var curWeek = weekOfYear(createLocal(), dow, doy);
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- dow = 1;
- doy = 4;
+ weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);
- // TODO: We need to take the current isoWeekYear, but that depends on
- // how we interpret now (local, utc, fixed offset). So create
- // a now version of current config (take local/utc/offset flags, and
- // create now).
- weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year);
- week = defaults(w.W, 1);
- weekday = defaults(w.E, 1);
- if (weekday < 1 || weekday > 7) {
+ // Default to current week.
+ week = defaults(w.w, curWeek.week);
+
+ if (w.d != null) {
+ // weekday -- low day numbers are considered next week
+ weekday = w.d;
+ if (weekday < 0 || weekday > 6) {
weekdayOverflow = true;
}
- } else {
- dow = config._locale._week.dow;
- doy = config._locale._week.doy;
-
- weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year);
- week = defaults(w.w, 1);
-
- if (w.d != null) {
- // weekday -- low day numbers are considered next week
- weekday = w.d;
- if (weekday < 0 || weekday > 6) {
- weekdayOverflow = true;
- }
- } else if (w.e != null) {
- // local weekday -- counting starts from begining of week
- weekday = w.e + dow;
- if (w.e < 0 || w.e > 6) {
- weekdayOverflow = true;
- }
- } else {
- // default to begining of week
- weekday = dow;
+ } else if (w.e != null) {
+ // local weekday -- counting starts from begining of week
+ weekday = w.e + dow;
+ if (w.e < 0 || w.e > 6) {
+ weekdayOverflow = true;
}
- }
- if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
- getParsingFlags(config)._overflowWeeks = true;
- } else if (weekdayOverflow != null) {
- getParsingFlags(config)._overflowWeekday = true;
} else {
- temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
+ // default to begining of week
+ weekday = dow;
}
}
+ if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
+ getParsingFlags(config)._overflowWeeks = true;
+ } else if (weekdayOverflow != null) {
+ getParsingFlags(config)._overflowWeekday = true;
+ } else {
+ temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
+ config._a[YEAR] = temp.year;
+ config._dayOfYear = temp.dayOfYear;
+ }
+}
- // constant that refers to the ISO standard
- utils_hooks__hooks.ISO_8601 = function () {};
+// constant that refers to the ISO standard
+hooks.ISO_8601 = function () {};
- // date from string and format string
- function configFromStringAndFormat(config) {
- // TODO: Move this to another part of the creation flow to prevent circular deps
- if (config._f === utils_hooks__hooks.ISO_8601) {
- configFromISO(config);
- return;
- }
+// date from string and format string
+function configFromStringAndFormat(config) {
+ // TODO: Move this to another part of the creation flow to prevent circular deps
+ if (config._f === hooks.ISO_8601) {
+ configFromISO(config);
+ return;
+ }
- config._a = [];
- getParsingFlags(config).empty = true;
+ config._a = [];
+ getParsingFlags(config).empty = true;
- // This array is used to make a Date, either with `new Date` or `Date.UTC`
- var string = '' + config._i,
- i, parsedInput, tokens, token, skipped,
- stringLength = string.length,
- totalParsedInputLength = 0;
+ // This array is used to make a Date, either with `new Date` or `Date.UTC`
+ var string = '' + config._i,
+ i, parsedInput, tokens, token, skipped,
+ stringLength = string.length,
+ totalParsedInputLength = 0;
- tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
+ tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
- for (i = 0; i < tokens.length; i++) {
- token = tokens[i];
- parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
- // console.log('token', token, 'parsedInput', parsedInput,
- // 'regex', getParseRegexForToken(token, config));
+ for (i = 0; i < tokens.length; i++) {
+ token = tokens[i];
+ parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
+ // console.log('token', token, 'parsedInput', parsedInput,
+ // 'regex', getParseRegexForToken(token, config));
+ if (parsedInput) {
+ skipped = string.substr(0, string.indexOf(parsedInput));
+ if (skipped.length > 0) {
+ getParsingFlags(config).unusedInput.push(skipped);
+ }
+ string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
+ totalParsedInputLength += parsedInput.length;
+ }
+ // don't parse if it's not a known token
+ if (formatTokenFunctions[token]) {
if (parsedInput) {
- skipped = string.substr(0, string.indexOf(parsedInput));
- if (skipped.length > 0) {
- getParsingFlags(config).unusedInput.push(skipped);
- }
- string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
- totalParsedInputLength += parsedInput.length;
+ getParsingFlags(config).empty = false;
}
- // don't parse if it's not a known token
- if (formatTokenFunctions[token]) {
- if (parsedInput) {
- getParsingFlags(config).empty = false;
- }
- else {
- getParsingFlags(config).unusedTokens.push(token);
- }
- addTimeToArrayFromToken(token, parsedInput, config);
- }
- else if (config._strict && !parsedInput) {
+ else {
getParsingFlags(config).unusedTokens.push(token);
}
+ addTimeToArrayFromToken(token, parsedInput, config);
}
-
- // add remaining unparsed input length to the string
- getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
- if (string.length > 0) {
- getParsingFlags(config).unusedInput.push(string);
+ else if (config._strict && !parsedInput) {
+ getParsingFlags(config).unusedTokens.push(token);
}
+ }
- // clear _12h flag if hour is <= 12
- if (config._a[HOUR] <= 12 &&
- getParsingFlags(config).bigHour === true &&
- config._a[HOUR] > 0) {
- getParsingFlags(config).bigHour = undefined;
- }
+ // add remaining unparsed input length to the string
+ getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
+ if (string.length > 0) {
+ getParsingFlags(config).unusedInput.push(string);
+ }
- getParsingFlags(config).parsedDateParts = config._a.slice(0);
- getParsingFlags(config).meridiem = config._meridiem;
- // handle meridiem
- config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
-
- configFromArray(config);
- checkOverflow(config);
+ // clear _12h flag if hour is <= 12
+ if (config._a[HOUR] <= 12 &&
+ getParsingFlags(config).bigHour === true &&
+ config._a[HOUR] > 0) {
+ getParsingFlags(config).bigHour = undefined;
}
+ getParsingFlags(config).parsedDateParts = config._a.slice(0);
+ getParsingFlags(config).meridiem = config._meridiem;
+ // handle meridiem
+ config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
- function meridiemFixWrap (locale, hour, meridiem) {
- var isPm;
+ configFromArray(config);
+ checkOverflow(config);
+}
- if (meridiem == null) {
- // nothing to do
- return hour;
+
+function meridiemFixWrap (locale, hour, meridiem) {
+ var isPm;
+
+ if (meridiem == null) {
+ // nothing to do
+ return hour;
+ }
+ if (locale.meridiemHour != null) {
+ return locale.meridiemHour(hour, meridiem);
+ } else if (locale.isPM != null) {
+ // Fallback
+ isPm = locale.isPM(meridiem);
+ if (isPm && hour < 12) {
+ hour += 12;
}
- if (locale.meridiemHour != null) {
- return locale.meridiemHour(hour, meridiem);
- } else if (locale.isPM != null) {
- // Fallback
- isPm = locale.isPM(meridiem);
- if (isPm && hour < 12) {
- hour += 12;
- }
- if (!isPm && hour === 12) {
- hour = 0;
- }
- return hour;
- } else {
- // this is not supposed to happen
- return hour;
+ if (!isPm && hour === 12) {
+ hour = 0;
}
+ return hour;
+ } else {
+ // this is not supposed to happen
+ return hour;
}
+}
- // date from string and array of format strings
- function configFromStringAndArray(config) {
- var tempConfig,
- bestMoment,
+// date from string and array of format strings
+function configFromStringAndArray(config) {
+ var tempConfig,
+ bestMoment,
- scoreToBeat,
- i,
- currentScore;
+ scoreToBeat,
+ i,
+ currentScore;
- if (config._f.length === 0) {
- getParsingFlags(config).invalidFormat = true;
- config._d = new Date(NaN);
- return;
+ if (config._f.length === 0) {
+ getParsingFlags(config).invalidFormat = true;
+ config._d = new Date(NaN);
+ return;
+ }
+
+ for (i = 0; i < config._f.length; i++) {
+ currentScore = 0;
+ tempConfig = copyConfig({}, config);
+ if (config._useUTC != null) {
+ tempConfig._useUTC = config._useUTC;
}
+ tempConfig._f = config._f[i];
+ configFromStringAndFormat(tempConfig);
- for (i = 0; i < config._f.length; i++) {
- currentScore = 0;
- tempConfig = copyConfig({}, config);
- if (config._useUTC != null) {
- tempConfig._useUTC = config._useUTC;
- }
- tempConfig._f = config._f[i];
- configFromStringAndFormat(tempConfig);
+ if (!isValid(tempConfig)) {
+ continue;
+ }
- if (!valid__isValid(tempConfig)) {
- continue;
- }
+ // if there is any input that was not parsed add a penalty for that format
+ currentScore += getParsingFlags(tempConfig).charsLeftOver;
- // if there is any input that was not parsed add a penalty for that format
- currentScore += getParsingFlags(tempConfig).charsLeftOver;
+ //or tokens
+ currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
- //or tokens
- currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
+ getParsingFlags(tempConfig).score = currentScore;
- getParsingFlags(tempConfig).score = currentScore;
-
- if (scoreToBeat == null || currentScore < scoreToBeat) {
- scoreToBeat = currentScore;
- bestMoment = tempConfig;
- }
+ if (scoreToBeat == null || currentScore < scoreToBeat) {
+ scoreToBeat = currentScore;
+ bestMoment = tempConfig;
}
-
- extend(config, bestMoment || tempConfig);
}
- function configFromObject(config) {
- if (config._d) {
- return;
- }
+ extend(config, bestMoment || tempConfig);
+}
- var i = normalizeObjectUnits(config._i);
- config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
- return obj && parseInt(obj, 10);
- });
-
- configFromArray(config);
+function configFromObject(config) {
+ if (config._d) {
+ return;
}
- function createFromConfig (config) {
- var res = new Moment(checkOverflow(prepareConfig(config)));
- if (res._nextDay) {
- // Adding is smart enough around DST
- res.add(1, 'd');
- res._nextDay = undefined;
- }
+ var i = normalizeObjectUnits(config._i);
+ config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
+ return obj && parseInt(obj, 10);
+ });
- return res;
+ configFromArray(config);
+}
+
+function createFromConfig (config) {
+ var res = new Moment(checkOverflow(prepareConfig(config)));
+ if (res._nextDay) {
+ // Adding is smart enough around DST
+ res.add(1, 'd');
+ res._nextDay = undefined;
}
- function prepareConfig (config) {
- var input = config._i,
- format = config._f;
+ return res;
+}
- config._locale = config._locale || locale_locales__getLocale(config._l);
+function prepareConfig (config) {
+ var input = config._i,
+ format = config._f;
- if (input === null || (format === undefined && input === '')) {
- return valid__createInvalid({nullInput: true});
- }
+ config._locale = config._locale || getLocale(config._l);
- if (typeof input === 'string') {
- config._i = input = config._locale.preparse(input);
- }
+ if (input === null || (format === undefined && input === '')) {
+ return createInvalid({nullInput: true});
+ }
- if (isMoment(input)) {
- return new Moment(checkOverflow(input));
- } else if (isArray(format)) {
- configFromStringAndArray(config);
- } else if (isDate(input)) {
- config._d = input;
- } else if (format) {
- configFromStringAndFormat(config);
- } else {
- configFromInput(config);
- }
+ if (typeof input === 'string') {
+ config._i = input = config._locale.preparse(input);
+ }
- if (!valid__isValid(config)) {
- config._d = null;
- }
-
- return config;
+ if (isMoment(input)) {
+ return new Moment(checkOverflow(input));
+ } else if (isDate(input)) {
+ config._d = input;
+ } else if (isArray(format)) {
+ configFromStringAndArray(config);
+ } else if (format) {
+ configFromStringAndFormat(config);
+ } else {
+ configFromInput(config);
}
- function configFromInput(config) {
- var input = config._i;
- if (input === undefined) {
- config._d = new Date(utils_hooks__hooks.now());
- } else if (isDate(input)) {
- config._d = new Date(input.valueOf());
- } else if (typeof input === 'string') {
- configFromString(config);
- } else if (isArray(input)) {
- config._a = map(input.slice(0), function (obj) {
- return parseInt(obj, 10);
- });
- configFromArray(config);
- } else if (typeof(input) === 'object') {
- configFromObject(config);
- } else if (typeof(input) === 'number') {
- // from milliseconds
- config._d = new Date(input);
- } else {
- utils_hooks__hooks.createFromInputFallback(config);
- }
+ if (!isValid(config)) {
+ config._d = null;
}
- function createLocalOrUTC (input, format, locale, strict, isUTC) {
- var c = {};
+ return config;
+}
- if (typeof(locale) === 'boolean') {
- strict = locale;
- locale = undefined;
- }
+function configFromInput(config) {
+ var input = config._i;
+ if (input === undefined) {
+ config._d = new Date(hooks.now());
+ } else if (isDate(input)) {
+ config._d = new Date(input.valueOf());
+ } else if (typeof input === 'string') {
+ configFromString(config);
+ } else if (isArray(input)) {
+ config._a = map(input.slice(0), function (obj) {
+ return parseInt(obj, 10);
+ });
+ configFromArray(config);
+ } else if (typeof(input) === 'object') {
+ configFromObject(config);
+ } else if (isNumber(input)) {
+ // from milliseconds
+ config._d = new Date(input);
+ } else {
+ hooks.createFromInputFallback(config);
+ }
+}
- if ((isObject(input) && isObjectEmpty(input)) ||
- (isArray(input) && input.length === 0)) {
- input = undefined;
- }
- // object construction must be done this way.
- // https://github.com/moment/moment/issues/1423
- c._isAMomentObject = true;
- c._useUTC = c._isUTC = isUTC;
- c._l = locale;
- c._i = input;
- c._f = format;
- c._strict = strict;
+function createLocalOrUTC (input, format, locale, strict, isUTC) {
+ var c = {};
- return createFromConfig(c);
+ if (locale === true || locale === false) {
+ strict = locale;
+ locale = undefined;
}
- function local__createLocal (input, format, locale, strict) {
- return createLocalOrUTC(input, format, locale, strict, false);
+ if ((isObject(input) && isObjectEmpty(input)) ||
+ (isArray(input) && input.length === 0)) {
+ input = undefined;
}
+ // object construction must be done this way.
+ // https://github.com/moment/moment/issues/1423
+ c._isAMomentObject = true;
+ c._useUTC = c._isUTC = isUTC;
+ c._l = locale;
+ c._i = input;
+ c._f = format;
+ c._strict = strict;
- var prototypeMin = deprecate(
- 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
- function () {
- var other = local__createLocal.apply(null, arguments);
- if (this.isValid() && other.isValid()) {
- return other < this ? this : other;
- } else {
- return valid__createInvalid();
- }
+ return createFromConfig(c);
+}
+
+function createLocal (input, format, locale, strict) {
+ return createLocalOrUTC(input, format, locale, strict, false);
+}
+
+var prototypeMin = deprecate(
+ 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
+ function () {
+ var other = createLocal.apply(null, arguments);
+ if (this.isValid() && other.isValid()) {
+ return other < this ? this : other;
+ } else {
+ return createInvalid();
}
- );
+ }
+);
- var prototypeMax = deprecate(
- 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
- function () {
- var other = local__createLocal.apply(null, arguments);
- if (this.isValid() && other.isValid()) {
- return other > this ? this : other;
- } else {
- return valid__createInvalid();
- }
+var prototypeMax = deprecate(
+ 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
+ function () {
+ var other = createLocal.apply(null, arguments);
+ if (this.isValid() && other.isValid()) {
+ return other > this ? this : other;
+ } else {
+ return createInvalid();
}
- );
+ }
+);
- // Pick a moment m from moments so that m[fn](other) is true for all
- // other. This relies on the function fn to be transitive.
- //
- // moments should either be an array of moment objects or an array, whose
- // first element is an array of moment objects.
- function pickBy(fn, moments) {
- var res, i;
- if (moments.length === 1 && isArray(moments[0])) {
- moments = moments[0];
+// Pick a moment m from moments so that m[fn](other) is true for all
+// other. This relies on the function fn to be transitive.
+//
+// moments should either be an array of moment objects or an array, whose
+// first element is an array of moment objects.
+function pickBy(fn, moments) {
+ var res, i;
+ if (moments.length === 1 && isArray(moments[0])) {
+ moments = moments[0];
+ }
+ if (!moments.length) {
+ return createLocal();
+ }
+ res = moments[0];
+ for (i = 1; i < moments.length; ++i) {
+ if (!moments[i].isValid() || moments[i][fn](res)) {
+ res = moments[i];
}
- if (!moments.length) {
- return local__createLocal();
- }
- res = moments[0];
- for (i = 1; i < moments.length; ++i) {
- if (!moments[i].isValid() || moments[i][fn](res)) {
- res = moments[i];
- }
- }
- return res;
}
+ return res;
+}
- // TODO: Use [].sort instead?
- function min () {
- var args = [].slice.call(arguments, 0);
+// TODO: Use [].sort instead?
+function min () {
+ var args = [].slice.call(arguments, 0);
- return pickBy('isBefore', args);
- }
+ return pickBy('isBefore', args);
+}
- function max () {
- var args = [].slice.call(arguments, 0);
+function max () {
+ var args = [].slice.call(arguments, 0);
- return pickBy('isAfter', args);
- }
+ return pickBy('isAfter', args);
+}
- var now = function () {
- return Date.now ? Date.now() : +(new Date());
- };
+var now = function () {
+ return Date.now ? Date.now() : +(new Date());
+};
- function Duration (duration) {
- var normalizedInput = normalizeObjectUnits(duration),
- years = normalizedInput.year || 0,
- quarters = normalizedInput.quarter || 0,
- months = normalizedInput.month || 0,
- weeks = normalizedInput.week || 0,
- days = normalizedInput.day || 0,
- hours = normalizedInput.hour || 0,
- minutes = normalizedInput.minute || 0,
- seconds = normalizedInput.second || 0,
- milliseconds = normalizedInput.millisecond || 0;
+function Duration (duration) {
+ var normalizedInput = normalizeObjectUnits(duration),
+ years = normalizedInput.year || 0,
+ quarters = normalizedInput.quarter || 0,
+ months = normalizedInput.month || 0,
+ weeks = normalizedInput.week || 0,
+ days = normalizedInput.day || 0,
+ hours = normalizedInput.hour || 0,
+ minutes = normalizedInput.minute || 0,
+ seconds = normalizedInput.second || 0,
+ milliseconds = normalizedInput.millisecond || 0;
- // representation for dateAddRemove
- this._milliseconds = +milliseconds +
- seconds * 1e3 + // 1000
- minutes * 6e4 + // 1000 * 60
- hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
- // Because of dateAddRemove treats 24 hours as different from a
- // day when working around DST, we need to store them separately
- this._days = +days +
- weeks * 7;
- // It is impossible translate months into days without knowing
- // which months you are are talking about, so we have to store
- // it separately.
- this._months = +months +
- quarters * 3 +
- years * 12;
+ // representation for dateAddRemove
+ this._milliseconds = +milliseconds +
+ seconds * 1e3 + // 1000
+ minutes * 6e4 + // 1000 * 60
+ hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
+ // Because of dateAddRemove treats 24 hours as different from a
+ // day when working around DST, we need to store them separately
+ this._days = +days +
+ weeks * 7;
+ // It is impossible translate months into days without knowing
+ // which months you are are talking about, so we have to store
+ // it separately.
+ this._months = +months +
+ quarters * 3 +
+ years * 12;
- this._data = {};
+ this._data = {};
- this._locale = locale_locales__getLocale();
+ this._locale = getLocale();
- this._bubble();
- }
+ this._bubble();
+}
- function isDuration (obj) {
- return obj instanceof Duration;
+function isDuration (obj) {
+ return obj instanceof Duration;
+}
+
+function absRound (number) {
+ if (number < 0) {
+ return Math.round(-1 * number) * -1;
+ } else {
+ return Math.round(number);
}
+}
- // FORMATTING
+// FORMATTING
- function offset (token, separator) {
- addFormatToken(token, 0, 0, function () {
- var offset = this.utcOffset();
- var sign = '+';
- if (offset < 0) {
- offset = -offset;
- sign = '-';
- }
- return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
- });
- }
+function offset (token, separator) {
+ addFormatToken(token, 0, 0, function () {
+ var offset = this.utcOffset();
+ var sign = '+';
+ if (offset < 0) {
+ offset = -offset;
+ sign = '-';
+ }
+ return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
+ });
+}
- offset('Z', ':');
- offset('ZZ', '');
+offset('Z', ':');
+offset('ZZ', '');
- // PARSING
+// PARSING
- addRegexToken('Z', matchShortOffset);
- addRegexToken('ZZ', matchShortOffset);
- addParseToken(['Z', 'ZZ'], function (input, array, config) {
- config._useUTC = true;
- config._tzm = offsetFromString(matchShortOffset, input);
- });
+addRegexToken('Z', matchShortOffset);
+addRegexToken('ZZ', matchShortOffset);
+addParseToken(['Z', 'ZZ'], function (input, array, config) {
+ config._useUTC = true;
+ config._tzm = offsetFromString(matchShortOffset, input);
+});
- // HELPERS
+// HELPERS
- // timezone chunker
- // '+10:00' > ['10', '00']
- // '-1530' > ['-15', '30']
- var chunkOffset = /([\+\-]|\d\d)/gi;
+// timezone chunker
+// '+10:00' > ['10', '00']
+// '-1530' > ['-15', '30']
+var chunkOffset = /([\+\-]|\d\d)/gi;
- function offsetFromString(matcher, string) {
- var matches = ((string || '').match(matcher) || []);
- var chunk = matches[matches.length - 1] || [];
- var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
- var minutes = +(parts[1] * 60) + toInt(parts[2]);
+function offsetFromString(matcher, string) {
+ var matches = (string || '').match(matcher);
- return parts[0] === '+' ? minutes : -minutes;
+ if (matches === null) {
+ return null;
}
- // Return a moment from input, that is local/utc/zone equivalent to model.
- function cloneWithOffset(input, model) {
- var res, diff;
- if (model._isUTC) {
- res = model.clone();
- diff = (isMoment(input) || isDate(input) ? input.valueOf() : local__createLocal(input).valueOf()) - res.valueOf();
- // Use low-level api, because this fn is low-level api.
- res._d.setTime(res._d.valueOf() + diff);
- utils_hooks__hooks.updateOffset(res, false);
- return res;
- } else {
- return local__createLocal(input).local();
- }
- }
+ var chunk = matches[matches.length - 1] || [];
+ var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
+ var minutes = +(parts[1] * 60) + toInt(parts[2]);
- function getDateOffset (m) {
- // On Firefox.24 Date#getTimezoneOffset returns a floating point.
- // https://github.com/moment/moment/pull/1871
- return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
+ return minutes === 0 ?
+ 0 :
+ parts[0] === '+' ? minutes : -minutes;
+}
+
+// Return a moment from input, that is local/utc/zone equivalent to model.
+function cloneWithOffset(input, model) {
+ var res, diff;
+ if (model._isUTC) {
+ res = model.clone();
+ diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
+ // Use low-level api, because this fn is low-level api.
+ res._d.setTime(res._d.valueOf() + diff);
+ hooks.updateOffset(res, false);
+ return res;
+ } else {
+ return createLocal(input).local();
}
+}
- // HOOKS
+function getDateOffset (m) {
+ // On Firefox.24 Date#getTimezoneOffset returns a floating point.
+ // https://github.com/moment/moment/pull/1871
+ return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
+}
- // This function will be called whenever a moment is mutated.
- // It is intended to keep the offset in sync with the timezone.
- utils_hooks__hooks.updateOffset = function () {};
+// HOOKS
- // MOMENTS
+// This function will be called whenever a moment is mutated.
+// It is intended to keep the offset in sync with the timezone.
+hooks.updateOffset = function () {};
- // keepLocalTime = true means only change the timezone, without
- // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
- // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
- // +0200, so we adjust the time as needed, to be valid.
- //
- // Keeping the time actually adds/subtracts (one hour)
- // from the actual represented time. That is why we call updateOffset
- // a second time. In case it wants us to change the offset again
- // _changeInProgress == true case, then we have to adjust, because
- // there is no such time in the given timezone.
- function getSetOffset (input, keepLocalTime) {
- var offset = this._offset || 0,
- localAdjust;
- if (!this.isValid()) {
- return input != null ? this : NaN;
+// MOMENTS
+
+// keepLocalTime = true means only change the timezone, without
+// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
+// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
+// +0200, so we adjust the time as needed, to be valid.
+//
+// Keeping the time actually adds/subtracts (one hour)
+// from the actual represented time. That is why we call updateOffset
+// a second time. In case it wants us to change the offset again
+// _changeInProgress == true case, then we have to adjust, because
+// there is no such time in the given timezone.
+function getSetOffset (input, keepLocalTime) {
+ var offset = this._offset || 0,
+ localAdjust;
+ if (!this.isValid()) {
+ return input != null ? this : NaN;
+ }
+ if (input != null) {
+ if (typeof input === 'string') {
+ input = offsetFromString(matchShortOffset, input);
+ if (input === null) {
+ return this;
+ }
+ } else if (Math.abs(input) < 16) {
+ input = input * 60;
}
- if (input != null) {
- if (typeof input === 'string') {
- input = offsetFromString(matchShortOffset, input);
- } else if (Math.abs(input) < 16) {
- input = input * 60;
+ if (!this._isUTC && keepLocalTime) {
+ localAdjust = getDateOffset(this);
+ }
+ this._offset = input;
+ this._isUTC = true;
+ if (localAdjust != null) {
+ this.add(localAdjust, 'm');
+ }
+ if (offset !== input) {
+ if (!keepLocalTime || this._changeInProgress) {
+ addSubtract(this, createDuration(input - offset, 'm'), 1, false);
+ } else if (!this._changeInProgress) {
+ this._changeInProgress = true;
+ hooks.updateOffset(this, true);
+ this._changeInProgress = null;
}
- if (!this._isUTC && keepLocalTime) {
- localAdjust = getDateOffset(this);
- }
- this._offset = input;
- this._isUTC = true;
- if (localAdjust != null) {
- this.add(localAdjust, 'm');
- }
- if (offset !== input) {
- if (!keepLocalTime || this._changeInProgress) {
- add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false);
- } else if (!this._changeInProgress) {
- this._changeInProgress = true;
- utils_hooks__hooks.updateOffset(this, true);
- this._changeInProgress = null;
- }
- }
- return this;
- } else {
- return this._isUTC ? offset : getDateOffset(this);
}
+ return this;
+ } else {
+ return this._isUTC ? offset : getDateOffset(this);
}
+}
- function getSetZone (input, keepLocalTime) {
- if (input != null) {
- if (typeof input !== 'string') {
- input = -input;
- }
+function getSetZone (input, keepLocalTime) {
+ if (input != null) {
+ if (typeof input !== 'string') {
+ input = -input;
+ }
- this.utcOffset(input, keepLocalTime);
+ this.utcOffset(input, keepLocalTime);
- return this;
- } else {
- return -this.utcOffset();
- }
+ return this;
+ } else {
+ return -this.utcOffset();
}
+}
- function setOffsetToUTC (keepLocalTime) {
- return this.utcOffset(0, keepLocalTime);
- }
+function setOffsetToUTC (keepLocalTime) {
+ return this.utcOffset(0, keepLocalTime);
+}
- function setOffsetToLocal (keepLocalTime) {
- if (this._isUTC) {
- this.utcOffset(0, keepLocalTime);
- this._isUTC = false;
+function setOffsetToLocal (keepLocalTime) {
+ if (this._isUTC) {
+ this.utcOffset(0, keepLocalTime);
+ this._isUTC = false;
- if (keepLocalTime) {
- this.subtract(getDateOffset(this), 'm');
- }
+ if (keepLocalTime) {
+ this.subtract(getDateOffset(this), 'm');
}
- return this;
}
+ return this;
+}
- function setOffsetToParsedOffset () {
- if (this._tzm) {
- this.utcOffset(this._tzm);
- } else if (typeof this._i === 'string') {
- this.utcOffset(offsetFromString(matchOffset, this._i));
+function setOffsetToParsedOffset () {
+ if (this._tzm != null) {
+ this.utcOffset(this._tzm);
+ } else if (typeof this._i === 'string') {
+ var tZone = offsetFromString(matchOffset, this._i);
+ if (tZone != null) {
+ this.utcOffset(tZone);
}
- return this;
- }
-
- function hasAlignedHourOffset (input) {
- if (!this.isValid()) {
- return false;
+ else {
+ this.utcOffset(0, true);
}
- input = input ? local__createLocal(input).utcOffset() : 0;
-
- return (this.utcOffset() - input) % 60 === 0;
}
+ return this;
+}
- function isDaylightSavingTime () {
- return (
- this.utcOffset() > this.clone().month(0).utcOffset() ||
- this.utcOffset() > this.clone().month(5).utcOffset()
- );
+function hasAlignedHourOffset (input) {
+ if (!this.isValid()) {
+ return false;
}
+ input = input ? createLocal(input).utcOffset() : 0;
- function isDaylightSavingTimeShifted () {
- if (!isUndefined(this._isDSTShifted)) {
- return this._isDSTShifted;
- }
+ return (this.utcOffset() - input) % 60 === 0;
+}
- var c = {};
+function isDaylightSavingTime () {
+ return (
+ this.utcOffset() > this.clone().month(0).utcOffset() ||
+ this.utcOffset() > this.clone().month(5).utcOffset()
+ );
+}
- copyConfig(c, this);
- c = prepareConfig(c);
-
- if (c._a) {
- var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a);
- this._isDSTShifted = this.isValid() &&
- compareArrays(c._a, other.toArray()) > 0;
- } else {
- this._isDSTShifted = false;
- }
-
+function isDaylightSavingTimeShifted () {
+ if (!isUndefined(this._isDSTShifted)) {
return this._isDSTShifted;
}
- function isLocal () {
- return this.isValid() ? !this._isUTC : false;
- }
+ var c = {};
- function isUtcOffset () {
- return this.isValid() ? this._isUTC : false;
- }
+ copyConfig(c, this);
+ c = prepareConfig(c);
- function isUtc () {
- return this.isValid() ? this._isUTC && this._offset === 0 : false;
+ if (c._a) {
+ var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
+ this._isDSTShifted = this.isValid() &&
+ compareArrays(c._a, other.toArray()) > 0;
+ } else {
+ this._isDSTShifted = false;
}
- // ASP.NET json date format regex
- var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/;
+ return this._isDSTShifted;
+}
- // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
- // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
- // and further modified to allow for strings containing both week and day
- 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)?)?$/;
+function isLocal () {
+ return this.isValid() ? !this._isUTC : false;
+}
- function create__createDuration (input, key) {
- var duration = input,
- // matching against regexp is expensive, do it on demand
- match = null,
- sign,
- ret,
- diffRes;
+function isUtcOffset () {
+ return this.isValid() ? this._isUTC : false;
+}
- if (isDuration(input)) {
- duration = {
- ms : input._milliseconds,
- d : input._days,
- M : input._months
- };
- } else if (typeof input === 'number') {
- duration = {};
- if (key) {
- duration[key] = input;
- } else {
- duration.milliseconds = input;
- }
- } else if (!!(match = aspNetRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- duration = {
- y : 0,
- d : toInt(match[DATE]) * sign,
- h : toInt(match[HOUR]) * sign,
- m : toInt(match[MINUTE]) * sign,
- s : toInt(match[SECOND]) * sign,
- ms : toInt(match[MILLISECOND]) * sign
- };
- } else if (!!(match = isoRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- duration = {
- y : parseIso(match[2], sign),
- M : parseIso(match[3], sign),
- w : parseIso(match[4], sign),
- d : parseIso(match[5], sign),
- h : parseIso(match[6], sign),
- m : parseIso(match[7], sign),
- s : parseIso(match[8], sign)
- };
- } else if (duration == null) {// checks for null or undefined
- duration = {};
- } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
- diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to));
+function isUtc () {
+ return this.isValid() ? this._isUTC && this._offset === 0 : false;
+}
- duration = {};
- duration.ms = diffRes.milliseconds;
- duration.M = diffRes.months;
- }
+// ASP.NET json date format regex
+var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;
- ret = new Duration(duration);
+// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
+// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
+// and further modified to allow for strings containing both week and day
+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)?)?$/;
- if (isDuration(input) && hasOwnProp(input, '_locale')) {
- ret._locale = input._locale;
+function createDuration (input, key) {
+ var duration = input,
+ // matching against regexp is expensive, do it on demand
+ match = null,
+ sign,
+ ret,
+ diffRes;
+
+ if (isDuration(input)) {
+ duration = {
+ ms : input._milliseconds,
+ d : input._days,
+ M : input._months
+ };
+ } else if (isNumber(input)) {
+ duration = {};
+ if (key) {
+ duration[key] = input;
+ } else {
+ duration.milliseconds = input;
}
+ } else if (!!(match = aspNetRegex.exec(input))) {
+ sign = (match[1] === '-') ? -1 : 1;
+ duration = {
+ y : 0,
+ d : toInt(match[DATE]) * sign,
+ h : toInt(match[HOUR]) * sign,
+ m : toInt(match[MINUTE]) * sign,
+ s : toInt(match[SECOND]) * sign,
+ ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
+ };
+ } else if (!!(match = isoRegex.exec(input))) {
+ sign = (match[1] === '-') ? -1 : 1;
+ duration = {
+ y : parseIso(match[2], sign),
+ M : parseIso(match[3], sign),
+ w : parseIso(match[4], sign),
+ d : parseIso(match[5], sign),
+ h : parseIso(match[6], sign),
+ m : parseIso(match[7], sign),
+ s : parseIso(match[8], sign)
+ };
+ } else if (duration == null) {// checks for null or undefined
+ duration = {};
+ } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
+ diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
- return ret;
+ duration = {};
+ duration.ms = diffRes.milliseconds;
+ duration.M = diffRes.months;
}
- create__createDuration.fn = Duration.prototype;
+ ret = new Duration(duration);
- function parseIso (inp, sign) {
- // We'd normally use ~~inp for this, but unfortunately it also
- // converts floats to ints.
- // inp may be undefined, so careful calling replace on it.
- var res = inp && parseFloat(inp.replace(',', '.'));
- // apply sign while we're at it
- return (isNaN(res) ? 0 : res) * sign;
+ if (isDuration(input) && hasOwnProp(input, '_locale')) {
+ ret._locale = input._locale;
}
- function positiveMomentsDifference(base, other) {
- var res = {milliseconds: 0, months: 0};
+ return ret;
+}
- res.months = other.month() - base.month() +
- (other.year() - base.year()) * 12;
- if (base.clone().add(res.months, 'M').isAfter(other)) {
- --res.months;
- }
+createDuration.fn = Duration.prototype;
- res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
+function parseIso (inp, sign) {
+ // We'd normally use ~~inp for this, but unfortunately it also
+ // converts floats to ints.
+ // inp may be undefined, so careful calling replace on it.
+ var res = inp && parseFloat(inp.replace(',', '.'));
+ // apply sign while we're at it
+ return (isNaN(res) ? 0 : res) * sign;
+}
- return res;
+function positiveMomentsDifference(base, other) {
+ var res = {milliseconds: 0, months: 0};
+
+ res.months = other.month() - base.month() +
+ (other.year() - base.year()) * 12;
+ if (base.clone().add(res.months, 'M').isAfter(other)) {
+ --res.months;
}
- function momentsDifference(base, other) {
- var res;
- if (!(base.isValid() && other.isValid())) {
- return {milliseconds: 0, months: 0};
- }
+ res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
- other = cloneWithOffset(other, base);
- if (base.isBefore(other)) {
- res = positiveMomentsDifference(base, other);
- } else {
- res = positiveMomentsDifference(other, base);
- res.milliseconds = -res.milliseconds;
- res.months = -res.months;
- }
+ return res;
+}
- return res;
+function momentsDifference(base, other) {
+ var res;
+ if (!(base.isValid() && other.isValid())) {
+ return {milliseconds: 0, months: 0};
}
- function absRound (number) {
- if (number < 0) {
- return Math.round(-1 * number) * -1;
- } else {
- return Math.round(number);
- }
+ other = cloneWithOffset(other, base);
+ if (base.isBefore(other)) {
+ res = positiveMomentsDifference(base, other);
+ } else {
+ res = positiveMomentsDifference(other, base);
+ res.milliseconds = -res.milliseconds;
+ res.months = -res.months;
}
- // TODO: remove 'name' arg after deprecation is removed
- function createAdder(direction, name) {
- return function (val, period) {
- var dur, tmp;
- //invert the arguments, but complain about it
- if (period !== null && !isNaN(+period)) {
- deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
- 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
- tmp = val; val = period; period = tmp;
- }
+ return res;
+}
- val = typeof val === 'string' ? +val : val;
- dur = create__createDuration(val, period);
- add_subtract__addSubtract(this, dur, direction);
- return this;
- };
- }
-
- function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) {
- var milliseconds = duration._milliseconds,
- days = absRound(duration._days),
- months = absRound(duration._months);
-
- if (!mom.isValid()) {
- // No op
- return;
+// TODO: remove 'name' arg after deprecation is removed
+function createAdder(direction, name) {
+ return function (val, period) {
+ var dur, tmp;
+ //invert the arguments, but complain about it
+ if (period !== null && !isNaN(+period)) {
+ deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
+ 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
+ tmp = val; val = period; period = tmp;
}
- updateOffset = updateOffset == null ? true : updateOffset;
+ val = typeof val === 'string' ? +val : val;
+ dur = createDuration(val, period);
+ addSubtract(this, dur, direction);
+ return this;
+ };
+}
- if (milliseconds) {
- mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
- }
- if (days) {
- get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding);
- }
- if (months) {
- setMonth(mom, get_set__get(mom, 'Month') + months * isAdding);
- }
- if (updateOffset) {
- utils_hooks__hooks.updateOffset(mom, days || months);
- }
+function addSubtract (mom, duration, isAdding, updateOffset) {
+ var milliseconds = duration._milliseconds,
+ days = absRound(duration._days),
+ months = absRound(duration._months);
+
+ if (!mom.isValid()) {
+ // No op
+ return;
}
- var add_subtract__add = createAdder(1, 'add');
- var add_subtract__subtract = createAdder(-1, 'subtract');
+ updateOffset = updateOffset == null ? true : updateOffset;
- function getCalendarFormat(myMoment, now) {
- var diff = myMoment.diff(now, 'days', true);
- return diff < -6 ? 'sameElse' :
- diff < -1 ? 'lastWeek' :
- diff < 0 ? 'lastDay' :
- diff < 1 ? 'sameDay' :
- diff < 2 ? 'nextDay' :
- diff < 7 ? 'nextWeek' : 'sameElse';
+ if (milliseconds) {
+ mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
}
+ if (days) {
+ set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
+ }
+ if (months) {
+ setMonth(mom, get(mom, 'Month') + months * isAdding);
+ }
+ if (updateOffset) {
+ hooks.updateOffset(mom, days || months);
+ }
+}
- function moment_calendar__calendar (time, formats) {
- // We want to compare the start of today, vs this.
- // Getting start-of-today depends on whether we're local/utc/offset or not.
- var now = time || local__createLocal(),
- sod = cloneWithOffset(now, this).startOf('day'),
- format = utils_hooks__hooks.calendarFormat(this, sod) || 'sameElse';
+var add = createAdder(1, 'add');
+var subtract = createAdder(-1, 'subtract');
- var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
+function getCalendarFormat(myMoment, now) {
+ var diff = myMoment.diff(now, 'days', true);
+ return diff < -6 ? 'sameElse' :
+ diff < -1 ? 'lastWeek' :
+ diff < 0 ? 'lastDay' :
+ diff < 1 ? 'sameDay' :
+ diff < 2 ? 'nextDay' :
+ diff < 7 ? 'nextWeek' : 'sameElse';
+}
- return this.format(output || this.localeData().calendar(format, this, local__createLocal(now)));
- }
+function calendar$1 (time, formats) {
+ // We want to compare the start of today, vs this.
+ // Getting start-of-today depends on whether we're local/utc/offset or not.
+ var now = time || createLocal(),
+ sod = cloneWithOffset(now, this).startOf('day'),
+ format = hooks.calendarFormat(this, sod) || 'sameElse';
- function clone () {
- return new Moment(this);
- }
+ var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
- function isAfter (input, units) {
- var localInput = isMoment(input) ? input : local__createLocal(input);
- if (!(this.isValid() && localInput.isValid())) {
- return false;
- }
- units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
- if (units === 'millisecond') {
- return this.valueOf() > localInput.valueOf();
- } else {
- return localInput.valueOf() < this.clone().startOf(units).valueOf();
- }
- }
+ return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
+}
- function isBefore (input, units) {
- var localInput = isMoment(input) ? input : local__createLocal(input);
- if (!(this.isValid() && localInput.isValid())) {
- return false;
- }
- units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
- if (units === 'millisecond') {
- return this.valueOf() < localInput.valueOf();
- } else {
- return this.clone().endOf(units).valueOf() < localInput.valueOf();
- }
- }
+function clone () {
+ return new Moment(this);
+}
- function isBetween (from, to, units, inclusivity) {
- inclusivity = inclusivity || '()';
- return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
- (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
+function isAfter (input, units) {
+ var localInput = isMoment(input) ? input : createLocal(input);
+ if (!(this.isValid() && localInput.isValid())) {
+ return false;
}
-
- function isSame (input, units) {
- var localInput = isMoment(input) ? input : local__createLocal(input),
- inputMs;
- if (!(this.isValid() && localInput.isValid())) {
- return false;
- }
- units = normalizeUnits(units || 'millisecond');
- if (units === 'millisecond') {
- return this.valueOf() === localInput.valueOf();
- } else {
- inputMs = localInput.valueOf();
- return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
- }
+ units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
+ if (units === 'millisecond') {
+ return this.valueOf() > localInput.valueOf();
+ } else {
+ return localInput.valueOf() < this.clone().startOf(units).valueOf();
}
+}
- function isSameOrAfter (input, units) {
- return this.isSame(input, units) || this.isAfter(input,units);
+function isBefore (input, units) {
+ var localInput = isMoment(input) ? input : createLocal(input);
+ if (!(this.isValid() && localInput.isValid())) {
+ return false;
}
-
- function isSameOrBefore (input, units) {
- return this.isSame(input, units) || this.isBefore(input,units);
+ units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
+ if (units === 'millisecond') {
+ return this.valueOf() < localInput.valueOf();
+ } else {
+ return this.clone().endOf(units).valueOf() < localInput.valueOf();
}
+}
- function diff (input, units, asFloat) {
- var that,
- zoneDelta,
- delta, output;
+function isBetween (from, to, units, inclusivity) {
+ inclusivity = inclusivity || '()';
+ return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
+ (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
+}
- if (!this.isValid()) {
- return NaN;
- }
+function isSame (input, units) {
+ var localInput = isMoment(input) ? input : createLocal(input),
+ inputMs;
+ if (!(this.isValid() && localInput.isValid())) {
+ return false;
+ }
+ units = normalizeUnits(units || 'millisecond');
+ if (units === 'millisecond') {
+ return this.valueOf() === localInput.valueOf();
+ } else {
+ inputMs = localInput.valueOf();
+ return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
+ }
+}
- that = cloneWithOffset(input, this);
+function isSameOrAfter (input, units) {
+ return this.isSame(input, units) || this.isAfter(input,units);
+}
- if (!that.isValid()) {
- return NaN;
- }
+function isSameOrBefore (input, units) {
+ return this.isSame(input, units) || this.isBefore(input,units);
+}
- zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
+function diff (input, units, asFloat) {
+ var that,
+ zoneDelta,
+ delta, output;
- units = normalizeUnits(units);
-
- if (units === 'year' || units === 'month' || units === 'quarter') {
- output = monthDiff(this, that);
- if (units === 'quarter') {
- output = output / 3;
- } else if (units === 'year') {
- output = output / 12;
- }
- } else {
- delta = this - that;
- output = units === 'second' ? delta / 1e3 : // 1000
- units === 'minute' ? delta / 6e4 : // 1000 * 60
- units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
- units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
- units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
- delta;
- }
- return asFloat ? output : absFloor(output);
+ if (!this.isValid()) {
+ return NaN;
}
- function monthDiff (a, b) {
- // difference in months
- var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
- // b is in (anchor - 1 month, anchor + 1 month)
- anchor = a.clone().add(wholeMonthDiff, 'months'),
- anchor2, adjust;
+ that = cloneWithOffset(input, this);
- if (b - anchor < 0) {
- anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
- // linear across the month
- adjust = (b - anchor) / (anchor - anchor2);
- } else {
- anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
- // linear across the month
- adjust = (b - anchor) / (anchor2 - anchor);
- }
-
- //check for negative zero, return zero if negative zero
- return -(wholeMonthDiff + adjust) || 0;
+ if (!that.isValid()) {
+ return NaN;
}
- utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
- utils_hooks__hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
+ zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
- function toString () {
- return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
- }
+ units = normalizeUnits(units);
- function moment_format__toISOString () {
- var m = this.clone().utc();
- if (0 < m.year() && m.year() <= 9999) {
- if (isFunction(Date.prototype.toISOString)) {
- // native implementation is ~50x faster, use it when we can
- return this.toDate().toISOString();
- } else {
- return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
- } else {
- return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+ if (units === 'year' || units === 'month' || units === 'quarter') {
+ output = monthDiff(this, that);
+ if (units === 'quarter') {
+ output = output / 3;
+ } else if (units === 'year') {
+ output = output / 12;
}
+ } else {
+ delta = this - that;
+ output = units === 'second' ? delta / 1e3 : // 1000
+ units === 'minute' ? delta / 6e4 : // 1000 * 60
+ units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
+ units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
+ units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
+ delta;
}
+ return asFloat ? output : absFloor(output);
+}
- function format (inputString) {
- if (!inputString) {
- inputString = this.isUtc() ? utils_hooks__hooks.defaultFormatUtc : utils_hooks__hooks.defaultFormat;
- }
- var output = formatMoment(this, inputString);
- return this.localeData().postformat(output);
- }
+function monthDiff (a, b) {
+ // difference in months
+ var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
+ // b is in (anchor - 1 month, anchor + 1 month)
+ anchor = a.clone().add(wholeMonthDiff, 'months'),
+ anchor2, adjust;
- function from (time, withoutSuffix) {
- if (this.isValid() &&
- ((isMoment(time) && time.isValid()) ||
- local__createLocal(time).isValid())) {
- return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
- } else {
- return this.localeData().invalidDate();
- }
+ if (b - anchor < 0) {
+ anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
+ // linear across the month
+ adjust = (b - anchor) / (anchor - anchor2);
+ } else {
+ anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
+ // linear across the month
+ adjust = (b - anchor) / (anchor2 - anchor);
}
- function fromNow (withoutSuffix) {
- return this.from(local__createLocal(), withoutSuffix);
- }
+ //check for negative zero, return zero if negative zero
+ return -(wholeMonthDiff + adjust) || 0;
+}
- function to (time, withoutSuffix) {
- if (this.isValid() &&
- ((isMoment(time) && time.isValid()) ||
- local__createLocal(time).isValid())) {
- return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
+hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
+hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
+
+function toString () {
+ return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
+}
+
+function toISOString () {
+ var m = this.clone().utc();
+ if (0 < m.year() && m.year() <= 9999) {
+ if (isFunction(Date.prototype.toISOString)) {
+ // native implementation is ~50x faster, use it when we can
+ return this.toDate().toISOString();
} else {
- return this.localeData().invalidDate();
+ return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
}
+ } else {
+ return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
}
+}
- function toNow (withoutSuffix) {
- return this.to(local__createLocal(), withoutSuffix);
+/**
+ * Return a human readable representation of a moment that can
+ * also be evaluated to get a new moment which is the same
+ *
+ * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
+ */
+function inspect () {
+ if (!this.isValid()) {
+ return 'moment.invalid(/* ' + this._i + ' */)';
}
+ var func = 'moment';
+ var zone = '';
+ if (!this.isLocal()) {
+ func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
+ zone = 'Z';
+ }
+ var prefix = '[' + func + '("]';
+ var year = (0 < this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
+ var datetime = '-MM-DD[T]HH:mm:ss.SSS';
+ var suffix = zone + '[")]';
- // If passed a locale key, it will set the locale for this
- // instance. Otherwise, it will return the locale configuration
- // variables for this instance.
- function locale (key) {
- var newLocaleData;
+ return this.format(prefix + year + datetime + suffix);
+}
- if (key === undefined) {
- return this._locale._abbr;
- } else {
- newLocaleData = locale_locales__getLocale(key);
- if (newLocaleData != null) {
- this._locale = newLocaleData;
- }
- return this;
- }
+function format (inputString) {
+ if (!inputString) {
+ inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
}
+ var output = formatMoment(this, inputString);
+ return this.localeData().postformat(output);
+}
- var lang = deprecate(
- 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
- function (key) {
- if (key === undefined) {
- return this.localeData();
- } else {
- return this.locale(key);
- }
- }
- );
+function from (time, withoutSuffix) {
+ if (this.isValid() &&
+ ((isMoment(time) && time.isValid()) ||
+ createLocal(time).isValid())) {
+ return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
+ } else {
+ return this.localeData().invalidDate();
+ }
+}
- function localeData () {
- return this._locale;
+function fromNow (withoutSuffix) {
+ return this.from(createLocal(), withoutSuffix);
+}
+
+function to (time, withoutSuffix) {
+ if (this.isValid() &&
+ ((isMoment(time) && time.isValid()) ||
+ createLocal(time).isValid())) {
+ return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
+ } else {
+ return this.localeData().invalidDate();
}
+}
- function startOf (units) {
- units = normalizeUnits(units);
- // the following switch intentionally omits break keywords
- // to utilize falling through the cases.
- switch (units) {
- case 'year':
- this.month(0);
- /* falls through */
- case 'quarter':
- case 'month':
- this.date(1);
- /* falls through */
- case 'week':
- case 'isoWeek':
- case 'day':
- case 'date':
- this.hours(0);
- /* falls through */
- case 'hour':
- this.minutes(0);
- /* falls through */
- case 'minute':
- this.seconds(0);
- /* falls through */
- case 'second':
- this.milliseconds(0);
- }
+function toNow (withoutSuffix) {
+ return this.to(createLocal(), withoutSuffix);
+}
- // weeks are a special case
- if (units === 'week') {
- this.weekday(0);
- }
- if (units === 'isoWeek') {
- this.isoWeekday(1);
- }
+// If passed a locale key, it will set the locale for this
+// instance. Otherwise, it will return the locale configuration
+// variables for this instance.
+function locale (key) {
+ var newLocaleData;
- // quarters are also special
- if (units === 'quarter') {
- this.month(Math.floor(this.month() / 3) * 3);
+ if (key === undefined) {
+ return this._locale._abbr;
+ } else {
+ newLocaleData = getLocale(key);
+ if (newLocaleData != null) {
+ this._locale = newLocaleData;
}
-
return this;
}
+}
- function endOf (units) {
- units = normalizeUnits(units);
- if (units === undefined || units === 'millisecond') {
- return this;
+var lang = deprecate(
+ 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
+ function (key) {
+ if (key === undefined) {
+ return this.localeData();
+ } else {
+ return this.locale(key);
}
+ }
+);
- // 'date' is an alias for 'day', so it should be considered as such.
- if (units === 'date') {
- units = 'day';
- }
+function localeData () {
+ return this._locale;
+}
- return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
+function startOf (units) {
+ units = normalizeUnits(units);
+ // the following switch intentionally omits break keywords
+ // to utilize falling through the cases.
+ switch (units) {
+ case 'year':
+ this.month(0);
+ /* falls through */
+ case 'quarter':
+ case 'month':
+ this.date(1);
+ /* falls through */
+ case 'week':
+ case 'isoWeek':
+ case 'day':
+ case 'date':
+ this.hours(0);
+ /* falls through */
+ case 'hour':
+ this.minutes(0);
+ /* falls through */
+ case 'minute':
+ this.seconds(0);
+ /* falls through */
+ case 'second':
+ this.milliseconds(0);
}
- function to_type__valueOf () {
- return this._d.valueOf() - ((this._offset || 0) * 60000);
+ // weeks are a special case
+ if (units === 'week') {
+ this.weekday(0);
}
-
- function unix () {
- return Math.floor(this.valueOf() / 1000);
+ if (units === 'isoWeek') {
+ this.isoWeekday(1);
}
- function toDate () {
- return new Date(this.valueOf());
+ // quarters are also special
+ if (units === 'quarter') {
+ this.month(Math.floor(this.month() / 3) * 3);
}
- function toArray () {
- var m = this;
- return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
- }
+ return this;
+}
- function toObject () {
- var m = this;
- return {
- years: m.year(),
- months: m.month(),
- date: m.date(),
- hours: m.hours(),
- minutes: m.minutes(),
- seconds: m.seconds(),
- milliseconds: m.milliseconds()
- };
+function endOf (units) {
+ units = normalizeUnits(units);
+ if (units === undefined || units === 'millisecond') {
+ return this;
}
- function toJSON () {
- // new Date(NaN).toJSON() === null
- return this.isValid() ? this.toISOString() : null;
+ // 'date' is an alias for 'day', so it should be considered as such.
+ if (units === 'date') {
+ units = 'day';
}
- function moment_valid__isValid () {
- return valid__isValid(this);
- }
+ return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
+}
- function parsingFlags () {
- return extend({}, getParsingFlags(this));
- }
+function valueOf () {
+ return this._d.valueOf() - ((this._offset || 0) * 60000);
+}
- function invalidAt () {
- return getParsingFlags(this).overflow;
- }
+function unix () {
+ return Math.floor(this.valueOf() / 1000);
+}
- function creationData() {
- return {
- input: this._i,
- format: this._f,
- locale: this._locale,
- isUTC: this._isUTC,
- strict: this._strict
- };
- }
+function toDate () {
+ return new Date(this.valueOf());
+}
- // FORMATTING
+function toArray () {
+ var m = this;
+ return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
+}
- addFormatToken(0, ['gg', 2], 0, function () {
- return this.weekYear() % 100;
- });
+function toObject () {
+ var m = this;
+ return {
+ years: m.year(),
+ months: m.month(),
+ date: m.date(),
+ hours: m.hours(),
+ minutes: m.minutes(),
+ seconds: m.seconds(),
+ milliseconds: m.milliseconds()
+ };
+}
- addFormatToken(0, ['GG', 2], 0, function () {
- return this.isoWeekYear() % 100;
- });
+function toJSON () {
+ // new Date(NaN).toJSON() === null
+ return this.isValid() ? this.toISOString() : null;
+}
- function addWeekYearFormatToken (token, getter) {
- addFormatToken(0, [token, token.length], 0, getter);
- }
+function isValid$1 () {
+ return isValid(this);
+}
- addWeekYearFormatToken('gggg', 'weekYear');
- addWeekYearFormatToken('ggggg', 'weekYear');
- addWeekYearFormatToken('GGGG', 'isoWeekYear');
- addWeekYearFormatToken('GGGGG', 'isoWeekYear');
+function parsingFlags () {
+ return extend({}, getParsingFlags(this));
+}
- // ALIASES
+function invalidAt () {
+ return getParsingFlags(this).overflow;
+}
- addUnitAlias('weekYear', 'gg');
- addUnitAlias('isoWeekYear', 'GG');
+function creationData() {
+ return {
+ input: this._i,
+ format: this._f,
+ locale: this._locale,
+ isUTC: this._isUTC,
+ strict: this._strict
+ };
+}
- // PRIORITY
+// FORMATTING
- addUnitPriority('weekYear', 1);
- addUnitPriority('isoWeekYear', 1);
+addFormatToken(0, ['gg', 2], 0, function () {
+ return this.weekYear() % 100;
+});
+addFormatToken(0, ['GG', 2], 0, function () {
+ return this.isoWeekYear() % 100;
+});
- // PARSING
+function addWeekYearFormatToken (token, getter) {
+ addFormatToken(0, [token, token.length], 0, getter);
+}
- addRegexToken('G', matchSigned);
- addRegexToken('g', matchSigned);
- addRegexToken('GG', match1to2, match2);
- addRegexToken('gg', match1to2, match2);
- addRegexToken('GGGG', match1to4, match4);
- addRegexToken('gggg', match1to4, match4);
- addRegexToken('GGGGG', match1to6, match6);
- addRegexToken('ggggg', match1to6, match6);
+addWeekYearFormatToken('gggg', 'weekYear');
+addWeekYearFormatToken('ggggg', 'weekYear');
+addWeekYearFormatToken('GGGG', 'isoWeekYear');
+addWeekYearFormatToken('GGGGG', 'isoWeekYear');
- addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
- week[token.substr(0, 2)] = toInt(input);
- });
+// ALIASES
- addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
- week[token] = utils_hooks__hooks.parseTwoDigitYear(input);
- });
+addUnitAlias('weekYear', 'gg');
+addUnitAlias('isoWeekYear', 'GG');
- // MOMENTS
+// PRIORITY
- function getSetWeekYear (input) {
- return getSetWeekYearHelper.call(this,
- input,
- this.week(),
- this.weekday(),
- this.localeData()._week.dow,
- this.localeData()._week.doy);
- }
+addUnitPriority('weekYear', 1);
+addUnitPriority('isoWeekYear', 1);
- function getSetISOWeekYear (input) {
- return getSetWeekYearHelper.call(this,
- input, this.isoWeek(), this.isoWeekday(), 1, 4);
- }
- function getISOWeeksInYear () {
- return weeksInYear(this.year(), 1, 4);
- }
+// PARSING
- function getWeeksInYear () {
- var weekInfo = this.localeData()._week;
- return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
- }
+addRegexToken('G', matchSigned);
+addRegexToken('g', matchSigned);
+addRegexToken('GG', match1to2, match2);
+addRegexToken('gg', match1to2, match2);
+addRegexToken('GGGG', match1to4, match4);
+addRegexToken('gggg', match1to4, match4);
+addRegexToken('GGGGG', match1to6, match6);
+addRegexToken('ggggg', match1to6, match6);
- function getSetWeekYearHelper(input, week, weekday, dow, doy) {
- var weeksTarget;
- if (input == null) {
- return weekOfYear(this, dow, doy).year;
- } else {
- weeksTarget = weeksInYear(input, dow, doy);
- if (week > weeksTarget) {
- week = weeksTarget;
- }
- return setWeekAll.call(this, input, week, weekday, dow, doy);
+addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
+ week[token.substr(0, 2)] = toInt(input);
+});
+
+addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
+ week[token] = hooks.parseTwoDigitYear(input);
+});
+
+// MOMENTS
+
+function getSetWeekYear (input) {
+ return getSetWeekYearHelper.call(this,
+ input,
+ this.week(),
+ this.weekday(),
+ this.localeData()._week.dow,
+ this.localeData()._week.doy);
+}
+
+function getSetISOWeekYear (input) {
+ return getSetWeekYearHelper.call(this,
+ input, this.isoWeek(), this.isoWeekday(), 1, 4);
+}
+
+function getISOWeeksInYear () {
+ return weeksInYear(this.year(), 1, 4);
+}
+
+function getWeeksInYear () {
+ var weekInfo = this.localeData()._week;
+ return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
+}
+
+function getSetWeekYearHelper(input, week, weekday, dow, doy) {
+ var weeksTarget;
+ if (input == null) {
+ return weekOfYear(this, dow, doy).year;
+ } else {
+ weeksTarget = weeksInYear(input, dow, doy);
+ if (week > weeksTarget) {
+ week = weeksTarget;
}
+ return setWeekAll.call(this, input, week, weekday, dow, doy);
}
+}
- function setWeekAll(weekYear, week, weekday, dow, doy) {
- var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
- date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
+function setWeekAll(weekYear, week, weekday, dow, doy) {
+ var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
+ date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
- this.year(date.getUTCFullYear());
- this.month(date.getUTCMonth());
- this.date(date.getUTCDate());
- return this;
- }
+ this.year(date.getUTCFullYear());
+ this.month(date.getUTCMonth());
+ this.date(date.getUTCDate());
+ return this;
+}
- // FORMATTING
+// FORMATTING
- addFormatToken('Q', 0, 'Qo', 'quarter');
+addFormatToken('Q', 0, 'Qo', 'quarter');
- // ALIASES
+// ALIASES
- addUnitAlias('quarter', 'Q');
+addUnitAlias('quarter', 'Q');
- // PRIORITY
+// PRIORITY
- addUnitPriority('quarter', 7);
+addUnitPriority('quarter', 7);
- // PARSING
+// PARSING
- addRegexToken('Q', match1);
- addParseToken('Q', function (input, array) {
- array[MONTH] = (toInt(input) - 1) * 3;
- });
+addRegexToken('Q', match1);
+addParseToken('Q', function (input, array) {
+ array[MONTH] = (toInt(input) - 1) * 3;
+});
- // MOMENTS
+// MOMENTS
- function getSetQuarter (input) {
- return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
- }
+function getSetQuarter (input) {
+ return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
+}
- // FORMATTING
+// FORMATTING
- addFormatToken('D', ['DD', 2], 'Do', 'date');
+addFormatToken('D', ['DD', 2], 'Do', 'date');
- // ALIASES
+// ALIASES
- addUnitAlias('date', 'D');
+addUnitAlias('date', 'D');
- // PRIOROITY
- addUnitPriority('date', 9);
+// PRIOROITY
+addUnitPriority('date', 9);
- // PARSING
+// PARSING
- addRegexToken('D', match1to2);
- addRegexToken('DD', match1to2, match2);
- addRegexToken('Do', function (isStrict, locale) {
- return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
- });
+addRegexToken('D', match1to2);
+addRegexToken('DD', match1to2, match2);
+addRegexToken('Do', function (isStrict, locale) {
+ return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
+});
- addParseToken(['D', 'DD'], DATE);
- addParseToken('Do', function (input, array) {
- array[DATE] = toInt(input.match(match1to2)[0], 10);
- });
+addParseToken(['D', 'DD'], DATE);
+addParseToken('Do', function (input, array) {
+ array[DATE] = toInt(input.match(match1to2)[0], 10);
+});
- // MOMENTS
+// MOMENTS
- var getSetDayOfMonth = makeGetSet('Date', true);
+var getSetDayOfMonth = makeGetSet('Date', true);
- // FORMATTING
+// FORMATTING
- addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
+addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
- // ALIASES
+// ALIASES
- addUnitAlias('dayOfYear', 'DDD');
+addUnitAlias('dayOfYear', 'DDD');
- // PRIORITY
- addUnitPriority('dayOfYear', 4);
+// PRIORITY
+addUnitPriority('dayOfYear', 4);
- // PARSING
+// PARSING
- addRegexToken('DDD', match1to3);
- addRegexToken('DDDD', match3);
- addParseToken(['DDD', 'DDDD'], function (input, array, config) {
- config._dayOfYear = toInt(input);
- });
+addRegexToken('DDD', match1to3);
+addRegexToken('DDDD', match3);
+addParseToken(['DDD', 'DDDD'], function (input, array, config) {
+ config._dayOfYear = toInt(input);
+});
- // HELPERS
+// HELPERS
- // MOMENTS
+// MOMENTS
- function getSetDayOfYear (input) {
- var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
- return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
- }
+function getSetDayOfYear (input) {
+ var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
+ return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
+}
- // FORMATTING
+// FORMATTING
- addFormatToken('m', ['mm', 2], 0, 'minute');
+addFormatToken('m', ['mm', 2], 0, 'minute');
- // ALIASES
+// ALIASES
- addUnitAlias('minute', 'm');
+addUnitAlias('minute', 'm');
- // PRIORITY
+// PRIORITY
- addUnitPriority('minute', 14);
+addUnitPriority('minute', 14);
- // PARSING
+// PARSING
- addRegexToken('m', match1to2);
- addRegexToken('mm', match1to2, match2);
- addParseToken(['m', 'mm'], MINUTE);
+addRegexToken('m', match1to2);
+addRegexToken('mm', match1to2, match2);
+addParseToken(['m', 'mm'], MINUTE);
- // MOMENTS
+// MOMENTS
- var getSetMinute = makeGetSet('Minutes', false);
+var getSetMinute = makeGetSet('Minutes', false);
- // FORMATTING
+// FORMATTING
- addFormatToken('s', ['ss', 2], 0, 'second');
+addFormatToken('s', ['ss', 2], 0, 'second');
- // ALIASES
+// ALIASES
- addUnitAlias('second', 's');
+addUnitAlias('second', 's');
- // PRIORITY
+// PRIORITY
- addUnitPriority('second', 15);
+addUnitPriority('second', 15);
- // PARSING
+// PARSING
- addRegexToken('s', match1to2);
- addRegexToken('ss', match1to2, match2);
- addParseToken(['s', 'ss'], SECOND);
+addRegexToken('s', match1to2);
+addRegexToken('ss', match1to2, match2);
+addParseToken(['s', 'ss'], SECOND);
- // MOMENTS
+// MOMENTS
- var getSetSecond = makeGetSet('Seconds', false);
+var getSetSecond = makeGetSet('Seconds', false);
- // FORMATTING
+// FORMATTING
- addFormatToken('S', 0, 0, function () {
- return ~~(this.millisecond() / 100);
- });
+addFormatToken('S', 0, 0, function () {
+ return ~~(this.millisecond() / 100);
+});
- addFormatToken(0, ['SS', 2], 0, function () {
- return ~~(this.millisecond() / 10);
- });
+addFormatToken(0, ['SS', 2], 0, function () {
+ return ~~(this.millisecond() / 10);
+});
- addFormatToken(0, ['SSS', 3], 0, 'millisecond');
- addFormatToken(0, ['SSSS', 4], 0, function () {
- return this.millisecond() * 10;
- });
- addFormatToken(0, ['SSSSS', 5], 0, function () {
- return this.millisecond() * 100;
- });
- addFormatToken(0, ['SSSSSS', 6], 0, function () {
- return this.millisecond() * 1000;
- });
- addFormatToken(0, ['SSSSSSS', 7], 0, function () {
- return this.millisecond() * 10000;
- });
- addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
- return this.millisecond() * 100000;
- });
- addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
- return this.millisecond() * 1000000;
- });
+addFormatToken(0, ['SSS', 3], 0, 'millisecond');
+addFormatToken(0, ['SSSS', 4], 0, function () {
+ return this.millisecond() * 10;
+});
+addFormatToken(0, ['SSSSS', 5], 0, function () {
+ return this.millisecond() * 100;
+});
+addFormatToken(0, ['SSSSSS', 6], 0, function () {
+ return this.millisecond() * 1000;
+});
+addFormatToken(0, ['SSSSSSS', 7], 0, function () {
+ return this.millisecond() * 10000;
+});
+addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
+ return this.millisecond() * 100000;
+});
+addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
+ return this.millisecond() * 1000000;
+});
- // ALIASES
+// ALIASES
- addUnitAlias('millisecond', 'ms');
+addUnitAlias('millisecond', 'ms');
- // PRIORITY
+// PRIORITY
- addUnitPriority('millisecond', 16);
+addUnitPriority('millisecond', 16);
- // PARSING
+// PARSING
- addRegexToken('S', match1to3, match1);
- addRegexToken('SS', match1to3, match2);
- addRegexToken('SSS', match1to3, match3);
+addRegexToken('S', match1to3, match1);
+addRegexToken('SS', match1to3, match2);
+addRegexToken('SSS', match1to3, match3);
- var token;
- for (token = 'SSSS'; token.length <= 9; token += 'S') {
- addRegexToken(token, matchUnsigned);
- }
+var token;
+for (token = 'SSSS'; token.length <= 9; token += 'S') {
+ addRegexToken(token, matchUnsigned);
+}
- function parseMs(input, array) {
- array[MILLISECOND] = toInt(('0.' + input) * 1000);
- }
+function parseMs(input, array) {
+ array[MILLISECOND] = toInt(('0.' + input) * 1000);
+}
- for (token = 'S'; token.length <= 9; token += 'S') {
- addParseToken(token, parseMs);
- }
- // MOMENTS
+for (token = 'S'; token.length <= 9; token += 'S') {
+ addParseToken(token, parseMs);
+}
+// MOMENTS
- var getSetMillisecond = makeGetSet('Milliseconds', false);
+var getSetMillisecond = makeGetSet('Milliseconds', false);
- // FORMATTING
+// FORMATTING
- addFormatToken('z', 0, 0, 'zoneAbbr');
- addFormatToken('zz', 0, 0, 'zoneName');
+addFormatToken('z', 0, 0, 'zoneAbbr');
+addFormatToken('zz', 0, 0, 'zoneName');
- // MOMENTS
+// MOMENTS
- function getZoneAbbr () {
- return this._isUTC ? 'UTC' : '';
- }
+function getZoneAbbr () {
+ return this._isUTC ? 'UTC' : '';
+}
- function getZoneName () {
- return this._isUTC ? 'Coordinated Universal Time' : '';
- }
+function getZoneName () {
+ return this._isUTC ? 'Coordinated Universal Time' : '';
+}
- var momentPrototype__proto = Moment.prototype;
+var proto = Moment.prototype;
- momentPrototype__proto.add = add_subtract__add;
- momentPrototype__proto.calendar = moment_calendar__calendar;
- momentPrototype__proto.clone = clone;
- momentPrototype__proto.diff = diff;
- momentPrototype__proto.endOf = endOf;
- momentPrototype__proto.format = format;
- momentPrototype__proto.from = from;
- momentPrototype__proto.fromNow = fromNow;
- momentPrototype__proto.to = to;
- momentPrototype__proto.toNow = toNow;
- momentPrototype__proto.get = stringGet;
- momentPrototype__proto.invalidAt = invalidAt;
- momentPrototype__proto.isAfter = isAfter;
- momentPrototype__proto.isBefore = isBefore;
- momentPrototype__proto.isBetween = isBetween;
- momentPrototype__proto.isSame = isSame;
- momentPrototype__proto.isSameOrAfter = isSameOrAfter;
- momentPrototype__proto.isSameOrBefore = isSameOrBefore;
- momentPrototype__proto.isValid = moment_valid__isValid;
- momentPrototype__proto.lang = lang;
- momentPrototype__proto.locale = locale;
- momentPrototype__proto.localeData = localeData;
- momentPrototype__proto.max = prototypeMax;
- momentPrototype__proto.min = prototypeMin;
- momentPrototype__proto.parsingFlags = parsingFlags;
- momentPrototype__proto.set = stringSet;
- momentPrototype__proto.startOf = startOf;
- momentPrototype__proto.subtract = add_subtract__subtract;
- momentPrototype__proto.toArray = toArray;
- momentPrototype__proto.toObject = toObject;
- momentPrototype__proto.toDate = toDate;
- momentPrototype__proto.toISOString = moment_format__toISOString;
- momentPrototype__proto.toJSON = toJSON;
- momentPrototype__proto.toString = toString;
- momentPrototype__proto.unix = unix;
- momentPrototype__proto.valueOf = to_type__valueOf;
- momentPrototype__proto.creationData = creationData;
+proto.add = add;
+proto.calendar = calendar$1;
+proto.clone = clone;
+proto.diff = diff;
+proto.endOf = endOf;
+proto.format = format;
+proto.from = from;
+proto.fromNow = fromNow;
+proto.to = to;
+proto.toNow = toNow;
+proto.get = stringGet;
+proto.invalidAt = invalidAt;
+proto.isAfter = isAfter;
+proto.isBefore = isBefore;
+proto.isBetween = isBetween;
+proto.isSame = isSame;
+proto.isSameOrAfter = isSameOrAfter;
+proto.isSameOrBefore = isSameOrBefore;
+proto.isValid = isValid$1;
+proto.lang = lang;
+proto.locale = locale;
+proto.localeData = localeData;
+proto.max = prototypeMax;
+proto.min = prototypeMin;
+proto.parsingFlags = parsingFlags;
+proto.set = stringSet;
+proto.startOf = startOf;
+proto.subtract = subtract;
+proto.toArray = toArray;
+proto.toObject = toObject;
+proto.toDate = toDate;
+proto.toISOString = toISOString;
+proto.inspect = inspect;
+proto.toJSON = toJSON;
+proto.toString = toString;
+proto.unix = unix;
+proto.valueOf = valueOf;
+proto.creationData = creationData;
- // Year
- momentPrototype__proto.year = getSetYear;
- momentPrototype__proto.isLeapYear = getIsLeapYear;
+// Year
+proto.year = getSetYear;
+proto.isLeapYear = getIsLeapYear;
- // Week Year
- momentPrototype__proto.weekYear = getSetWeekYear;
- momentPrototype__proto.isoWeekYear = getSetISOWeekYear;
+// Week Year
+proto.weekYear = getSetWeekYear;
+proto.isoWeekYear = getSetISOWeekYear;
- // Quarter
- momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter;
+// Quarter
+proto.quarter = proto.quarters = getSetQuarter;
- // Month
- momentPrototype__proto.month = getSetMonth;
- momentPrototype__proto.daysInMonth = getDaysInMonth;
+// Month
+proto.month = getSetMonth;
+proto.daysInMonth = getDaysInMonth;
- // Week
- momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek;
- momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek;
- momentPrototype__proto.weeksInYear = getWeeksInYear;
- momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear;
+// Week
+proto.week = proto.weeks = getSetWeek;
+proto.isoWeek = proto.isoWeeks = getSetISOWeek;
+proto.weeksInYear = getWeeksInYear;
+proto.isoWeeksInYear = getISOWeeksInYear;
- // Day
- momentPrototype__proto.date = getSetDayOfMonth;
- momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek;
- momentPrototype__proto.weekday = getSetLocaleDayOfWeek;
- momentPrototype__proto.isoWeekday = getSetISODayOfWeek;
- momentPrototype__proto.dayOfYear = getSetDayOfYear;
+// Day
+proto.date = getSetDayOfMonth;
+proto.day = proto.days = getSetDayOfWeek;
+proto.weekday = getSetLocaleDayOfWeek;
+proto.isoWeekday = getSetISODayOfWeek;
+proto.dayOfYear = getSetDayOfYear;
- // Hour
- momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour;
+// Hour
+proto.hour = proto.hours = getSetHour;
- // Minute
- momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute;
+// Minute
+proto.minute = proto.minutes = getSetMinute;
- // Second
- momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond;
+// Second
+proto.second = proto.seconds = getSetSecond;
- // Millisecond
- momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond;
+// Millisecond
+proto.millisecond = proto.milliseconds = getSetMillisecond;
- // Offset
- momentPrototype__proto.utcOffset = getSetOffset;
- momentPrototype__proto.utc = setOffsetToUTC;
- momentPrototype__proto.local = setOffsetToLocal;
- momentPrototype__proto.parseZone = setOffsetToParsedOffset;
- momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset;
- momentPrototype__proto.isDST = isDaylightSavingTime;
- momentPrototype__proto.isLocal = isLocal;
- momentPrototype__proto.isUtcOffset = isUtcOffset;
- momentPrototype__proto.isUtc = isUtc;
- momentPrototype__proto.isUTC = isUtc;
+// Offset
+proto.utcOffset = getSetOffset;
+proto.utc = setOffsetToUTC;
+proto.local = setOffsetToLocal;
+proto.parseZone = setOffsetToParsedOffset;
+proto.hasAlignedHourOffset = hasAlignedHourOffset;
+proto.isDST = isDaylightSavingTime;
+proto.isLocal = isLocal;
+proto.isUtcOffset = isUtcOffset;
+proto.isUtc = isUtc;
+proto.isUTC = isUtc;
- // Timezone
- momentPrototype__proto.zoneAbbr = getZoneAbbr;
- momentPrototype__proto.zoneName = getZoneName;
+// Timezone
+proto.zoneAbbr = getZoneAbbr;
+proto.zoneName = getZoneName;
- // Deprecations
- momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
- momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
- momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
- momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
- momentPrototype__proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
+// Deprecations
+proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
+proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
+proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
+proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
+proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
- var momentPrototype = momentPrototype__proto;
+function createUnix (input) {
+ return createLocal(input * 1000);
+}
- function moment__createUnix (input) {
- return local__createLocal(input * 1000);
- }
+function createInZone () {
+ return createLocal.apply(null, arguments).parseZone();
+}
- function moment__createInZone () {
- return local__createLocal.apply(null, arguments).parseZone();
- }
+function preParsePostFormat (string) {
+ return string;
+}
- function preParsePostFormat (string) {
- return string;
- }
+var proto$1 = Locale.prototype;
- var prototype__proto = Locale.prototype;
+proto$1.calendar = calendar;
+proto$1.longDateFormat = longDateFormat;
+proto$1.invalidDate = invalidDate;
+proto$1.ordinal = ordinal;
+proto$1.preparse = preParsePostFormat;
+proto$1.postformat = preParsePostFormat;
+proto$1.relativeTime = relativeTime;
+proto$1.pastFuture = pastFuture;
+proto$1.set = set;
- prototype__proto.calendar = locale_calendar__calendar;
- prototype__proto.longDateFormat = longDateFormat;
- prototype__proto.invalidDate = invalidDate;
- prototype__proto.ordinal = ordinal;
- prototype__proto.preparse = preParsePostFormat;
- prototype__proto.postformat = preParsePostFormat;
- prototype__proto.relativeTime = relative__relativeTime;
- prototype__proto.pastFuture = pastFuture;
- prototype__proto.set = locale_set__set;
+// Month
+proto$1.months = localeMonths;
+proto$1.monthsShort = localeMonthsShort;
+proto$1.monthsParse = localeMonthsParse;
+proto$1.monthsRegex = monthsRegex;
+proto$1.monthsShortRegex = monthsShortRegex;
- // Month
- prototype__proto.months = localeMonths;
- prototype__proto.monthsShort = localeMonthsShort;
- prototype__proto.monthsParse = localeMonthsParse;
- prototype__proto.monthsRegex = monthsRegex;
- prototype__proto.monthsShortRegex = monthsShortRegex;
+// Week
+proto$1.week = localeWeek;
+proto$1.firstDayOfYear = localeFirstDayOfYear;
+proto$1.firstDayOfWeek = localeFirstDayOfWeek;
- // Week
- prototype__proto.week = localeWeek;
- prototype__proto.firstDayOfYear = localeFirstDayOfYear;
- prototype__proto.firstDayOfWeek = localeFirstDayOfWeek;
+// Day of Week
+proto$1.weekdays = localeWeekdays;
+proto$1.weekdaysMin = localeWeekdaysMin;
+proto$1.weekdaysShort = localeWeekdaysShort;
+proto$1.weekdaysParse = localeWeekdaysParse;
- // Day of Week
- prototype__proto.weekdays = localeWeekdays;
- prototype__proto.weekdaysMin = localeWeekdaysMin;
- prototype__proto.weekdaysShort = localeWeekdaysShort;
- prototype__proto.weekdaysParse = localeWeekdaysParse;
+proto$1.weekdaysRegex = weekdaysRegex;
+proto$1.weekdaysShortRegex = weekdaysShortRegex;
+proto$1.weekdaysMinRegex = weekdaysMinRegex;
- prototype__proto.weekdaysRegex = weekdaysRegex;
- prototype__proto.weekdaysShortRegex = weekdaysShortRegex;
- prototype__proto.weekdaysMinRegex = weekdaysMinRegex;
+// Hours
+proto$1.isPM = localeIsPM;
+proto$1.meridiem = localeMeridiem;
- // Hours
- prototype__proto.isPM = localeIsPM;
- prototype__proto.meridiem = localeMeridiem;
+function get$1 (format, index, field, setter) {
+ var locale = getLocale();
+ var utc = createUTC().set(setter, index);
+ return locale[field](utc, format);
+}
- function lists__get (format, index, field, setter) {
- var locale = locale_locales__getLocale();
- var utc = create_utc__createUTC().set(setter, index);
- return locale[field](utc, format);
+function listMonthsImpl (format, index, field) {
+ if (isNumber(format)) {
+ index = format;
+ format = undefined;
}
- function listMonthsImpl (format, index, field) {
- if (typeof format === 'number') {
+ format = format || '';
+
+ if (index != null) {
+ return get$1(format, index, field, 'month');
+ }
+
+ var i;
+ var out = [];
+ for (i = 0; i < 12; i++) {
+ out[i] = get$1(format, i, field, 'month');
+ }
+ return out;
+}
+
+// ()
+// (5)
+// (fmt, 5)
+// (fmt)
+// (true)
+// (true, 5)
+// (true, fmt, 5)
+// (true, fmt)
+function listWeekdaysImpl (localeSorted, format, index, field) {
+ if (typeof localeSorted === 'boolean') {
+ if (isNumber(format)) {
index = format;
format = undefined;
}
format = format || '';
+ } else {
+ format = localeSorted;
+ index = format;
+ localeSorted = false;
- if (index != null) {
- return lists__get(format, index, field, 'month');
+ if (isNumber(format)) {
+ index = format;
+ format = undefined;
}
- var i;
- var out = [];
- for (i = 0; i < 12; i++) {
- out[i] = lists__get(format, i, field, 'month');
- }
- return out;
+ format = format || '';
}
- // ()
- // (5)
- // (fmt, 5)
- // (fmt)
- // (true)
- // (true, 5)
- // (true, fmt, 5)
- // (true, fmt)
- function listWeekdaysImpl (localeSorted, format, index, field) {
- if (typeof localeSorted === 'boolean') {
- if (typeof format === 'number') {
- index = format;
- format = undefined;
- }
+ var locale = getLocale(),
+ shift = localeSorted ? locale._week.dow : 0;
- format = format || '';
- } else {
- format = localeSorted;
- index = format;
- localeSorted = false;
+ if (index != null) {
+ return get$1(format, (index + shift) % 7, field, 'day');
+ }
- if (typeof format === 'number') {
- index = format;
- format = undefined;
- }
+ var i;
+ var out = [];
+ for (i = 0; i < 7; i++) {
+ out[i] = get$1(format, (i + shift) % 7, field, 'day');
+ }
+ return out;
+}
- format = format || '';
- }
+function listMonths (format, index) {
+ return listMonthsImpl(format, index, 'months');
+}
- var locale = locale_locales__getLocale(),
- shift = localeSorted ? locale._week.dow : 0;
+function listMonthsShort (format, index) {
+ return listMonthsImpl(format, index, 'monthsShort');
+}
- if (index != null) {
- return lists__get(format, (index + shift) % 7, field, 'day');
- }
+function listWeekdays (localeSorted, format, index) {
+ return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
+}
- var i;
- var out = [];
- for (i = 0; i < 7; i++) {
- out[i] = lists__get(format, (i + shift) % 7, field, 'day');
- }
- return out;
- }
+function listWeekdaysShort (localeSorted, format, index) {
+ return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
+}
- function lists__listMonths (format, index) {
- return listMonthsImpl(format, index, 'months');
- }
+function listWeekdaysMin (localeSorted, format, index) {
+ return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
+}
- function lists__listMonthsShort (format, index) {
- return listMonthsImpl(format, index, 'monthsShort');
+getSetGlobalLocale('en', {
+ ordinalParse: /\d{1,2}(th|st|nd|rd)/,
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (toInt(number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
}
+});
- function lists__listWeekdays (localeSorted, format, index) {
- return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
- }
+// Side effect imports
+hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
+hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
- function lists__listWeekdaysShort (localeSorted, format, index) {
- return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
- }
+var mathAbs = Math.abs;
- function lists__listWeekdaysMin (localeSorted, format, index) {
- return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
- }
+function abs () {
+ var data = this._data;
- locale_locales__getSetGlobalLocale('en', {
- ordinalParse: /\d{1,2}(th|st|nd|rd)/,
- ordinal : function (number) {
- var b = number % 10,
- output = (toInt(number % 100 / 10) === 1) ? 'th' :
- (b === 1) ? 'st' :
- (b === 2) ? 'nd' :
- (b === 3) ? 'rd' : 'th';
- return number + output;
- }
- });
+ this._milliseconds = mathAbs(this._milliseconds);
+ this._days = mathAbs(this._days);
+ this._months = mathAbs(this._months);
- // Side effect imports
- utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale);
- utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale);
+ data.milliseconds = mathAbs(data.milliseconds);
+ data.seconds = mathAbs(data.seconds);
+ data.minutes = mathAbs(data.minutes);
+ data.hours = mathAbs(data.hours);
+ data.months = mathAbs(data.months);
+ data.years = mathAbs(data.years);
- var mathAbs = Math.abs;
+ return this;
+}
- function duration_abs__abs () {
- var data = this._data;
+function addSubtract$1 (duration, input, value, direction) {
+ var other = createDuration(input, value);
- this._milliseconds = mathAbs(this._milliseconds);
- this._days = mathAbs(this._days);
- this._months = mathAbs(this._months);
+ duration._milliseconds += direction * other._milliseconds;
+ duration._days += direction * other._days;
+ duration._months += direction * other._months;
- data.milliseconds = mathAbs(data.milliseconds);
- data.seconds = mathAbs(data.seconds);
- data.minutes = mathAbs(data.minutes);
- data.hours = mathAbs(data.hours);
- data.months = mathAbs(data.months);
- data.years = mathAbs(data.years);
+ return duration._bubble();
+}
- return this;
- }
+// supports only 2.0-style add(1, 's') or add(duration)
+function add$1 (input, value) {
+ return addSubtract$1(this, input, value, 1);
+}
- function duration_add_subtract__addSubtract (duration, input, value, direction) {
- var other = create__createDuration(input, value);
+// supports only 2.0-style subtract(1, 's') or subtract(duration)
+function subtract$1 (input, value) {
+ return addSubtract$1(this, input, value, -1);
+}
- duration._milliseconds += direction * other._milliseconds;
- duration._days += direction * other._days;
- duration._months += direction * other._months;
-
- return duration._bubble();
+function absCeil (number) {
+ if (number < 0) {
+ return Math.floor(number);
+ } else {
+ return Math.ceil(number);
}
+}
- // supports only 2.0-style add(1, 's') or add(duration)
- function duration_add_subtract__add (input, value) {
- return duration_add_subtract__addSubtract(this, input, value, 1);
- }
+function bubble () {
+ var milliseconds = this._milliseconds;
+ var days = this._days;
+ var months = this._months;
+ var data = this._data;
+ var seconds, minutes, hours, years, monthsFromDays;
- // supports only 2.0-style subtract(1, 's') or subtract(duration)
- function duration_add_subtract__subtract (input, value) {
- return duration_add_subtract__addSubtract(this, input, value, -1);
+ // if we have a mix of positive and negative values, bubble down first
+ // check: https://github.com/moment/moment/issues/2166
+ if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
+ (milliseconds <= 0 && days <= 0 && months <= 0))) {
+ milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
+ days = 0;
+ months = 0;
}
- function absCeil (number) {
- if (number < 0) {
- return Math.floor(number);
- } else {
- return Math.ceil(number);
- }
- }
+ // The following code bubbles up values, see the tests for
+ // examples of what that means.
+ data.milliseconds = milliseconds % 1000;
- function bubble () {
- var milliseconds = this._milliseconds;
- var days = this._days;
- var months = this._months;
- var data = this._data;
- var seconds, minutes, hours, years, monthsFromDays;
+ seconds = absFloor(milliseconds / 1000);
+ data.seconds = seconds % 60;
- // if we have a mix of positive and negative values, bubble down first
- // check: https://github.com/moment/moment/issues/2166
- if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
- (milliseconds <= 0 && days <= 0 && months <= 0))) {
- milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
- days = 0;
- months = 0;
- }
+ minutes = absFloor(seconds / 60);
+ data.minutes = minutes % 60;
- // The following code bubbles up values, see the tests for
- // examples of what that means.
- data.milliseconds = milliseconds % 1000;
+ hours = absFloor(minutes / 60);
+ data.hours = hours % 24;
- seconds = absFloor(milliseconds / 1000);
- data.seconds = seconds % 60;
+ days += absFloor(hours / 24);
- minutes = absFloor(seconds / 60);
- data.minutes = minutes % 60;
+ // convert days to months
+ monthsFromDays = absFloor(daysToMonths(days));
+ months += monthsFromDays;
+ days -= absCeil(monthsToDays(monthsFromDays));
- hours = absFloor(minutes / 60);
- data.hours = hours % 24;
+ // 12 months -> 1 year
+ years = absFloor(months / 12);
+ months %= 12;
- days += absFloor(hours / 24);
+ data.days = days;
+ data.months = months;
+ data.years = years;
- // convert days to months
- monthsFromDays = absFloor(daysToMonths(days));
- months += monthsFromDays;
- days -= absCeil(monthsToDays(monthsFromDays));
+ return this;
+}
- // 12 months -> 1 year
- years = absFloor(months / 12);
- months %= 12;
+function daysToMonths (days) {
+ // 400 years have 146097 days (taking into account leap year rules)
+ // 400 years have 12 months === 4800
+ return days * 4800 / 146097;
+}
- data.days = days;
- data.months = months;
- data.years = years;
+function monthsToDays (months) {
+ // the reverse of daysToMonths
+ return months * 146097 / 4800;
+}
- return this;
- }
+function as (units) {
+ var days;
+ var months;
+ var milliseconds = this._milliseconds;
- function daysToMonths (days) {
- // 400 years have 146097 days (taking into account leap year rules)
- // 400 years have 12 months === 4800
- return days * 4800 / 146097;
- }
+ units = normalizeUnits(units);
- function monthsToDays (months) {
- // the reverse of daysToMonths
- return months * 146097 / 4800;
- }
-
- function as (units) {
- var days;
- var months;
- var milliseconds = this._milliseconds;
-
- units = normalizeUnits(units);
-
- if (units === 'month' || units === 'year') {
- days = this._days + milliseconds / 864e5;
- months = this._months + daysToMonths(days);
- return units === 'month' ? months : months / 12;
- } else {
- // handle milliseconds separately because of floating point math errors (issue #1867)
- days = this._days + Math.round(monthsToDays(this._months));
- switch (units) {
- case 'week' : return days / 7 + milliseconds / 6048e5;
- case 'day' : return days + milliseconds / 864e5;
- case 'hour' : return days * 24 + milliseconds / 36e5;
- case 'minute' : return days * 1440 + milliseconds / 6e4;
- case 'second' : return days * 86400 + milliseconds / 1000;
- // Math.floor prevents floating point math errors here
- case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
- default: throw new Error('Unknown unit ' + units);
- }
+ if (units === 'month' || units === 'year') {
+ days = this._days + milliseconds / 864e5;
+ months = this._months + daysToMonths(days);
+ return units === 'month' ? months : months / 12;
+ } else {
+ // handle milliseconds separately because of floating point math errors (issue #1867)
+ days = this._days + Math.round(monthsToDays(this._months));
+ switch (units) {
+ case 'week' : return days / 7 + milliseconds / 6048e5;
+ case 'day' : return days + milliseconds / 864e5;
+ case 'hour' : return days * 24 + milliseconds / 36e5;
+ case 'minute' : return days * 1440 + milliseconds / 6e4;
+ case 'second' : return days * 86400 + milliseconds / 1000;
+ // Math.floor prevents floating point math errors here
+ case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
+ default: throw new Error('Unknown unit ' + units);
}
}
+}
- // TODO: Use this.as('ms')?
- function duration_as__valueOf () {
- return (
- this._milliseconds +
- this._days * 864e5 +
- (this._months % 12) * 2592e6 +
- toInt(this._months / 12) * 31536e6
- );
- }
+// TODO: Use this.as('ms')?
+function valueOf$1 () {
+ return (
+ this._milliseconds +
+ this._days * 864e5 +
+ (this._months % 12) * 2592e6 +
+ toInt(this._months / 12) * 31536e6
+ );
+}
- function makeAs (alias) {
- return function () {
- return this.as(alias);
- };
- }
+function makeAs (alias) {
+ return function () {
+ return this.as(alias);
+ };
+}
- var asMilliseconds = makeAs('ms');
- var asSeconds = makeAs('s');
- var asMinutes = makeAs('m');
- var asHours = makeAs('h');
- var asDays = makeAs('d');
- var asWeeks = makeAs('w');
- var asMonths = makeAs('M');
- var asYears = makeAs('y');
+var asMilliseconds = makeAs('ms');
+var asSeconds = makeAs('s');
+var asMinutes = makeAs('m');
+var asHours = makeAs('h');
+var asDays = makeAs('d');
+var asWeeks = makeAs('w');
+var asMonths = makeAs('M');
+var asYears = makeAs('y');
- function duration_get__get (units) {
- units = normalizeUnits(units);
- return this[units + 's']();
- }
+function get$2 (units) {
+ units = normalizeUnits(units);
+ return this[units + 's']();
+}
- function makeGetter(name) {
- return function () {
- return this._data[name];
- };
- }
+function makeGetter(name) {
+ return function () {
+ return this._data[name];
+ };
+}
- var milliseconds = makeGetter('milliseconds');
- var seconds = makeGetter('seconds');
- var minutes = makeGetter('minutes');
- var hours = makeGetter('hours');
- var days = makeGetter('days');
- var months = makeGetter('months');
- var years = makeGetter('years');
+var milliseconds = makeGetter('milliseconds');
+var seconds = makeGetter('seconds');
+var minutes = makeGetter('minutes');
+var hours = makeGetter('hours');
+var days = makeGetter('days');
+var months = makeGetter('months');
+var years = makeGetter('years');
- function weeks () {
- return absFloor(this.days() / 7);
- }
+function weeks () {
+ return absFloor(this.days() / 7);
+}
- var round = Math.round;
- var thresholds = {
- s: 45, // seconds to minute
- m: 45, // minutes to hour
- h: 22, // hours to day
- d: 26, // days to month
- M: 11 // months to year
- };
+var round = Math.round;
+var thresholds = {
+ s: 45, // seconds to minute
+ m: 45, // minutes to hour
+ h: 22, // hours to day
+ d: 26, // days to month
+ M: 11 // months to year
+};
- // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
- function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
- return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
- }
+// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
+ return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
+}
- function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) {
- var duration = create__createDuration(posNegDuration).abs();
- var seconds = round(duration.as('s'));
- var minutes = round(duration.as('m'));
- var hours = round(duration.as('h'));
- var days = round(duration.as('d'));
- var months = round(duration.as('M'));
- var years = round(duration.as('y'));
+function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
+ var duration = createDuration(posNegDuration).abs();
+ var seconds = round(duration.as('s'));
+ var minutes = round(duration.as('m'));
+ var hours = round(duration.as('h'));
+ var days = round(duration.as('d'));
+ var months = round(duration.as('M'));
+ var years = round(duration.as('y'));
- var a = seconds < thresholds.s && ['s', seconds] ||
- minutes <= 1 && ['m'] ||
- minutes < thresholds.m && ['mm', minutes] ||
- hours <= 1 && ['h'] ||
- hours < thresholds.h && ['hh', hours] ||
- days <= 1 && ['d'] ||
- days < thresholds.d && ['dd', days] ||
- months <= 1 && ['M'] ||
- months < thresholds.M && ['MM', months] ||
- years <= 1 && ['y'] || ['yy', years];
+ var a = seconds < thresholds.s && ['s', seconds] ||
+ minutes <= 1 && ['m'] ||
+ minutes < thresholds.m && ['mm', minutes] ||
+ hours <= 1 && ['h'] ||
+ hours < thresholds.h && ['hh', hours] ||
+ days <= 1 && ['d'] ||
+ days < thresholds.d && ['dd', days] ||
+ months <= 1 && ['M'] ||
+ months < thresholds.M && ['MM', months] ||
+ years <= 1 && ['y'] || ['yy', years];
- a[2] = withoutSuffix;
- a[3] = +posNegDuration > 0;
- a[4] = locale;
- return substituteTimeAgo.apply(null, a);
+ a[2] = withoutSuffix;
+ a[3] = +posNegDuration > 0;
+ a[4] = locale;
+ return substituteTimeAgo.apply(null, a);
+}
+
+// This function allows you to set the rounding function for relative time strings
+function getSetRelativeTimeRounding (roundingFunction) {
+ if (roundingFunction === undefined) {
+ return round;
}
+ if (typeof(roundingFunction) === 'function') {
+ round = roundingFunction;
+ return true;
+ }
+ return false;
+}
- // This function allows you to set the rounding function for relative time strings
- function duration_humanize__getSetRelativeTimeRounding (roundingFunction) {
- if (roundingFunction === undefined) {
- return round;
- }
- if (typeof(roundingFunction) === 'function') {
- round = roundingFunction;
- return true;
- }
+// This function allows you to set a threshold for relative time strings
+function getSetRelativeTimeThreshold (threshold, limit) {
+ if (thresholds[threshold] === undefined) {
return false;
}
-
- // This function allows you to set a threshold for relative time strings
- function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) {
- if (thresholds[threshold] === undefined) {
- return false;
- }
- if (limit === undefined) {
- return thresholds[threshold];
- }
- thresholds[threshold] = limit;
- return true;
+ if (limit === undefined) {
+ return thresholds[threshold];
}
+ thresholds[threshold] = limit;
+ return true;
+}
- function humanize (withSuffix) {
- var locale = this.localeData();
- var output = duration_humanize__relativeTime(this, !withSuffix, locale);
+function humanize (withSuffix) {
+ var locale = this.localeData();
+ var output = relativeTime$1(this, !withSuffix, locale);
- if (withSuffix) {
- output = locale.pastFuture(+this, output);
- }
-
- return locale.postformat(output);
+ if (withSuffix) {
+ output = locale.pastFuture(+this, output);
}
- var iso_string__abs = Math.abs;
+ return locale.postformat(output);
+}
- function iso_string__toISOString() {
- // for ISO strings we do not use the normal bubbling rules:
- // * milliseconds bubble up until they become hours
- // * days do not bubble at all
- // * months bubble up until they become years
- // This is because there is no context-free conversion between hours and days
- // (think of clock changes)
- // and also not between days and months (28-31 days per month)
- var seconds = iso_string__abs(this._milliseconds) / 1000;
- var days = iso_string__abs(this._days);
- var months = iso_string__abs(this._months);
- var minutes, hours, years;
+var abs$1 = Math.abs;
- // 3600 seconds -> 60 minutes -> 1 hour
- minutes = absFloor(seconds / 60);
- hours = absFloor(minutes / 60);
- seconds %= 60;
- minutes %= 60;
+function toISOString$1() {
+ // for ISO strings we do not use the normal bubbling rules:
+ // * milliseconds bubble up until they become hours
+ // * days do not bubble at all
+ // * months bubble up until they become years
+ // This is because there is no context-free conversion between hours and days
+ // (think of clock changes)
+ // and also not between days and months (28-31 days per month)
+ var seconds = abs$1(this._milliseconds) / 1000;
+ var days = abs$1(this._days);
+ var months = abs$1(this._months);
+ var minutes, hours, years;
- // 12 months -> 1 year
- years = absFloor(months / 12);
- months %= 12;
+ // 3600 seconds -> 60 minutes -> 1 hour
+ minutes = absFloor(seconds / 60);
+ hours = absFloor(minutes / 60);
+ seconds %= 60;
+ minutes %= 60;
+ // 12 months -> 1 year
+ years = absFloor(months / 12);
+ months %= 12;
- // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
- var Y = years;
- var M = months;
- var D = days;
- var h = hours;
- var m = minutes;
- var s = seconds;
- var total = this.asSeconds();
- if (!total) {
- // this is the same as C#'s (Noda) and python (isodate)...
- // but not other JS (goog.date)
- return 'P0D';
- }
+ // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+ var Y = years;
+ var M = months;
+ var D = days;
+ var h = hours;
+ var m = minutes;
+ var s = seconds;
+ var total = this.asSeconds();
- return (total < 0 ? '-' : '') +
- 'P' +
- (Y ? Y + 'Y' : '') +
- (M ? M + 'M' : '') +
- (D ? D + 'D' : '') +
- ((h || m || s) ? 'T' : '') +
- (h ? h + 'H' : '') +
- (m ? m + 'M' : '') +
- (s ? s + 'S' : '');
+ if (!total) {
+ // this is the same as C#'s (Noda) and python (isodate)...
+ // but not other JS (goog.date)
+ return 'P0D';
}
- var duration_prototype__proto = Duration.prototype;
+ return (total < 0 ? '-' : '') +
+ 'P' +
+ (Y ? Y + 'Y' : '') +
+ (M ? M + 'M' : '') +
+ (D ? D + 'D' : '') +
+ ((h || m || s) ? 'T' : '') +
+ (h ? h + 'H' : '') +
+ (m ? m + 'M' : '') +
+ (s ? s + 'S' : '');
+}
- duration_prototype__proto.abs = duration_abs__abs;
- duration_prototype__proto.add = duration_add_subtract__add;
- duration_prototype__proto.subtract = duration_add_subtract__subtract;
- duration_prototype__proto.as = as;
- duration_prototype__proto.asMilliseconds = asMilliseconds;
- duration_prototype__proto.asSeconds = asSeconds;
- duration_prototype__proto.asMinutes = asMinutes;
- duration_prototype__proto.asHours = asHours;
- duration_prototype__proto.asDays = asDays;
- duration_prototype__proto.asWeeks = asWeeks;
- duration_prototype__proto.asMonths = asMonths;
- duration_prototype__proto.asYears = asYears;
- duration_prototype__proto.valueOf = duration_as__valueOf;
- duration_prototype__proto._bubble = bubble;
- duration_prototype__proto.get = duration_get__get;
- duration_prototype__proto.milliseconds = milliseconds;
- duration_prototype__proto.seconds = seconds;
- duration_prototype__proto.minutes = minutes;
- duration_prototype__proto.hours = hours;
- duration_prototype__proto.days = days;
- duration_prototype__proto.weeks = weeks;
- duration_prototype__proto.months = months;
- duration_prototype__proto.years = years;
- duration_prototype__proto.humanize = humanize;
- duration_prototype__proto.toISOString = iso_string__toISOString;
- duration_prototype__proto.toString = iso_string__toISOString;
- duration_prototype__proto.toJSON = iso_string__toISOString;
- duration_prototype__proto.locale = locale;
- duration_prototype__proto.localeData = localeData;
+var proto$2 = Duration.prototype;
- // Deprecations
- duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString);
- duration_prototype__proto.lang = lang;
+proto$2.abs = abs;
+proto$2.add = add$1;
+proto$2.subtract = subtract$1;
+proto$2.as = as;
+proto$2.asMilliseconds = asMilliseconds;
+proto$2.asSeconds = asSeconds;
+proto$2.asMinutes = asMinutes;
+proto$2.asHours = asHours;
+proto$2.asDays = asDays;
+proto$2.asWeeks = asWeeks;
+proto$2.asMonths = asMonths;
+proto$2.asYears = asYears;
+proto$2.valueOf = valueOf$1;
+proto$2._bubble = bubble;
+proto$2.get = get$2;
+proto$2.milliseconds = milliseconds;
+proto$2.seconds = seconds;
+proto$2.minutes = minutes;
+proto$2.hours = hours;
+proto$2.days = days;
+proto$2.weeks = weeks;
+proto$2.months = months;
+proto$2.years = years;
+proto$2.humanize = humanize;
+proto$2.toISOString = toISOString$1;
+proto$2.toString = toISOString$1;
+proto$2.toJSON = toISOString$1;
+proto$2.locale = locale;
+proto$2.localeData = localeData;
- // Side effect imports
+// Deprecations
+proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
+proto$2.lang = lang;
- // FORMATTING
+// Side effect imports
- addFormatToken('X', 0, 0, 'unix');
- addFormatToken('x', 0, 0, 'valueOf');
+// FORMATTING
- // PARSING
+addFormatToken('X', 0, 0, 'unix');
+addFormatToken('x', 0, 0, 'valueOf');
- addRegexToken('x', matchSigned);
- addRegexToken('X', matchTimestamp);
- addParseToken('X', function (input, array, config) {
- config._d = new Date(parseFloat(input, 10) * 1000);
- });
- addParseToken('x', function (input, array, config) {
- config._d = new Date(toInt(input));
- });
+// PARSING
- // Side effect imports
+addRegexToken('x', matchSigned);
+addRegexToken('X', matchTimestamp);
+addParseToken('X', function (input, array, config) {
+ config._d = new Date(parseFloat(input, 10) * 1000);
+});
+addParseToken('x', function (input, array, config) {
+ config._d = new Date(toInt(input));
+});
+// Side effect imports
- utils_hooks__hooks.version = '2.14.1';
- setHookCallback(local__createLocal);
+hooks.version = '2.17.1';
- utils_hooks__hooks.fn = momentPrototype;
- utils_hooks__hooks.min = min;
- utils_hooks__hooks.max = max;
- utils_hooks__hooks.now = now;
- utils_hooks__hooks.utc = create_utc__createUTC;
- utils_hooks__hooks.unix = moment__createUnix;
- utils_hooks__hooks.months = lists__listMonths;
- utils_hooks__hooks.isDate = isDate;
- utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale;
- utils_hooks__hooks.invalid = valid__createInvalid;
- utils_hooks__hooks.duration = create__createDuration;
- utils_hooks__hooks.isMoment = isMoment;
- utils_hooks__hooks.weekdays = lists__listWeekdays;
- utils_hooks__hooks.parseZone = moment__createInZone;
- utils_hooks__hooks.localeData = locale_locales__getLocale;
- utils_hooks__hooks.isDuration = isDuration;
- utils_hooks__hooks.monthsShort = lists__listMonthsShort;
- utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin;
- utils_hooks__hooks.defineLocale = defineLocale;
- utils_hooks__hooks.updateLocale = updateLocale;
- utils_hooks__hooks.locales = locale_locales__listLocales;
- utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort;
- utils_hooks__hooks.normalizeUnits = normalizeUnits;
- utils_hooks__hooks.relativeTimeRounding = duration_humanize__getSetRelativeTimeRounding;
- utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold;
- utils_hooks__hooks.calendarFormat = getCalendarFormat;
- utils_hooks__hooks.prototype = momentPrototype;
+setHookCallback(createLocal);
- var _moment = utils_hooks__hooks;
+hooks.fn = proto;
+hooks.min = min;
+hooks.max = max;
+hooks.now = now;
+hooks.utc = createUTC;
+hooks.unix = createUnix;
+hooks.months = listMonths;
+hooks.isDate = isDate;
+hooks.locale = getSetGlobalLocale;
+hooks.invalid = createInvalid;
+hooks.duration = createDuration;
+hooks.isMoment = isMoment;
+hooks.weekdays = listWeekdays;
+hooks.parseZone = createInZone;
+hooks.localeData = getLocale;
+hooks.isDuration = isDuration;
+hooks.monthsShort = listMonthsShort;
+hooks.weekdaysMin = listWeekdaysMin;
+hooks.defineLocale = defineLocale;
+hooks.updateLocale = updateLocale;
+hooks.locales = listLocales;
+hooks.weekdaysShort = listWeekdaysShort;
+hooks.normalizeUnits = normalizeUnits;
+hooks.relativeTimeRounding = getSetRelativeTimeRounding;
+hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
+hooks.calendarFormat = getCalendarFormat;
+hooks.prototype = proto;
- return _moment;
+return hooks;
-}));
+})));
+
},{}],7:[function(require,module,exports){
/**
* @namespace Chart
*/
-var Chart = require(27)();
+var Chart = require(28)();
require(26)(Chart);
+require(42)(Chart);
require(22)(Chart);
+require(31)(Chart);
require(25)(Chart);
require(21)(Chart);
require(23)(Chart);
require(24)(Chart);
-require(28)(Chart);
-require(32)(Chart);
-require(30)(Chart);
-require(31)(Chart);
-require(33)(Chart);
require(29)(Chart);
+require(33)(Chart);
require(34)(Chart);
-
+require(32)(Chart);
require(35)(Chart);
+require(30)(Chart);
+require(27)(Chart);
require(36)(Chart);
+
require(37)(Chart);
require(38)(Chart);
-
-require(41)(Chart);
require(39)(Chart);
require(40)(Chart);
-require(42)(Chart);
+
+require(45)(Chart);
require(43)(Chart);
require(44)(Chart);
+require(46)(Chart);
+require(47)(Chart);
+require(48)(Chart);
// Controllers must be loaded after elements
// See Chart.core.datasetController.dataElementType
require(15)(Chart);
require(16)(Chart);
@@ -5904,103 +6014,107 @@
require(13)(Chart);
require(14)(Chart);
window.Chart = module.exports = Chart;
-},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"8":8,"9":9}],8:[function(require,module,exports){
-"use strict";
+},{"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,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"8":8,"9":9}],8:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
Chart.Bar = function(context, config) {
config.type = 'bar';
return new Chart(context, config);
};
};
+
},{}],9:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
Chart.Bubble = function(context, config) {
config.type = 'bubble';
return new Chart(context, config);
};
};
+
},{}],10:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
Chart.Doughnut = function(context, config) {
config.type = 'doughnut';
return new Chart(context, config);
};
};
+
},{}],11:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
Chart.Line = function(context, config) {
config.type = 'line';
return new Chart(context, config);
};
};
+
},{}],12:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
Chart.PolarArea = function(context, config) {
config.type = 'polarArea';
return new Chart(context, config);
};
};
+
},{}],13:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
-
+
Chart.Radar = function(context, config) {
- config.options = Chart.helpers.configMerge({ aspectRatio: 1 }, config.options);
config.type = 'radar';
return new Chart(context, config);
};
};
},{}],14:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var defaultConfig = {
hover: {
mode: 'single'
},
scales: {
xAxes: [{
- type: "linear", // scatter should not use a category axis
- position: "bottom",
- id: "x-axis-1" // need an ID so datasets can reference the scale
+ type: 'linear', // scatter should not use a category axis
+ position: 'bottom',
+ id: 'x-axis-1' // need an ID so datasets can reference the scale
}],
yAxes: [{
- type: "linear",
- position: "left",
- id: "y-axis-1"
+ type: 'linear',
+ position: 'left',
+ id: 'y-axis-1'
}]
},
tooltips: {
callbacks: {
@@ -6025,25 +6139,26 @@
config.type = 'scatter';
return new Chart(context, config);
};
};
+
},{}],15:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.bar = {
hover: {
- mode: "label"
+ mode: 'label'
},
scales: {
xAxes: [{
- type: "category",
+ type: 'category',
// Specific to Bar Controller
categoryPercentage: 0.8,
barPercentage: 0.9,
@@ -6051,11 +6166,11 @@
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
- type: "linear"
+ type: 'linear'
}]
}
};
Chart.controllers.bar = Chart.DatasetController.extend({
@@ -6063,25 +6178,37 @@
dataElementType: Chart.elements.Rectangle,
initialize: function(chart, datasetIndex) {
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
+ var me = this;
+ var meta = me.getMeta();
+ var dataset = me.getDataset();
+
+ meta.stack = dataset.stack;
// Use this to indicate that this is a bar dataset.
- this.getMeta().bar = true;
+ meta.bar = true;
},
- // Get the number of datasets that display bars. We use this to correctly calculate the bar width
- getBarCount: function() {
+ // Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible
+ getStackCount: function() {
var me = this;
- var barCount = 0;
+ var meta = me.getMeta();
+ var yScale = me.getScaleForId(meta.yAxisID);
+
+ var stacks = [];
helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
- var meta = me.chart.getDatasetMeta(datasetIndex);
- if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) {
- ++barCount;
+ var dsMeta = me.chart.getDatasetMeta(datasetIndex);
+ if (dsMeta.bar && me.chart.isDatasetVisible(datasetIndex) &&
+ (yScale.options.stacked === false ||
+ (yScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
+ (yScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
+ stacks.push(dsMeta.stack);
}
}, me);
- return barCount;
+
+ return stacks.length;
},
update: function(reset) {
var me = this;
helpers.each(me.getMeta().data, function(rectangle, index) {
@@ -6097,162 +6224,155 @@
var scaleBase = yScale.getBasePixel();
var rectangleElementOptions = me.chart.options.elements.rectangle;
var custom = rectangle.custom || {};
var dataset = me.getDataset();
- helpers.extend(rectangle, {
- // Utility
- _xScale: xScale,
- _yScale: yScale,
- _datasetIndex: me.index,
- _index: index,
+ rectangle._xScale = xScale;
+ rectangle._yScale = yScale;
+ rectangle._datasetIndex = me.index;
+ rectangle._index = index;
- // Desired view properties
- _model: {
- x: me.calculateBarX(index, me.index),
- y: reset ? scaleBase : me.calculateBarY(index, me.index),
+ var ruler = me.getRuler(index); // The index argument for compatible
+ rectangle._model = {
+ x: me.calculateBarX(index, me.index, ruler),
+ y: reset ? scaleBase : me.calculateBarY(index, me.index),
- // Tooltip
- label: me.chart.data.labels[index],
- datasetLabel: dataset.label,
+ // Tooltip
+ label: me.chart.data.labels[index],
+ datasetLabel: dataset.label,
- // Appearance
- base: reset ? scaleBase : me.calculateBarBase(me.index, index),
- width: me.calculateBarWidth(index),
- backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
- borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
- borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
- borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
- }
- });
+ // Appearance
+ horizontal: false,
+ base: reset ? scaleBase : me.calculateBarBase(me.index, index),
+ width: me.calculateBarWidth(ruler),
+ backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
+ borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
+ borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
+ borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
+ };
+
rectangle.pivot();
},
calculateBarBase: function(datasetIndex, index) {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
- var base = 0;
+ var base = yScale.getBaseValue();
+ var original = base;
- if (yScale.options.stacked) {
+ if ((yScale.options.stacked === true) ||
+ (yScale.options.stacked === undefined && meta.stack !== undefined)) {
var chart = me.chart;
var datasets = chart.data.datasets;
var value = Number(datasets[datasetIndex].data[index]);
for (var i = 0; i < datasetIndex; i++) {
var currentDs = datasets[i];
var currentDsMeta = chart.getDatasetMeta(i);
- if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
+ if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i) &&
+ meta.stack === currentDsMeta.stack) {
var currentVal = Number(currentDs.data[index]);
- base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0);
+ base += value < 0 ? Math.min(currentVal, original) : Math.max(currentVal, original);
}
}
return yScale.getPixelForValue(base);
}
return yScale.getBasePixel();
},
- getRuler: function(index) {
+ getRuler: function() {
var me = this;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
- var datasetCount = me.getBarCount();
+ var stackCount = me.getStackCount();
- var tickWidth;
-
- if (xScale.options.type === 'category') {
- tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index);
- } else {
- // Average width
- tickWidth = xScale.width / xScale.ticks.length;
- }
+ var tickWidth = xScale.width / xScale.ticks.length;
var categoryWidth = tickWidth * xScale.options.categoryPercentage;
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
- var fullBarWidth = categoryWidth / datasetCount;
+ var fullBarWidth = categoryWidth / stackCount;
- if (xScale.ticks.length !== me.chart.data.labels.length) {
- var perc = xScale.ticks.length / me.chart.data.labels.length;
- fullBarWidth = fullBarWidth * perc;
- }
-
var barWidth = fullBarWidth * xScale.options.barPercentage;
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
return {
- datasetCount: datasetCount,
+ stackCount: stackCount,
tickWidth: tickWidth,
categoryWidth: categoryWidth,
categorySpacing: categorySpacing,
fullBarWidth: fullBarWidth,
barWidth: barWidth,
barSpacing: barSpacing
};
},
- calculateBarWidth: function(index) {
- var xScale = this.getScaleForId(this.getMeta().xAxisID);
+ calculateBarWidth: function(ruler) {
+ var me = this;
+ var meta = me.getMeta();
+ var xScale = me.getScaleForId(meta.xAxisID);
if (xScale.options.barThickness) {
return xScale.options.barThickness;
}
- var ruler = this.getRuler(index);
- return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth;
+ return ruler.barWidth;
},
- // Get bar index from the given dataset index accounting for the fact that not all bars are visible
- getBarIndex: function(datasetIndex) {
- var barIndex = 0;
- var meta, j;
+ // Get stack index from the given dataset index accounting for stacks and the fact that not all bars are visible
+ getStackIndex: function(datasetIndex) {
+ var me = this;
+ var meta = me.chart.getDatasetMeta(datasetIndex);
+ var yScale = me.getScaleForId(meta.yAxisID);
+ var dsMeta, j;
+ var stacks = [meta.stack];
for (j = 0; j < datasetIndex; ++j) {
- meta = this.chart.getDatasetMeta(j);
- if (meta.bar && this.chart.isDatasetVisible(j)) {
- ++barIndex;
+ dsMeta = this.chart.getDatasetMeta(j);
+ if (dsMeta.bar && this.chart.isDatasetVisible(j) &&
+ (yScale.options.stacked === false ||
+ (yScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
+ (yScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
+ stacks.push(dsMeta.stack);
}
}
- return barIndex;
+ return stacks.length - 1;
},
- calculateBarX: function(index, datasetIndex) {
+ calculateBarX: function(index, datasetIndex, ruler) {
var me = this;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
- var barIndex = me.getBarIndex(datasetIndex);
-
- var ruler = me.getRuler(index);
+ var stackIndex = me.getStackIndex(datasetIndex);
var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0;
- if (xScale.options.stacked) {
- return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
- }
-
return leftTick +
(ruler.barWidth / 2) +
ruler.categorySpacing +
- (ruler.barWidth * barIndex) +
+ (ruler.barWidth * stackIndex) +
(ruler.barSpacing / 2) +
- (ruler.barSpacing * barIndex);
+ (ruler.barSpacing * stackIndex);
},
calculateBarY: function(index, datasetIndex) {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
var value = Number(me.getDataset().data[index]);
- if (yScale.options.stacked) {
+ if (yScale.options.stacked ||
+ (yScale.options.stacked === undefined && meta.stack !== undefined)) {
+ var base = yScale.getBaseValue();
+ var sumPos = base,
+ sumNeg = base;
- var sumPos = 0,
- sumNeg = 0;
-
for (var i = 0; i < datasetIndex; i++) {
var ds = me.chart.data.datasets[i];
var dsMeta = me.chart.getDatasetMeta(i);
- if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) {
+ if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i) &&
+ meta.stack === dsMeta.stack) {
var stackedVal = Number(ds.data[index]);
if (stackedVal < 0) {
sumNeg += stackedVal || 0;
} else {
sumPos += stackedVal || 0;
@@ -6260,27 +6380,32 @@
}
}
if (value < 0) {
return yScale.getPixelForValue(sumNeg + value);
- } else {
- return yScale.getPixelForValue(sumPos + value);
}
+ return yScale.getPixelForValue(sumPos + value);
}
return yScale.getPixelForValue(value);
},
draw: function(ease) {
var me = this;
var easingDecimal = ease || 1;
- helpers.each(me.getMeta().data, function(rectangle, index) {
- var d = me.getDataset().data[index];
+ var metaData = me.getMeta().data;
+ var dataset = me.getDataset();
+ var i, len;
+
+ Chart.canvasHelpers.clipArea(me.chart.chart.ctx, me.chart.chartArea);
+ for (i = 0, len = metaData.length; i < len; ++i) {
+ var d = dataset.data[i];
if (d !== null && d !== undefined && !isNaN(d)) {
- rectangle.transition(easingDecimal).draw();
+ metaData[i].transition(easingDecimal).draw();
}
- }, me);
+ }
+ Chart.canvasHelpers.unclipArea(me.chart.chart.ctx);
},
setHoverStyle: function(rectangle) {
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
var index = rectangle._index;
@@ -6309,21 +6434,21 @@
// including horizontalBar in the bar file, instead of a file of its own
// it extends bar (like pie extends doughnut)
Chart.defaults.horizontalBar = {
hover: {
- mode: "label"
+ mode: 'label'
},
scales: {
xAxes: [{
- type: "linear",
- position: "bottom"
+ type: 'linear',
+ position: 'bottom'
}],
yAxes: [{
- position: "left",
- type: "category",
+ position: 'left',
+ type: 'category',
// Specific to Horizontal Bar Controller
categoryPercentage: 0.8,
barPercentage: 0.9,
@@ -6354,213 +6479,177 @@
return title;
},
label: function(tooltipItem, data) {
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
- return datasetLabel + ': ' + tooltipItem.xLabel;
+ return datasetLabel + ': ' + tooltipItem.xLabel;
}
}
}
};
Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
+
+ // Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible
+ getStackCount: function() {
+ var me = this;
+ var meta = me.getMeta();
+ var xScale = me.getScaleForId(meta.xAxisID);
+
+ var stacks = [];
+ helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
+ var dsMeta = me.chart.getDatasetMeta(datasetIndex);
+ if (dsMeta.bar && me.chart.isDatasetVisible(datasetIndex) &&
+ (xScale.options.stacked === false ||
+ (xScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
+ (xScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
+ stacks.push(dsMeta.stack);
+ }
+ }, me);
+
+ return stacks.length;
+ },
+
updateElement: function(rectangle, index, reset) {
var me = this;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
var yScale = me.getScaleForId(meta.yAxisID);
var scaleBase = xScale.getBasePixel();
var custom = rectangle.custom || {};
var dataset = me.getDataset();
var rectangleElementOptions = me.chart.options.elements.rectangle;
- helpers.extend(rectangle, {
- // Utility
- _xScale: xScale,
- _yScale: yScale,
- _datasetIndex: me.index,
- _index: index,
+ rectangle._xScale = xScale;
+ rectangle._yScale = yScale;
+ rectangle._datasetIndex = me.index;
+ rectangle._index = index;
- // Desired view properties
- _model: {
- x: reset ? scaleBase : me.calculateBarX(index, me.index),
- y: me.calculateBarY(index, me.index),
+ var ruler = me.getRuler(index); // The index argument for compatible
+ rectangle._model = {
+ x: reset ? scaleBase : me.calculateBarX(index, me.index),
+ y: me.calculateBarY(index, me.index, ruler),
- // Tooltip
- label: me.chart.data.labels[index],
- datasetLabel: dataset.label,
+ // Tooltip
+ label: me.chart.data.labels[index],
+ datasetLabel: dataset.label,
- // Appearance
- base: reset ? scaleBase : me.calculateBarBase(me.index, index),
- height: me.calculateBarHeight(index),
- backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
- borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
- borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
- borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
- },
+ // Appearance
+ horizontal: true,
+ base: reset ? scaleBase : me.calculateBarBase(me.index, index),
+ height: me.calculateBarHeight(ruler),
+ backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
+ borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
+ borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
+ borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
+ };
- draw: function () {
- var ctx = this._chart.ctx;
- var vm = this._view;
-
- var halfHeight = vm.height / 2,
- topY = vm.y - halfHeight,
- bottomY = vm.y + halfHeight,
- right = vm.base - (vm.base - vm.x),
- halfStroke = vm.borderWidth / 2;
-
- // Canvas doesn't allow us to stroke inside the width so we can
- // adjust the sizes to fit if we're setting a stroke on the line
- if (vm.borderWidth) {
- topY += halfStroke;
- bottomY -= halfStroke;
- right += halfStroke;
- }
-
- ctx.beginPath();
-
- ctx.fillStyle = vm.backgroundColor;
- ctx.strokeStyle = vm.borderColor;
- ctx.lineWidth = vm.borderWidth;
-
- // Corner points, from bottom-left to bottom-right clockwise
- // | 1 2 |
- // | 0 3 |
- var corners = [
- [vm.base, bottomY],
- [vm.base, topY],
- [right, topY],
- [right, bottomY]
- ];
-
- // Find first (starting) corner with fallback to 'bottom'
- var borders = ['bottom', 'left', 'top', 'right'];
- var startCorner = borders.indexOf(vm.borderSkipped, 0);
- if (startCorner === -1)
- startCorner = 0;
-
- function cornerAt(index) {
- return corners[(startCorner + index) % 4];
- }
-
- // Draw rectangle from 'startCorner'
- ctx.moveTo.apply(ctx, cornerAt(0));
- for (var i = 1; i < 4; i++)
- ctx.lineTo.apply(ctx, cornerAt(i));
-
- ctx.fill();
- if (vm.borderWidth) {
- ctx.stroke();
- }
- },
-
- inRange: function (mouseX, mouseY) {
- var vm = this._view;
- var inRange = false;
-
- if (vm) {
- if (vm.x < vm.base) {
- inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.x && mouseX <= vm.base);
- } else {
- inRange = (mouseY >= vm.y - vm.height / 2 && mouseY <= vm.y + vm.height / 2) && (mouseX >= vm.base && mouseX <= vm.x);
- }
- }
-
- return inRange;
- }
- });
-
rectangle.pivot();
},
- calculateBarBase: function (datasetIndex, index) {
+ calculateBarBase: function(datasetIndex, index) {
var me = this;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
- var base = 0;
+ var base = xScale.getBaseValue();
+ var originalBase = base;
- if (xScale.options.stacked) {
+ if (xScale.options.stacked ||
+ (xScale.options.stacked === undefined && meta.stack !== undefined)) {
var chart = me.chart;
var datasets = chart.data.datasets;
var value = Number(datasets[datasetIndex].data[index]);
for (var i = 0; i < datasetIndex; i++) {
var currentDs = datasets[i];
var currentDsMeta = chart.getDatasetMeta(i);
- if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) {
+ if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i) &&
+ meta.stack === currentDsMeta.stack) {
var currentVal = Number(currentDs.data[index]);
- base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0);
+ base += value < 0 ? Math.min(currentVal, originalBase) : Math.max(currentVal, originalBase);
}
}
return xScale.getPixelForValue(base);
}
return xScale.getBasePixel();
},
- getRuler: function (index) {
+ getRuler: function() {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
- var datasetCount = me.getBarCount();
+ var stackCount = me.getStackCount();
- var tickHeight;
- if (yScale.options.type === 'category') {
- tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index);
- } else {
- // Average width
- tickHeight = yScale.width / yScale.ticks.length;
- }
+ var tickHeight = yScale.height / yScale.ticks.length;
var categoryHeight = tickHeight * yScale.options.categoryPercentage;
var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
- var fullBarHeight = categoryHeight / datasetCount;
+ var fullBarHeight = categoryHeight / stackCount;
- if (yScale.ticks.length !== me.chart.data.labels.length) {
- var perc = yScale.ticks.length / me.chart.data.labels.length;
- fullBarHeight = fullBarHeight * perc;
- }
-
var barHeight = fullBarHeight * yScale.options.barPercentage;
var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);
return {
- datasetCount: datasetCount,
+ stackCount: stackCount,
tickHeight: tickHeight,
categoryHeight: categoryHeight,
categorySpacing: categorySpacing,
fullBarHeight: fullBarHeight,
barHeight: barHeight,
barSpacing: barSpacing
};
},
- calculateBarHeight: function (index) {
+ calculateBarHeight: function(ruler) {
var me = this;
- var yScale = me.getScaleForId(me.getMeta().yAxisID);
+ var meta = me.getMeta();
+ var yScale = me.getScaleForId(meta.yAxisID);
if (yScale.options.barThickness) {
return yScale.options.barThickness;
}
- var ruler = me.getRuler(index);
- return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight;
+ return ruler.barHeight;
},
- calculateBarX: function (index, datasetIndex) {
+ // Get stack index from the given dataset index accounting for stacks and the fact that not all bars are visible
+ getStackIndex: function(datasetIndex) {
var me = this;
+ var meta = me.chart.getDatasetMeta(datasetIndex);
+ var xScale = me.getScaleForId(meta.xAxisID);
+ var dsMeta, j;
+ var stacks = [meta.stack];
+
+ for (j = 0; j < datasetIndex; ++j) {
+ dsMeta = this.chart.getDatasetMeta(j);
+ if (dsMeta.bar && this.chart.isDatasetVisible(j) &&
+ (xScale.options.stacked === false ||
+ (xScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
+ (xScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
+ stacks.push(dsMeta.stack);
+ }
+ }
+
+ return stacks.length - 1;
+ },
+
+ calculateBarX: function(index, datasetIndex) {
+ var me = this;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
var value = Number(me.getDataset().data[index]);
- if (xScale.options.stacked) {
+ if (xScale.options.stacked ||
+ (xScale.options.stacked === undefined && meta.stack !== undefined)) {
+ var base = xScale.getBaseValue();
+ var sumPos = base,
+ sumNeg = base;
- var sumPos = 0,
- sumNeg = 0;
-
for (var i = 0; i < datasetIndex; i++) {
var ds = me.chart.data.datasets[i];
var dsMeta = me.chart.getDatasetMeta(i);
- if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) {
+ if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i) &&
+ meta.stack === dsMeta.stack) {
var stackedVal = Number(ds.data[index]);
if (stackedVal < 0) {
sumNeg += stackedVal || 0;
} else {
sumPos += stackedVal || 0;
@@ -6568,64 +6657,57 @@
}
}
if (value < 0) {
return xScale.getPixelForValue(sumNeg + value);
- } else {
- return xScale.getPixelForValue(sumPos + value);
}
+ return xScale.getPixelForValue(sumPos + value);
}
return xScale.getPixelForValue(value);
},
- calculateBarY: function (index, datasetIndex) {
+ calculateBarY: function(index, datasetIndex, ruler) {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
- var barIndex = me.getBarIndex(datasetIndex);
-
- var ruler = me.getRuler(index);
+ var stackIndex = me.getStackIndex(datasetIndex);
var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0;
- if (yScale.options.stacked) {
- return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
- }
-
return topTick +
(ruler.barHeight / 2) +
ruler.categorySpacing +
- (ruler.barHeight * barIndex) +
+ (ruler.barHeight * stackIndex) +
(ruler.barSpacing / 2) +
- (ruler.barSpacing * barIndex);
+ (ruler.barSpacing * stackIndex);
}
});
};
},{}],16:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.bubble = {
hover: {
- mode: "single"
+ mode: 'single'
},
scales: {
xAxes: [{
- type: "linear", // bubble should probably use a linear scale by default
- position: "bottom",
- id: "x-axis-0" // need an ID so datasets can reference the scale
+ type: 'linear', // bubble should probably use a linear scale by default
+ position: 'bottom',
+ id: 'x-axis-0' // need an ID so datasets can reference the scale
}],
yAxes: [{
- type: "linear",
- position: "left",
- id: "y-axis-0"
+ type: 'linear',
+ position: 'left',
+ id: 'y-axis-0'
}]
},
tooltips: {
callbacks: {
@@ -6634,11 +6716,11 @@
return '';
},
label: function(tooltipItem, data) {
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
- return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')';
+ return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')';
}
}
}
};
@@ -6725,22 +6807,22 @@
}
});
};
},{}],17:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers,
defaults = Chart.defaults;
defaults.doughnut = {
animation: {
- //Boolean - Whether we animate the rotation of the Doughnut
+ // Boolean - Whether we animate the rotation of the Doughnut
animateRotate: true,
- //Boolean - Whether we animate scaling the Doughnut from the centre
+ // Boolean - Whether we animate scaling the Doughnut from the centre
animateScale: false
},
aspectRatio: 1,
hover: {
mode: 'single'
@@ -6762,11 +6844,11 @@
text.push('</li>');
}
}
text.push('</ul>');
- return text.join("");
+ return text.join('');
},
legend: {
labels: {
generateLabels: function(chart) {
var data = chart.data;
@@ -6791,47 +6873,61 @@
// Extra data used for toggling the correct item
index: i
};
});
- } else {
- return [];
}
+ return [];
}
},
onClick: function(e, legendItem) {
var index = legendItem.index;
var chart = this.chart;
var i, ilen, meta;
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
meta = chart.getDatasetMeta(i);
- meta.data[index].hidden = !meta.data[index].hidden;
+ // toggle visibility of index if exists
+ if (meta.data[index]) {
+ meta.data[index].hidden = !meta.data[index].hidden;
+ }
}
chart.update();
}
},
- //The percentage of the chart that we cut out of the middle.
+ // The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
- //The rotation of the chart, where the first data arc begins.
+ // The rotation of the chart, where the first data arc begins.
rotation: Math.PI * -0.5,
- //The total circumference of the chart.
+ // The total circumference of the chart.
circumference: Math.PI * 2.0,
// Need to override these to give a nice default
tooltips: {
callbacks: {
title: function() {
return '';
},
label: function(tooltipItem, data) {
- return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
+ var dataLabel = data.labels[tooltipItem.index];
+ var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
+
+ if (helpers.isArray(dataLabel)) {
+ // show value on first line of multiline label
+ // need to clone because we are changing the value
+ dataLabel = dataLabel.slice();
+ dataLabel[0] += value;
+ } else {
+ dataLabel += value;
+ }
+
+ return dataLabel;
}
}
}
};
@@ -6893,22 +6989,22 @@
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))};
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
}
- chart.borderWidth = me.getMaxBorderWidth(meta.data);
+ chart.borderWidth = me.getMaxBorderWidth(meta.data);
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
- chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0);
+ chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
chart.offsetX = offset.x * chart.outerRadius;
chart.offsetY = offset.y * chart.outerRadius;
meta.total = me.calculateTotal();
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
- me.innerRadius = me.outerRadius - chart.radiusLength;
+ me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
helpers.each(meta.data, function(arc, index) {
me.updateElement(arc, index, reset);
});
},
@@ -6980,68 +7076,67 @@
if (!isNaN(value) && !element.hidden) {
total += Math.abs(value);
}
});
- /*if (total === 0) {
+ /* if (total === 0) {
total = NaN;
}*/
return total;
},
calculateCircumference: function(value) {
var total = this.getMeta().total;
if (total > 0 && !isNaN(value)) {
return (Math.PI * 2.0) * (value / total);
- } else {
- return 0;
}
+ return 0;
},
-
- //gets the max border or hover width to properly scale pie charts
- getMaxBorderWidth: function (elements) {
- var max = 0,
+
+ // gets the max border or hover width to properly scale pie charts
+ getMaxBorderWidth: function(elements) {
+ var max = 0,
index = this.index,
length = elements.length,
borderWidth,
hoverWidth;
- for (var i = 0; i < length; i++) {
- borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0;
- hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
-
- max = borderWidth > max ? borderWidth : max;
- max = hoverWidth > max ? hoverWidth : max;
- }
- return max;
- }
+ for (var i = 0; i < length; i++) {
+ borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0;
+ hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
+
+ max = borderWidth > max ? borderWidth : max;
+ max = hoverWidth > max ? hoverWidth : max;
+ }
+ return max;
+ }
});
};
},{}],18:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.line = {
showLines: true,
spanGaps: false,
hover: {
- mode: "label"
+ mode: 'label'
},
scales: {
xAxes: [{
- type: "category",
+ type: 'category',
id: 'x-axis-0'
}],
yAxes: [{
- type: "linear",
+ type: 'linear',
id: 'y-axis-0'
}]
}
};
@@ -7053,23 +7148,10 @@
datasetElementType: Chart.elements.Line,
dataElementType: Chart.elements.Point,
- addElementAndReset: function(index) {
- var me = this;
- var options = me.chart.options;
- var meta = me.getMeta();
-
- Chart.DatasetController.prototype.addElementAndReset.call(me, index);
-
- // Make sure bezier control points are updated
- if (lineEnabled(me.getDataset(), options) && meta.dataset._model.tension !== 0) {
- me.updateBezierControlPoints();
- }
- },
-
update: function(reset) {
var me = this;
var meta = me.getMeta();
var line = meta.dataset;
var points = meta.data || [];
@@ -7097,11 +7179,11 @@
// Model
line._model = {
// Appearance
// The default behavior of lines is to break at null values, according
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
- // This option gives linse the ability to span gaps
+ // This option gives lines the ability to span gaps
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
@@ -7171,15 +7253,15 @@
getPointBorderWidth: function(point, index) {
var borderWidth = this.chart.options.elements.point.borderWidth;
var dataset = this.getDataset();
var custom = point.custom || {};
- if (custom.borderWidth) {
+ if (!isNaN(custom.borderWidth)) {
borderWidth = custom.borderWidth;
- } else if (dataset.pointBorderWidth) {
+ } else if (!isNaN(dataset.pointBorderWidth)) {
borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
- } else if (dataset.borderWidth) {
+ } else if (!isNaN(dataset.borderWidth)) {
borderWidth = dataset.borderWidth;
}
return borderWidth;
},
@@ -7257,36 +7339,38 @@
}
var rightValue = Number(yScale.getRightValue(value));
if (rightValue < 0) {
return yScale.getPixelForValue(sumNeg + rightValue);
- } else {
- return yScale.getPixelForValue(sumPos + rightValue);
}
+ return yScale.getPixelForValue(sumPos + rightValue);
}
return yScale.getPixelForValue(value);
},
updateBezierControlPoints: function() {
var me = this;
var meta = me.getMeta();
var area = me.chart.chartArea;
-
- // Only consider points that are drawn in case the spanGaps option is used
var points = (meta.data || []);
- if (meta.dataset._model.spanGaps) points = points.filter(function(pt) { return !pt._model.skip; });
var i, ilen, point, model, controlPoints;
+ // Only consider points that are drawn in case the spanGaps option is used
+ if (meta.dataset._model.spanGaps) {
+ points = points.filter(function(pt) {
+ return !pt._model.skip;
+ });
+ }
+
function capControlPoint(pt, min, max) {
return Math.max(Math.min(pt, max), min);
}
- if (meta.dataset._model.cubicInterpolationMode == 'monotone') {
+ if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
helpers.splineCurveMonotone(points);
- }
- else {
+ } else {
for (i = 0, ilen = points.length; i < ilen; ++i) {
point = points[i];
model = point._model;
controlPoints = helpers.splineCurve(
helpers.previousItem(points, i)._model,
@@ -7308,11 +7392,10 @@
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
}
}
-
},
draw: function(ease) {
var me = this;
var meta = me.getMeta();
@@ -7323,18 +7406,20 @@
// Transition Point Locations
for (i=0, ilen=points.length; i<ilen; ++i) {
points[i].transition(easingDecimal);
}
+ Chart.canvasHelpers.clipArea(me.chart.chart.ctx, me.chart.chartArea);
// Transition and Draw the line
if (lineEnabled(me.getDataset(), me.chart.options)) {
meta.dataset.transition(easingDecimal).draw();
}
+ Chart.canvasHelpers.unclipArea(me.chart.chart.ctx);
// Draw the points
for (i=0, ilen=points.length; i<ilen; ++i) {
- points[i].draw();
+ points[i].draw(me.chart.chartArea);
}
},
setHoverStyle: function(point) {
// Point
@@ -7368,27 +7453,27 @@
}
});
};
},{}],19:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.polarArea = {
scale: {
- type: "radialLinear",
+ type: 'radialLinear',
lineArc: true, // so that lines are circular
ticks: {
beginAtZero: true
}
},
- //Boolean - Whether to animate the rotation of the chart
+ // Boolean - Whether to animate the rotation of the chart
animation: {
animateRotate: true,
animateScale: true
},
@@ -7402,20 +7487,20 @@
var datasets = data.datasets;
var labels = data.labels;
if (datasets.length) {
for (var i = 0; i < datasets[0].data.length; ++i) {
- text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '">');
+ text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
if (labels[i]) {
text.push(labels[i]);
}
- text.push('</span></li>');
+ text.push('</li>');
}
}
text.push('</ul>');
- return text.join("");
+ return text.join('');
},
legend: {
labels: {
generateLabels: function(chart) {
var data = chart.data;
@@ -7440,13 +7525,12 @@
// Extra data used for toggling the correct item
index: i
};
});
- } else {
- return [];
}
+ return [];
}
},
onClick: function(e, legendItem) {
var index = legendItem.index;
@@ -7525,11 +7609,11 @@
if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
++visibleCount;
}
}
- //var negHalfPI = -0.5 * Math.PI;
+ // var negHalfPI = -0.5 * Math.PI;
var datasetStartAngle = opts.startAngle;
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
var startAngle = datasetStartAngle + (circumference * visibleCount);
var endAngle = startAngle + (arc.hidden ? 0 : circumference);
@@ -7579,27 +7663,27 @@
calculateCircumference: function(value) {
var count = this.getMeta().count;
if (count > 0 && !isNaN(value)) {
return (2 * Math.PI) / count;
- } else {
- return 0;
}
+ return 0;
}
});
};
},{}],20:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.radar = {
+ aspectRatio: 1,
scale: {
- type: "radialLinear"
+ type: 'radialLinear'
},
elements: {
line: {
tension: 0 // no bezier in radar
}
@@ -7612,17 +7696,10 @@
dataElementType: Chart.elements.Point,
linkScales: helpers.noop,
- addElementAndReset: function(index) {
- Chart.DatasetController.prototype.addElementAndReset.call(this, index);
-
- // Make sure bezier control points are updated
- this.updateBezierControlPoints();
- },
-
update: function(reset) {
var me = this;
var meta = me.getMeta();
var line = meta.dataset;
var points = meta.data;
@@ -7667,11 +7744,10 @@
// Update Points
helpers.each(points, function(point, index) {
me.updateElement(point, index, reset);
}, me);
-
// Update bezier control points
me.updateBezierControlPoints();
},
updateElement: function(point, index, reset) {
var me = this;
@@ -7691,11 +7767,11 @@
_model: {
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
y: reset ? scale.yCenter : pointPosition.y,
// Appearance
- tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension),
+ tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
@@ -7777,28 +7853,28 @@
}
});
};
},{}],21:[function(require,module,exports){
-/*global window: false */
-"use strict";
+/* global window: false */
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.animation = {
duration: 1000,
- easing: "easeOutQuart",
+ easing: 'easeOutQuart',
onProgress: helpers.noop,
onComplete: helpers.noop
};
Chart.Animation = Chart.Element.extend({
currentStep: null, // the current animation step
numSteps: 60, // default number of steps
- easing: "", // the easing to use for this animation
+ easing: '', // the easing to use for this animation
render: null, // render function used by the animation service
onAnimationProgress: null, // user specified callback to fire on each step of the animation
onAnimationComplete: null // user specified callback to fire when the animation finishes
});
@@ -7806,10 +7882,18 @@
Chart.animationService = {
frameDuration: 17,
animations: [],
dropFrames: 0,
request: null,
+
+ /**
+ * @function Chart.animationService.addAnimation
+ * @param chartInstance {ChartController} the chart to animate
+ * @param animationObject {IAnimation} the animation that we will animate
+ * @param duration {Number} length of animation in ms
+ * @param lazy {Boolean} if true, the chart is not marked as animating to enable more responsive interactions
+ */
addAnimation: function(chartInstance, animationObject, duration, lazy) {
var me = this;
if (!lazy) {
chartInstance.animating = true;
@@ -7908,12 +7992,13 @@
me.requestAnimationFrame();
}
}
};
};
+
},{}],22:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
// Global Chart canvas helpers object for drawing items to canvas
var helpers = Chart.canvasHelpers = {};
@@ -7951,13 +8036,21 @@
ctx.fill();
break;
case 'rect':
size = 1 / Math.SQRT2 * radius;
ctx.beginPath();
- ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
+ ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
break;
+ case 'rectRounded':
+ var offset = radius / Math.SQRT2;
+ var leftX = x - offset;
+ var topY = y - offset;
+ var sideSize = Math.SQRT2 * radius;
+ Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2);
+ ctx.fill();
+ break;
case 'rectRot':
size = 1 / Math.SQRT2 * radius;
ctx.beginPath();
ctx.moveTo(x - size, y);
ctx.lineTo(x, y + size);
@@ -8012,127 +8105,208 @@
break;
}
ctx.stroke();
};
+
+ helpers.clipArea = function(ctx, clipArea) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top);
+ ctx.clip();
+ };
+
+ helpers.unclipArea = function(ctx) {
+ ctx.restore();
+ };
+
};
+
},{}],23:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
- //Create a dictionary of chart types, to allow for extension of existing types
+ var plugins = Chart.plugins;
+ var platform = Chart.platform;
+
+ // Create a dictionary of chart types, to allow for extension of existing types
Chart.types = {};
- //Store a reference to each instance - allowing us to globally resize chart instances on window resize.
- //Destroy method on the chart will remove the instance of the chart from this reference.
+ // Store a reference to each instance - allowing us to globally resize chart instances on window resize.
+ // Destroy method on the chart will remove the instance of the chart from this reference.
Chart.instances = {};
// Controllers available for dataset visualization eg. bar, line, slice, etc.
Chart.controllers = {};
/**
+ * Initializes the given config with global and chart default values.
+ */
+ function initConfig(config) {
+ config = config || {};
+
+ // Do NOT use configMerge() for the data object because this method merges arrays
+ // and so would change references to labels and datasets, preventing data updates.
+ var data = config.data = config.data || {};
+ data.datasets = data.datasets || [];
+ data.labels = data.labels || [];
+
+ config.options = helpers.configMerge(
+ Chart.defaults.global,
+ Chart.defaults[config.type],
+ config.options || {});
+
+ return config;
+ }
+
+ /**
+ * Updates the config of the chart
+ * @param chart {Chart.Controller} chart to update the options for
+ */
+ function updateConfig(chart) {
+ var newOptions = chart.options;
+
+ // Update Scale(s) with options
+ if (newOptions.scale) {
+ chart.scale.options = newOptions.scale;
+ } else if (newOptions.scales) {
+ newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
+ chart.scales[scaleOptions.id].options = scaleOptions;
+ });
+ }
+
+ // Tooltip
+ chart.tooltip._options = newOptions.tooltips;
+ }
+
+ /**
* @class Chart.Controller
* The main controller of a chart.
*/
- Chart.Controller = function(instance) {
+ Chart.Controller = function(item, config, instance) {
+ var me = this;
- this.chart = instance;
- this.config = instance.config;
- this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
- this.id = helpers.uid();
+ config = initConfig(config);
- Object.defineProperty(this, 'data', {
+ var context = platform.acquireContext(item, config);
+ var canvas = context && context.canvas;
+ var height = canvas && canvas.height;
+ var width = canvas && canvas.width;
+
+ instance.ctx = context;
+ instance.canvas = canvas;
+ instance.config = config;
+ instance.width = width;
+ instance.height = height;
+ instance.aspectRatio = height? width / height : null;
+
+ me.id = helpers.uid();
+ me.chart = instance;
+ me.config = config;
+ me.options = config.options;
+ me._bufferedRender = false;
+
+ // Add the chart instance to the global namespace
+ Chart.instances[me.id] = me;
+
+ Object.defineProperty(me, 'data', {
get: function() {
- return this.config.data;
+ return me.config.data;
}
});
- //Add the chart instance to the global namespace
- Chart.instances[this.id] = this;
-
- if (this.options.responsive) {
- // Silent resize before chart draws
- this.resize(true);
+ if (!context || !canvas) {
+ // The given item is not a compatible context2d element, let's return before finalizing
+ // the chart initialization but after setting basic chart / controller properties that
+ // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
+ // https://github.com/chartjs/Chart.js/issues/2807
+ console.error("Failed to create chart: can't acquire context from the given item");
+ return me;
}
- this.initialize();
+ me.initialize();
+ me.update();
- return this;
+ return me;
};
- helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ {
-
+ helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller.prototype */ {
initialize: function() {
var me = this;
+
// Before init plugin notification
- Chart.plugins.notify('beforeInit', [me]);
+ plugins.notify(me, 'beforeInit');
+ helpers.retinaScale(me.chart);
+
me.bindEvents();
- // Make sure controllers are built first so that each dataset is bound to an axis before the scales
- // are built
+ if (me.options.responsive) {
+ // Initial resize before chart draws (must be silent to preserve initial animations).
+ me.resize(true);
+ }
+
+ // Make sure scales have IDs and are built before we build any controllers.
me.ensureScalesHaveIDs();
- me.buildOrUpdateControllers();
me.buildScales();
- me.updateLayout();
- me.resetElements();
me.initToolTip();
- me.update();
// After init plugin notification
- Chart.plugins.notify('afterInit', [me]);
+ plugins.notify(me, 'afterInit');
return me;
},
clear: function() {
helpers.clear(this.chart);
return this;
},
stop: function() {
- // Stops any current animation loop occuring
+ // Stops any current animation loop occurring
Chart.animationService.cancelAnimation(this);
return this;
},
- resize: function resize(silent) {
+ resize: function(silent) {
var me = this;
var chart = me.chart;
+ var options = me.options;
var canvas = chart.canvas;
- var newWidth = helpers.getMaximumWidth(canvas);
- var aspectRatio = chart.aspectRatio;
- var newHeight = (me.options.maintainAspectRatio && isNaN(aspectRatio) === false && isFinite(aspectRatio) && aspectRatio !== 0) ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas);
+ var aspectRatio = (options.maintainAspectRatio && chart.aspectRatio) || null;
- var sizeChanged = chart.width !== newWidth || chart.height !== newHeight;
+ // the canvas render width and height will be casted to integers so make sure that
+ // the canvas display style uses the same integer values to avoid blurring effect.
+ var newWidth = Math.floor(helpers.getMaximumWidth(canvas));
+ var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas));
- if (!sizeChanged) {
- return me;
+ if (chart.width === newWidth && chart.height === newHeight) {
+ return;
}
canvas.width = chart.width = newWidth;
canvas.height = chart.height = newHeight;
+ canvas.style.width = newWidth + 'px';
+ canvas.style.height = newHeight + 'px';
helpers.retinaScale(chart);
- // Notify any plugins about the resize
- var newSize = { width: newWidth, height: newHeight };
- Chart.plugins.notify('resize', [me, newSize]);
+ if (!silent) {
+ // Notify any plugins about the resize
+ var newSize = {width: newWidth, height: newHeight};
+ plugins.notify(me, 'resize', [newSize]);
- // Notify of resize
- if (me.options.onResize) {
- me.options.onResize(me, newSize);
- }
+ // Notify of resize
+ if (me.options.onResize) {
+ me.options.onResize(me, newSize);
+ }
- if (!silent) {
me.stop();
me.update(me.options.responsiveAnimationDuration);
}
-
- return me;
},
ensureScalesHaveIDs: function() {
var options = this.options;
var scalesOptions = options.scales || {};
@@ -8161,17 +8335,20 @@
var items = [];
if (options.scales) {
items = items.concat(
(options.scales.xAxes || []).map(function(xAxisOptions) {
- return { options: xAxisOptions, dtype: 'category' }; }),
+ return {options: xAxisOptions, dtype: 'category'};
+ }),
(options.scales.yAxes || []).map(function(yAxisOptions) {
- return { options: yAxisOptions, dtype: 'linear' }; }));
+ return {options: yAxisOptions, dtype: 'linear'};
+ })
+ );
}
if (options.scale) {
- items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true });
+ items.push({options: options.scale, dtype: 'radialLinear', isDefault: true});
}
helpers.each(items, function(item) {
var scaleOptions = item.options;
var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype);
@@ -8198,14 +8375,10 @@
});
Chart.scaleService.addScalesToLayout(this);
},
- updateLayout: function() {
- Chart.layoutService.update(this, this.chart.width, this.chart.height);
- },
-
buildOrUpdateControllers: function() {
var me = this;
var types = [];
var newControllers = [];
@@ -8235,21 +8408,38 @@
}
return newControllers;
},
+ /**
+ * Reset the elements of all datasets
+ * @private
+ */
resetElements: function() {
var me = this;
helpers.each(me.data.datasets, function(dataset, datasetIndex) {
me.getDatasetMeta(datasetIndex).controller.reset();
}, me);
},
- update: function update(animationDuration, lazy) {
+ /**
+ * Resets the chart back to it's state before the initial animation
+ */
+ reset: function() {
+ this.resetElements();
+ this.tooltip.initialize();
+ },
+
+ update: function(animationDuration, lazy) {
var me = this;
- Chart.plugins.notify('beforeUpdate', [me]);
+ updateConfig(me);
+
+ if (plugins.notify(me, 'beforeUpdate') === false) {
+ return;
+ }
+
// In case the entire data object changed
me.tooltip._data = me.data;
// Make sure dataset controllers are updated and new controllers are reset
var newControllers = me.buildOrUpdateControllers();
@@ -8257,77 +8447,94 @@
// Make sure all dataset controllers have correct meta data counts
helpers.each(me.data.datasets, function(dataset, datasetIndex) {
me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
}, me);
- Chart.layoutService.update(me, me.chart.width, me.chart.height);
+ me.updateLayout();
- // Apply changes to the dataets that require the scales to have been calculated i.e BorderColor chages
- Chart.plugins.notify('afterScaleUpdate', [me]);
-
// Can only reset the new controllers after the scales have been updated
helpers.each(newControllers, function(controller) {
controller.reset();
});
me.updateDatasets();
// Do this before render so that any plugins that need final scale updates can use it
- Chart.plugins.notify('afterUpdate', [me]);
+ plugins.notify(me, 'afterUpdate');
- me.render(animationDuration, lazy);
+ if (me._bufferedRender) {
+ me._bufferedRequest = {
+ lazy: lazy,
+ duration: animationDuration
+ };
+ } else {
+ me.render(animationDuration, lazy);
+ }
},
/**
- * @method beforeDatasetsUpdate
- * @description Called before all datasets are updated. If a plugin returns false,
- * the datasets update will be cancelled until another chart update is triggered.
- * @param {Object} instance the chart instance being updated.
- * @returns {Boolean} false to cancel the datasets update.
- * @memberof Chart.PluginBase
- * @since version 2.1.5
- * @instance
+ * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
+ * hook, in which case, plugins will not be called on `afterLayout`.
+ * @private
*/
+ updateLayout: function() {
+ var me = this;
- /**
- * @method afterDatasetsUpdate
- * @description Called after all datasets have been updated. Note that this
- * extension will not be called if the datasets update has been cancelled.
- * @param {Object} instance the chart instance being updated.
- * @memberof Chart.PluginBase
- * @since version 2.1.5
- * @instance
- */
+ if (plugins.notify(me, 'beforeLayout') === false) {
+ return;
+ }
+ Chart.layoutService.update(this, this.chart.width, this.chart.height);
+
+ /**
+ * Provided for backward compatibility, use `afterLayout` instead.
+ * @method IPlugin#afterScaleUpdate
+ * @deprecated since version 2.5.0
+ * @todo remove at version 3
+ */
+ plugins.notify(me, 'afterScaleUpdate');
+ plugins.notify(me, 'afterLayout');
+ },
+
/**
- * Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate
- * extension, in which case no datasets will be updated and the afterDatasetsUpdate
- * notification will be skipped.
- * @protected
- * @instance
+ * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
+ * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
+ * @private
*/
updateDatasets: function() {
var me = this;
- var i, ilen;
- if (Chart.plugins.notify('beforeDatasetsUpdate', [ me ])) {
- for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
- me.getDatasetMeta(i).controller.update();
- }
+ if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
+ return;
+ }
- Chart.plugins.notify('afterDatasetsUpdate', [ me ]);
+ for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
+ me.getDatasetMeta(i).controller.update();
}
+
+ plugins.notify(me, 'afterDatasetsUpdate');
},
- render: function render(duration, lazy) {
+ render: function(duration, lazy) {
var me = this;
- Chart.plugins.notify('beforeRender', [me]);
+ if (plugins.notify(me, 'beforeRender') === false) {
+ return;
+ }
+
var animationOptions = me.options.animation;
+ var onComplete = function() {
+ plugins.notify(me, 'afterRender');
+ var callback = animationOptions && animationOptions.onComplete;
+ if (callback && callback.call) {
+ callback.call(me);
+ }
+ };
+
if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
var animation = new Chart.Animation();
- animation.numSteps = (duration || animationOptions.duration) / 16.66; //60 fps
+ animation.numSteps = (duration || animationOptions.duration) / 16.66; // 60 fps
animation.easing = animationOptions.easing;
// render function
animation.render = function(chartInstance, animationObject) {
var easingFunction = helpers.easingEffects[animationObject.easing];
@@ -8337,176 +8544,98 @@
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
};
// user events
animation.onAnimationProgress = animationOptions.onProgress;
- animation.onAnimationComplete = animationOptions.onComplete;
+ animation.onAnimationComplete = onComplete;
Chart.animationService.addAnimation(me, animation, duration, lazy);
} else {
me.draw();
- if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) {
- animationOptions.onComplete.call(me);
- }
+ onComplete();
}
+
return me;
},
- draw: function(ease) {
+ draw: function(easingValue) {
var me = this;
- var easingDecimal = ease || 1;
+
me.clear();
- Chart.plugins.notify('beforeDraw', [me, easingDecimal]);
+ if (easingValue === undefined || easingValue === null) {
+ easingValue = 1;
+ }
+ if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
+ return;
+ }
+
// Draw all the scales
helpers.each(me.boxes, function(box) {
box.draw(me.chartArea);
}, me);
+
if (me.scale) {
me.scale.draw();
}
- Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]);
+ me.drawDatasets(easingValue);
+ // Finally draw the tooltip
+ me.tooltip.transition(easingValue).draw();
+
+ plugins.notify(me, 'afterDraw', [easingValue]);
+ },
+
+ /**
+ * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
+ * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
+ * @private
+ */
+ drawDatasets: function(easingValue) {
+ var me = this;
+
+ if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
+ return;
+ }
+
// Draw each dataset via its respective controller (reversed to support proper line stacking)
helpers.each(me.data.datasets, function(dataset, datasetIndex) {
if (me.isDatasetVisible(datasetIndex)) {
- me.getDatasetMeta(datasetIndex).controller.draw(ease);
+ me.getDatasetMeta(datasetIndex).controller.draw(easingValue);
}
}, me, true);
- Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]);
-
- // Finally draw the tooltip
- me.tooltip.transition(easingDecimal).draw();
-
- Chart.plugins.notify('afterDraw', [me, easingDecimal]);
+ plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
},
// Get the single element that was clicked on
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
getElementAtEvent: function(e) {
- var me = this;
- var eventPosition = helpers.getRelativePosition(e, me.chart);
- var elementsArray = [];
-
- helpers.each(me.data.datasets, function(dataset, datasetIndex) {
- if (me.isDatasetVisible(datasetIndex)) {
- var meta = me.getDatasetMeta(datasetIndex);
- helpers.each(meta.data, function(element) {
- if (element.inRange(eventPosition.x, eventPosition.y)) {
- elementsArray.push(element);
- return elementsArray;
- }
- });
- }
- });
-
- return elementsArray.slice(0, 1);
+ return Chart.Interaction.modes.single(this, e);
},
getElementsAtEvent: function(e) {
- var me = this;
- var eventPosition = helpers.getRelativePosition(e, me.chart);
- var elementsArray = [];
-
- var found = (function() {
- if (me.data.datasets) {
- for (var i = 0; i < me.data.datasets.length; i++) {
- var meta = me.getDatasetMeta(i);
- if (me.isDatasetVisible(i)) {
- for (var j = 0; j < meta.data.length; j++) {
- if (meta.data[j].inRange(eventPosition.x, eventPosition.y)) {
- return meta.data[j];
- }
- }
- }
- }
- }
- }).call(me);
-
- if (!found) {
- return elementsArray;
- }
-
- helpers.each(me.data.datasets, function(dataset, datasetIndex) {
- if (me.isDatasetVisible(datasetIndex)) {
- var meta = me.getDatasetMeta(datasetIndex),
- element = meta.data[found._index];
- if(element && !element._view.skip){
- elementsArray.push(element);
- }
- }
- }, me);
-
- return elementsArray;
+ return Chart.Interaction.modes.label(this, e, {intersect: true});
},
getElementsAtXAxis: function(e) {
- var me = this;
- var eventPosition = helpers.getRelativePosition(e, me.chart);
- var elementsArray = [];
+ return Chart.Interaction.modes['x-axis'](this, e, {intersect: true});
+ },
- var found = (function() {
- if (me.data.datasets) {
- for (var i = 0; i < me.data.datasets.length; i++) {
- var meta = me.getDatasetMeta(i);
- if (me.isDatasetVisible(i)) {
- for (var j = 0; j < meta.data.length; j++) {
- if (meta.data[j].inLabelRange(eventPosition.x, eventPosition.y)) {
- return meta.data[j];
- }
- }
- }
- }
- }
- }).call(me);
-
- if (!found) {
- return elementsArray;
+ getElementsAtEventForMode: function(e, mode, options) {
+ var method = Chart.Interaction.modes[mode];
+ if (typeof method === 'function') {
+ return method(this, e, options);
}
- helpers.each(me.data.datasets, function(dataset, datasetIndex) {
- if (me.isDatasetVisible(datasetIndex)) {
- var meta = me.getDatasetMeta(datasetIndex);
- var index = helpers.findIndex(meta.data, function (it) {
- return found._model.x === it._model.x;
- });
- if(index !== -1 && !meta.data[index]._view.skip) {
- elementsArray.push(meta.data[index]);
- }
- }
- }, me);
-
- return elementsArray;
- },
-
- getElementsAtEventForMode: function(e, mode) {
- var me = this;
- switch (mode) {
- case 'single':
- return me.getElementAtEvent(e);
- case 'label':
- return me.getElementsAtEvent(e);
- case 'dataset':
- return me.getDatasetAtEvent(e);
- case 'x-axis':
- return me.getElementsAtXAxis(e);
- default:
- return e;
- }
+ return [];
},
getDatasetAtEvent: function(e) {
- var elementsArray = this.getElementAtEvent(e);
-
- if (elementsArray.length > 0) {
- elementsArray = this.getDatasetMeta(elementsArray[0]._datasetIndex).data;
- }
-
- return elementsArray;
+ return Chart.Interaction.modes.dataset(this, e, {intersect: true});
},
getDatasetMeta: function(datasetIndex) {
var me = this;
var dataset = me.data.datasets[datasetIndex];
@@ -8515,27 +8644,27 @@
}
var meta = dataset._meta[me.id];
if (!meta) {
meta = dataset._meta[me.id] = {
- type: null,
- data: [],
- dataset: null,
- controller: null,
- hidden: null, // See isDatasetVisible() comment
- xAxisID: null,
- yAxisID: null
- };
+ type: null,
+ data: [],
+ dataset: null,
+ controller: null,
+ hidden: null, // See isDatasetVisible() comment
+ xAxisID: null,
+ yAxisID: null
+ };
}
return meta;
},
getVisibleDatasetCount: function() {
var count = 0;
for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) {
- if (this.isDatasetVisible(i)) {
+ if (this.isDatasetVisible(i)) {
count++;
}
}
return count;
},
@@ -8552,31 +8681,34 @@
return this.options.legendCallback(this);
},
destroy: function() {
var me = this;
+ var canvas = me.chart.canvas;
+ var meta, i, ilen;
+
me.stop();
- me.clear();
- helpers.unbindEvents(me, me.events);
- helpers.removeResizeListener(me.chart.canvas.parentNode);
- // Reset canvas height/width attributes
- var canvas = me.chart.canvas;
- canvas.width = me.chart.width;
- canvas.height = me.chart.height;
+ // dataset controllers need to cleanup associated data
+ for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
+ meta = me.getDatasetMeta(i);
+ if (meta.controller) {
+ meta.controller.destroy();
+ meta.controller = null;
+ }
+ }
- // if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here
- if (me.chart.originalDevicePixelRatio !== undefined) {
- me.chart.ctx.scale(1 / me.chart.originalDevicePixelRatio, 1 / me.chart.originalDevicePixelRatio);
+ if (canvas) {
+ me.unbindEvents();
+ helpers.clear(me.chart);
+ platform.releaseContext(me.chart.ctx);
+ me.chart.canvas = null;
+ me.chart.ctx = null;
}
- // Reset to the old style since it may have been changed by the device pixel ratio changes
- canvas.style.width = me.chart.originalCanvasStyleWidth;
- canvas.style.height = me.chart.originalCanvasStyleHeight;
+ plugins.notify(me, 'destroy');
- Chart.plugins.notify('destroy', [me]);
-
delete Chart.instances[me.id];
},
toBase64Image: function() {
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
@@ -8588,76 +8720,141 @@
_chart: me.chart,
_chartInstance: me,
_data: me.data,
_options: me.options.tooltips
}, me);
+ me.tooltip.initialize();
},
+ /**
+ * @private
+ */
bindEvents: function() {
var me = this;
- helpers.bindEvents(me, me.options.events, function(evt) {
- me.eventHandler(evt);
+ var listeners = me._listeners = {};
+ var listener = function() {
+ me.eventHandler.apply(me, arguments);
+ };
+
+ helpers.each(me.options.events, function(type) {
+ platform.addEventListener(me, type, listener);
+ listeners[type] = listener;
});
+
+ // Responsiveness is currently based on the use of an iframe, however this method causes
+ // performance issues and could be troublesome when used with ad blockers. So make sure
+ // that the user is still able to create a chart without iframe when responsive is false.
+ // See https://github.com/chartjs/Chart.js/issues/2210
+ if (me.options.responsive) {
+ listener = function() {
+ me.resize();
+ };
+
+ platform.addEventListener(me, 'resize', listener);
+ listeners.resize = listener;
+ }
},
+ /**
+ * @private
+ */
+ unbindEvents: function() {
+ var me = this;
+ var listeners = me._listeners;
+ if (!listeners) {
+ return;
+ }
+
+ delete me._listeners;
+ helpers.each(listeners, function(listener, type) {
+ platform.removeEventListener(me, type, listener);
+ });
+ },
+
updateHoverStyle: function(elements, mode, enabled) {
var method = enabled? 'setHoverStyle' : 'removeHoverStyle';
var element, i, ilen;
- switch (mode) {
- case 'single':
- elements = [ elements[0] ];
- break;
- case 'label':
- case 'dataset':
- case 'x-axis':
- // elements = elements;
- break;
- default:
- // unsupported mode
- return;
- }
-
for (i=0, ilen=elements.length; i<ilen; ++i) {
element = elements[i];
if (element) {
this.getDatasetMeta(element._datasetIndex).controller[method](element);
}
}
},
- eventHandler: function eventHandler(e) {
+ /**
+ * @private
+ */
+ eventHandler: function(e) {
var me = this;
var tooltip = me.tooltip;
+
+ if (plugins.notify(me, 'beforeEvent', [e]) === false) {
+ return;
+ }
+
+ // Buffer any update calls so that renders do not occur
+ me._bufferedRender = true;
+ me._bufferedRequest = null;
+
+ var changed = me.handleEvent(e);
+ changed |= tooltip && tooltip.handleEvent(e);
+
+ plugins.notify(me, 'afterEvent', [e]);
+
+ var bufferedRequest = me._bufferedRequest;
+ if (bufferedRequest) {
+ // If we have an update that was triggered, we need to do a normal render
+ me.render(bufferedRequest.duration, bufferedRequest.lazy);
+ } else if (changed && !me.animating) {
+ // If entering, leaving, or changing elements, animate the change via pivot
+ me.stop();
+
+ // We only need to render at this point. Updating will cause scales to be
+ // recomputed generating flicker & using more memory than necessary.
+ me.render(me.options.hover.animationDuration, true);
+ }
+
+ me._bufferedRender = false;
+ me._bufferedRequest = null;
+
+ return me;
+ },
+
+ /**
+ * Handle an event
+ * @private
+ * @param {IEvent} event the event to handle
+ * @return {Boolean} true if the chart needs to re-render
+ */
+ handleEvent: function(e) {
+ var me = this;
var options = me.options || {};
var hoverOptions = options.hover;
- var tooltipsOptions = options.tooltips;
+ var changed = false;
me.lastActive = me.lastActive || [];
- me.lastTooltipActive = me.lastTooltipActive || [];
// Find Active Elements for hover and tooltips
if (e.type === 'mouseout') {
me.active = [];
- me.tooltipActive = [];
} else {
- me.active = me.getElementsAtEventForMode(e, hoverOptions.mode);
- me.tooltipActive = me.getElementsAtEventForMode(e, tooltipsOptions.mode);
+ me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
}
// On Hover hook
if (hoverOptions.onHover) {
- hoverOptions.onHover.call(me, me.active);
+ // Need to call with native event here to not break backwards compatibility
+ hoverOptions.onHover.call(me, e.native, me.active);
}
if (e.type === 'mouseup' || e.type === 'click') {
if (options.onClick) {
- options.onClick.call(me, e, me.active);
+ // Use e.native here for backwards compatibility
+ options.onClick.call(me, e.native, me.active);
}
- if (me.legend && me.legend.handleEvent) {
- me.legend.handleEvent(e);
- }
}
// Remove styling for last active (even if it may still be active)
if (me.lastActive.length) {
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
@@ -8666,56 +8863,101 @@
// Built in hover styling
if (me.active.length && hoverOptions.mode) {
me.updateHoverStyle(me.active, hoverOptions.mode, true);
}
- // Built in Tooltips
- if (tooltipsOptions.enabled || tooltipsOptions.custom) {
- tooltip.initialize();
- tooltip._active = me.tooltipActive;
- tooltip.update(true);
- }
+ changed = !helpers.arrayEquals(me.active, me.lastActive);
- // Hover animations
- tooltip.pivot();
-
- if (!me.animating) {
- // If entering, leaving, or changing elements, animate the change via pivot
- if (!helpers.arrayEquals(me.active, me.lastActive) ||
- !helpers.arrayEquals(me.tooltipActive, me.lastTooltipActive)) {
-
- me.stop();
-
- if (tooltipsOptions.enabled || tooltipsOptions.custom) {
- tooltip.update(true);
- }
-
- // We only need to render at this point. Updating will cause scales to be
- // recomputed generating flicker & using more memory than necessary.
- me.render(hoverOptions.animationDuration, true);
- }
- }
-
// Remember Last Actives
me.lastActive = me.active;
- me.lastTooltipActive = me.tooltipActive;
- return me;
+
+ return changed;
}
});
};
},{}],24:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
- var noop = helpers.noop;
+ var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
+
+ /**
+ * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
+ * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
+ * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
+ */
+ function listenArrayEvents(array, listener) {
+ if (array._chartjs) {
+ array._chartjs.listeners.push(listener);
+ return;
+ }
+
+ Object.defineProperty(array, '_chartjs', {
+ configurable: true,
+ enumerable: false,
+ value: {
+ listeners: [listener]
+ }
+ });
+
+ arrayEvents.forEach(function(key) {
+ var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
+ var base = array[key];
+
+ Object.defineProperty(array, key, {
+ configurable: true,
+ enumerable: false,
+ value: function() {
+ var args = Array.prototype.slice.call(arguments);
+ var res = base.apply(this, args);
+
+ helpers.each(array._chartjs.listeners, function(object) {
+ if (typeof object[method] === 'function') {
+ object[method].apply(object, args);
+ }
+ });
+
+ return res;
+ }
+ });
+ });
+ }
+
+ /**
+ * Removes the given array event listener and cleanup extra attached properties (such as
+ * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
+ */
+ function unlistenArrayEvents(array, listener) {
+ var stub = array._chartjs;
+ if (!stub) {
+ return;
+ }
+
+ var listeners = stub.listeners;
+ var index = listeners.indexOf(listener);
+ if (index !== -1) {
+ listeners.splice(index, 1);
+ }
+
+ if (listeners.length > 0) {
+ return;
+ }
+
+ arrayEvents.forEach(function(key) {
+ delete array[key];
+ });
+
+ delete array._chartjs;
+ }
+
// Base class for all dataset controllers (line, bar, etc)
Chart.DatasetController = function(chart, datasetIndex) {
- this.initialize.call(this, chart, datasetIndex);
+ this.initialize(chart, datasetIndex);
};
helpers.extend(Chart.DatasetController.prototype, {
/**
@@ -8769,10 +9011,19 @@
reset: function() {
this.update(true);
},
+ /**
+ * @private
+ */
+ destroy: function() {
+ if (this._data) {
+ unlistenArrayEvents(this._data, this);
+ }
+ },
+
createMetaDataset: function() {
var me = this;
var type = me.datasetElementType;
return type && new type({
_chart: me.chart.chart,
@@ -8796,49 +9047,54 @@
var data = me.getDataset().data || [];
var metaData = meta.data;
var i, ilen;
for (i=0, ilen=data.length; i<ilen; ++i) {
- metaData[i] = metaData[i] || me.createMetaData(meta, i);
+ metaData[i] = metaData[i] || me.createMetaData(i);
}
meta.dataset = meta.dataset || me.createMetaDataset();
},
addElementAndReset: function(index) {
- var me = this;
- var element = me.createMetaData(index);
- me.getMeta().data.splice(index, 0, element);
- me.updateElement(element, index, true);
+ var element = this.createMetaData(index);
+ this.getMeta().data.splice(index, 0, element);
+ this.updateElement(element, index, true);
},
buildOrUpdateElements: function() {
- // Handle the number of data points changing
- var meta = this.getMeta(),
- md = meta.data,
- numData = this.getDataset().data.length,
- numMetaData = md.length;
+ var me = this;
+ var dataset = me.getDataset();
+ var data = dataset.data || (dataset.data = []);
- // Make sure that we handle number of datapoints changing
- if (numData < numMetaData) {
- // Remove excess bars for data points that have been removed
- md.splice(numData, numMetaData - numData);
- } else if (numData > numMetaData) {
- // Add new elements
- for (var index = numMetaData; index < numData; ++index) {
- this.addElementAndReset(index);
+ // In order to correctly handle data addition/deletion animation (an thus simulate
+ // real-time charts), we need to monitor these data modifications and synchronize
+ // the internal meta data accordingly.
+ if (me._data !== data) {
+ if (me._data) {
+ // This case happens when the user replaced the data array instance.
+ unlistenArrayEvents(me._data, me);
}
+
+ listenArrayEvents(data, me);
+ me._data = data;
}
+
+ // Re-sync meta data in case the user replaced the data array or if we missed
+ // any updates and so make sure that we handle number of datapoints changing.
+ me.resyncElements();
},
- update: noop,
+ update: helpers.noop,
draw: function(ease) {
var easingDecimal = ease || 1;
- helpers.each(this.getMeta().data, function(element) {
- element.transition(easingDecimal).draw();
- });
+ var i, len;
+ var metaData = this.getMeta().data;
+ for (i = 0, len = metaData.length; i < len; ++i) {
+ metaData[i].transition(easingDecimal).draw();
+ }
},
removeHoverStyle: function(element, elementOpts) {
var dataset = this.chart.data.datasets[element._datasetIndex],
index = element._index,
@@ -8860,135 +9116,188 @@
model = element._model;
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
+ },
+
+ /**
+ * @private
+ */
+ resyncElements: function() {
+ var me = this;
+ var meta = me.getMeta();
+ var data = me.getDataset().data;
+ var numMeta = meta.data.length;
+ var numData = data.length;
+
+ if (numData < numMeta) {
+ meta.data.splice(numData, numMeta - numData);
+ } else if (numData > numMeta) {
+ me.insertElements(numMeta, numData - numMeta);
+ }
+ },
+
+ /**
+ * @private
+ */
+ insertElements: function(start, count) {
+ for (var i=0; i<count; ++i) {
+ this.addElementAndReset(start + i);
+ }
+ },
+
+ /**
+ * @private
+ */
+ onDataPush: function() {
+ this.insertElements(this.getDataset().data.length-1, arguments.length);
+ },
+
+ /**
+ * @private
+ */
+ onDataPop: function() {
+ this.getMeta().data.pop();
+ },
+
+ /**
+ * @private
+ */
+ onDataShift: function() {
+ this.getMeta().data.shift();
+ },
+
+ /**
+ * @private
+ */
+ onDataSplice: function(start, count) {
+ this.getMeta().data.splice(start, count);
+ this.insertElements(start, arguments.length - 2);
+ },
+
+ /**
+ * @private
+ */
+ onDataUnshift: function() {
+ this.insertElements(0, arguments.length);
}
-
- });
-
+ });
Chart.DatasetController.extend = helpers.inherits;
};
+
},{}],25:[function(require,module,exports){
-"use strict";
+'use strict';
module.exports = function(Chart) {
- var helpers = Chart.helpers;
+ var helpers = Chart.helpers;
- Chart.elements = {};
+ Chart.elements = {};
- Chart.Element = function(configuration) {
- helpers.extend(this, configuration);
- this.initialize.apply(this, arguments);
- };
+ Chart.Element = function(configuration) {
+ helpers.extend(this, configuration);
+ this.initialize.apply(this, arguments);
+ };
- helpers.extend(Chart.Element.prototype, {
+ helpers.extend(Chart.Element.prototype, {
- initialize: function() {
- this.hidden = false;
- },
+ initialize: function() {
+ this.hidden = false;
+ },
- pivot: function() {
- var me = this;
- if (!me._view) {
- me._view = helpers.clone(me._model);
- }
- me._start = helpers.clone(me._view);
- return me;
- },
+ pivot: function() {
+ var me = this;
+ if (!me._view) {
+ me._view = helpers.clone(me._model);
+ }
+ me._start = helpers.clone(me._view);
+ return me;
+ },
- transition: function(ease) {
- var me = this;
-
- if (!me._view) {
- me._view = helpers.clone(me._model);
- }
+ transition: function(ease) {
+ var me = this;
- // No animation -> No Transition
- if (ease === 1) {
- me._view = me._model;
- me._start = null;
- return me;
- }
+ if (!me._view) {
+ me._view = helpers.clone(me._model);
+ }
- if (!me._start) {
- me.pivot();
- }
+ // No animation -> No Transition
+ if (ease === 1) {
+ me._view = me._model;
+ me._start = null;
+ return me;
+ }
- helpers.each(me._model, function(value, key) {
+ if (!me._start) {
+ me.pivot();
+ }
- if (key[0] === '_') {
- // Only non-underscored properties
- }
+ helpers.each(me._model, function(value, key) {
- // Init if doesn't exist
- else if (!me._view.hasOwnProperty(key)) {
- if (typeof value === 'number' && !isNaN(me._view[key])) {
- me._view[key] = value * ease;
- } else {
- me._view[key] = value;
- }
- }
+ if (key[0] === '_') {
+ // Only non-underscored properties
+ // Init if doesn't exist
+ } else if (!me._view.hasOwnProperty(key)) {
+ if (typeof value === 'number' && !isNaN(me._view[key])) {
+ me._view[key] = value * ease;
+ } else {
+ me._view[key] = value;
+ }
+ // No unnecessary computations
+ } else if (value === me._view[key]) {
+ // It's the same! Woohoo!
+ // Color transitions if possible
+ } else if (typeof value === 'string') {
+ try {
+ var color = helpers.color(me._model[key]).mix(helpers.color(me._start[key]), ease);
+ me._view[key] = color.rgbString();
+ } catch (err) {
+ me._view[key] = value;
+ }
+ // Number transitions
+ } else if (typeof value === 'number') {
+ var startVal = me._start[key] !== undefined && isNaN(me._start[key]) === false ? me._start[key] : 0;
+ me._view[key] = ((me._model[key] - startVal) * ease) + startVal;
+ // Everything else
+ } else {
+ me._view[key] = value;
+ }
+ }, me);
- // No unnecessary computations
- else if (value === me._view[key]) {
- // It's the same! Woohoo!
- }
+ return me;
+ },
- // Color transitions if possible
- else if (typeof value === 'string') {
- try {
- var color = helpers.color(me._model[key]).mix(helpers.color(me._start[key]), ease);
- me._view[key] = color.rgbString();
- } catch (err) {
- me._view[key] = value;
- }
- }
- // Number transitions
- else if (typeof value === 'number') {
- var startVal = me._start[key] !== undefined && isNaN(me._start[key]) === false ? me._start[key] : 0;
- me._view[key] = ((me._model[key] - startVal) * ease) + startVal;
- }
- // Everything else
- else {
- me._view[key] = value;
- }
- }, me);
+ tooltipPosition: function() {
+ return {
+ x: this._model.x,
+ y: this._model.y
+ };
+ },
- return me;
- },
+ hasValue: function() {
+ return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
+ }
+ });
- tooltipPosition: function() {
- return {
- x: this._model.x,
- y: this._model.y
- };
- },
+ Chart.Element.extend = helpers.inherits;
- hasValue: function() {
- return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
- }
- });
-
- Chart.Element.extend = helpers.inherits;
-
};
},{}],26:[function(require,module,exports){
-/*global window: false */
-/*global document: false */
-"use strict";
+/* global window: false */
+/* global document: false */
+'use strict';
var color = require(2);
module.exports = function(Chart) {
- //Global Chart helpers object for utility methods and classes
+ // Global Chart helpers object for utility methods and classes
var helpers = Chart.helpers = {};
- //-- Basic js utility methods
+ // -- Basic js utility methods
helpers.each = function(loopable, callback, self, reverse) {
// Check to see if null or undefined firstly.
var i, len;
if (helpers.isArray(loopable)) {
len = loopable.length;
@@ -9021,52 +9330,40 @@
}
});
return objClone;
};
helpers.extend = function(base) {
- var setFn = function(value, key) { base[key] = value; };
+ var setFn = function(value, key) {
+ base[key] = value;
+ };
for (var i = 1, ilen = arguments.length; i < ilen; i++) {
helpers.each(arguments[i], setFn);
}
return base;
};
// Need a special merge function to chart configs since they are now grouped
helpers.configMerge = function(_base) {
var base = helpers.clone(_base);
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
helpers.each(extension, function(value, key) {
- if (key === 'scales') {
- // Scale config merging is complex. Add out own function here for that
- base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value);
+ var baseHasProperty = base.hasOwnProperty(key);
+ var baseVal = baseHasProperty ? base[key] : {};
+ if (key === 'scales') {
+ // Scale config merging is complex. Add our own function here for that
+ base[key] = helpers.scaleMerge(baseVal, value);
} else if (key === 'scale') {
// Used in polar area & radar charts since there is only one scale
- base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value);
- } else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) {
- // In this case we have an array of objects replacing another array. Rather than doing a strict replace,
- // merge. This allows easy scale option merging
- var baseArray = base[key];
-
- helpers.each(value, function(valueObj, index) {
-
- if (index < baseArray.length) {
- if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) {
- // Two objects are coming together. Do a merge of them.
- baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
- } else {
- // Just overwrite in this case since there is nothing to merge
- baseArray[index] = valueObj;
- }
- } else {
- baseArray.push(valueObj); // nothing to merge
- }
- });
-
- } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") {
+ base[key] = helpers.configMerge(baseVal, Chart.scaleService.getScaleDefaults(value.type), value);
+ } else if (baseHasProperty
+ && typeof baseVal === 'object'
+ && !helpers.isArray(baseVal)
+ && baseVal !== null
+ && typeof value === 'object'
+ && !helpers.isArray(value)) {
// If we are overwriting an object with an object, do a merge of the properties.
- base[key] = helpers.configMerge(base[key], value);
-
+ base[key] = helpers.configMerge(baseVal, value);
} else {
// can just overwrite the value in this case
base[key] = value;
}
});
@@ -9099,11 +9396,11 @@
helpers.each(value, function(valueObj) {
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj));
});
}
- } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") {
+ } else if (base.hasOwnProperty(key) && typeof base[key] === 'object' && base[key] !== null && typeof value === 'object') {
// If we are overwriting an object with an object, do a merge of the properties.
base[key] = helpers.configMerge(base[key], value);
} else {
// can just overwrite the value in this case
@@ -9126,37 +9423,40 @@
};
helpers.getValueOrDefault = function(value, defaultValue) {
return value === undefined ? defaultValue : value;
};
helpers.indexOf = Array.prototype.indexOf?
- function(array, item) { return array.indexOf(item); } :
function(array, item) {
+ return array.indexOf(item);
+ }:
+ function(array, item) {
for (var i = 0, ilen = array.length; i < ilen; ++i) {
if (array[i] === item) {
return i;
}
}
return -1;
};
helpers.where = function(collection, filterCallback) {
if (helpers.isArray(collection) && Array.prototype.filter) {
return collection.filter(filterCallback);
- } else {
- var filtered = [];
+ }
+ var filtered = [];
- helpers.each(collection, function(item) {
- if (filterCallback(item)) {
- filtered.push(item);
- }
- });
+ helpers.each(collection, function(item) {
+ if (filterCallback(item)) {
+ filtered.push(item);
+ }
+ });
- return filtered;
- }
+ return filtered;
};
helpers.findIndex = Array.prototype.findIndex?
- function(array, callback, scope) { return array.findIndex(callback, scope); } :
function(array, callback, scope) {
+ return array.findIndex(callback, scope);
+ } :
+ function(array, callback, scope) {
scope = scope === undefined? array : scope;
for (var i = 0, ilen = array.length; i < ilen; ++i) {
if (callback.call(scope, array[i], i, array)) {
return i;
}
@@ -9186,76 +9486,82 @@
return currentItem;
}
}
};
helpers.inherits = function(extensions) {
- //Basic javascript inheritance based on the model created in Backbone.js
- var parent = this;
- var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() {
- return parent.apply(this, arguments);
+ // Basic javascript inheritance based on the model created in Backbone.js
+ var me = this;
+ var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
+ return me.apply(this, arguments);
};
var Surrogate = function() {
this.constructor = ChartElement;
};
- Surrogate.prototype = parent.prototype;
+ Surrogate.prototype = me.prototype;
ChartElement.prototype = new Surrogate();
ChartElement.extend = helpers.inherits;
if (extensions) {
helpers.extend(ChartElement.prototype, extensions);
}
- ChartElement.__super__ = parent.prototype;
+ ChartElement.__super__ = me.prototype;
return ChartElement;
};
helpers.noop = function() {};
helpers.uid = (function() {
var id = 0;
return function() {
return id++;
};
- })();
- //-- Math methods
+ }());
+ // -- Math methods
helpers.isNumber = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
helpers.almostEquals = function(x, y, epsilon) {
return Math.abs(x - y) < epsilon;
};
+ helpers.almostWhole = function(x, epsilon) {
+ var rounded = Math.round(x);
+ return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
+ };
helpers.max = function(array) {
return array.reduce(function(max, value) {
if (!isNaN(value)) {
return Math.max(max, value);
- } else {
- return max;
}
+ return max;
}, Number.NEGATIVE_INFINITY);
};
helpers.min = function(array) {
return array.reduce(function(min, value) {
if (!isNaN(value)) {
return Math.min(min, value);
- } else {
- return min;
}
+ return min;
}, Number.POSITIVE_INFINITY);
};
helpers.sign = Math.sign?
- function(x) { return Math.sign(x); } :
function(x) {
+ return Math.sign(x);
+ } :
+ function(x) {
x = +x; // convert to a number
if (x === 0 || isNaN(x)) {
return x;
}
return x > 0 ? 1 : -1;
};
helpers.log10 = Math.log10?
- function(x) { return Math.log10(x); } :
function(x) {
+ return Math.log10(x);
+ } :
+ function(x) {
return Math.log(x) / Math.LN10;
};
helpers.toRadians = function(degrees) {
return degrees * (Math.PI / 180);
};
@@ -9277,16 +9583,19 @@
return {
angle: angle,
distance: radialDistanceFromCenter
};
};
+ helpers.distanceBetweenPoints = function(pt1, pt2) {
+ return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
+ };
helpers.aliasPixel = function(pixelWidth) {
return (pixelWidth % 2 === 0) ? 0 : 0.5;
};
helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
- //Props to Rob Spencer at scaled innovation for his post on splining between points
- //http://scaledinnovation.com/analytics/splines/aboutSplines.html
+ // Props to Rob Spencer at scaled innovation for his post on splining between points
+ // http://scaledinnovation.com/analytics/splines/aboutSplines.html
// This function must also respect "skipped" points
var previous = firstPoint.skip ? middlePoint : firstPoint,
current = middlePoint,
@@ -9334,47 +9643,68 @@
// Calculate slopes (deltaK) and initialize tangents (mK)
var pointsLen = pointsWithTangents.length;
var i, pointBefore, pointCurrent, pointAfter;
for (i = 0; i < pointsLen; ++i) {
pointCurrent = pointsWithTangents[i];
- if (pointCurrent.model.skip) continue;
+ if (pointCurrent.model.skip) {
+ continue;
+ }
+
pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
if (pointAfter && !pointAfter.model.skip) {
- pointCurrent.deltaK = (pointAfter.model.y - pointCurrent.model.y) / (pointAfter.model.x - pointCurrent.model.x);
+ var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
+
+ // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
+ pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
}
- if (!pointBefore || pointBefore.model.skip) pointCurrent.mK = pointCurrent.deltaK;
- else if (!pointAfter || pointAfter.model.skip) pointCurrent.mK = pointBefore.deltaK;
- else if (this.sign(pointBefore.deltaK) != this.sign(pointCurrent.deltaK)) pointCurrent.mK = 0;
- else pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
+
+ if (!pointBefore || pointBefore.model.skip) {
+ pointCurrent.mK = pointCurrent.deltaK;
+ } else if (!pointAfter || pointAfter.model.skip) {
+ pointCurrent.mK = pointBefore.deltaK;
+ } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
+ pointCurrent.mK = 0;
+ } else {
+ pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
+ }
}
// Adjust tangents to ensure monotonic properties
var alphaK, betaK, tauK, squaredMagnitude;
for (i = 0; i < pointsLen - 1; ++i) {
pointCurrent = pointsWithTangents[i];
pointAfter = pointsWithTangents[i + 1];
- if (pointCurrent.model.skip || pointAfter.model.skip) continue;
- if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON))
- {
+ if (pointCurrent.model.skip || pointAfter.model.skip) {
+ continue;
+ }
+
+ if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
pointCurrent.mK = pointAfter.mK = 0;
continue;
}
+
alphaK = pointCurrent.mK / pointCurrent.deltaK;
betaK = pointAfter.mK / pointCurrent.deltaK;
squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
- if (squaredMagnitude <= 9) continue;
+ if (squaredMagnitude <= 9) {
+ continue;
+ }
+
tauK = 3 / Math.sqrt(squaredMagnitude);
pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
- pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
+ pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
}
// Compute control points
var deltaX;
for (i = 0; i < pointsLen; ++i) {
pointCurrent = pointsWithTangents[i];
- if (pointCurrent.model.skip) continue;
+ if (pointCurrent.model.skip) {
+ continue;
+ }
+
pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
if (pointBefore && !pointBefore.model.skip) {
deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
@@ -9389,11 +9719,10 @@
};
helpers.nextItem = function(collection, index, loop) {
if (loop) {
return index >= collection.length - 1 ? collection[0] : collection[index + 1];
}
-
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
};
helpers.previousItem = function(collection, index, loop) {
if (loop) {
return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
@@ -9414,26 +9743,24 @@
} else if (fraction < 7) {
niceFraction = 5;
} else {
niceFraction = 10;
}
+ } else if (fraction <= 1.0) {
+ niceFraction = 1;
+ } else if (fraction <= 2) {
+ niceFraction = 2;
+ } else if (fraction <= 5) {
+ niceFraction = 5;
} else {
- if (fraction <= 1.0) {
- niceFraction = 1;
- } else if (fraction <= 2) {
- niceFraction = 2;
- } else if (fraction <= 5) {
- niceFraction = 5;
- } else {
- niceFraction = 10;
- }
+ niceFraction = 10;
}
return niceFraction * Math.pow(10, exponent);
};
- //Easing functions adapted from Robert Penner's easing equations
- //http://www.robertpenner.com/easing/
+ // Easing functions adapted from Robert Penner's easing equations
+ // http://www.robertpenner.com/easing/
var easingEffects = helpers.easingEffects = {
linear: function(t) {
return t;
},
easeInQuad: function(t) {
@@ -9615,43 +9942,32 @@
return 1 * (7.5625 * t * t);
} else if (t < (2 / 2.75)) {
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
} else if (t < (2.5 / 2.75)) {
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
- } else {
- return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
}
+ return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
},
easeInOutBounce: function(t) {
if (t < 1 / 2) {
return easingEffects.easeInBounce(t * 2) * 0.5;
}
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
}
};
- //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
+ // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
helpers.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
return window.setTimeout(callback, 1000 / 60);
};
- })();
- helpers.cancelAnimFrame = (function() {
- return window.cancelAnimationFrame ||
- window.webkitCancelAnimationFrame ||
- window.mozCancelAnimationFrame ||
- window.oCancelAnimationFrame ||
- window.msCancelAnimationFrame ||
- function(callback) {
- return window.clearTimeout(callback, 1000 / 60);
- };
- })();
- //-- DOM methods
+ }());
+ // -- DOM methods
helpers.getRelativePosition = function(evt, chart) {
var mouseX, mouseY;
var e = evt.originalEvent || evt,
canvas = evt.currentTarget || evt.srcElement,
boundingRect = canvas.getBoundingClientRect();
@@ -9689,49 +10005,32 @@
};
helpers.addEvent = function(node, eventType, method) {
if (node.addEventListener) {
node.addEventListener(eventType, method);
} else if (node.attachEvent) {
- node.attachEvent("on" + eventType, method);
+ node.attachEvent('on' + eventType, method);
} else {
- node["on" + eventType] = method;
+ node['on' + eventType] = method;
}
};
helpers.removeEvent = function(node, eventType, handler) {
if (node.removeEventListener) {
node.removeEventListener(eventType, handler, false);
} else if (node.detachEvent) {
- node.detachEvent("on" + eventType, handler);
+ node.detachEvent('on' + eventType, handler);
} else {
- node["on" + eventType] = helpers.noop;
+ node['on' + eventType] = helpers.noop;
}
};
- helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) {
- // Create the events object if it's not already present
- var events = chartInstance.events = chartInstance.events || {};
- helpers.each(arrayOfEvents, function(eventName) {
- events[eventName] = function() {
- handler.apply(chartInstance, arguments);
- };
- helpers.addEvent(chartInstance.chart.canvas, eventName, events[eventName]);
- });
- };
- helpers.unbindEvents = function(chartInstance, arrayOfEvents) {
- var canvas = chartInstance.chart.canvas;
- helpers.each(arrayOfEvents, function(handler, eventName) {
- helpers.removeEvent(canvas, eventName, handler);
- });
- };
-
// Private helper function to convert max-width/max-height values that may be percentages into a number
function parseMaxStyle(styleValue, node, parentProperty) {
var valueInPixels;
if (typeof(styleValue) === 'string') {
valueInPixels = parseInt(styleValue, 10);
- if (styleValue.indexOf('%') != -1) {
+ if (styleValue.indexOf('%') !== -1) {
// percentage * size in dimension
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
}
} else {
valueInPixels = styleValue;
@@ -9743,11 +10042,11 @@
/**
* Returns if the given value contains an effective constraint.
* @private
*/
function isConstrainedValue(value) {
- return value !== undefined && value !== null && value !== 'none';
+ return value !== undefined && value !== null && value !== 'none';
}
// Private helper to get a constraint dimension
// @param domNode : the node to check the constraint on
// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
@@ -9778,54 +10077,55 @@
helpers.getConstraintHeight = function(domNode) {
return getConstraintDimension(domNode, 'max-height', 'clientHeight');
};
helpers.getMaximumWidth = function(domNode) {
var container = domNode.parentNode;
- var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right'));
- var w = container.clientWidth - padding;
+ var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
+ var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
+ var w = container.clientWidth - paddingLeft - paddingRight;
var cw = helpers.getConstraintWidth(domNode);
return isNaN(cw)? w : Math.min(w, cw);
};
helpers.getMaximumHeight = function(domNode) {
var container = domNode.parentNode;
- var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom'));
- var h = container.clientHeight - padding;
+ var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
+ var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
+ var h = container.clientHeight - paddingTop - paddingBottom;
var ch = helpers.getConstraintHeight(domNode);
return isNaN(ch)? h : Math.min(h, ch);
};
helpers.getStyle = function(el, property) {
return el.currentStyle ?
el.currentStyle[property] :
document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
};
helpers.retinaScale = function(chart) {
- var ctx = chart.ctx;
- var canvas = chart.canvas;
- var width = canvas.width;
- var height = canvas.height;
var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
+ if (pixelRatio === 1) {
+ return;
+ }
- if (pixelRatio !== 1) {
- canvas.height = height * pixelRatio;
- canvas.width = width * pixelRatio;
- ctx.scale(pixelRatio, pixelRatio);
+ var canvas = chart.canvas;
+ var height = chart.height;
+ var width = chart.width;
- // Store the device pixel ratio so that we can go backwards in `destroy`.
- // The devicePixelRatio changes with zoom, so there are no guarantees that it is the same
- // when destroy is called
- chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio;
- }
+ canvas.height = height * pixelRatio;
+ canvas.width = width * pixelRatio;
+ chart.ctx.scale(pixelRatio, pixelRatio);
- canvas.style.width = width + 'px';
+ // If no style has been set on the canvas, the render size is used as display size,
+ // making the chart visually bigger, so let's enforce it to the "correct" values.
+ // See https://github.com/chartjs/Chart.js/issues/3575
canvas.style.height = height + 'px';
+ canvas.style.width = width + 'px';
};
- //-- Canvas methods
+ // -- Canvas methods
helpers.clear = function(chart) {
chart.ctx.clearRect(0, 0, chart.width, chart.height);
};
helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
- return fontStyle + " " + pixelSize + "px " + fontFamily;
+ return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
};
helpers.longestText = function(ctx, font, arrayOfThings, cache) {
cache = cache || {};
var data = cache.data = cache.data || {};
var gc = cache.garbageCollect = cache.garbageCollect || [];
@@ -9861,11 +10161,11 @@
}
gc.splice(0, gcLen);
}
return longest;
};
- helpers.measureText = function (ctx, data, gc, longest, string) {
+ helpers.measureText = function(ctx, data, gc, longest, string) {
var textWidth = data[string];
if (!textWidth) {
textWidth = data[string] = ctx.measureText(string).width;
gc.push(string);
}
@@ -9898,74 +10198,33 @@
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
};
helpers.color = function(c) {
if (!color) {
- console.log('Color.js not found!');
+ console.error('Color.js not found!');
return c;
}
/* global CanvasGradient */
if (c instanceof CanvasGradient) {
return color(Chart.defaults.global.defaultColor);
}
return color(c);
};
- helpers.addResizeListener = function(node, callback) {
- // Hide an iframe before the node
- var hiddenIframe = document.createElement('iframe');
- var hiddenIframeClass = 'chartjs-hidden-iframe';
-
- if (hiddenIframe.classlist) {
- // can use classlist
- hiddenIframe.classlist.add(hiddenIframeClass);
- } else {
- hiddenIframe.setAttribute('class', hiddenIframeClass);
- }
-
- // Set the style
- hiddenIframe.tabIndex = -1;
- var style = hiddenIframe.style;
- style.width = '100%';
- style.display = 'block';
- style.border = 0;
- style.height = 0;
- style.margin = 0;
- style.position = 'absolute';
- style.left = 0;
- style.right = 0;
- style.top = 0;
- style.bottom = 0;
-
- // Insert the iframe so that contentWindow is available
- node.insertBefore(hiddenIframe, node.firstChild);
-
- (hiddenIframe.contentWindow || hiddenIframe).onresize = function() {
- if (callback) {
- callback();
- }
- };
- };
- helpers.removeResizeListener = function(node) {
- var hiddenIframe = node.querySelector('.chartjs-hidden-iframe');
-
- // Remove the resize detect iframe
- if (hiddenIframe) {
- hiddenIframe.parentNode.removeChild(hiddenIframe);
- }
- };
helpers.isArray = Array.isArray?
- function(obj) { return Array.isArray(obj); } :
function(obj) {
+ return Array.isArray(obj);
+ } :
+ function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
- //! @see http://stackoverflow.com/a/14853974
+ // ! @see http://stackoverflow.com/a/14853974
helpers.arrayEquals = function(a0, a1) {
var i, ilen, v0, v1;
- if (!a0 || !a1 || a0.length != a1.length) {
+ if (!a0 || !a1 || a0.length !== a1.length) {
return false;
}
for (i = 0, ilen=a0.length; i < ilen; ++i) {
v0 = a0[i];
@@ -9973,11 +10232,11 @@
if (v0 instanceof Array && v1 instanceof Array) {
if (!helpers.arrayEquals(v0, v1)) {
return false;
}
- } else if (v0 != v1) {
+ } else if (v0 !== v1) {
// NOTE: two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
@@ -9986,93 +10245,354 @@
helpers.callCallback = function(fn, args, _tArg) {
if (fn && typeof fn.call === 'function') {
fn.apply(_tArg, args);
}
};
- helpers.getHoverColor = function(color) {
+ helpers.getHoverColor = function(colorValue) {
/* global CanvasPattern */
- return (color instanceof CanvasPattern) ?
- color :
- helpers.color(color).saturate(0.5).darken(0.1).rgbString();
+ return (colorValue instanceof CanvasPattern) ?
+ colorValue :
+ helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
};
};
},{"2":2}],27:[function(require,module,exports){
-"use strict";
+'use strict';
-module.exports = function() {
+module.exports = function(Chart) {
+ var helpers = Chart.helpers;
- //Occupy the global variable of Chart, and create a simple base class
- var Chart = function(context, config) {
- var me = this;
- var helpers = Chart.helpers;
- me.config = config || {
- data: {
- datasets: []
+ /**
+ * Helper function to get relative position for an event
+ * @param {Event|IEvent} event - The event to get the position for
+ * @param {Chart} chart - The chart
+ * @returns {Point} the event position
+ */
+ function getRelativePosition(e, chart) {
+ if (e.native) {
+ return {
+ x: e.x,
+ y: e.y
+ };
+ }
+
+ return helpers.getRelativePosition(e, chart);
+ }
+
+ /**
+ * Helper function to traverse all of the visible elements in the chart
+ * @param chart {chart} the chart
+ * @param handler {Function} the callback to execute for each visible item
+ */
+ function parseVisibleItems(chart, handler) {
+ var datasets = chart.data.datasets;
+ var meta, i, j, ilen, jlen;
+
+ for (i = 0, ilen = datasets.length; i < ilen; ++i) {
+ if (!chart.isDatasetVisible(i)) {
+ continue;
}
- };
- // Support a jQuery'd canvas element
- if (context.length && context[0].getContext) {
- context = context[0];
+ meta = chart.getDatasetMeta(i);
+ for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
+ var element = meta.data[j];
+ if (!element._view.skip) {
+ handler(element);
+ }
+ }
}
+ }
- // Support a canvas domnode
- if (context.getContext) {
- context = context.getContext("2d");
+ /**
+ * Helper function to get the items that intersect the event position
+ * @param items {ChartElement[]} elements to filter
+ * @param position {Point} the point to be nearest to
+ * @return {ChartElement[]} the nearest items
+ */
+ function getIntersectItems(chart, position) {
+ var elements = [];
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inRange(position.x, position.y)) {
+ elements.push(element);
+ }
+ });
+
+ return elements;
+ }
+
+ /**
+ * Helper function to get the items nearest to the event position considering all visible items in teh chart
+ * @param chart {Chart} the chart to look at elements from
+ * @param position {Point} the point to be nearest to
+ * @param intersect {Boolean} if true, only consider items that intersect the position
+ * @param distanceMetric {Function} Optional function to provide the distance between
+ * @return {ChartElement[]} the nearest items
+ */
+ function getNearestItems(chart, position, intersect, distanceMetric) {
+ var minDistance = Number.POSITIVE_INFINITY;
+ var nearestItems = [];
+
+ if (!distanceMetric) {
+ distanceMetric = helpers.distanceBetweenPoints;
}
- me.ctx = context;
- me.canvas = context.canvas;
+ parseVisibleItems(chart, function(element) {
+ if (intersect && !element.inRange(position.x, position.y)) {
+ return;
+ }
- context.canvas.style.display = context.canvas.style.display || 'block';
+ var center = element.getCenterPoint();
+ var distance = distanceMetric(position, center);
- // Figure out what the size of the chart will be.
- // If the canvas has a specified width and height, we use those else
- // we look to see if the canvas node has a CSS width and height.
- // If there is still no height, fill the parent container
- me.width = context.canvas.width || parseInt(helpers.getStyle(context.canvas, 'width'), 10) || helpers.getMaximumWidth(context.canvas);
- me.height = context.canvas.height || parseInt(helpers.getStyle(context.canvas, 'height'), 10) || helpers.getMaximumHeight(context.canvas);
+ if (distance < minDistance) {
+ nearestItems = [element];
+ minDistance = distance;
+ } else if (distance === minDistance) {
+ // Can have multiple items at the same distance in which case we sort by size
+ nearestItems.push(element);
+ }
+ });
- me.aspectRatio = me.width / me.height;
+ return nearestItems;
+ }
- if (isNaN(me.aspectRatio) || isFinite(me.aspectRatio) === false) {
- // If the canvas has no size, try and figure out what the aspect ratio will be.
- // Some charts prefer square canvases (pie, radar, etc). If that is specified, use that
- // else use the canvas default ratio of 2
- me.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2;
+ function indexMode(chart, e, options) {
+ var position = getRelativePosition(e, chart.chart);
+ var distanceMetric = function(pt1, pt2) {
+ return Math.abs(pt1.x - pt2.x);
+ };
+ var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
+ var elements = [];
+
+ if (!items.length) {
+ return [];
}
- // Store the original style of the element so we can set it back
- me.originalCanvasStyleWidth = context.canvas.style.width;
- me.originalCanvasStyleHeight = context.canvas.style.height;
+ chart.data.datasets.forEach(function(dataset, datasetIndex) {
+ if (chart.isDatasetVisible(datasetIndex)) {
+ var meta = chart.getDatasetMeta(datasetIndex),
+ element = meta.data[items[0]._index];
- // High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
- helpers.retinaScale(me);
- me.controller = new Chart.Controller(me);
-
- // Always bind this so that if the responsive state changes we still work
- helpers.addResizeListener(context.canvas.parentNode, function() {
- if (me.controller && me.controller.config.options.responsive) {
- me.controller.resize();
+ // don't count items that are skipped (null data)
+ if (element && !element._view.skip) {
+ elements.push(element);
+ }
}
});
- return me.controller ? me.controller : me;
+ return elements;
+ }
+ /**
+ * @interface IInteractionOptions
+ */
+ /**
+ * If true, only consider items that intersect the point
+ * @name IInterfaceOptions#boolean
+ * @type Boolean
+ */
+
+ /**
+ * Contains interaction related functions
+ * @namespace Chart.Interaction
+ */
+ Chart.Interaction = {
+ // Helper function for different modes
+ modes: {
+ single: function(chart, e) {
+ var position = getRelativePosition(e, chart.chart);
+ var elements = [];
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inRange(position.x, position.y)) {
+ elements.push(element);
+ return elements;
+ }
+ });
+
+ return elements.slice(0, 1);
+ },
+
+ /**
+ * @function Chart.Interaction.modes.label
+ * @deprecated since version 2.4.0
+ */
+ label: indexMode,
+
+ /**
+ * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
+ * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
+ * @function Chart.Interaction.modes.index
+ * @since v2.4.0
+ * @param chart {chart} the chart we are returning items from
+ * @param e {Event} the event we are find things at
+ * @param options {IInteractionOptions} options to use during interaction
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ index: indexMode,
+
+ /**
+ * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
+ * If the options.intersect is false, we find the nearest item and return the items in that dataset
+ * @function Chart.Interaction.modes.dataset
+ * @param chart {chart} the chart we are returning items from
+ * @param e {Event} the event we are find things at
+ * @param options {IInteractionOptions} options to use during interaction
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ dataset: function(chart, e, options) {
+ var position = getRelativePosition(e, chart.chart);
+ var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false);
+
+ if (items.length > 0) {
+ items = chart.getDatasetMeta(items[0]._datasetIndex).data;
+ }
+
+ return items;
+ },
+
+ /**
+ * @function Chart.Interaction.modes.x-axis
+ * @deprecated since version 2.4.0. Use index mode and intersect == true
+ */
+ 'x-axis': function(chart, e) {
+ return indexMode(chart, e, true);
+ },
+
+ /**
+ * Point mode returns all elements that hit test based on the event position
+ * of the event
+ * @function Chart.Interaction.modes.intersect
+ * @param chart {chart} the chart we are returning items from
+ * @param e {Event} the event we are find things at
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ point: function(chart, e) {
+ var position = getRelativePosition(e, chart.chart);
+ return getIntersectItems(chart, position);
+ },
+
+ /**
+ * nearest mode returns the element closest to the point
+ * @function Chart.Interaction.modes.intersect
+ * @param chart {chart} the chart we are returning items from
+ * @param e {Event} the event we are find things at
+ * @param options {IInteractionOptions} options to use
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ nearest: function(chart, e, options) {
+ var position = getRelativePosition(e, chart.chart);
+ var nearestItems = getNearestItems(chart, position, options.intersect);
+
+ // We have multiple items at the same distance from the event. Now sort by smallest
+ if (nearestItems.length > 1) {
+ nearestItems.sort(function(a, b) {
+ var sizeA = a.getArea();
+ var sizeB = b.getArea();
+ var ret = sizeA - sizeB;
+
+ if (ret === 0) {
+ // if equal sort by dataset index
+ ret = a._datasetIndex - b._datasetIndex;
+ }
+
+ return ret;
+ });
+ }
+
+ // Return only 1 item
+ return nearestItems.slice(0, 1);
+ },
+
+ /**
+ * x mode returns the elements that hit-test at the current x coordinate
+ * @function Chart.Interaction.modes.x
+ * @param chart {chart} the chart we are returning items from
+ * @param e {Event} the event we are find things at
+ * @param options {IInteractionOptions} options to use
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ x: function(chart, e, options) {
+ var position = getRelativePosition(e, chart.chart);
+ var items = [];
+ var intersectsItem = false;
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inXRange(position.x)) {
+ items.push(element);
+ }
+
+ if (element.inRange(position.x, position.y)) {
+ intersectsItem = true;
+ }
+ });
+
+ // If we want to trigger on an intersect and we don't have any items
+ // that intersect the position, return nothing
+ if (options.intersect && !intersectsItem) {
+ items = [];
+ }
+ return items;
+ },
+
+ /**
+ * y mode returns the elements that hit-test at the current y coordinate
+ * @function Chart.Interaction.modes.y
+ * @param chart {chart} the chart we are returning items from
+ * @param e {Event} the event we are find things at
+ * @param options {IInteractionOptions} options to use
+ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
+ */
+ y: function(chart, e, options) {
+ var position = getRelativePosition(e, chart.chart);
+ var items = [];
+ var intersectsItem = false;
+
+ parseVisibleItems(chart, function(element) {
+ if (element.inYRange(position.y)) {
+ items.push(element);
+ }
+
+ if (element.inRange(position.x, position.y)) {
+ intersectsItem = true;
+ }
+ });
+
+ // If we want to trigger on an intersect and we don't have any items
+ // that intersect the position, return nothing
+ if (options.intersect && !intersectsItem) {
+ items = [];
+ }
+ return items;
+ }
+ }
};
+};
- //Globally expose the defaults to allow for user updating/changing
+},{}],28:[function(require,module,exports){
+'use strict';
+
+module.exports = function() {
+
+ // Occupy the global variable of Chart, and create a simple base class
+ var Chart = function(item, config) {
+ this.controller = new Chart.Controller(item, config, this);
+ return this.controller;
+ };
+
+ // Globally expose the defaults to allow for user updating/changing
Chart.defaults = {
global: {
responsive: true,
responsiveAnimationDuration: 0,
maintainAspectRatio: true,
- events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"],
+ events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
hover: {
onHover: null,
- mode: 'single',
+ mode: 'nearest',
+ intersect: true,
animationDuration: 400
},
onClick: null,
defaultColor: 'rgba(0,0,0,0.1)',
defaultFontColor: '#666',
@@ -10095,23 +10615,22 @@
}
text.push('</li>');
}
text.push('</ul>');
- return text.join("");
+ return text.join('');
}
}
};
Chart.Chart = Chart;
return Chart;
-
};
-},{}],28:[function(require,module,exports){
-"use strict";
+},{}],29:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
@@ -10141,29 +10660,47 @@
if (!chartInstance) {
return;
}
- var xPadding = 0;
- var yPadding = 0;
+ var layoutOptions = chartInstance.options.layout;
+ var padding = layoutOptions ? layoutOptions.padding : null;
+ var leftPadding = 0;
+ var rightPadding = 0;
+ var topPadding = 0;
+ var bottomPadding = 0;
+
+ if (!isNaN(padding)) {
+ // options.layout.padding is a number. assign to all
+ leftPadding = padding;
+ rightPadding = padding;
+ topPadding = padding;
+ bottomPadding = padding;
+ } else {
+ leftPadding = padding.left || 0;
+ rightPadding = padding.right || 0;
+ topPadding = padding.top || 0;
+ bottomPadding = padding.bottom || 0;
+ }
+
var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
- return box.options.position === "left";
+ return box.options.position === 'left';
});
var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
- return box.options.position === "right";
+ return box.options.position === 'right';
});
var topBoxes = helpers.where(chartInstance.boxes, function(box) {
- return box.options.position === "top";
+ return box.options.position === 'top';
});
var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
- return box.options.position === "bottom";
+ return box.options.position === 'bottom';
});
// Boxes that overlay the chartarea such as the radialLinear scale
var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
- return box.options.position === "chartArea";
+ return box.options.position === 'chartArea';
});
// Ensure that full width boxes are at the very top / bottom
topBoxes.sort(function(a, b) {
return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
@@ -10208,12 +10745,12 @@
// 7. Position each axis in the final location
// 8. Tell the chart the final location of the chart area
// 9. Tell any axes that overlay the chart area the positions of the chart area
// Step 1
- var chartWidth = width - (2 * xPadding);
- var chartHeight = height - (2 * yPadding);
+ var chartWidth = width - leftPadding - rightPadding;
+ var chartHeight = height - topPadding - bottomPadding;
var chartAreaWidth = chartWidth / 2; // min 50%
var chartAreaHeight = chartHeight / 2; // min 50%
// Step 2
var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
@@ -10224,12 +10761,10 @@
// Step 4
var maxChartAreaWidth = chartWidth;
var maxChartAreaHeight = chartHeight;
var minBoxSizes = [];
- helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
-
function getMinimumBoxSize(box) {
var minSize;
var isHorizontal = box.isHorizontal();
if (isHorizontal) {
@@ -10241,48 +10776,57 @@
}
minBoxSizes.push({
horizontal: isHorizontal,
minSize: minSize,
- box: box
+ box: box,
});
}
- // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
- // be if the axes are drawn at their minimum sizes.
+ helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
- // Steps 5 & 6
- var totalLeftBoxesWidth = xPadding;
- var totalRightBoxesWidth = xPadding;
- var totalTopBoxesHeight = yPadding;
- var totalBottomBoxesHeight = yPadding;
+ // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
+ var maxHorizontalLeftPadding = 0;
+ var maxHorizontalRightPadding = 0;
+ var maxVerticalTopPadding = 0;
+ var maxVerticalBottomPadding = 0;
- // Update, and calculate the left and right margins for the horizontal boxes
- helpers.each(leftBoxes.concat(rightBoxes), fitBox);
-
- helpers.each(leftBoxes, function(box) {
- totalLeftBoxesWidth += box.width;
+ helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
+ if (horizontalBox.getPadding) {
+ var boxPadding = horizontalBox.getPadding();
+ maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
+ maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
+ }
});
- helpers.each(rightBoxes, function(box) {
- totalRightBoxesWidth += box.width;
+ helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
+ if (verticalBox.getPadding) {
+ var boxPadding = verticalBox.getPadding();
+ maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
+ maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
+ }
});
- // Set the Left and Right margins for the horizontal boxes
- helpers.each(topBoxes.concat(bottomBoxes), fitBox);
+ // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
+ // be if the axes are drawn at their minimum sizes.
+ // Steps 5 & 6
+ var totalLeftBoxesWidth = leftPadding;
+ var totalRightBoxesWidth = rightPadding;
+ var totalTopBoxesHeight = topPadding;
+ var totalBottomBoxesHeight = bottomPadding;
// Function to fit a box
function fitBox(box) {
- var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
- return minBoxSize.box === box;
+ var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
+ return minBox.box === box;
});
if (minBoxSize) {
if (box.isHorizontal()) {
var scaleMargin = {
- left: totalLeftBoxesWidth,
- right: totalRightBoxesWidth,
+ left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
+ right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
top: 0,
bottom: 0
};
// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
@@ -10292,25 +10836,36 @@
box.update(minBoxSize.minSize.width, maxChartAreaHeight);
}
}
}
+ // Update, and calculate the left and right margins for the horizontal boxes
+ helpers.each(leftBoxes.concat(rightBoxes), fitBox);
+
+ helpers.each(leftBoxes, function(box) {
+ totalLeftBoxesWidth += box.width;
+ });
+
+ helpers.each(rightBoxes, function(box) {
+ totalRightBoxesWidth += box.width;
+ });
+
+ // Set the Left and Right margins for the horizontal boxes
+ helpers.each(topBoxes.concat(bottomBoxes), fitBox);
+
// Figure out how much margin is on the top and bottom of the vertical boxes
helpers.each(topBoxes, function(box) {
totalTopBoxesHeight += box.height;
});
helpers.each(bottomBoxes, function(box) {
totalBottomBoxesHeight += box.height;
});
- // Let the left layout know the final margin
- helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
-
function finalFitVerticalBox(box) {
- var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
- return minBoxSize.box === box;
+ var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
+ return minSize.box === box;
});
var scaleMargin = {
left: 0,
right: 0,
@@ -10321,15 +10876,18 @@
if (minBoxSize) {
box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
}
}
+ // Let the left layout know the final margin
+ helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
+
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
- totalLeftBoxesWidth = xPadding;
- totalRightBoxesWidth = xPadding;
- totalTopBoxesHeight = yPadding;
- totalBottomBoxesHeight = yPadding;
+ totalLeftBoxesWidth = leftPadding;
+ totalRightBoxesWidth = rightPadding;
+ totalTopBoxesHeight = topPadding;
+ totalBottomBoxesHeight = bottomPadding;
helpers.each(leftBoxes, function(box) {
totalLeftBoxesWidth += box.width;
});
@@ -10342,10 +10900,19 @@
});
helpers.each(bottomBoxes, function(box) {
totalBottomBoxesHeight += box.height;
});
+ // We may be adding some padding to account for rotated x axis labels
+ var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
+ totalLeftBoxesWidth += leftPaddingAddition;
+ totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);
+
+ var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
+ totalTopBoxesHeight += topPaddingAddition;
+ totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);
+
// Figure out if our chart area changed. This would occur if the dataset layout label rotation
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
// without calling `fit` again
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
@@ -10374,26 +10941,17 @@
maxChartAreaHeight = newMaxChartAreaHeight;
maxChartAreaWidth = newMaxChartAreaWidth;
}
// Step 7 - Position the boxes
- var left = xPadding;
- var top = yPadding;
+ var left = leftPadding + leftPaddingAddition;
+ var top = topPadding + topPaddingAddition;
- helpers.each(leftBoxes.concat(topBoxes), placeBox);
-
- // Account for chart width and height
- left += maxChartAreaWidth;
- top += maxChartAreaHeight;
-
- helpers.each(rightBoxes, placeBox);
- helpers.each(bottomBoxes, placeBox);
-
function placeBox(box) {
if (box.isHorizontal()) {
- box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth;
- box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth;
+ box.left = box.options.fullWidth ? leftPadding : totalLeftBoxesWidth;
+ box.right = box.options.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
box.top = top;
box.bottom = top + box.height;
// Move to next point
top = box.bottom;
@@ -10408,10 +10966,19 @@
// Move to next point
left = box.right;
}
}
+ helpers.each(leftBoxes.concat(topBoxes), placeBox);
+
+ // Account for chart width and height
+ left += maxChartAreaWidth;
+ top += maxChartAreaHeight;
+
+ helpers.each(rightBoxes, placeBox);
+ helpers.each(bottomBoxes, placeBox);
+
// Step 8
chartInstance.chartArea = {
left: totalLeftBoxesWidth,
top: totalTopBoxesHeight,
right: totalLeftBoxesWidth + maxChartAreaWidth,
@@ -10429,12 +10996,12 @@
});
}
};
};
-},{}],29:[function(require,module,exports){
-"use strict";
+},{}],30:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
var noop = helpers.noop;
@@ -10457,10 +11024,12 @@
// We hid a dataset ... rerender the chart
ci.update();
},
+ onHover: null,
+
labels: {
boxWidth: 40,
padding: 10,
// Generates labels shown in the legend
// Valid properties to return:
@@ -10494,10 +11063,22 @@
}, this) : [];
}
}
};
+ /**
+ * Helper function to get the box width based on the usePointStyle option
+ * @param labelopts {Object} the label options on the legend
+ * @param fontSize {Number} the label font size
+ * @return {Number} width of the color box area
+ */
+ function getBoxWidth(labelOpts, fontSize) {
+ return labelOpts.usePointStyle ?
+ fontSize * Math.SQRT2 :
+ labelOpts.boxWidth;
+ }
+
Chart.Legend = Chart.Element.extend({
initialize: function(config) {
helpers.extend(this, config);
@@ -10506,11 +11087,11 @@
// Are we in doughnut mode which has a different data type
this.doughnutMode = false;
},
- // These methods are ordered by lifecyle. Utilities then follow.
+ // These methods are ordered by lifecycle. Utilities then follow.
// Any function defined here is inherited by all legend types.
// Any function can be extended by the legend type
beforeUpdate: noop,
update: function(maxWidth, maxHeight, margins) {
@@ -10580,14 +11161,24 @@
//
beforeBuildLabels: noop,
buildLabels: function() {
var me = this;
- me.legendItems = me.options.labels.generateLabels.call(me, me.chart);
- if(me.options.reverse){
- me.legendItems.reverse();
+ var labelOpts = me.options.labels;
+ var legendItems = labelOpts.generateLabels.call(me, me.chart);
+
+ if (labelOpts.filter) {
+ legendItems = legendItems.filter(function(item) {
+ return labelOpts.filter(item, me.chart.data);
+ });
}
+
+ if (me.options.reverse) {
+ legendItems.reverse();
+ }
+
+ me.legendItems = legendItems;
},
afterBuildLabels: noop,
//
@@ -10630,19 +11221,17 @@
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
var lineWidths = me.lineWidths = [0];
var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
- ctx.textAlign = "left";
+ ctx.textAlign = 'left';
ctx.textBaseline = 'top';
helpers.each(me.legendItems, function(legendItem, i) {
- var boxWidth = labelOpts.usePointStyle ?
- fontSize * Math.sqrt(2) :
- labelOpts.boxWidth;
-
+ var boxWidth = getBoxWidth(labelOpts, fontSize);
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
+
if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
totalHeight += fontSize + (labelOpts.padding);
lineWidths[lineWidths.length] = me.left;
}
@@ -10666,14 +11255,11 @@
var currentColWidth = 0;
var currentColHeight = 0;
var itemHeight = fontSize + vPadding;
helpers.each(me.legendItems, function(legendItem, i) {
- // If usePointStyle is set, multiple boxWidth by 2 since it represents
- // the radius and not truly the width
- var boxWidth = labelOpts.usePointStyle ? 2 * labelOpts.boxWidth : labelOpts.boxWidth;
-
+ var boxWidth = getBoxWidth(labelOpts, fontSize);
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
// If too tall, go to new column
if (currentColHeight + itemHeight > minSize.height) {
totalWidth += currentColWidth + labelOpts.padding;
@@ -10707,14 +11293,14 @@
},
afterFit: noop,
// Shared Methods
isHorizontal: function() {
- return this.options.position === "top" || this.options.position === "bottom";
+ return this.options.position === 'top' || this.options.position === 'bottom';
},
- // Actualy draw the legend on the canvas
+ // Actually draw the legend on the canvas
draw: function() {
var me = this;
var opts = me.options;
var labelOpts = opts.labels;
var globalDefault = Chart.defaults.global,
@@ -10731,18 +11317,18 @@
fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
// Canvas setup
- ctx.textAlign = "left";
+ ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.lineWidth = 0.5;
ctx.strokeStyle = fontColor; // for strikethrough effect
ctx.fillStyle = fontColor; // render in correct colour
ctx.font = labelFont;
- var boxWidth = labelOpts.boxWidth,
+ var boxWidth = getBoxWidth(labelOpts, fontSize),
hitboxes = me.legendHitBoxes;
// current position
var drawLegendBox = function(x, y, legendItem) {
if (isNaN(boxWidth) || boxWidth <= 0) {
@@ -10756,30 +11342,32 @@
ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
+ var isLineWidthZero = (itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
if (ctx.setLineDash) {
// IE 9 and 10 do not support line dash
ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
}
if (opts.labels && opts.labels.usePointStyle) {
- // Recalulate x and y for drawPoint() because its expecting
+ // Recalculate x and y for drawPoint() because its expecting
// x and y to be center of figure (instead of top left)
var radius = fontSize * Math.SQRT2 / 2;
var offSet = radius / Math.SQRT2;
var centerX = x + offSet;
var centerY = y + offSet;
// Draw pointStyle as legend symbol
Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
- }
- else {
+ } else {
// Draw box as legend symbol
- ctx.strokeRect(x, y, boxWidth, fontSize);
+ if (!isLineWidthZero) {
+ ctx.strokeRect(x, y, boxWidth, fontSize);
+ }
ctx.fillRect(x, y, boxWidth, fontSize);
}
ctx.restore();
};
@@ -10813,28 +11401,24 @@
}
var itemHeight = fontSize + labelOpts.padding;
helpers.each(me.legendItems, function(legendItem, i) {
var textWidth = ctx.measureText(legendItem.text).width,
- width = labelOpts.usePointStyle ?
- fontSize + (fontSize / 2) + textWidth :
- boxWidth + (fontSize / 2) + textWidth,
+ width = boxWidth + (fontSize / 2) + textWidth,
x = cursor.x,
y = cursor.y;
if (isHorizontal) {
if (x + width >= legendWidth) {
y = cursor.y += itemHeight;
cursor.line++;
x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
}
- } else {
- if (y + itemHeight > me.bottom) {
- x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
- y = cursor.y = me.top;
- cursor.line++;
- }
+ } else if (y + itemHeight > me.bottom) {
+ x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
+ y = cursor.y = me.top + labelOpts.padding;
+ cursor.line++;
}
drawLegendBox(x, y, legendItem);
hitboxes[i].left = x;
@@ -10851,81 +11435,151 @@
});
}
},
- // Handle an event
+ /**
+ * Handle an event
+ * @private
+ * @param {IEvent} event - The event to handle
+ * @return {Boolean} true if a change occured
+ */
handleEvent: function(e) {
var me = this;
- var position = helpers.getRelativePosition(e, me.chart.chart),
- x = position.x,
- y = position.y,
- opts = me.options;
+ var opts = me.options;
+ var type = e.type === 'mouseup' ? 'click' : e.type;
+ var changed = false;
+ if (type === 'mousemove') {
+ if (!opts.onHover) {
+ return;
+ }
+ } else if (type === 'click') {
+ if (!opts.onClick) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ // Chart event already has relative position in it
+ var x = e.x,
+ y = e.y;
+
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
// See if we are touching one of the dataset boxes
var lh = me.legendHitBoxes;
for (var i = 0; i < lh.length; ++i) {
var hitBox = lh[i];
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
// Touching an element
- if (opts.onClick) {
- opts.onClick.call(me, e, me.legendItems[i]);
+ if (type === 'click') {
+ // use e.native for backwards compatibility
+ opts.onClick.call(me, e.native, me.legendItems[i]);
+ changed = true;
+ break;
+ } else if (type === 'mousemove') {
+ // use e.native for backwards compatibility
+ opts.onHover.call(me, e.native, me.legendItems[i]);
+ changed = true;
+ break;
}
- break;
}
}
}
+
+ return changed;
}
});
+ function createNewLegendAndAttach(chartInstance, legendOpts) {
+ var legend = new Chart.Legend({
+ ctx: chartInstance.chart.ctx,
+ options: legendOpts,
+ chart: chartInstance
+ });
+ chartInstance.legend = legend;
+ Chart.layoutService.addBox(chartInstance, legend);
+ }
+
// Register the legend plugin
Chart.plugins.register({
beforeInit: function(chartInstance) {
- var opts = chartInstance.options;
- var legendOpts = opts.legend;
+ var legendOpts = chartInstance.options.legend;
if (legendOpts) {
- chartInstance.legend = new Chart.Legend({
- ctx: chartInstance.chart.ctx,
- options: legendOpts,
- chart: chartInstance
- });
+ createNewLegendAndAttach(chartInstance, legendOpts);
+ }
+ },
+ beforeUpdate: function(chartInstance) {
+ var legendOpts = chartInstance.options.legend;
- Chart.layoutService.addBox(chartInstance, chartInstance.legend);
+ if (legendOpts) {
+ legendOpts = helpers.configMerge(Chart.defaults.global.legend, legendOpts);
+
+ if (chartInstance.legend) {
+ chartInstance.legend.options = legendOpts;
+ } else {
+ createNewLegendAndAttach(chartInstance, legendOpts);
+ }
+ } else {
+ Chart.layoutService.removeBox(chartInstance, chartInstance.legend);
+ delete chartInstance.legend;
}
+ },
+ afterEvent: function(chartInstance, e) {
+ var legend = chartInstance.legend;
+ if (legend) {
+ legend.handleEvent(e);
+ }
}
});
};
-},{}],30:[function(require,module,exports){
-"use strict";
+},{}],31:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
- var noop = Chart.helpers.noop;
+ var helpers = Chart.helpers;
+ Chart.defaults.global.plugins = {};
+
/**
* The plugin service singleton
* @namespace Chart.plugins
* @since 2.1.0
*/
Chart.plugins = {
+ /**
+ * Globally registered plugins.
+ * @private
+ */
_plugins: [],
/**
+ * This identifier is used to invalidate the descriptors cache attached to each chart
+ * when a global plugin is registered or unregistered. In this case, the cache ID is
+ * incremented and descriptors are regenerated during following API calls.
+ * @private
+ */
+ _cacheId: 0,
+
+ /**
* Registers the given plugin(s) if not already registered.
* @param {Array|Object} plugins plugin instance(s).
*/
register: function(plugins) {
var p = this._plugins;
([]).concat(plugins).forEach(function(plugin) {
if (p.indexOf(plugin) === -1) {
p.push(plugin);
}
});
+
+ this._cacheId++;
},
/**
* Unregisters the given plugin(s) only if registered.
* @param {Array|Object} plugins plugin instance(s).
@@ -10936,18 +11590,21 @@
var idx = p.indexOf(plugin);
if (idx !== -1) {
p.splice(idx, 1);
}
});
+
+ this._cacheId++;
},
/**
- * Remove all registered p^lugins.
+ * Remove all registered plugins.
* @since 2.1.5
*/
clear: function() {
this._plugins = [];
+ this._cacheId++;
},
/**
* Returns the number of registered plugins?
* @returns {Number}
@@ -10956,103 +11613,280 @@
count: function() {
return this._plugins.length;
},
/**
- * Returns all registered plugin intances.
+ * Returns all registered plugin instances.
* @returns {Array} array of plugin objects.
* @since 2.1.5
*/
getAll: function() {
return this._plugins;
},
/**
- * Calls registered plugins on the specified extension, with the given args. This
- * method immediately returns as soon as a plugin explicitly returns false. The
+ * Calls enabled plugins for `chart` on the specified hook and with the given args.
+ * This method immediately returns as soon as a plugin explicitly returns false. The
* returned value can be used, for instance, to interrupt the current action.
- * @param {String} extension the name of the plugin method to call (e.g. 'beforeUpdate').
- * @param {Array} [args] extra arguments to apply to the extension call.
+ * @param {Object} chart - The chart instance for which plugins should be called.
+ * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
+ * @param {Array} [args] - Extra arguments to apply to the hook call.
* @returns {Boolean} false if any of the plugins return false, else returns true.
*/
- notify: function(extension, args) {
- var plugins = this._plugins;
- var ilen = plugins.length;
- var i, plugin;
+ notify: function(chart, hook, args) {
+ var descriptors = this.descriptors(chart);
+ var ilen = descriptors.length;
+ var i, descriptor, plugin, params, method;
for (i=0; i<ilen; ++i) {
- plugin = plugins[i];
- if (typeof plugin[extension] === 'function') {
- if (plugin[extension].apply(plugin, args || []) === false) {
+ descriptor = descriptors[i];
+ plugin = descriptor.plugin;
+ method = plugin[hook];
+ if (typeof method === 'function') {
+ params = [chart].concat(args || []);
+ params.push(descriptor.options);
+ if (method.apply(plugin, params) === false) {
return false;
}
}
}
return true;
- }
- };
+ },
- /**
- * Plugin extension methods.
- * @interface Chart.PluginBase
- * @since 2.1.0
- */
- Chart.PluginBase = Chart.Element.extend({
- // Called at start of chart init
- beforeInit: noop,
+ /**
+ * Returns descriptors of enabled plugins for the given chart.
+ * @returns {Array} [{ plugin, options }]
+ * @private
+ */
+ descriptors: function(chart) {
+ var cache = chart._plugins || (chart._plugins = {});
+ if (cache.id === this._cacheId) {
+ return cache.descriptors;
+ }
- // Called at end of chart init
- afterInit: noop,
+ var plugins = [];
+ var descriptors = [];
+ var config = (chart && chart.config) || {};
+ var defaults = Chart.defaults.global.plugins;
+ var options = (config.options && config.options.plugins) || {};
- // Called at start of update
- beforeUpdate: noop,
+ this._plugins.concat(config.plugins || []).forEach(function(plugin) {
+ var idx = plugins.indexOf(plugin);
+ if (idx !== -1) {
+ return;
+ }
- // Called at end of update
- afterUpdate: noop,
+ var id = plugin.id;
+ var opts = options[id];
+ if (opts === false) {
+ return;
+ }
- // Called at start of draw
- beforeDraw: noop,
+ if (opts === true) {
+ opts = helpers.clone(defaults[id]);
+ }
- // Called at end of draw
- afterDraw: noop,
+ plugins.push(plugin);
+ descriptors.push({
+ plugin: plugin,
+ options: opts || {}
+ });
+ });
- // Called during destroy
- destroy: noop
- });
+ cache.descriptors = descriptors;
+ cache.id = this._cacheId;
+ return descriptors;
+ }
+ };
/**
+ * Plugin extension hooks.
+ * @interface IPlugin
+ * @since 2.1.0
+ */
+ /**
+ * @method IPlugin#beforeInit
+ * @desc Called before initializing `chart`.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#afterInit
+ * @desc Called after `chart` has been initialized and before the first update.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#beforeUpdate
+ * @desc Called before updating `chart`. If any plugin returns `false`, the update
+ * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ * @returns {Boolean} `false` to cancel the chart update.
+ */
+ /**
+ * @method IPlugin#afterUpdate
+ * @desc Called after `chart` has been updated and before rendering. Note that this
+ * hook will not be called if the chart update has been previously cancelled.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#beforeDatasetsUpdate
+ * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
+ * the datasets update is cancelled until another `update` is triggered.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ * @returns {Boolean} false to cancel the datasets update.
+ * @since version 2.1.5
+ */
+ /**
+ * @method IPlugin#afterDatasetsUpdate
+ * @desc Called after the `chart` datasets have been updated. Note that this hook
+ * will not be called if the datasets update has been previously cancelled.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ * @since version 2.1.5
+ */
+ /**
+ * @method IPlugin#beforeLayout
+ * @desc Called before laying out `chart`. If any plugin returns `false`,
+ * the layout update is cancelled until another `update` is triggered.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ * @returns {Boolean} `false` to cancel the chart layout.
+ */
+ /**
+ * @method IPlugin#afterLayout
+ * @desc Called after the `chart` has been layed out. Note that this hook will not
+ * be called if the layout update has been previously cancelled.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#beforeRender
+ * @desc Called before rendering `chart`. If any plugin returns `false`,
+ * the rendering is cancelled until another `render` is triggered.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ * @returns {Boolean} `false` to cancel the chart rendering.
+ */
+ /**
+ * @method IPlugin#afterRender
+ * @desc Called after the `chart` has been fully rendered (and animation completed). Note
+ * that this hook will not be called if the rendering has been previously cancelled.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#beforeDraw
+ * @desc Called before drawing `chart` at every animation frame specified by the given
+ * easing value. If any plugin returns `false`, the frame drawing is cancelled until
+ * another `render` is triggered.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
+ * @param {Object} options - The plugin options.
+ * @returns {Boolean} `false` to cancel the chart drawing.
+ */
+ /**
+ * @method IPlugin#afterDraw
+ * @desc Called after the `chart` has been drawn for the specific easing value. Note
+ * that this hook will not be called if the drawing has been previously cancelled.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#beforeDatasetsDraw
+ * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
+ * the datasets drawing is cancelled until another `render` is triggered.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
+ * @param {Object} options - The plugin options.
+ * @returns {Boolean} `false` to cancel the chart datasets drawing.
+ */
+ /**
+ * @method IPlugin#afterDatasetsDraw
+ * @desc Called after the `chart` datasets have been drawn. Note that this hook
+ * will not be called if the datasets drawing has been previously cancelled.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#beforeEvent
+ * @desc Called before processing the specified `event`. If any plugin returns `false`,
+ * the event will be discarded.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {IEvent} event - The event object.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#afterEvent
+ * @desc Called after the `event` has been consumed. Note that this hook
+ * will not be called if the `event` has been previously discarded.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {IEvent} event - The event object.
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#resize
+ * @desc Called after the chart as been resized.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
+ * @param {Object} options - The plugin options.
+ */
+ /**
+ * @method IPlugin#destroy
+ * @desc Called after the chart as been destroyed.
+ * @param {Chart.Controller} chart - The chart instance.
+ * @param {Object} options - The plugin options.
+ */
+
+ /**
* Provided for backward compatibility, use Chart.plugins instead
* @namespace Chart.pluginService
* @deprecated since version 2.1.5
- * @todo remove me at version 3
+ * @todo remove at version 3
+ * @private
*/
Chart.pluginService = Chart.plugins;
+
+ /**
+ * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
+ * effect, instead simply create/register plugins via plain JavaScript objects.
+ * @interface Chart.PluginBase
+ * @deprecated since version 2.5.0
+ * @todo remove at version 3
+ * @private
+ */
+ Chart.PluginBase = helpers.inherits({});
};
-},{}],31:[function(require,module,exports){
-"use strict";
+},{}],32:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.scale = {
display: true,
- position: "left",
+ position: 'left',
// grid line settings
gridLines: {
display: true,
- color: "rgba(0, 0, 0, 0.1)",
+ color: 'rgba(0, 0, 0, 0.1)',
lineWidth: 1,
drawBorder: true,
drawOnChartArea: true,
drawTicks: true,
tickMarkLength: 10,
zeroLineWidth: 1,
- zeroLineColor: "rgba(0,0,0,0.25)",
+ zeroLineColor: 'rgba(0,0,0,0.25)',
offsetGridLines: false,
borderDash: [],
borderDashOffset: 0.0
},
@@ -11069,24 +11903,58 @@
ticks: {
beginAtZero: false,
minRotation: 0,
maxRotation: 50,
mirror: false,
- padding: 10,
+ padding: 0,
reverse: false,
display: true,
autoSkip: true,
autoSkipPadding: 0,
labelOffset: 0,
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
- callback: function(value) {
- return helpers.isArray(value) ? value : '' + value;
- }
+ callback: Chart.Ticks.formatters.values
}
};
+ function computeTextSize(context, tick, font) {
+ return helpers.isArray(tick) ?
+ helpers.longestText(context, font, tick) :
+ context.measureText(tick).width;
+ }
+
+ function parseFontOptions(options) {
+ var getValueOrDefault = helpers.getValueOrDefault;
+ var globalDefaults = Chart.defaults.global;
+ var size = getValueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
+ var style = getValueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
+ var family = getValueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
+
+ return {
+ size: size,
+ style: style,
+ family: family,
+ font: helpers.fontString(size, style, family)
+ };
+ }
+
Chart.Scale = Chart.Element.extend({
+ /**
+ * Get the padding needed for the scale
+ * @method getPadding
+ * @private
+ * @returns {Padding} the necessary padding
+ */
+ getPadding: function() {
+ var me = this;
+ return {
+ left: me.paddingLeft || 0,
+ top: me.paddingTop || 0,
+ right: me.paddingRight || 0,
+ bottom: me.paddingBottom || 0
+ };
+ },
// These methods are ordered by lifecyle. Utilities then follow.
// Any function defined here is inherited by all scale types.
// Any function can be extended by the scale type
@@ -11106,10 +11974,11 @@
left: 0,
right: 0,
top: 0,
bottom: 0
}, margins);
+ me.longestTextCache = me.longestTextCache || {};
// Dimensions
me.beforeSetDimensions();
me.setDimensions();
me.afterSetDimensions();
@@ -11199,17 +12068,12 @@
helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
},
convertTicksToLabels: function() {
var me = this;
// Convert ticks to strings
- me.ticks = me.ticks.map(function(numericalTick, index, ticks) {
- if (me.options.ticks.userCallback) {
- return me.options.ticks.userCallback(numericalTick, index, ticks);
- }
- return me.options.ticks.callback(numericalTick, index, ticks);
- },
- me);
+ var tickOpts = me.options.ticks;
+ me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback);
},
afterTickToLabelConversion: function() {
helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
},
@@ -11219,76 +12083,46 @@
helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
},
calculateTickRotation: function() {
var me = this;
var context = me.ctx;
- var globalDefaults = Chart.defaults.global;
- var optionTicks = me.options.ticks;
+ var tickOpts = me.options.ticks;
- //Get the width of each grid by calculating the difference
- //between x offsets between 0 and 1.
- var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize);
- var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle);
- var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
- var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
- context.font = tickLabelFont;
+ // Get the width of each grid by calculating the difference
+ // between x offsets between 0 and 1.
+ var tickFont = parseFontOptions(tickOpts);
+ context.font = tickFont.font;
- var firstWidth = context.measureText(me.ticks[0]).width;
- var lastWidth = context.measureText(me.ticks[me.ticks.length - 1]).width;
- var firstRotated;
+ var labelRotation = tickOpts.minRotation || 0;
- me.labelRotation = optionTicks.minRotation || 0;
- me.paddingRight = 0;
- me.paddingLeft = 0;
+ if (me.options.display && me.isHorizontal()) {
+ var originalLabelWidth = helpers.longestText(context, tickFont.font, me.ticks, me.longestTextCache);
+ var labelWidth = originalLabelWidth;
+ var cosRotation;
+ var sinRotation;
- if (me.options.display) {
- if (me.isHorizontal()) {
- me.paddingRight = lastWidth / 2 + 3;
- me.paddingLeft = firstWidth / 2 + 3;
+ // Allow 3 pixels x2 padding either side for label readability
+ var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
- if (!me.longestTextCache) {
- me.longestTextCache = {};
- }
- var originalLabelWidth = helpers.longestText(context, tickLabelFont, me.ticks, me.longestTextCache);
- var labelWidth = originalLabelWidth;
- var cosRotation;
- var sinRotation;
+ // Max label rotation can be set or default to 90 - also act as a loop counter
+ while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
+ var angleRadians = helpers.toRadians(labelRotation);
+ cosRotation = Math.cos(angleRadians);
+ sinRotation = Math.sin(angleRadians);
- // Allow 3 pixels x2 padding either side for label readability
- // only the index matters for a dataset scale, but we want a consistent interface between scales
- var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
-
- //Max label rotation can be set or default to 90 - also act as a loop counter
- while (labelWidth > tickWidth && me.labelRotation < optionTicks.maxRotation) {
- cosRotation = Math.cos(helpers.toRadians(me.labelRotation));
- sinRotation = Math.sin(helpers.toRadians(me.labelRotation));
-
- firstRotated = cosRotation * firstWidth;
-
- // We're right aligning the text now.
- if (firstRotated + tickFontSize / 2 > me.yLabelWidth) {
- me.paddingLeft = firstRotated + tickFontSize / 2;
- }
-
- me.paddingRight = tickFontSize / 2;
-
- if (sinRotation * originalLabelWidth > me.maxHeight) {
- // go back one step
- me.labelRotation--;
- break;
- }
-
- me.labelRotation++;
- labelWidth = cosRotation * originalLabelWidth;
+ if (sinRotation * originalLabelWidth > me.maxHeight) {
+ // go back one step
+ labelRotation--;
+ break;
}
+
+ labelRotation++;
+ labelWidth = cosRotation * originalLabelWidth;
}
}
- if (me.margins) {
- me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
- me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
- }
+ me.labelRotation = labelRotation;
},
afterCalculateTickRotation: function() {
helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
},
@@ -11304,24 +12138,18 @@
width: 0,
height: 0
};
var opts = me.options;
- var globalDefaults = Chart.defaults.global;
var tickOpts = opts.ticks;
var scaleLabelOpts = opts.scaleLabel;
var gridLineOpts = opts.gridLines;
var display = opts.display;
var isHorizontal = me.isHorizontal();
- var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
- var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
- var tickFontFamily = helpers.getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
- var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
-
- var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabelOpts.fontSize, globalDefaults.defaultFontSize);
-
+ var tickFont = parseFontOptions(tickOpts);
+ var scaleLabelFontSize = parseFontOptions(scaleLabelOpts).size * 1.5;
var tickMarkLength = opts.gridLines.tickMarkLength;
// Width
if (isHorizontal) {
// subtract the margins to line up with the chartArea if we are a full width scale
@@ -11338,89 +12166,95 @@
}
// Are we showing a title for the scale?
if (scaleLabelOpts.display && display) {
if (isHorizontal) {
- minSize.height += (scaleLabelFontSize * 1.5);
+ minSize.height += scaleLabelFontSize;
} else {
- minSize.width += (scaleLabelFontSize * 1.5);
+ minSize.width += scaleLabelFontSize;
}
}
+ // Don't bother fitting the ticks if we are not showing them
if (tickOpts.display && display) {
- // Don't bother fitting the ticks if we are not showing them
- if (!me.longestTextCache) {
- me.longestTextCache = {};
- }
-
- var largestTextWidth = helpers.longestText(me.ctx, tickLabelFont, me.ticks, me.longestTextCache);
+ var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, me.ticks, me.longestTextCache);
var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks);
- var lineSpace = tickFontSize * 0.5;
+ var lineSpace = tickFont.size * 0.5;
if (isHorizontal) {
// A horizontal axis is more constrained by the height.
me.longestLabelWidth = largestTextWidth;
+ var angleRadians = helpers.toRadians(me.labelRotation);
+ var cosRotation = Math.cos(angleRadians);
+ var sinRotation = Math.sin(angleRadians);
+
// TODO - improve this calculation
- var labelHeight = (Math.sin(helpers.toRadians(me.labelRotation)) * me.longestLabelWidth) + (tickFontSize * tallestLabelHeightInLines) + (lineSpace * tallestLabelHeightInLines);
+ var labelHeight = (sinRotation * largestTextWidth)
+ + (tickFont.size * tallestLabelHeightInLines)
+ + (lineSpace * tallestLabelHeightInLines);
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight);
- me.ctx.font = tickLabelFont;
+ me.ctx.font = tickFont.font;
- var firstLabelWidth = me.ctx.measureText(me.ticks[0]).width;
- var lastLabelWidth = me.ctx.measureText(me.ticks[me.ticks.length - 1]).width;
+ var firstTick = me.ticks[0];
+ var firstLabelWidth = computeTextSize(me.ctx, firstTick, tickFont.font);
+ var lastTick = me.ticks[me.ticks.length - 1];
+ var lastLabelWidth = computeTextSize(me.ctx, lastTick, tickFont.font);
+
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
// by the font height
- var cosRotation = Math.cos(helpers.toRadians(me.labelRotation));
- var sinRotation = Math.sin(helpers.toRadians(me.labelRotation));
- me.paddingLeft = me.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
- me.paddingRight = me.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated
+ if (me.labelRotation !== 0) {
+ me.paddingLeft = opts.position === 'bottom'? (cosRotation * firstLabelWidth) + 3: (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
+ me.paddingRight = opts.position === 'bottom'? (cosRotation * lineSpace) + 3: (cosRotation * lastLabelWidth) + 3;
+ } else {
+ me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
+ me.paddingRight = lastLabelWidth / 2 + 3;
+ }
} else {
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
- var maxLabelWidth = me.maxWidth - minSize.width;
-
// Account for padding
- var mirror = tickOpts.mirror;
- if (!mirror) {
- largestTextWidth += me.options.ticks.padding;
- } else {
- // If mirrored text is on the inside so don't expand
- largestTextWidth = 0;
- }
- if (largestTextWidth < maxLabelWidth) {
- // We don't need all the room
- minSize.width += largestTextWidth;
+ if (tickOpts.mirror) {
+ largestTextWidth = 0;
} else {
- // Expand to max size
- minSize.width = me.maxWidth;
+ largestTextWidth += me.options.ticks.padding;
}
-
- me.paddingTop = tickFontSize / 2;
- me.paddingBottom = tickFontSize / 2;
+ minSize.width += largestTextWidth;
+ me.paddingTop = tickFont.size / 2;
+ me.paddingBottom = tickFont.size / 2;
}
}
+ me.handleMargins();
+
+ me.width = minSize.width;
+ me.height = minSize.height;
+ },
+
+ /**
+ * Handle margins and padding interactions
+ * @private
+ */
+ handleMargins: function() {
+ var me = this;
if (me.margins) {
me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
}
-
- me.width = minSize.width;
- me.height = minSize.height;
-
},
+
afterFit: function() {
helpers.callCallback(this.options.afterFit, [this]);
},
// Shared Methods
isHorizontal: function() {
- return this.options.position === "top" || this.options.position === "bottom";
+ return this.options.position === 'top' || this.options.position === 'bottom';
},
isFullWidth: function() {
return (this.options.fullWidth);
},
@@ -11428,21 +12262,20 @@
getRightValue: function(rawValue) {
// Null and undefined values first
if (rawValue === null || typeof(rawValue) === 'undefined') {
return NaN;
}
- // isNaN(object) returns true, so make sure NaN is checking for a number
- if (typeof(rawValue) === 'number' && isNaN(rawValue)) {
+ // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
+ if (typeof(rawValue) === 'number' && !isFinite(rawValue)) {
return NaN;
}
// If it is in fact an object, dive in one more level
- if (typeof(rawValue) === "object") {
+ if (typeof(rawValue) === 'object') {
if ((rawValue instanceof Date) || (rawValue.isValid)) {
return rawValue;
- } else {
- return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
}
+ return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
}
// Value is good, return it
return rawValue;
},
@@ -11470,44 +12303,45 @@
}
var finalVal = me.left + Math.round(pixel);
finalVal += me.isFullWidth() ? me.margins.left : 0;
return finalVal;
- } else {
- var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
- return me.top + (index * (innerHeight / (me.ticks.length - 1)));
}
+ var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
+ return me.top + (index * (innerHeight / (me.ticks.length - 1)));
},
// Utility for getting the pixel location of a percentage of scale
- getPixelForDecimal: function(decimal /*, includeOffset*/ ) {
+ getPixelForDecimal: function(decimal /* , includeOffset*/) {
var me = this;
if (me.isHorizontal()) {
var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
var valueOffset = (innerWidth * decimal) + me.paddingLeft;
var finalVal = me.left + Math.round(valueOffset);
finalVal += me.isFullWidth() ? me.margins.left : 0;
return finalVal;
- } else {
- return me.top + (decimal * me.height);
}
+ return me.top + (decimal * me.height);
},
getBasePixel: function() {
+ return this.getPixelForValue(this.getBaseValue());
+ },
+
+ getBaseValue: function() {
var me = this;
var min = me.min;
var max = me.max;
- return me.getPixelForValue(
- me.beginAtZero? 0:
+ return me.beginAtZero ? 0:
min < 0 && max < 0? max :
min > 0 && max > 0? min :
- 0);
+ 0;
},
- // Actualy draw the scale on the canvas
+ // Actually draw the scale on the canvas
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
draw: function(chartArea) {
var me = this;
var options = me.options;
if (!options.display) {
@@ -11530,23 +12364,18 @@
if (optionTicks.maxTicksLimit) {
maxTicks = optionTicks.maxTicksLimit;
}
var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
- var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize);
- var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle);
- var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
- var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
- var tl = gridLines.tickMarkLength;
+ var tickFont = parseFontOptions(optionTicks);
+
+ var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
var borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
var borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
- var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize);
- var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabel.fontStyle, globalDefaults.defaultFontStyle);
- var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily);
- var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily);
+ var scaleLabelFont = parseFontOptions(scaleLabel);
var labelRotationRadians = helpers.toRadians(me.labelRotation);
var cosRotation = Math.cos(labelRotationRadians);
var longestRotatedLabel = me.longestLabelWidth * cosRotation;
@@ -11583,14 +12412,14 @@
skipRatio = false;
}
}
- var xTickStart = options.position === "right" ? me.left : me.right - tl;
- var xTickEnd = options.position === "right" ? me.left + tl : me.right;
- var yTickStart = options.position === "bottom" ? me.top : me.bottom - tl;
- var yTickEnd = options.position === "bottom" ? me.top + tl : me.bottom;
+ var xTickStart = options.position === 'right' ? me.left : me.right - tl;
+ var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
+ var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
+ var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;
helpers.each(me.ticks, function(label, index) {
// If the callback returned a null or undefined value, do not draw this line
if (label === undefined || label === null) {
return;
@@ -11607,55 +12436,57 @@
var lineWidth, lineColor;
if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) {
// Draw the first index specially
lineWidth = gridLines.zeroLineWidth;
lineColor = gridLines.zeroLineColor;
- } else {
+ } else {
lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index);
lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index);
}
// Common properties
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
- var textAlign, textBaseline = 'middle';
+ var textAlign = 'middle';
+ var textBaseline = 'middle';
if (isHorizontal) {
- if (!isRotated) {
- textBaseline = options.position === 'top' ? 'bottom' : 'top';
+
+ if (options.position === 'bottom') {
+ // bottom
+ textBaseline = !isRotated? 'top':'middle';
+ textAlign = !isRotated? 'center': 'right';
+ labelY = me.top + tl;
+ } else {
+ // top
+ textBaseline = !isRotated? 'bottom':'middle';
+ textAlign = !isRotated? 'center': 'left';
+ labelY = me.bottom - tl;
}
- textAlign = isRotated ? 'right' : 'center';
-
var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines
labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
- labelY = (isRotated) ? me.top + 12 : options.position === 'top' ? me.bottom - tl : me.top + tl;
tx1 = tx2 = x1 = x2 = xLineValue;
ty1 = yTickStart;
ty2 = yTickEnd;
y1 = chartArea.top;
y2 = chartArea.bottom;
} else {
- if (options.position === 'left') {
- if (optionTicks.mirror) {
- labelX = me.right + optionTicks.padding;
- textAlign = 'left';
- } else {
- labelX = me.right - optionTicks.padding;
- textAlign = 'right';
- }
+ var isLeft = options.position === 'left';
+ var tickPadding = optionTicks.padding;
+ var labelXOffset;
+
+ if (optionTicks.mirror) {
+ textAlign = isLeft ? 'left' : 'right';
+ labelXOffset = tickPadding;
} else {
- // right side
- if (optionTicks.mirror) {
- labelX = me.left - optionTicks.padding;
- textAlign = 'right';
- } else {
- labelX = me.left + optionTicks.padding;
- textAlign = 'left';
- }
+ textAlign = isLeft ? 'right' : 'left';
+ labelXOffset = tl + tickPadding;
}
+ labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
+
var yLineValue = me.getPixelForTick(index); // xvalues for grid lines
yLineValue += helpers.aliasPixel(lineWidth);
labelY = me.getPixelForTick(index, gridLines.offsetGridLines);
tx1 = xTickStart;
@@ -11716,21 +12547,21 @@
if (optionTicks.display) {
context.save();
context.translate(itemToDraw.labelX, itemToDraw.labelY);
context.rotate(itemToDraw.rotation);
- context.font = tickLabelFont;
+ context.font = tickFont.font;
context.textBaseline = itemToDraw.textBaseline;
context.textAlign = itemToDraw.textAlign;
var label = itemToDraw.label;
if (helpers.isArray(label)) {
for (var i = 0, y = 0; i < label.length; ++i) {
// We just make sure the multiline element is a string here..
context.fillText('' + label[i], 0, y);
// apply same lineSpacing as calculated @ L#320
- y += (tickFontSize * 1.5);
+ y += (tickFont.size * 1.5);
}
} else {
context.fillText(label, 0, 0);
}
context.restore();
@@ -11743,25 +12574,25 @@
var scaleLabelY;
var rotation = 0;
if (isHorizontal) {
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
- scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFontSize / 2) : me.top + (scaleLabelFontSize / 2);
+ scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFont.size / 2) : me.top + (scaleLabelFont.size / 2);
} else {
var isLeft = options.position === 'left';
- scaleLabelX = isLeft ? me.left + (scaleLabelFontSize / 2) : me.right - (scaleLabelFontSize / 2);
+ scaleLabelX = isLeft ? me.left + (scaleLabelFont.size / 2) : me.right - (scaleLabelFont.size / 2);
scaleLabelY = me.top + ((me.bottom - me.top) / 2);
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
}
context.save();
context.translate(scaleLabelX, scaleLabelY);
context.rotate(rotation);
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillStyle = scaleLabelFontColor; // render in correct colour
- context.font = scaleLabelFont;
+ context.font = scaleLabelFont.font;
context.fillText(scaleLabel.labelString, 0, 0);
context.restore();
}
if (gridLines.drawBorder) {
@@ -11791,12 +12622,12 @@
}
}
});
};
-},{}],32:[function(require,module,exports){
-"use strict";
+},{}],33:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
@@ -11832,17 +12663,228 @@
Chart.layoutService.addBox(chartInstance, scale);
});
}
};
};
-},{}],33:[function(require,module,exports){
-"use strict";
+},{}],34:[function(require,module,exports){
+'use strict';
+
module.exports = function(Chart) {
var helpers = Chart.helpers;
+ /**
+ * Namespace to hold static tick generation functions
+ * @namespace Chart.Ticks
+ */
+ Chart.Ticks = {
+ /**
+ * Namespace to hold generators for different types of ticks
+ * @namespace Chart.Ticks.generators
+ */
+ generators: {
+ /**
+ * Interface for the options provided to the numeric tick generator
+ * @interface INumericTickGenerationOptions
+ */
+ /**
+ * The maximum number of ticks to display
+ * @name INumericTickGenerationOptions#maxTicks
+ * @type Number
+ */
+ /**
+ * The distance between each tick.
+ * @name INumericTickGenerationOptions#stepSize
+ * @type Number
+ * @optional
+ */
+ /**
+ * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
+ * @name INumericTickGenerationOptions#min
+ * @type Number
+ * @optional
+ */
+ /**
+ * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
+ * @name INumericTickGenerationOptions#max
+ * @type Number
+ * @optional
+ */
+
+ /**
+ * Generate a set of linear ticks
+ * @method Chart.Ticks.generators.linear
+ * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
+ * @param dataRange {IRange} the range of the data
+ * @returns {Array<Number>} array of tick values
+ */
+ linear: function(generationOptions, dataRange) {
+ var ticks = [];
+ // To get a "nice" value for the tick spacing, we will use the appropriately named
+ // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
+ // for details.
+
+ var spacing;
+ if (generationOptions.stepSize && generationOptions.stepSize > 0) {
+ spacing = generationOptions.stepSize;
+ } else {
+ var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
+ spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
+ }
+ var niceMin = Math.floor(dataRange.min / spacing) * spacing;
+ var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
+
+ // If min, max and stepSize is set and they make an evenly spaced scale use it.
+ if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
+ // If very close to our whole number, use it.
+ if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
+ niceMin = generationOptions.min;
+ niceMax = generationOptions.max;
+ }
+ }
+
+ var numSpaces = (niceMax - niceMin) / spacing;
+ // If very close to our rounded value, use it.
+ if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
+ numSpaces = Math.round(numSpaces);
+ } else {
+ numSpaces = Math.ceil(numSpaces);
+ }
+
+ // Put the values into the ticks array
+ ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
+ for (var j = 1; j < numSpaces; ++j) {
+ ticks.push(niceMin + (j * spacing));
+ }
+ ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
+
+ return ticks;
+ },
+
+ /**
+ * Generate a set of logarithmic ticks
+ * @method Chart.Ticks.generators.logarithmic
+ * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
+ * @param dataRange {IRange} the range of the data
+ * @returns {Array<Number>} array of tick values
+ */
+ logarithmic: function(generationOptions, dataRange) {
+ var ticks = [];
+ var getValueOrDefault = helpers.getValueOrDefault;
+
+ // Figure out what the max number of ticks we can support it is based on the size of
+ // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
+ // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
+ // the graph
+ var tickVal = getValueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
+
+ var endExp = Math.floor(helpers.log10(dataRange.max));
+ var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
+ var exp;
+ var significand;
+
+ if (tickVal === 0) {
+ exp = Math.floor(helpers.log10(dataRange.minNotZero));
+ significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
+
+ ticks.push(tickVal);
+ tickVal = significand * Math.pow(10, exp);
+ } else {
+ exp = Math.floor(helpers.log10(tickVal));
+ significand = Math.floor(tickVal / Math.pow(10, exp));
+ }
+
+ do {
+ ticks.push(tickVal);
+
+ ++significand;
+ if (significand === 10) {
+ significand = 1;
+ ++exp;
+ }
+
+ tickVal = significand * Math.pow(10, exp);
+ } while (exp < endExp || (exp === endExp && significand < endSignificand));
+
+ var lastTick = getValueOrDefault(generationOptions.max, tickVal);
+ ticks.push(lastTick);
+
+ return ticks;
+ }
+ },
+
+ /**
+ * Namespace to hold formatters for different types of ticks
+ * @namespace Chart.Ticks.formatters
+ */
+ formatters: {
+ /**
+ * Formatter for value labels
+ * @method Chart.Ticks.formatters.values
+ * @param value the value to display
+ * @return {String|Array} the label to display
+ */
+ values: function(value) {
+ return helpers.isArray(value) ? value : '' + value;
+ },
+
+ /**
+ * Formatter for linear numeric ticks
+ * @method Chart.Ticks.formatters.linear
+ * @param tickValue {Number} the value to be formatted
+ * @param index {Number} the position of the tickValue parameter in the ticks array
+ * @param ticks {Array<Number>} the list of ticks being converted
+ * @return {String} string representation of the tickValue parameter
+ */
+ linear: function(tickValue, index, ticks) {
+ // If we have lots of ticks, don't use the ones
+ var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
+
+ // If we have a number like 2.5 as the delta, figure out how many decimal places we need
+ if (Math.abs(delta) > 1) {
+ if (tickValue !== Math.floor(tickValue)) {
+ // not an integer
+ delta = tickValue - Math.floor(tickValue);
+ }
+ }
+
+ var logDelta = helpers.log10(Math.abs(delta));
+ var tickString = '';
+
+ if (tickValue !== 0) {
+ var numDecimal = -1 * Math.floor(logDelta);
+ numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
+ tickString = tickValue.toFixed(numDecimal);
+ } else {
+ tickString = '0'; // never show decimal places for 0
+ }
+
+ return tickString;
+ },
+
+ logarithmic: function(tickValue, index, ticks) {
+ var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
+
+ if (tickValue === 0) {
+ return '0';
+ } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
+ return tickValue.toExponential();
+ }
+ return '';
+ }
+ }
+ };
+};
+
+},{}],35:[function(require,module,exports){
+'use strict';
+
+module.exports = function(Chart) {
+
+ var helpers = Chart.helpers;
+
Chart.defaults.global.title = {
display: false,
position: 'top',
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
@@ -11857,24 +12899,18 @@
Chart.Title = Chart.Element.extend({
initialize: function(config) {
var me = this;
helpers.extend(me, config);
- me.options = helpers.configMerge(Chart.defaults.global.title, config.options);
// Contains hit boxes for each dataset (in dataset order)
me.legendHitBoxes = [];
},
- // These methods are ordered by lifecyle. Utilities then follow.
+ // These methods are ordered by lifecycle. Utilities then follow.
- beforeUpdate: function () {
- var chartOpts = this.chart.options;
- if (chartOpts && chartOpts.title) {
- this.options = helpers.configMerge(Chart.defaults.global.title, chartOpts.title);
- }
- },
+ beforeUpdate: noop,
update: function(maxWidth, maxHeight, margins) {
var me = this;
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
me.beforeUpdate();
@@ -11971,14 +13007,14 @@
afterFit: noop,
// Shared Methods
isHorizontal: function() {
var pos = this.options.position;
- return pos === "top" || pos === "bottom";
+ return pos === 'top' || pos === 'bottom';
},
- // Actualy draw the title block on the canvas
+ // Actually draw the title block on the canvas
draw: function() {
var me = this,
ctx = me.ctx,
valueOrDefault = helpers.getValueOrDefault,
opts = me.options,
@@ -11993,87 +13029,118 @@
titleX,
titleY,
top = me.top,
left = me.left,
bottom = me.bottom,
- right = me.right;
+ right = me.right,
+ maxWidth;
ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
ctx.font = titleFont;
// Horizontal
if (me.isHorizontal()) {
titleX = left + ((right - left) / 2); // midpoint of the width
titleY = top + ((bottom - top) / 2); // midpoint of the height
+ maxWidth = right - left;
} else {
titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2);
titleY = top + ((bottom - top) / 2);
+ maxWidth = bottom - top;
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
}
ctx.save();
ctx.translate(titleX, titleY);
ctx.rotate(rotation);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
- ctx.fillText(opts.text, 0, 0);
+ ctx.fillText(opts.text, 0, 0, maxWidth);
ctx.restore();
}
}
});
+ function createNewTitleBlockAndAttach(chartInstance, titleOpts) {
+ var title = new Chart.Title({
+ ctx: chartInstance.chart.ctx,
+ options: titleOpts,
+ chart: chartInstance
+ });
+ chartInstance.titleBlock = title;
+ Chart.layoutService.addBox(chartInstance, title);
+ }
+
// Register the title plugin
Chart.plugins.register({
beforeInit: function(chartInstance) {
- var opts = chartInstance.options;
- var titleOpts = opts.title;
+ var titleOpts = chartInstance.options.title;
if (titleOpts) {
- chartInstance.titleBlock = new Chart.Title({
- ctx: chartInstance.chart.ctx,
- options: titleOpts,
- chart: chartInstance
- });
+ createNewTitleBlockAndAttach(chartInstance, titleOpts);
+ }
+ },
+ beforeUpdate: function(chartInstance) {
+ var titleOpts = chartInstance.options.title;
- Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock);
+ if (titleOpts) {
+ titleOpts = helpers.configMerge(Chart.defaults.global.title, titleOpts);
+
+ if (chartInstance.titleBlock) {
+ chartInstance.titleBlock.options = titleOpts;
+ } else {
+ createNewTitleBlockAndAttach(chartInstance, titleOpts);
+ }
+ } else {
+ Chart.layoutService.removeBox(chartInstance, chartInstance.titleBlock);
+ delete chartInstance.titleBlock;
}
}
});
};
-},{}],34:[function(require,module,exports){
-"use strict";
+},{}],36:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
+ /**
+ * Helper method to merge the opacity into a color
+ */
+ function mergeOpacity(colorString, opacity) {
+ var color = helpers.color(colorString);
+ return color.alpha(opacity * color.alpha()).rgbaString();
+ }
+
Chart.defaults.global.tooltips = {
enabled: true,
custom: null,
- mode: 'single',
- backgroundColor: "rgba(0,0,0,0.8)",
- titleFontStyle: "bold",
+ mode: 'nearest',
+ position: 'average',
+ intersect: true,
+ backgroundColor: 'rgba(0,0,0,0.8)',
+ titleFontStyle: 'bold',
titleSpacing: 2,
titleMarginBottom: 6,
- titleFontColor: "#fff",
- titleAlign: "left",
+ titleFontColor: '#fff',
+ titleAlign: 'left',
bodySpacing: 2,
- bodyFontColor: "#fff",
- bodyAlign: "left",
- footerFontStyle: "bold",
+ bodyFontColor: '#fff',
+ bodyAlign: 'left',
+ footerFontStyle: 'bold',
footerSpacing: 2,
footerMarginTop: 6,
- footerFontColor: "#fff",
- footerAlign: "left",
+ footerFontColor: '#fff',
+ footerAlign: 'left',
yPadding: 6,
xPadding: 6,
- yAlign : 'center',
- xAlign : 'center',
caretSize: 5,
cornerRadius: 6,
multiKeyBackground: '#fff',
+ displayColors: true,
callbacks: {
// Args are: (tooltipItems, data)
beforeTitle: helpers.noop,
title: function(tooltipItems, data) {
// Pick first xLabel for now
@@ -12127,54 +13194,21 @@
// Helper to push or concat based on if the 2nd parameter is an array or not
function pushOrConcat(base, toPush) {
if (toPush) {
if (helpers.isArray(toPush)) {
- //base = base.concat(toPush);
+ // base = base.concat(toPush);
Array.prototype.push.apply(base, toPush);
} else {
base.push(toPush);
}
}
return base;
}
- function getAveragePosition(elements) {
- if (!elements.length) {
- return false;
- }
-
- var i, len;
- var xPositions = [];
- var yPositions = [];
-
- for (i = 0, len = elements.length; i < len; ++i) {
- var el = elements[i];
- if (el && el.hasValue()){
- var pos = el.tooltipPosition();
- xPositions.push(pos.x);
- yPositions.push(pos.y);
- }
- }
-
- var x = 0,
- y = 0;
- for (i = 0; i < xPositions.length; ++i) {
- if (xPositions[ i ]) {
- x += xPositions[i];
- y += yPositions[i];
- }
- }
-
- return {
- x: Math.round(x / xPositions.length),
- y: Math.round(y / xPositions.length)
- };
- }
-
- // Private helper to create a tooltip iteam model
+ // Private helper to create a tooltip item model
// @param element : the chart element (point, arc, bar) to create the tooltip item for
// @return : new tooltip item
function createTooltipItem(element) {
var xScale = element._xScale;
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
@@ -12183,63 +13217,259 @@
return {
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
index: index,
- datasetIndex: datasetIndex
+ datasetIndex: datasetIndex,
+ x: element._model.x,
+ y: element._model.y
};
}
- Chart.Tooltip = Chart.Element.extend({
- initialize: function() {
- var me = this;
- var globalDefaults = Chart.defaults.global;
- var tooltipOpts = me._options;
- var getValueOrDefault = helpers.getValueOrDefault;
+ /**
+ * Helper to get the reset model for the tooltip
+ * @param tooltipOpts {Object} the tooltip options
+ */
+ function getBaseModel(tooltipOpts) {
+ var globalDefaults = Chart.defaults.global;
+ var getValueOrDefault = helpers.getValueOrDefault;
- helpers.extend(me, {
- _model: {
- // Positioning
- xPadding: tooltipOpts.xPadding,
- yPadding: tooltipOpts.yPadding,
- xAlign : tooltipOpts.xAlign,
- yAlign : tooltipOpts.yAlign,
+ return {
+ // Positioning
+ xPadding: tooltipOpts.xPadding,
+ yPadding: tooltipOpts.yPadding,
+ xAlign: tooltipOpts.xAlign,
+ yAlign: tooltipOpts.yAlign,
- // Body
- bodyFontColor: tooltipOpts.bodyFontColor,
- _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
- _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
- _bodyAlign: tooltipOpts.bodyAlign,
- bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
- bodySpacing: tooltipOpts.bodySpacing,
+ // Body
+ bodyFontColor: tooltipOpts.bodyFontColor,
+ _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
+ _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
+ _bodyAlign: tooltipOpts.bodyAlign,
+ bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
+ bodySpacing: tooltipOpts.bodySpacing,
- // Title
- titleFontColor: tooltipOpts.titleFontColor,
- _titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
- _titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
- titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
- _titleAlign: tooltipOpts.titleAlign,
- titleSpacing: tooltipOpts.titleSpacing,
- titleMarginBottom: tooltipOpts.titleMarginBottom,
+ // Title
+ titleFontColor: tooltipOpts.titleFontColor,
+ _titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
+ _titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
+ titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
+ _titleAlign: tooltipOpts.titleAlign,
+ titleSpacing: tooltipOpts.titleSpacing,
+ titleMarginBottom: tooltipOpts.titleMarginBottom,
- // Footer
- footerFontColor: tooltipOpts.footerFontColor,
- _footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
- _footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
- footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
- _footerAlign: tooltipOpts.footerAlign,
- footerSpacing: tooltipOpts.footerSpacing,
- footerMarginTop: tooltipOpts.footerMarginTop,
+ // Footer
+ footerFontColor: tooltipOpts.footerFontColor,
+ _footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
+ _footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
+ footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
+ _footerAlign: tooltipOpts.footerAlign,
+ footerSpacing: tooltipOpts.footerSpacing,
+ footerMarginTop: tooltipOpts.footerMarginTop,
- // Appearance
- caretSize: tooltipOpts.caretSize,
- cornerRadius: tooltipOpts.cornerRadius,
- backgroundColor: tooltipOpts.backgroundColor,
- opacity: 0,
- legendColorBackground: tooltipOpts.multiKeyBackground
- }
- });
+ // Appearance
+ caretSize: tooltipOpts.caretSize,
+ cornerRadius: tooltipOpts.cornerRadius,
+ backgroundColor: tooltipOpts.backgroundColor,
+ opacity: 0,
+ legendColorBackground: tooltipOpts.multiKeyBackground,
+ displayColors: tooltipOpts.displayColors
+ };
+ }
+
+ /**
+ * Get the size of the tooltip
+ */
+ function getTooltipSize(tooltip, model) {
+ var ctx = tooltip._chart.ctx;
+
+ var height = model.yPadding * 2; // Tooltip Padding
+ var width = 0;
+
+ // Count of all lines in the body
+ var body = model.body;
+ var combinedBodyLength = body.reduce(function(count, bodyItem) {
+ return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
+ }, 0);
+ combinedBodyLength += model.beforeBody.length + model.afterBody.length;
+
+ var titleLineCount = model.title.length;
+ var footerLineCount = model.footer.length;
+ var titleFontSize = model.titleFontSize,
+ bodyFontSize = model.bodyFontSize,
+ footerFontSize = model.footerFontSize;
+
+ height += titleLineCount * titleFontSize; // Title Lines
+ height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
+ height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
+ height += combinedBodyLength * bodyFontSize; // Body Lines
+ height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
+ height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
+ height += footerLineCount * (footerFontSize); // Footer Lines
+ height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
+
+ // Title width
+ var widthPadding = 0;
+ var maxLineWidth = function(line) {
+ width = Math.max(width, ctx.measureText(line).width + widthPadding);
+ };
+
+ ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
+ helpers.each(model.title, maxLineWidth);
+
+ // Body width
+ ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
+ helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
+
+ // Body lines may include some extra width due to the color box
+ widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
+ helpers.each(body, function(bodyItem) {
+ helpers.each(bodyItem.before, maxLineWidth);
+ helpers.each(bodyItem.lines, maxLineWidth);
+ helpers.each(bodyItem.after, maxLineWidth);
+ });
+
+ // Reset back to 0
+ widthPadding = 0;
+
+ // Footer width
+ ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
+ helpers.each(model.footer, maxLineWidth);
+
+ // Add padding
+ width += 2 * model.xPadding;
+
+ return {
+ width: width,
+ height: height
+ };
+ }
+
+ /**
+ * Helper to get the alignment of a tooltip given the size
+ */
+ function determineAlignment(tooltip, size) {
+ var model = tooltip._model;
+ var chart = tooltip._chart;
+ var chartArea = tooltip._chartInstance.chartArea;
+ var xAlign = 'center';
+ var yAlign = 'center';
+
+ if (model.y < size.height) {
+ yAlign = 'top';
+ } else if (model.y > (chart.height - size.height)) {
+ yAlign = 'bottom';
+ }
+
+ var lf, rf; // functions to determine left, right alignment
+ var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
+ var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
+ var midX = (chartArea.left + chartArea.right) / 2;
+ var midY = (chartArea.top + chartArea.bottom) / 2;
+
+ if (yAlign === 'center') {
+ lf = function(x) {
+ return x <= midX;
+ };
+ rf = function(x) {
+ return x > midX;
+ };
+ } else {
+ lf = function(x) {
+ return x <= (size.width / 2);
+ };
+ rf = function(x) {
+ return x >= (chart.width - (size.width / 2));
+ };
+ }
+
+ olf = function(x) {
+ return x + size.width > chart.width;
+ };
+ orf = function(x) {
+ return x - size.width < 0;
+ };
+ yf = function(y) {
+ return y <= midY ? 'top' : 'bottom';
+ };
+
+ if (lf(model.x)) {
+ xAlign = 'left';
+
+ // Is tooltip too wide and goes over the right side of the chart.?
+ if (olf(model.x)) {
+ xAlign = 'center';
+ yAlign = yf(model.y);
+ }
+ } else if (rf(model.x)) {
+ xAlign = 'right';
+
+ // Is tooltip too wide and goes outside left edge of canvas?
+ if (orf(model.x)) {
+ xAlign = 'center';
+ yAlign = yf(model.y);
+ }
+ }
+
+ var opts = tooltip._options;
+ return {
+ xAlign: opts.xAlign ? opts.xAlign : xAlign,
+ yAlign: opts.yAlign ? opts.yAlign : yAlign
+ };
+ }
+
+ /**
+ * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
+ */
+ function getBackgroundPoint(vm, size, alignment) {
+ // Background Position
+ var x = vm.x;
+ var y = vm.y;
+
+ var caretSize = vm.caretSize,
+ caretPadding = vm.caretPadding,
+ cornerRadius = vm.cornerRadius,
+ xAlign = alignment.xAlign,
+ yAlign = alignment.yAlign,
+ paddingAndSize = caretSize + caretPadding,
+ radiusAndPadding = cornerRadius + caretPadding;
+
+ if (xAlign === 'right') {
+ x -= size.width;
+ } else if (xAlign === 'center') {
+ x -= (size.width / 2);
+ }
+
+ if (yAlign === 'top') {
+ y += paddingAndSize;
+ } else if (yAlign === 'bottom') {
+ y -= size.height + paddingAndSize;
+ } else {
+ y -= (size.height / 2);
+ }
+
+ if (yAlign === 'center') {
+ if (xAlign === 'left') {
+ x += paddingAndSize;
+ } else if (xAlign === 'right') {
+ x -= paddingAndSize;
+ }
+ } else if (xAlign === 'left') {
+ x -= radiusAndPadding;
+ } else if (xAlign === 'right') {
+ x += radiusAndPadding;
+ }
+
+ return {
+ x: x,
+ y: y
+ };
+ }
+
+ Chart.Tooltip = Chart.Element.extend({
+ initialize: function() {
+ this._model = getBaseModel(this._options);
},
// Get the title
// Args are: (tooltipItem, data)
getTitle: function() {
@@ -12312,240 +13542,114 @@
},
update: function(changed) {
var me = this;
var opts = me._options;
- var model = me._model;
+
+ // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
+ // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
+ // which breaks any animations.
+ var existingModel = me._model;
+ var model = me._model = getBaseModel(opts);
var active = me._active;
var data = me._data;
var chartInstance = me._chartInstance;
+ // In the case where active.length === 0 we need to keep these at existing values for good animations
+ var alignment = {
+ xAlign: existingModel.xAlign,
+ yAlign: existingModel.yAlign
+ };
+ var backgroundPoint = {
+ x: existingModel.x,
+ y: existingModel.y
+ };
+ var tooltipSize = {
+ width: existingModel.width,
+ height: existingModel.height
+ };
+ var tooltipPosition = {
+ x: existingModel.caretX,
+ y: existingModel.caretY
+ };
+
var i, len;
if (active.length) {
model.opacity = 1;
- var labelColors = [],
- tooltipPosition = getAveragePosition(active);
+ var labelColors = [];
+ tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);
var tooltipItems = [];
for (i = 0, len = active.length; i < len; ++i) {
tooltipItems.push(createTooltipItem(active[i]));
}
- // If the user provided a sorting function, use it to modify the tooltip items
- if (opts.itemSort) {
- tooltipItems = tooltipItems.sort(function(a,b) {
- return opts.itemSort(a,b, data);
+ // If the user provided a filter function, use it to modify the tooltip items
+ if (opts.filter) {
+ tooltipItems = tooltipItems.filter(function(a) {
+ return opts.filter(a, data);
});
}
- // If there is more than one item, show color items
- if (active.length > 1) {
- helpers.each(tooltipItems, function(tooltipItem) {
- labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance));
+ // If the user provided a sorting function, use it to modify the tooltip items
+ if (opts.itemSort) {
+ tooltipItems = tooltipItems.sort(function(a, b) {
+ return opts.itemSort(a, b, data);
});
}
- // Build the Text Lines
- helpers.extend(model, {
- title: me.getTitle(tooltipItems, data),
- beforeBody: me.getBeforeBody(tooltipItems, data),
- body: me.getBody(tooltipItems, data),
- afterBody: me.getAfterBody(tooltipItems, data),
- footer: me.getFooter(tooltipItems, data),
- x: Math.round(tooltipPosition.x),
- y: Math.round(tooltipPosition.y),
- caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2),
- labelColors: labelColors
+ // Determine colors for boxes
+ helpers.each(tooltipItems, function(tooltipItem) {
+ labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance));
});
- // We need to determine alignment of
- var tooltipSize = me.getTooltipSize(model);
- me.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas
+ // Build the Text Lines
+ model.title = me.getTitle(tooltipItems, data);
+ model.beforeBody = me.getBeforeBody(tooltipItems, data);
+ model.body = me.getBody(tooltipItems, data);
+ model.afterBody = me.getAfterBody(tooltipItems, data);
+ model.footer = me.getFooter(tooltipItems, data);
- helpers.extend(model, me.getBackgroundPoint(model, tooltipSize));
- } else {
- me._model.opacity = 0;
- }
+ // Initial positioning and colors
+ model.x = Math.round(tooltipPosition.x);
+ model.y = Math.round(tooltipPosition.y);
+ model.caretPadding = helpers.getValueOrDefault(tooltipPosition.padding, 2);
+ model.labelColors = labelColors;
- if (changed && opts.custom) {
- opts.custom.call(me, model);
- }
+ // data points
+ model.dataPoints = tooltipItems;
- return me;
- },
- getTooltipSize: function(vm) {
- var ctx = this._chart.ctx;
-
- var size = {
- height: vm.yPadding * 2, // Tooltip Padding
- width: 0
- };
-
- // Count of all lines in the body
- var body = vm.body;
- var combinedBodyLength = body.reduce(function(count, bodyItem) {
- return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
- }, 0);
- combinedBodyLength += vm.beforeBody.length + vm.afterBody.length;
-
- var titleLineCount = vm.title.length;
- var footerLineCount = vm.footer.length;
- var titleFontSize = vm.titleFontSize,
- bodyFontSize = vm.bodyFontSize,
- footerFontSize = vm.footerFontSize;
-
- size.height += titleLineCount * titleFontSize; // Title Lines
- size.height += (titleLineCount - 1) * vm.titleSpacing; // Title Line Spacing
- size.height += titleLineCount ? vm.titleMarginBottom : 0; // Title's bottom Margin
- size.height += combinedBodyLength * bodyFontSize; // Body Lines
- size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
- size.height += footerLineCount ? vm.footerMarginTop : 0; // Footer Margin
- size.height += footerLineCount * (footerFontSize); // Footer Lines
- size.height += footerLineCount ? (footerLineCount - 1) * vm.footerSpacing : 0; // Footer Line Spacing
-
- // Title width
- var widthPadding = 0;
- var maxLineWidth = function(line) {
- size.width = Math.max(size.width, ctx.measureText(line).width + widthPadding);
- };
-
- ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
- helpers.each(vm.title, maxLineWidth);
-
- // Body width
- ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
- helpers.each(vm.beforeBody.concat(vm.afterBody), maxLineWidth);
-
- // Body lines may include some extra width due to the color box
- widthPadding = body.length > 1 ? (bodyFontSize + 2) : 0;
- helpers.each(body, function(bodyItem) {
- helpers.each(bodyItem.before, maxLineWidth);
- helpers.each(bodyItem.lines, maxLineWidth);
- helpers.each(bodyItem.after, maxLineWidth);
- });
-
- // Reset back to 0
- widthPadding = 0;
-
- // Footer width
- ctx.font = helpers.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
- helpers.each(vm.footer, maxLineWidth);
-
- // Add padding
- size.width += 2 * vm.xPadding;
-
- return size;
- },
- determineAlignment: function(size) {
- var me = this;
- var model = me._model;
- var chart = me._chart;
- var chartArea = me._chartInstance.chartArea;
-
- if (model.y < size.height) {
- model.yAlign = 'top';
- } else if (model.y > (chart.height - size.height)) {
- model.yAlign = 'bottom';
- }
-
- var lf, rf; // functions to determine left, right alignment
- var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
- var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
- var midX = (chartArea.left + chartArea.right) / 2;
- var midY = (chartArea.top + chartArea.bottom) / 2;
-
- if (model.yAlign === 'center') {
- lf = function(x) {
- return x <= midX;
- };
- rf = function(x) {
- return x > midX;
- };
+ // We need to determine alignment of the tooltip
+ tooltipSize = getTooltipSize(this, model);
+ alignment = determineAlignment(this, tooltipSize);
+ // Final Size and Position
+ backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
} else {
- lf = function(x) {
- return x <= (size.width / 2);
- };
- rf = function(x) {
- return x >= (chart.width - (size.width / 2));
- };
+ model.opacity = 0;
}
- olf = function(x) {
- return x + size.width > chart.width;
- };
- orf = function(x) {
- return x - size.width < 0;
- };
- yf = function(y) {
- return y <= midY ? 'top' : 'bottom';
- };
+ model.xAlign = alignment.xAlign;
+ model.yAlign = alignment.yAlign;
+ model.x = backgroundPoint.x;
+ model.y = backgroundPoint.y;
+ model.width = tooltipSize.width;
+ model.height = tooltipSize.height;
- if (lf(model.x)) {
- model.xAlign = 'left';
+ // Point where the caret on the tooltip points to
+ model.caretX = tooltipPosition.x;
+ model.caretY = tooltipPosition.y;
- // Is tooltip too wide and goes over the right side of the chart.?
- if (olf(model.x)) {
- model.xAlign = 'center';
- model.yAlign = yf(model.y);
- }
- } else if (rf(model.x)) {
- model.xAlign = 'right';
+ me._model = model;
- // Is tooltip too wide and goes outside left edge of canvas?
- if (orf(model.x)) {
- model.xAlign = 'center';
- model.yAlign = yf(model.y);
- }
+ if (changed && opts.custom) {
+ opts.custom.call(me, model);
}
- },
- getBackgroundPoint: function(vm, size) {
- // Background Position
- var pt = {
- x: vm.x,
- y: vm.y
- };
- var caretSize = vm.caretSize,
- caretPadding = vm.caretPadding,
- cornerRadius = vm.cornerRadius,
- xAlign = vm.xAlign,
- yAlign = vm.yAlign,
- paddingAndSize = caretSize + caretPadding,
- radiusAndPadding = cornerRadius + caretPadding;
-
- if (xAlign === 'right') {
- pt.x -= size.width;
- } else if (xAlign === 'center') {
- pt.x -= (size.width / 2);
- }
-
- if (yAlign === 'top') {
- pt.y += paddingAndSize;
- } else if (yAlign === 'bottom') {
- pt.y -= size.height + paddingAndSize;
- } else {
- pt.y -= (size.height / 2);
- }
-
- if (yAlign === 'center') {
- if (xAlign === 'left') {
- pt.x += paddingAndSize;
- } else if (xAlign === 'right') {
- pt.x -= paddingAndSize;
- }
- } else {
- if (xAlign === 'left') {
- pt.x -= radiusAndPadding;
- } else if (xAlign === 'right') {
- pt.x += radiusAndPadding;
- }
- }
-
- return pt;
+ return me;
},
drawCaret: function(tooltipPoint, size, opacity) {
var vm = this._view;
var ctx = this._chart.ctx;
var x1, x2, x3;
@@ -12598,12 +13702,11 @@
y2 = y1 + caretSize;
y3 = y1;
}
}
- var bgColor = helpers.color(vm.backgroundColor);
- ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
+ ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
@@ -12612,17 +13715,16 @@
drawTitle: function(pt, vm, ctx, opacity) {
var title = vm.title;
if (title.length) {
ctx.textAlign = vm._titleAlign;
- ctx.textBaseline = "top";
+ ctx.textBaseline = 'top';
var titleFontSize = vm.titleFontSize,
titleSpacing = vm.titleSpacing;
- var titleFontColor = helpers.color(vm.titleFontColor);
- ctx.fillStyle = titleFontColor.alpha(opacity * titleFontColor.alpha()).rgbString();
+ ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
var i, len;
for (i = 0, len = title.length; i < len; ++i) {
ctx.fillText(title[i], pt.x, pt.y);
@@ -12638,14 +13740,13 @@
var bodyFontSize = vm.bodyFontSize;
var bodySpacing = vm.bodySpacing;
var body = vm.body;
ctx.textAlign = vm._bodyAlign;
- ctx.textBaseline = "top";
+ ctx.textBaseline = 'top';
- var bodyFontColor = helpers.color(vm.bodyFontColor);
- var textColor = bodyFontColor.alpha(opacity * bodyFontColor.alpha()).rgbString();
+ var textColor = mergeOpacity(vm.bodyFontColor, opacity);
ctx.fillStyle = textColor;
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
// Before Body
var xLinePadding = 0;
@@ -12655,30 +13756,30 @@
};
// Before body lines
helpers.each(vm.beforeBody, fillLineOfText);
- var drawColorBoxes = body.length > 1;
+ var drawColorBoxes = vm.displayColors;
xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
// Draw body lines now
helpers.each(body, function(bodyItem, i) {
helpers.each(bodyItem.before, fillLineOfText);
helpers.each(bodyItem.lines, function(line) {
// Draw Legend-like boxes if needed
if (drawColorBoxes) {
// Fill a white rect so that colours merge nicely if the opacity is < 1
- ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
+ ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
// Border
- ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
+ ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
// Inner square
- ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
+ ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
ctx.fillStyle = textColor;
}
@@ -12700,45 +13801,49 @@
if (footer.length) {
pt.y += vm.footerMarginTop;
ctx.textAlign = vm._footerAlign;
- ctx.textBaseline = "top";
+ ctx.textBaseline = 'top';
- var footerFontColor = helpers.color(vm.footerFontColor);
- ctx.fillStyle = footerFontColor.alpha(opacity * footerFontColor.alpha()).rgbString();
+ ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(footer, function(line) {
ctx.fillText(line, pt.x, pt.y);
pt.y += vm.footerFontSize + vm.footerSpacing;
});
}
},
+ drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
+ ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
+ helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
+ ctx.fill();
+ },
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
if (vm.opacity === 0) {
return;
}
- var tooltipSize = this.getTooltipSize(vm);
+ var tooltipSize = {
+ width: vm.width,
+ height: vm.height
+ };
var pt = {
x: vm.x,
y: vm.y
};
// IE11/Edge does not like very small opacities, so snap to 0
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
if (this._options.enabled) {
// Draw Background
- var bgColor = helpers.color(vm.backgroundColor);
- ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
- helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
- ctx.fill();
+ this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
// Draw Caret
this.drawCaret(pt, tooltipSize, opacity);
// Draw Title, Body, and Footer
@@ -12752,111 +13857,239 @@
this.drawBody(pt, vm, ctx, opacity);
// Footer
this.drawFooter(pt, vm, ctx, opacity);
}
+ },
+
+ /**
+ * Handle an event
+ * @private
+ * @param {IEvent} event - The event to handle
+ * @returns {Boolean} true if the tooltip changed
+ */
+ handleEvent: function(e) {
+ var me = this;
+ var options = me._options;
+ var changed = false;
+
+ me._lastActive = me._lastActive || [];
+
+ // Find Active Elements for tooltips
+ if (e.type === 'mouseout') {
+ me._active = [];
+ } else {
+ me._active = me._chartInstance.getElementsAtEventForMode(e, options.mode, options);
+ }
+
+ // Remember Last Actives
+ changed = !helpers.arrayEquals(me._active, me._lastActive);
+ me._lastActive = me._active;
+
+ if (options.enabled || options.custom) {
+ me._eventPosition = {
+ x: e.x,
+ y: e.y
+ };
+
+ var model = me._model;
+ me.update(true);
+ me.pivot();
+
+ // See if our tooltip position changed
+ changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
+ }
+
+ return changed;
}
});
+
+ /**
+ * @namespace Chart.Tooltip.positioners
+ */
+ Chart.Tooltip.positioners = {
+ /**
+ * Average mode places the tooltip at the average position of the elements shown
+ * @function Chart.Tooltip.positioners.average
+ * @param elements {ChartElement[]} the elements being displayed in the tooltip
+ * @returns {Point} tooltip position
+ */
+ average: function(elements) {
+ if (!elements.length) {
+ return false;
+ }
+
+ var i, len;
+ var x = 0;
+ var y = 0;
+ var count = 0;
+
+ for (i = 0, len = elements.length; i < len; ++i) {
+ var el = elements[i];
+ if (el && el.hasValue()) {
+ var pos = el.tooltipPosition();
+ x += pos.x;
+ y += pos.y;
+ ++count;
+ }
+ }
+
+ return {
+ x: Math.round(x / count),
+ y: Math.round(y / count)
+ };
+ },
+
+ /**
+ * Gets the tooltip position nearest of the item nearest to the event position
+ * @function Chart.Tooltip.positioners.nearest
+ * @param elements {Chart.Element[]} the tooltip elements
+ * @param eventPosition {Point} the position of the event in canvas coordinates
+ * @returns {Point} the tooltip position
+ */
+ nearest: function(elements, eventPosition) {
+ var x = eventPosition.x;
+ var y = eventPosition.y;
+
+ var nearestElement;
+ var minDistance = Number.POSITIVE_INFINITY;
+ var i, len;
+ for (i = 0, len = elements.length; i < len; ++i) {
+ var el = elements[i];
+ if (el && el.hasValue()) {
+ var center = el.getCenterPoint();
+ var d = helpers.distanceBetweenPoints(eventPosition, center);
+
+ if (d < minDistance) {
+ minDistance = d;
+ nearestElement = el;
+ }
+ }
+ }
+
+ if (nearestElement) {
+ var tp = nearestElement.tooltipPosition();
+ x = tp.x;
+ y = tp.y;
+ }
+
+ return {
+ x: x,
+ y: y
+ };
+ }
+ };
};
-},{}],35:[function(require,module,exports){
-"use strict";
+},{}],37:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
- var helpers = Chart.helpers,
- globalOpts = Chart.defaults.global;
+ var helpers = Chart.helpers,
+ globalOpts = Chart.defaults.global;
- globalOpts.elements.arc = {
- backgroundColor: globalOpts.defaultColor,
- borderColor: "#fff",
- borderWidth: 2
- };
+ globalOpts.elements.arc = {
+ backgroundColor: globalOpts.defaultColor,
+ borderColor: '#fff',
+ borderWidth: 2
+ };
- Chart.elements.Arc = Chart.Element.extend({
- inLabelRange: function(mouseX) {
- var vm = this._view;
+ Chart.elements.Arc = Chart.Element.extend({
+ inLabelRange: function(mouseX) {
+ var vm = this._view;
- if (vm) {
- return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
- } else {
- return false;
- }
- },
- inRange: function(chartX, chartY) {
- var vm = this._view;
+ if (vm) {
+ return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
+ }
+ return false;
+ },
+ inRange: function(chartX, chartY) {
+ var vm = this._view;
- if (vm) {
- var pointRelativePosition = helpers.getAngleFromPoint(vm, {
- x: chartX,
- y: chartY
- }),
- angle = pointRelativePosition.angle,
- distance = pointRelativePosition.distance;
+ if (vm) {
+ var pointRelativePosition = helpers.getAngleFromPoint(vm, {
+ x: chartX,
+ y: chartY
+ }),
+ angle = pointRelativePosition.angle,
+ distance = pointRelativePosition.distance;
- //Sanitise angle range
- var startAngle = vm.startAngle;
- var endAngle = vm.endAngle;
- while (endAngle < startAngle) {
- endAngle += 2.0 * Math.PI;
- }
- while (angle > endAngle) {
- angle -= 2.0 * Math.PI;
- }
- while (angle < startAngle) {
- angle += 2.0 * Math.PI;
- }
+ // Sanitise angle range
+ var startAngle = vm.startAngle;
+ var endAngle = vm.endAngle;
+ while (endAngle < startAngle) {
+ endAngle += 2.0 * Math.PI;
+ }
+ while (angle > endAngle) {
+ angle -= 2.0 * Math.PI;
+ }
+ while (angle < startAngle) {
+ angle += 2.0 * Math.PI;
+ }
- //Check if within the range of the open/close angle
- var betweenAngles = (angle >= startAngle && angle <= endAngle),
- withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
+ // Check if within the range of the open/close angle
+ var betweenAngles = (angle >= startAngle && angle <= endAngle),
+ withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
- return (betweenAngles && withinRadius);
- } else {
- return false;
- }
- },
- tooltipPosition: function() {
- var vm = this._view;
+ return (betweenAngles && withinRadius);
+ }
+ return false;
+ },
+ getCenterPoint: function() {
+ var vm = this._view;
+ var halfAngle = (vm.startAngle + vm.endAngle) / 2;
+ var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
+ return {
+ x: vm.x + Math.cos(halfAngle) * halfRadius,
+ y: vm.y + Math.sin(halfAngle) * halfRadius
+ };
+ },
+ getArea: function() {
+ var vm = this._view;
+ return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
+ },
+ tooltipPosition: function() {
+ var vm = this._view;
- var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
- rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
- return {
- x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
- y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
- };
- },
- draw: function() {
+ var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
+ rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
+ return {
+ x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
+ y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
+ };
+ },
+ draw: function() {
- var ctx = this._chart.ctx,
- vm = this._view,
- sA = vm.startAngle,
- eA = vm.endAngle;
+ var ctx = this._chart.ctx,
+ vm = this._view,
+ sA = vm.startAngle,
+ eA = vm.endAngle;
- ctx.beginPath();
+ ctx.beginPath();
- ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
- ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
+ ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
+ ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
- ctx.closePath();
- ctx.strokeStyle = vm.borderColor;
- ctx.lineWidth = vm.borderWidth;
+ ctx.closePath();
+ ctx.strokeStyle = vm.borderColor;
+ ctx.lineWidth = vm.borderWidth;
- ctx.fillStyle = vm.backgroundColor;
+ ctx.fillStyle = vm.backgroundColor;
- ctx.fill();
- ctx.lineJoin = 'bevel';
+ ctx.fill();
+ ctx.lineJoin = 'bevel';
- if (vm.borderWidth) {
- ctx.stroke();
- }
- }
- });
+ if (vm.borderWidth) {
+ ctx.stroke();
+ }
+ }
+ });
};
-},{}],36:[function(require,module,exports){
-"use strict";
+},{}],38:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
var globalDefaults = Chart.defaults.global;
@@ -12869,40 +14102,49 @@
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
capBezierPoints: true,
- fill: true // do we fill in the area between the line and its base axis
+ fill: true, // do we fill in the area between the line and its base axis
};
Chart.elements.Line = Chart.Element.extend({
draw: function() {
var me = this;
var vm = me._view;
var spanGaps = vm.spanGaps;
- var scaleZero = vm.scaleZero;
+ var fillPoint = vm.scaleZero;
var loop = me._loop;
+ // Handle different fill modes for cartesian lines
+ if (!loop) {
+ if (vm.fill === 'top') {
+ fillPoint = vm.scaleTop;
+ } else if (vm.fill === 'bottom') {
+ fillPoint = vm.scaleBottom;
+ }
+ }
+
var ctx = me._chart.ctx;
ctx.save();
// Helper function to draw a line to a point
function lineToPoint(previousPoint, point) {
- var vm = point._view;
+ var pointVM = point._view;
if (point._view.steppedLine === true) {
- ctx.lineTo(point._view.x, previousPoint._view.y);
- ctx.lineTo(point._view.x, point._view.y);
+ ctx.lineTo(pointVM.x, previousPoint._view.y);
+ ctx.lineTo(pointVM.x, pointVM.y);
} else if (point._view.tension === 0) {
- ctx.lineTo(vm.x, vm.y);
+ ctx.lineTo(pointVM.x, pointVM.y);
} else {
ctx.bezierCurveTo(
previousPoint._view.controlPointNextX,
previousPoint._view.controlPointNextY,
- vm.controlPointPreviousX,
- vm.controlPointPreviousY,
- vm.x,
- vm.y
+ pointVM.controlPointPreviousX,
+ pointVM.controlPointPreviousY,
+ pointVM.x,
+ pointVM.y
);
}
}
var points = me._children.slice(); // clone array
@@ -12925,13 +14167,13 @@
currentVM = current._view;
// First point moves to it's starting position no matter what
if (index === 0) {
if (loop) {
- ctx.moveTo(scaleZero.x, scaleZero.y);
+ ctx.moveTo(fillPoint.x, fillPoint.y);
} else {
- ctx.moveTo(currentVM.x, scaleZero);
+ ctx.moveTo(currentVM.x, fillPoint);
}
if (!currentVM.skip) {
lastDrawnIndex = index;
ctx.lineTo(currentVM.x, currentVM.y);
@@ -12941,29 +14183,27 @@
if (currentVM.skip) {
// Only do this if this is the first point that is skipped
if (!spanGaps && lastDrawnIndex === (index - 1)) {
if (loop) {
- ctx.lineTo(scaleZero.x, scaleZero.y);
+ ctx.lineTo(fillPoint.x, fillPoint.y);
} else {
- ctx.lineTo(previous._view.x, scaleZero);
+ ctx.lineTo(previous._view.x, fillPoint);
}
}
} else {
if (lastDrawnIndex !== (index - 1)) {
- // There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case.
+ // There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case.
// If the first data point is NaN, then there is no real gap to skip
if (spanGaps && lastDrawnIndex !== -1) {
// We are spanning the gap, so simple draw a line to this point
lineToPoint(previous, current);
+ } else if (loop) {
+ ctx.lineTo(currentVM.x, currentVM.y);
} else {
- if (loop) {
- ctx.lineTo(currentVM.x, currentVM.y);
- } else {
- ctx.lineTo(currentVM.x, scaleZero);
- ctx.lineTo(currentVM.x, currentVM.y);
- }
+ ctx.lineTo(currentVM.x, fillPoint);
+ ctx.lineTo(currentVM.x, currentVM.y);
}
} else {
// Line to next point
lineToPoint(previous, current);
}
@@ -12971,11 +14211,11 @@
}
}
}
if (!loop && lastDrawnIndex !== -1) {
- ctx.lineTo(points[lastDrawnIndex]._view.x, scaleZero);
+ ctx.lineTo(points[lastDrawnIndex]._view.x, fillPoint);
}
ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor;
ctx.closePath();
ctx.fill();
@@ -13029,13 +14269,14 @@
ctx.stroke();
ctx.restore();
}
});
};
-},{}],37:[function(require,module,exports){
-"use strict";
+},{}],39:[function(require,module,exports){
+'use strict';
+
module.exports = function(Chart) {
var helpers = Chart.helpers,
globalOpts = Chart.defaults.global,
defaultColor = globalOpts.defaultColor;
@@ -13050,50 +14291,93 @@
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1
};
+ function xRange(mouseX) {
+ var vm = this._view;
+ return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
+ }
+
+ function yRange(mouseY) {
+ var vm = this._view;
+ return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
+ }
+
Chart.elements.Point = Chart.Element.extend({
inRange: function(mouseX, mouseY) {
var vm = this._view;
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
},
- inLabelRange: function(mouseX) {
+
+ inLabelRange: xRange,
+ inXRange: xRange,
+ inYRange: yRange,
+
+ getCenterPoint: function() {
var vm = this._view;
- return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
+ return {
+ x: vm.x,
+ y: vm.y
+ };
},
+ getArea: function() {
+ return Math.PI * Math.pow(this._view.radius, 2);
+ },
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
y: vm.y,
padding: vm.radius + vm.borderWidth
};
},
- draw: function() {
+ draw: function(chartArea) {
var vm = this._view;
+ var model = this._model;
var ctx = this._chart.ctx;
var pointStyle = vm.pointStyle;
var radius = vm.radius;
var x = vm.x;
var y = vm.y;
+ var color = Chart.helpers.color;
+ var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
+ var ratio = 0;
if (vm.skip) {
return;
}
ctx.strokeStyle = vm.borderColor || defaultColor;
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
ctx.fillStyle = vm.backgroundColor || defaultColor;
+ // Cliping for Points.
+ // going out from inner charArea?
+ if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right*errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom*errMargin < model.y))) {
+ // Point fade out
+ if (model.x < chartArea.left) {
+ ratio = (x - model.x) / (chartArea.left - model.x);
+ } else if (chartArea.right*errMargin < model.x) {
+ ratio = (model.x - x) / (model.x - chartArea.right);
+ } else if (model.y < chartArea.top) {
+ ratio = (y - model.y) / (chartArea.top - model.y);
+ } else if (chartArea.bottom*errMargin < model.y) {
+ ratio = (model.y - y) / (model.y - chartArea.bottom);
+ }
+ ratio = Math.round(ratio*100) / 100;
+ ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
+ ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
+ }
+
Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
}
});
};
-},{}],38:[function(require,module,exports){
-"use strict";
+},{}],40:[function(require,module,exports){
+'use strict';
module.exports = function(Chart) {
var globalOpts = Chart.defaults.global;
@@ -13102,79 +14386,194 @@
borderWidth: 0,
borderColor: globalOpts.defaultColor,
borderSkipped: 'bottom'
};
+ function isVertical(bar) {
+ return bar._view.width !== undefined;
+ }
+
+ /**
+ * Helper function to get the bounds of the bar regardless of the orientation
+ * @private
+ * @param bar {Chart.Element.Rectangle} the bar
+ * @return {Bounds} bounds of the bar
+ */
+ function getBarBounds(bar) {
+ var vm = bar._view;
+ var x1, x2, y1, y2;
+
+ if (isVertical(bar)) {
+ // vertical
+ var halfWidth = vm.width / 2;
+ x1 = vm.x - halfWidth;
+ x2 = vm.x + halfWidth;
+ y1 = Math.min(vm.y, vm.base);
+ y2 = Math.max(vm.y, vm.base);
+ } else {
+ // horizontal bar
+ var halfHeight = vm.height / 2;
+ x1 = Math.min(vm.x, vm.base);
+ x2 = Math.max(vm.x, vm.base);
+ y1 = vm.y - halfHeight;
+ y2 = vm.y + halfHeight;
+ }
+
+ return {
+ left: x1,
+ top: y1,
+ right: x2,
+ bottom: y2
+ };
+ }
+
Chart.elements.Rectangle = Chart.Element.extend({
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
+ var left, right, top, bottom, signX, signY, borderSkipped;
+ var borderWidth = vm.borderWidth;
- var halfWidth = vm.width / 2,
- leftX = vm.x - halfWidth,
- rightX = vm.x + halfWidth,
- top = vm.base - (vm.base - vm.y),
- halfStroke = vm.borderWidth / 2;
+ if (!vm.horizontal) {
+ // bar
+ left = vm.x - vm.width / 2;
+ right = vm.x + vm.width / 2;
+ top = vm.y;
+ bottom = vm.base;
+ signX = 1;
+ signY = bottom > top? 1: -1;
+ borderSkipped = vm.borderSkipped || 'bottom';
+ } else {
+ // horizontal bar
+ left = vm.base;
+ right = vm.x;
+ top = vm.y - vm.height / 2;
+ bottom = vm.y + vm.height / 2;
+ signX = right > left? 1: -1;
+ signY = 1;
+ borderSkipped = vm.borderSkipped || 'left';
+ }
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
- if (vm.borderWidth) {
- leftX += halfStroke;
- rightX -= halfStroke;
- top += halfStroke;
+ if (borderWidth) {
+ // borderWidth shold be less than bar width and bar height.
+ var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
+ borderWidth = borderWidth > barSize? barSize: borderWidth;
+ var halfStroke = borderWidth / 2;
+ // Adjust borderWidth when bar top position is near vm.base(zero).
+ var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0);
+ var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0);
+ var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0);
+ var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0);
+ // not become a vertical line?
+ if (borderLeft !== borderRight) {
+ top = borderTop;
+ bottom = borderBottom;
+ }
+ // not become a horizontal line?
+ if (borderTop !== borderBottom) {
+ left = borderLeft;
+ right = borderRight;
+ }
}
ctx.beginPath();
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
- ctx.lineWidth = vm.borderWidth;
+ ctx.lineWidth = borderWidth;
// Corner points, from bottom-left to bottom-right clockwise
// | 1 2 |
// | 0 3 |
var corners = [
- [leftX, vm.base],
- [leftX, top],
- [rightX, top],
- [rightX, vm.base]
+ [left, bottom],
+ [left, top],
+ [right, top],
+ [right, bottom]
];
// Find first (starting) corner with fallback to 'bottom'
var borders = ['bottom', 'left', 'top', 'right'];
- var startCorner = borders.indexOf(vm.borderSkipped, 0);
- if (startCorner === -1)
+ var startCorner = borders.indexOf(borderSkipped, 0);
+ if (startCorner === -1) {
startCorner = 0;
+ }
function cornerAt(index) {
return corners[(startCorner + index) % 4];
}
// Draw rectangle from 'startCorner'
- ctx.moveTo.apply(ctx, cornerAt(0));
- for (var i = 1; i < 4; i++)
- ctx.lineTo.apply(ctx, cornerAt(i));
+ var corner = cornerAt(0);
+ ctx.moveTo(corner[0], corner[1]);
+ for (var i = 1; i < 4; i++) {
+ corner = cornerAt(i);
+ ctx.lineTo(corner[0], corner[1]);
+ }
+
ctx.fill();
- if (vm.borderWidth) {
+ if (borderWidth) {
ctx.stroke();
}
},
height: function() {
var vm = this._view;
return vm.base - vm.y;
},
inRange: function(mouseX, mouseY) {
+ var inRange = false;
+
+ if (this._view) {
+ var bounds = getBarBounds(this);
+ inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
+ }
+
+ return inRange;
+ },
+ inLabelRange: function(mouseX, mouseY) {
+ var me = this;
+ if (!me._view) {
+ return false;
+ }
+
+ var inRange = false;
+ var bounds = getBarBounds(me);
+
+ if (isVertical(me)) {
+ inRange = mouseX >= bounds.left && mouseX <= bounds.right;
+ } else {
+ inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
+ }
+
+ return inRange;
+ },
+ inXRange: function(mouseX) {
+ var bounds = getBarBounds(this);
+ return mouseX >= bounds.left && mouseX <= bounds.right;
+ },
+ inYRange: function(mouseY) {
+ var bounds = getBarBounds(this);
+ return mouseY >= bounds.top && mouseY <= bounds.bottom;
+ },
+ getCenterPoint: function() {
var vm = this._view;
- return vm ?
- (vm.y < vm.base ?
- (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base) :
- (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y)) :
- false;
+ var x, y;
+ if (isVertical(this)) {
+ x = vm.x;
+ y = (vm.y + vm.base) / 2;
+ } else {
+ x = (vm.x + vm.base) / 2;
+ y = vm.y;
+ }
+
+ return {x: x, y: y};
},
- inLabelRange: function(mouseX) {
+ getArea: function() {
var vm = this._view;
- return vm ? (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) : false;
+ return vm.width * Math.abs(vm.y - vm.base);
},
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
@@ -13182,24 +14581,375 @@
};
}
});
};
-},{}],39:[function(require,module,exports){
-"use strict";
+},{}],41:[function(require,module,exports){
+'use strict';
+
+// Chart.Platform implementation for targeting a web browser
module.exports = function(Chart) {
+ var helpers = Chart.helpers;
+ // DOM event types -> Chart.js event types.
+ // Note: only events with different types are mapped.
+ // https://developer.mozilla.org/en-US/docs/Web/Events
+ var eventTypeMap = {
+ // Touch events
+ touchstart: 'mousedown',
+ touchmove: 'mousemove',
+ touchend: 'mouseup',
+
+ // Pointer events
+ pointerenter: 'mouseenter',
+ pointerdown: 'mousedown',
+ pointermove: 'mousemove',
+ pointerup: 'mouseup',
+ pointerleave: 'mouseout',
+ pointerout: 'mouseout'
+ };
+
+ /**
+ * The "used" size is the final value of a dimension property after all calculations have
+ * been performed. This method uses the computed style of `element` but returns undefined
+ * if the computed style is not expressed in pixels. That can happen in some cases where
+ * `element` has a size relative to its parent and this last one is not yet displayed,
+ * for example because of `display: none` on a parent node.
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
+ * @returns {Number} Size in pixels or undefined if unknown.
+ */
+ function readUsedSize(element, property) {
+ var value = helpers.getStyle(element, property);
+ var matches = value && value.match(/(\d+)px/);
+ return matches? Number(matches[1]) : undefined;
+ }
+
+ /**
+ * Initializes the canvas style and render size without modifying the canvas display size,
+ * since responsiveness is handled by the controller.resize() method. The config is used
+ * to determine the aspect ratio to apply in case no explicit height has been specified.
+ */
+ function initCanvas(canvas, config) {
+ var style = canvas.style;
+
+ // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
+ // returns null or '' if no explicit value has been set to the canvas attribute.
+ var renderHeight = canvas.getAttribute('height');
+ var renderWidth = canvas.getAttribute('width');
+
+ // Chart.js modifies some canvas values that we want to restore on destroy
+ canvas._chartjs = {
+ initial: {
+ height: renderHeight,
+ width: renderWidth,
+ style: {
+ display: style.display,
+ height: style.height,
+ width: style.width
+ }
+ }
+ };
+
+ // Force canvas to display as block to avoid extra space caused by inline
+ // elements, which would interfere with the responsive resize process.
+ // https://github.com/chartjs/Chart.js/issues/2538
+ style.display = style.display || 'block';
+
+ if (renderWidth === null || renderWidth === '') {
+ var displayWidth = readUsedSize(canvas, 'width');
+ if (displayWidth !== undefined) {
+ canvas.width = displayWidth;
+ }
+ }
+
+ if (renderHeight === null || renderHeight === '') {
+ if (canvas.style.height === '') {
+ // If no explicit render height and style height, let's apply the aspect ratio,
+ // which one can be specified by the user but also by charts as default option
+ // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
+ canvas.height = canvas.width / (config.options.aspectRatio || 2);
+ } else {
+ var displayHeight = readUsedSize(canvas, 'height');
+ if (displayWidth !== undefined) {
+ canvas.height = displayHeight;
+ }
+ }
+ }
+
+ return canvas;
+ }
+
+ function createEvent(type, chart, x, y, native) {
+ return {
+ type: type,
+ chart: chart,
+ native: native || null,
+ x: x !== undefined? x : null,
+ y: y !== undefined? y : null,
+ };
+ }
+
+ function fromNativeEvent(event, chart) {
+ var type = eventTypeMap[event.type] || event.type;
+ var pos = helpers.getRelativePosition(event, chart);
+ return createEvent(type, chart, pos.x, pos.y, event);
+ }
+
+ function createResizer(handler) {
+ var iframe = document.createElement('iframe');
+ iframe.className = 'chartjs-hidden-iframe';
+ iframe.style.cssText =
+ 'display:block;'+
+ 'overflow:hidden;'+
+ 'border:0;'+
+ 'margin:0;'+
+ 'top:0;'+
+ 'left:0;'+
+ 'bottom:0;'+
+ 'right:0;'+
+ 'height:100%;'+
+ 'width:100%;'+
+ 'position:absolute;'+
+ 'pointer-events:none;'+
+ 'z-index:-1;';
+
+ // Prevent the iframe to gain focus on tab.
+ // https://github.com/chartjs/Chart.js/issues/3090
+ iframe.tabIndex = -1;
+
+ // If the iframe is re-attached to the DOM, the resize listener is removed because the
+ // content is reloaded, so make sure to install the handler after the iframe is loaded.
+ // https://github.com/chartjs/Chart.js/issues/3521
+ helpers.addEvent(iframe, 'load', function() {
+ helpers.addEvent(iframe.contentWindow || iframe, 'resize', handler);
+
+ // The iframe size might have changed while loading, which can also
+ // happen if the size has been changed while detached from the DOM.
+ handler();
+ });
+
+ return iframe;
+ }
+
+ function addResizeListener(node, listener, chart) {
+ var stub = node._chartjs = {
+ ticking: false
+ };
+
+ // Throttle the callback notification until the next animation frame.
+ var notify = function() {
+ if (!stub.ticking) {
+ stub.ticking = true;
+ helpers.requestAnimFrame.call(window, function() {
+ if (stub.resizer) {
+ stub.ticking = false;
+ return listener(createEvent('resize', chart));
+ }
+ });
+ }
+ };
+
+ // Let's keep track of this added iframe and thus avoid DOM query when removing it.
+ stub.resizer = createResizer(notify);
+
+ node.insertBefore(stub.resizer, node.firstChild);
+ }
+
+ function removeResizeListener(node) {
+ if (!node || !node._chartjs) {
+ return;
+ }
+
+ var resizer = node._chartjs.resizer;
+ if (resizer) {
+ resizer.parentNode.removeChild(resizer);
+ node._chartjs.resizer = null;
+ }
+
+ delete node._chartjs;
+ }
+
+ return {
+ acquireContext: function(item, config) {
+ if (typeof item === 'string') {
+ item = document.getElementById(item);
+ } else if (item.length) {
+ // Support for array based queries (such as jQuery)
+ item = item[0];
+ }
+
+ if (item && item.canvas) {
+ // Support for any object associated to a canvas (including a context2d)
+ item = item.canvas;
+ }
+
+ if (item instanceof HTMLCanvasElement) {
+ // To prevent canvas fingerprinting, some add-ons undefine the getContext
+ // method, for example: https://github.com/kkapsner/CanvasBlocker
+ // https://github.com/chartjs/Chart.js/issues/2807
+ var context = item.getContext && item.getContext('2d');
+ if (context instanceof CanvasRenderingContext2D) {
+ initCanvas(item, config);
+ return context;
+ }
+ }
+
+ return null;
+ },
+
+ releaseContext: function(context) {
+ var canvas = context.canvas;
+ if (!canvas._chartjs) {
+ return;
+ }
+
+ var initial = canvas._chartjs.initial;
+ ['height', 'width'].forEach(function(prop) {
+ var value = initial[prop];
+ if (value === undefined || value === null) {
+ canvas.removeAttribute(prop);
+ } else {
+ canvas.setAttribute(prop, value);
+ }
+ });
+
+ helpers.each(initial.style || {}, function(value, key) {
+ canvas.style[key] = value;
+ });
+
+ // The canvas render size might have been changed (and thus the state stack discarded),
+ // we can't use save() and restore() to restore the initial state. So make sure that at
+ // least the canvas context is reset to the default state by setting the canvas width.
+ // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
+ canvas.width = canvas.width;
+
+ delete canvas._chartjs;
+ },
+
+ addEventListener: function(chart, type, listener) {
+ var canvas = chart.chart.canvas;
+ if (type === 'resize') {
+ // Note: the resize event is not supported on all browsers.
+ addResizeListener(canvas.parentNode, listener, chart.chart);
+ return;
+ }
+
+ var stub = listener._chartjs || (listener._chartjs = {});
+ var proxies = stub.proxies || (stub.proxies = {});
+ var proxy = proxies[chart.id + '_' + type] = function(event) {
+ listener(fromNativeEvent(event, chart.chart));
+ };
+
+ helpers.addEvent(canvas, type, proxy);
+ },
+
+ removeEventListener: function(chart, type, listener) {
+ var canvas = chart.chart.canvas;
+ if (type === 'resize') {
+ // Note: the resize event is not supported on all browsers.
+ removeResizeListener(canvas.parentNode, listener);
+ return;
+ }
+
+ var stub = listener._chartjs || {};
+ var proxies = stub.proxies || {};
+ var proxy = proxies[chart.id + '_' + type];
+ if (!proxy) {
+ return;
+ }
+
+ helpers.removeEvent(canvas, type, proxy);
+ }
+ };
+};
+
+},{}],42:[function(require,module,exports){
+'use strict';
+
+// By default, select the browser (DOM) platform.
+// @TODO Make possible to select another platform at build time.
+var implementation = require(41);
+
+module.exports = function(Chart) {
+ /**
+ * @namespace Chart.platform
+ * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
+ * @since 2.4.0
+ */
+ Chart.platform = {
+ /**
+ * Called at chart construction time, returns a context2d instance implementing
+ * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
+ * @param {*} item - The native item from which to acquire context (platform specific)
+ * @param {Object} options - The chart options
+ * @returns {CanvasRenderingContext2D} context2d instance
+ */
+ acquireContext: function() {},
+
+ /**
+ * Called at chart destruction time, releases any resources associated to the context
+ * previously returned by the acquireContext() method.
+ * @param {CanvasRenderingContext2D} context - The context2d instance
+ * @returns {Boolean} true if the method succeeded, else false
+ */
+ releaseContext: function() {},
+
+ /**
+ * Registers the specified listener on the given chart.
+ * @param {Chart} chart - Chart from which to listen for event
+ * @param {String} type - The ({@link IEvent}) type to listen for
+ * @param {Function} listener - Receives a notification (an object that implements
+ * the {@link IEvent} interface) when an event of the specified type occurs.
+ */
+ addEventListener: function() {},
+
+ /**
+ * Removes the specified listener previously registered with addEventListener.
+ * @param {Chart} chart -Chart from which to remove the listener
+ * @param {String} type - The ({@link IEvent}) type to remove
+ * @param {Function} listener - The listener function to remove from the event target.
+ */
+ removeEventListener: function() {}
+ };
+
+ /**
+ * @interface IPlatform
+ * Allows abstracting platform dependencies away from the chart
+ * @borrows Chart.platform.acquireContext as acquireContext
+ * @borrows Chart.platform.releaseContext as releaseContext
+ * @borrows Chart.platform.addEventListener as addEventListener
+ * @borrows Chart.platform.removeEventListener as removeEventListener
+ */
+
+ /**
+ * @interface IEvent
+ * @prop {String} type - The event type name, possible values are:
+ * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
+ * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
+ * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
+ * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
+ * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
+ */
+
+ Chart.helpers.extend(Chart.platform, implementation(Chart));
+};
+
+},{"41":41}],43:[function(require,module,exports){
+'use strict';
+
+module.exports = function(Chart) {
+
var helpers = Chart.helpers;
// Default config for a category scale
var defaultConfig = {
- position: "bottom"
+ position: 'bottom'
};
var DatasetScale = Chart.Scale.extend({
/**
- * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use tose
+ * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
* else fall back to data.labels
* @private
*/
getLabels: function() {
var data = this.chart.data;
@@ -13234,12 +14984,19 @@
var labels = me.getLabels();
// If we are viewing some subset of labels, slice the original array
me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
},
- getLabelForIndex: function(index) {
- return this.ticks[index];
+ getLabelForIndex: function(index, datasetIndex) {
+ var me = this;
+ var data = me.chart.data;
+ var isHorizontal = me.isHorizontal();
+
+ if (data.yLabels && !isHorizontal) {
+ return me.getRightValue(data.datasets[datasetIndex].data[index]);
+ }
+ return me.ticks[index - me.minIndex];
},
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
var me = this;
@@ -13251,48 +15008,43 @@
var idx = labels.indexOf(value);
index = idx !== -1 ? idx : index;
}
if (me.isHorizontal()) {
- var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
- var valueWidth = innerWidth / offsetAmt;
- var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft;
+ var valueWidth = me.width / offsetAmt;
+ var widthOffset = (valueWidth * (index - me.minIndex));
- if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) {
+ if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) {
widthOffset += (valueWidth / 2);
- }
+ }
return me.left + Math.round(widthOffset);
- } else {
- var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
- var valueHeight = innerHeight / offsetAmt;
- var heightOffset = (valueHeight * (index - me.minIndex)) + me.paddingTop;
+ }
+ var valueHeight = me.height / offsetAmt;
+ var heightOffset = (valueHeight * (index - me.minIndex));
- if (me.options.gridLines.offsetGridLines && includeOffset) {
- heightOffset += (valueHeight / 2);
- }
-
- return me.top + Math.round(heightOffset);
+ if (me.options.gridLines.offsetGridLines && includeOffset) {
+ heightOffset += (valueHeight / 2);
}
+
+ return me.top + Math.round(heightOffset);
},
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
},
getValueForPixel: function(pixel) {
var me = this;
var value;
var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
var horz = me.isHorizontal();
- var innerDimension = horz ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
- var valueDimension = innerDimension / offsetAmt;
+ var valueDimension = (horz ? me.width : me.height) / offsetAmt;
pixel -= horz ? me.left : me.top;
if (me.options.gridLines.offsetGridLines) {
pixel -= (valueDimension / 2);
}
- pixel -= horz ? me.paddingLeft : me.paddingTop;
if (pixel <= 0) {
value = 0;
} else {
value = Math.round(pixel / valueDimension);
@@ -13303,48 +15055,25 @@
getBasePixel: function() {
return this.bottom;
}
});
- Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
+ Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);
};
-},{}],40:[function(require,module,exports){
-"use strict";
+},{}],44:[function(require,module,exports){
+'use strict';
+
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
- position: "left",
+ position: 'left',
ticks: {
- callback: function(tickValue, index, ticks) {
- // If we have lots of ticks, don't use the ones
- var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
-
- // If we have a number like 2.5 as the delta, figure out how many decimal places we need
- if (Math.abs(delta) > 1) {
- if (tickValue !== Math.floor(tickValue)) {
- // not an integer
- delta = tickValue - Math.floor(tickValue);
- }
- }
-
- var logDelta = helpers.log10(Math.abs(delta));
- var tickString = '';
-
- if (tickValue !== 0) {
- var numDecimal = -1 * Math.floor(logDelta);
- numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
- tickString = tickValue.toFixed(numDecimal);
- } else {
- tickString = '0'; // never show decimal places for 0
- }
-
- return tickString;
- }
+ callback: Chart.Ticks.formatters.linear
}
};
var LinearScale = Chart.LinearScaleBase.extend({
determineDataLimits: function() {
@@ -13361,27 +15090,47 @@
// First Calculate the range
me.min = null;
me.max = null;
- if (opts.stacked) {
- var valuesPerType = {};
- var hasPositiveValues = false;
- var hasNegativeValues = false;
+ var hasStacks = opts.stacked;
+ if (hasStacks === undefined) {
+ helpers.each(datasets, function(dataset, datasetIndex) {
+ if (hasStacks) {
+ return;
+ }
+ var meta = chart.getDatasetMeta(datasetIndex);
+ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
+ meta.stack !== undefined) {
+ hasStacks = true;
+ }
+ });
+ }
+
+ if (opts.stacked || hasStacks) {
+ var valuesPerStack = {};
+
helpers.each(datasets, function(dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
- if (valuesPerType[meta.type] === undefined) {
- valuesPerType[meta.type] = {
+ var key = [
+ meta.type,
+ // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
+ ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
+ meta.stack
+ ].join('.');
+
+ if (valuesPerStack[key] === undefined) {
+ valuesPerStack[key] = {
positiveValues: [],
negativeValues: []
};
}
// Store these per type
- var positiveValues = valuesPerType[meta.type].positiveValues;
- var negativeValues = valuesPerType[meta.type].negativeValues;
+ var positiveValues = valuesPerStack[key].positiveValues;
+ var negativeValues = valuesPerStack[key].negativeValues;
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
helpers.each(dataset.data, function(rawValue, index) {
var value = +me.getRightValue(rawValue);
if (isNaN(value) || meta.data[index].hidden) {
@@ -13391,24 +15140,20 @@
positiveValues[index] = positiveValues[index] || 0;
negativeValues[index] = negativeValues[index] || 0;
if (opts.relativePoints) {
positiveValues[index] = 100;
+ } else if (value < 0) {
+ negativeValues[index] += value;
} else {
- if (value < 0) {
- hasNegativeValues = true;
- negativeValues[index] += value;
- } else {
- hasPositiveValues = true;
- positiveValues[index] += value;
- }
+ positiveValues[index] += value;
}
});
}
});
- helpers.each(valuesPerType, function(valuesForType) {
+ helpers.each(valuesPerStack, function(valuesForType) {
var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
var minVal = helpers.min(values);
var maxVal = helpers.max(values);
me.min = me.min === null ? minVal : Math.min(me.min, minVal);
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
@@ -13471,48 +15216,42 @@
// Utils
getPixelForValue: function(value) {
// This must be called after fit has been run so that
// this.left, this.top, this.right, and this.bottom have been defined
var me = this;
- var paddingLeft = me.paddingLeft;
- var paddingBottom = me.paddingBottom;
var start = me.start;
var rightValue = +me.getRightValue(value);
var pixel;
- var innerDimension;
var range = me.end - start;
if (me.isHorizontal()) {
- innerDimension = me.width - (paddingLeft + me.paddingRight);
- pixel = me.left + (innerDimension / range * (rightValue - start));
- return Math.round(pixel + paddingLeft);
- } else {
- innerDimension = me.height - (me.paddingTop + paddingBottom);
- pixel = (me.bottom - paddingBottom) - (innerDimension / range * (rightValue - start));
+ pixel = me.left + (me.width / range * (rightValue - start));
return Math.round(pixel);
}
+
+ pixel = me.bottom - (me.height / range * (rightValue - start));
+ return Math.round(pixel);
},
getValueForPixel: function(pixel) {
var me = this;
var isHorizontal = me.isHorizontal();
- var paddingLeft = me.paddingLeft;
- var paddingBottom = me.paddingBottom;
- var innerDimension = isHorizontal ? me.width - (paddingLeft + me.paddingRight) : me.height - (me.paddingTop + paddingBottom);
- var offset = (isHorizontal ? pixel - me.left - paddingLeft : me.bottom - paddingBottom - pixel) / innerDimension;
+ var innerDimension = isHorizontal ? me.width : me.height;
+ var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
return me.start + ((me.end - me.start) * offset);
},
getPixelForTick: function(index) {
return this.getPixelForValue(this.ticksAsNumbers[index]);
}
});
- Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
+ Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);
};
-},{}],41:[function(require,module,exports){
-"use strict";
+},{}],45:[function(require,module,exports){
+'use strict';
+
module.exports = function(Chart) {
var helpers = Chart.helpers,
noop = helpers.noop;
@@ -13531,11 +15270,11 @@
if (minSign < 0 && maxSign < 0) {
// move the top up to 0
me.max = 0;
} else if (minSign > 0 && maxSign > 0) {
- // move the botttom down to 0
+ // move the bottom down to 0
me.min = 0;
}
}
if (tickOpts.min !== undefined) {
@@ -13562,54 +15301,27 @@
handleDirectionalChanges: noop,
buildTicks: function() {
var me = this;
var opts = me.options;
- var ticks = me.ticks = [];
var tickOpts = opts.ticks;
- var getValueOrDefault = helpers.getValueOrDefault;
// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
- // the graph
-
+ // the graph. Make sure we always have at least 2 ticks
var maxTicks = me.getTickLimit();
-
- // Make sure we always have at least 2 ticks
maxTicks = Math.max(2, maxTicks);
- // To get a "nice" value for the tick spacing, we will use the appropriately named
- // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
- // for details.
+ var numericGeneratorOptions = {
+ maxTicks: maxTicks,
+ min: tickOpts.min,
+ max: tickOpts.max,
+ stepSize: helpers.getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
+ };
+ var ticks = me.ticks = Chart.Ticks.generators.linear(numericGeneratorOptions, me);
- var spacing;
- var fixedStepSizeSet = (tickOpts.fixedStepSize && tickOpts.fixedStepSize > 0) || (tickOpts.stepSize && tickOpts.stepSize > 0);
- if (fixedStepSizeSet) {
- spacing = getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize);
- } else {
- var niceRange = helpers.niceNum(me.max - me.min, false);
- spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
- }
- var niceMin = Math.floor(me.min / spacing) * spacing;
- var niceMax = Math.ceil(me.max / spacing) * spacing;
- var numSpaces = (niceMax - niceMin) / spacing;
-
- // If very close to our rounded value, use it.
- if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
- numSpaces = Math.round(numSpaces);
- } else {
- numSpaces = Math.ceil(numSpaces);
- }
-
- // Put the values into the ticks array
- ticks.push(tickOpts.min !== undefined ? tickOpts.min : niceMin);
- for (var j = 1; j < numSpaces; ++j) {
- ticks.push(niceMin + (j * spacing));
- }
- ticks.push(tickOpts.max !== undefined ? tickOpts.max : niceMax);
-
me.handleDirectionalChanges();
// At this point, we need to update our max and min given the tick values since we have expanded the
// range of the scale
me.max = helpers.max(ticks);
@@ -13632,33 +15344,24 @@
Chart.Scale.prototype.convertTicksToLabels.call(me);
}
});
};
-},{}],42:[function(require,module,exports){
-"use strict";
+},{}],46:[function(require,module,exports){
+'use strict';
+
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
- position: "left",
+ position: 'left',
// label settings
ticks: {
- callback: function(value, index, arr) {
- var remain = value / (Math.pow(10, Math.floor(helpers.log10(value))));
-
- if (value === 0){
- return '0';
- } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
- return value.toExponential();
- } else {
- return '';
- }
- }
+ callback: Chart.Ticks.formatters.logarithmic
}
};
var LogarithmicScale = Chart.Scale.extend({
determineDataLimits: function() {
@@ -13677,22 +15380,44 @@
// Calculate Range
me.min = null;
me.max = null;
me.minNotZero = null;
- if (opts.stacked) {
- var valuesPerType = {};
+ var hasStacks = opts.stacked;
+ if (hasStacks === undefined) {
+ helpers.each(datasets, function(dataset, datasetIndex) {
+ if (hasStacks) {
+ return;
+ }
+ var meta = chart.getDatasetMeta(datasetIndex);
+ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
+ meta.stack !== undefined) {
+ hasStacks = true;
+ }
+ });
+ }
+
+ if (opts.stacked || hasStacks) {
+ var valuesPerStack = {};
+
helpers.each(datasets, function(dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
+ var key = [
+ meta.type,
+ // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
+ ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
+ meta.stack
+ ].join('.');
+
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
- if (valuesPerType[meta.type] === undefined) {
- valuesPerType[meta.type] = [];
+ if (valuesPerStack[key] === undefined) {
+ valuesPerStack[key] = [];
}
helpers.each(dataset.data, function(rawValue, index) {
- var values = valuesPerType[meta.type];
+ var values = valuesPerStack[key];
var value = +me.getRightValue(rawValue);
if (isNaN(value) || meta.data[index].hidden) {
return;
}
@@ -13706,11 +15431,11 @@
}
});
}
});
- helpers.each(valuesPerType, function(valuesForType) {
+ helpers.each(valuesPerStack, function(valuesForType) {
var minVal = helpers.min(valuesForType);
var maxVal = helpers.max(valuesForType);
me.min = me.min === null ? minVal : Math.min(me.min, minVal);
me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
});
@@ -13735,11 +15460,11 @@
me.max = value;
} else if (value > me.max) {
me.max = value;
}
- if(value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
+ if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
me.minNotZero = value;
}
});
}
});
@@ -13760,48 +15485,17 @@
},
buildTicks: function() {
var me = this;
var opts = me.options;
var tickOpts = opts.ticks;
- var getValueOrDefault = helpers.getValueOrDefault;
- // Reset the ticks array. Later on, we will draw a grid line at these positions
- // The array simply contains the numerical value of the spots where ticks will be
- var ticks = me.ticks = [];
+ var generationOptions = {
+ min: tickOpts.min,
+ max: tickOpts.max
+ };
+ var ticks = me.ticks = Chart.Ticks.generators.logarithmic(generationOptions, me);
- // Figure out what the max number of ticks we can support it is based on the size of
- // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
- // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
- // the graph
-
- var tickVal = getValueOrDefault(tickOpts.min, Math.pow(10, Math.floor(helpers.log10(me.min))));
-
- while (tickVal < me.max) {
- ticks.push(tickVal);
-
- var exp;
- var significand;
-
- if(tickVal === 0){
- exp = Math.floor(helpers.log10(me.minNotZero));
- significand = Math.round(me.minNotZero / Math.pow(10, exp));
- } else {
- exp = Math.floor(helpers.log10(tickVal));
- significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
- }
-
- if (significand === 10) {
- significand = 1;
- ++exp;
- }
-
- tickVal = significand * Math.pow(10, exp);
- }
-
- var lastTick = getValueOrDefault(tickOpts.max, tickVal);
- ticks.push(lastTick);
-
if (!me.isHorizontal()) {
// We are in a vertical orientation. The top value is the highest. So reverse the array
ticks.reverse();
}
@@ -13838,124 +15532,379 @@
var pixel;
var start = me.start;
var newVal = +me.getRightValue(value);
var range;
- var paddingTop = me.paddingTop;
- var paddingBottom = me.paddingBottom;
- var paddingLeft = me.paddingLeft;
var opts = me.options;
var tickOpts = opts.ticks;
if (me.isHorizontal()) {
range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
if (newVal === 0) {
- pixel = me.left + paddingLeft;
+ pixel = me.left;
} else {
- innerDimension = me.width - (paddingLeft + me.paddingRight);
+ innerDimension = me.width;
pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
- pixel += paddingLeft;
}
} else {
- // Bottom - top since pixels increase downard on a screen
- innerDimension = me.height - (paddingTop + paddingBottom);
- if(start === 0 && !tickOpts.reverse){
+ // Bottom - top since pixels increase downward on a screen
+ innerDimension = me.height;
+ if (start === 0 && !tickOpts.reverse) {
range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
if (newVal === start) {
- pixel = me.bottom - paddingBottom;
- } else if(newVal === me.minNotZero){
- pixel = me.bottom - paddingBottom - innerDimension * 0.02;
+ pixel = me.bottom;
+ } else if (newVal === me.minNotZero) {
+ pixel = me.bottom - innerDimension * 0.02;
} else {
- pixel = me.bottom - paddingBottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
+ pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
}
- } else if (me.end === 0 && tickOpts.reverse){
+ } else if (me.end === 0 && tickOpts.reverse) {
range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
if (newVal === me.end) {
- pixel = me.top + paddingTop;
- } else if(newVal === me.minNotZero){
- pixel = me.top + paddingTop + innerDimension * 0.02;
+ pixel = me.top;
+ } else if (newVal === me.minNotZero) {
+ pixel = me.top + innerDimension * 0.02;
} else {
- pixel = me.top + paddingTop + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
+ pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
}
} else {
range = helpers.log10(me.end) - helpers.log10(start);
- innerDimension = me.height - (paddingTop + paddingBottom);
- pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
- }
+ innerDimension = me.height;
+ pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
+ }
}
return pixel;
},
getValueForPixel: function(pixel) {
var me = this;
var range = helpers.log10(me.end) - helpers.log10(me.start);
var value, innerDimension;
if (me.isHorizontal()) {
- innerDimension = me.width - (me.paddingLeft + me.paddingRight);
- value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension);
+ innerDimension = me.width;
+ value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
} else { // todo: if start === 0
- innerDimension = me.height - (me.paddingTop + me.paddingBottom);
- value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start;
+ innerDimension = me.height;
+ value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
}
return value;
}
});
- Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig);
+ Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
};
-},{}],43:[function(require,module,exports){
-"use strict";
+},{}],47:[function(require,module,exports){
+'use strict';
+
module.exports = function(Chart) {
var helpers = Chart.helpers;
var globalDefaults = Chart.defaults.global;
var defaultConfig = {
display: true,
- //Boolean - Whether to animate scaling the chart from the centre
+ // Boolean - Whether to animate scaling the chart from the centre
animate: true,
lineArc: false,
- position: "chartArea",
+ position: 'chartArea',
angleLines: {
display: true,
- color: "rgba(0, 0, 0, 0.1)",
+ color: 'rgba(0, 0, 0, 0.1)',
lineWidth: 1
},
// label settings
ticks: {
- //Boolean - Show a backdrop to the scale label
+ // Boolean - Show a backdrop to the scale label
showLabelBackdrop: true,
- //String - The colour of the label backdrop
- backdropColor: "rgba(255,255,255,0.75)",
+ // String - The colour of the label backdrop
+ backdropColor: 'rgba(255,255,255,0.75)',
- //Number - The backdrop padding above & below the label in pixels
+ // Number - The backdrop padding above & below the label in pixels
backdropPaddingY: 2,
- //Number - The backdrop padding to the side of the label in pixels
- backdropPaddingX: 2
+ // Number - The backdrop padding to the side of the label in pixels
+ backdropPaddingX: 2,
+
+ callback: Chart.Ticks.formatters.linear
},
pointLabels: {
- //Number - Point label font size in pixels
+ // Number - Point label font size in pixels
fontSize: 10,
- //Function - Used to convert point labels
+ // Function - Used to convert point labels
callback: function(label) {
return label;
}
}
};
+ function getValueCount(scale) {
+ return !scale.options.lineArc ? scale.chart.data.labels.length : 0;
+ }
+
+ function getPointLabelFontOptions(scale) {
+ var pointLabelOptions = scale.options.pointLabels;
+ var fontSize = helpers.getValueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
+ var fontStyle = helpers.getValueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
+ var fontFamily = helpers.getValueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
+ var font = helpers.fontString(fontSize, fontStyle, fontFamily);
+
+ return {
+ size: fontSize,
+ style: fontStyle,
+ family: fontFamily,
+ font: font
+ };
+ }
+
+ function measureLabelSize(ctx, fontSize, label) {
+ if (helpers.isArray(label)) {
+ return {
+ w: helpers.longestText(ctx, ctx.font, label),
+ h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
+ };
+ }
+
+ return {
+ w: ctx.measureText(label).width,
+ h: fontSize
+ };
+ }
+
+ function determineLimits(angle, pos, size, min, max) {
+ if (angle === min || angle === max) {
+ return {
+ start: pos - (size / 2),
+ end: pos + (size / 2)
+ };
+ } else if (angle < min || angle > max) {
+ return {
+ start: pos - size - 5,
+ end: pos
+ };
+ }
+
+ return {
+ start: pos,
+ end: pos + size + 5
+ };
+ }
+
+ /**
+ * Helper function to fit a radial linear scale with point labels
+ */
+ function fitWithPointLabels(scale) {
+ /*
+ * Right, this is really confusing and there is a lot of maths going on here
+ * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
+ *
+ * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
+ *
+ * Solution:
+ *
+ * We assume the radius of the polygon is half the size of the canvas at first
+ * at each index we check if the text overlaps.
+ *
+ * Where it does, we store that angle and that index.
+ *
+ * After finding the largest index and angle we calculate how much we need to remove
+ * from the shape radius to move the point inwards by that x.
+ *
+ * We average the left and right distances to get the maximum shape radius that can fit in the box
+ * along with labels.
+ *
+ * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
+ * on each side, removing that from the size, halving it and adding the left x protrusion width.
+ *
+ * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
+ * and position it in the most space efficient manner
+ *
+ * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
+ */
+
+ var plFont = getPointLabelFontOptions(scale);
+
+ // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
+ // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
+ var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
+ var furthestLimits = {
+ l: scale.width,
+ r: 0,
+ t: scale.height,
+ b: 0
+ };
+ var furthestAngles = {};
+ var i;
+ var textSize;
+ var pointPosition;
+
+ scale.ctx.font = plFont.font;
+ scale._pointLabelSizes = [];
+
+ var valueCount = getValueCount(scale);
+ for (i = 0; i < valueCount; i++) {
+ pointPosition = scale.getPointPosition(i, largestPossibleRadius);
+ textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
+ scale._pointLabelSizes[i] = textSize;
+
+ // Add quarter circle to make degree 0 mean top of circle
+ var angleRadians = scale.getIndexAngle(i);
+ var angle = helpers.toDegrees(angleRadians) % 360;
+ var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
+ var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
+
+ if (hLimits.start < furthestLimits.l) {
+ furthestLimits.l = hLimits.start;
+ furthestAngles.l = angleRadians;
+ }
+
+ if (hLimits.end > furthestLimits.r) {
+ furthestLimits.r = hLimits.end;
+ furthestAngles.r = angleRadians;
+ }
+
+ if (vLimits.start < furthestLimits.t) {
+ furthestLimits.t = vLimits.start;
+ furthestAngles.t = angleRadians;
+ }
+
+ if (vLimits.end > furthestLimits.b) {
+ furthestLimits.b = vLimits.end;
+ furthestAngles.b = angleRadians;
+ }
+ }
+
+ scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
+ }
+
+ /**
+ * Helper function to fit a radial linear scale with no point labels
+ */
+ function fit(scale) {
+ var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
+ scale.drawingArea = Math.round(largestPossibleRadius);
+ scale.setCenterPoint(0, 0, 0, 0);
+ }
+
+ function getTextAlignForAngle(angle) {
+ if (angle === 0 || angle === 180) {
+ return 'center';
+ } else if (angle < 180) {
+ return 'left';
+ }
+
+ return 'right';
+ }
+
+ function fillText(ctx, text, position, fontSize) {
+ if (helpers.isArray(text)) {
+ var y = position.y;
+ var spacing = 1.5 * fontSize;
+
+ for (var i = 0; i < text.length; ++i) {
+ ctx.fillText(text[i], position.x, y);
+ y+= spacing;
+ }
+ } else {
+ ctx.fillText(text, position.x, position.y);
+ }
+ }
+
+ function adjustPointPositionForLabelHeight(angle, textSize, position) {
+ if (angle === 90 || angle === 270) {
+ position.y -= (textSize.h / 2);
+ } else if (angle > 270 || angle < 90) {
+ position.y -= textSize.h;
+ }
+ }
+
+ function drawPointLabels(scale) {
+ var ctx = scale.ctx;
+ var getValueOrDefault = helpers.getValueOrDefault;
+ var opts = scale.options;
+ var angleLineOpts = opts.angleLines;
+ var pointLabelOpts = opts.pointLabels;
+
+ ctx.lineWidth = angleLineOpts.lineWidth;
+ ctx.strokeStyle = angleLineOpts.color;
+
+ var outerDistance = scale.getDistanceFromCenterForValue(opts.reverse ? scale.min : scale.max);
+
+ // Point Label Font
+ var plFont = getPointLabelFontOptions(scale);
+
+ ctx.textBaseline = 'top';
+
+ for (var i = getValueCount(scale) - 1; i >= 0; i--) {
+ if (angleLineOpts.display) {
+ var outerPosition = scale.getPointPosition(i, outerDistance);
+ ctx.beginPath();
+ ctx.moveTo(scale.xCenter, scale.yCenter);
+ ctx.lineTo(outerPosition.x, outerPosition.y);
+ ctx.stroke();
+ ctx.closePath();
+ }
+ // Extra 3px out for some label spacing
+ var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
+
+ // Keep this in loop since we may support array properties here
+ var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
+ ctx.font = plFont.font;
+ ctx.fillStyle = pointLabelFontColor;
+
+ var angleRadians = scale.getIndexAngle(i);
+ var angle = helpers.toDegrees(angleRadians);
+ ctx.textAlign = getTextAlignForAngle(angle);
+ adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
+ fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
+ }
+ }
+
+ function drawRadiusLine(scale, gridLineOpts, radius, index) {
+ var ctx = scale.ctx;
+ ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
+ ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
+
+ if (scale.options.lineArc) {
+ // Draw circular arcs between the points
+ ctx.beginPath();
+ ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
+ ctx.closePath();
+ ctx.stroke();
+ } else {
+ // Draw straight lines connecting each index
+ var valueCount = getValueCount(scale);
+
+ if (valueCount === 0) {
+ return;
+ }
+
+ ctx.beginPath();
+ var pointPosition = scale.getPointPosition(0, radius);
+ ctx.moveTo(pointPosition.x, pointPosition.y);
+
+ for (var i = 1; i < valueCount; i++) {
+ pointPosition = scale.getPointPosition(i, radius);
+ ctx.lineTo(pointPosition.x, pointPosition.y);
+ }
+
+ ctx.closePath();
+ ctx.stroke();
+ }
+ }
+
+ function numberOrZero(param) {
+ return helpers.isNumber(param) ? param : 0;
+ }
+
var LinearRadialScale = Chart.LinearScaleBase.extend({
- getValueCount: function() {
- return this.chart.data.labels.length;
- },
setDimensions: function() {
var me = this;
var opts = me.options;
var tickOpts = opts.ticks;
// Set the unconstrained dimension before label rotation
@@ -13969,39 +15918,32 @@
me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
},
determineDataLimits: function() {
var me = this;
var chart = me.chart;
- me.min = null;
- me.max = null;
+ var min = Number.POSITIVE_INFINITY;
+ var max = Number.NEGATIVE_INFINITY;
-
helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
if (chart.isDatasetVisible(datasetIndex)) {
var meta = chart.getDatasetMeta(datasetIndex);
helpers.each(dataset.data, function(rawValue, index) {
var value = +me.getRightValue(rawValue);
if (isNaN(value) || meta.data[index].hidden) {
return;
}
- if (me.min === null) {
- me.min = value;
- } else if (value < me.min) {
- me.min = value;
- }
-
- if (me.max === null) {
- me.max = value;
- } else if (value > me.max) {
- me.max = value;
- }
+ min = Math.min(value, min);
+ max = Math.max(value, max);
});
}
});
+ me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
+ me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
+
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
me.handleTickRangeOptions();
},
getTickLimit: function() {
var tickOpts = this.options.ticks;
@@ -14017,136 +15959,58 @@
},
getLabelForIndex: function(index, datasetIndex) {
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
},
fit: function() {
- /*
- * Right, this is really confusing and there is a lot of maths going on here
- * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
- *
- * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
- *
- * Solution:
- *
- * We assume the radius of the polygon is half the size of the canvas at first
- * at each index we check if the text overlaps.
- *
- * Where it does, we store that angle and that index.
- *
- * After finding the largest index and angle we calculate how much we need to remove
- * from the shape radius to move the point inwards by that x.
- *
- * We average the left and right distances to get the maximum shape radius that can fit in the box
- * along with labels.
- *
- * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
- * on each side, removing that from the size, halving it and adding the left x protrusion width.
- *
- * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
- * and position it in the most space efficient manner
- *
- * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
- */
-
- var pointLabels = this.options.pointLabels;
- var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize);
- var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle);
- var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily);
- var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
-
- // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
- // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
- var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]),
- pointPosition,
- i,
- textWidth,
- halfTextWidth,
- furthestRight = this.width,
- furthestRightIndex,
- furthestRightAngle,
- furthestLeft = 0,
- furthestLeftIndex,
- furthestLeftAngle,
- xProtrusionLeft,
- xProtrusionRight,
- radiusReductionRight,
- radiusReductionLeft;
- this.ctx.font = pointLabeFont;
-
- for (i = 0; i < this.getValueCount(); i++) {
- // 5px to space the text slightly out - similar to what we do in the draw function.
- pointPosition = this.getPointPosition(i, largestPossibleRadius);
- textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5;
-
- // Add quarter circle to make degree 0 mean top of circle
- var angleRadians = this.getIndexAngle(i) + (Math.PI / 2);
- var angle = (angleRadians * 360 / (2 * Math.PI)) % 360;
-
- if (angle === 0 || angle === 180) {
- // At angle 0 and 180, we're at exactly the top/bottom
- // of the radar chart, so text will be aligned centrally, so we'll half it and compare
- // w/left and right text sizes
- halfTextWidth = textWidth / 2;
- if (pointPosition.x + halfTextWidth > furthestRight) {
- furthestRight = pointPosition.x + halfTextWidth;
- furthestRightIndex = i;
- }
- if (pointPosition.x - halfTextWidth < furthestLeft) {
- furthestLeft = pointPosition.x - halfTextWidth;
- furthestLeftIndex = i;
- }
- } else if (angle < 180) {
- // Less than half the values means we'll left align the text
- if (pointPosition.x + textWidth > furthestRight) {
- furthestRight = pointPosition.x + textWidth;
- furthestRightIndex = i;
- }
- } else {
- // More than half the values means we'll right align the text
- if (pointPosition.x - textWidth < furthestLeft) {
- furthestLeft = pointPosition.x - textWidth;
- furthestLeftIndex = i;
- }
- }
+ if (this.options.lineArc) {
+ fit(this);
+ } else {
+ fitWithPointLabels(this);
}
+ },
+ /**
+ * Set radius reductions and determine new radius and center point
+ * @private
+ */
+ setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
+ var me = this;
+ var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
+ var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
+ var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
+ var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
- xProtrusionLeft = furthestLeft;
- xProtrusionRight = Math.ceil(furthestRight - this.width);
+ radiusReductionLeft = numberOrZero(radiusReductionLeft);
+ radiusReductionRight = numberOrZero(radiusReductionRight);
+ radiusReductionTop = numberOrZero(radiusReductionTop);
+ radiusReductionBottom = numberOrZero(radiusReductionBottom);
- furthestRightAngle = this.getIndexAngle(furthestRightIndex);
- furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
-
- radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
- radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
-
- // Ensure we actually need to reduce the size of the chart
- radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
- radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
-
- this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2);
- this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
+ me.drawingArea = Math.min(
+ Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
+ Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
+ me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
},
- setCenterPoint: function(leftMovement, rightMovement) {
+ setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
var me = this;
var maxRight = me.width - rightMovement - me.drawingArea,
- maxLeft = leftMovement + me.drawingArea;
+ maxLeft = leftMovement + me.drawingArea,
+ maxTop = topMovement + me.drawingArea,
+ maxBottom = me.height - bottomMovement - me.drawingArea;
me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
- // Always vertically in the centre as the text height doesn't change
- me.yCenter = Math.round((me.height / 2) + me.top);
+ me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
},
getIndexAngle: function(index) {
- var angleMultiplier = (Math.PI * 2) / this.getValueCount();
+ var angleMultiplier = (Math.PI * 2) / getValueCount(this);
var startAngle = this.chart.options && this.chart.options.startAngle ?
this.chart.options.startAngle :
0;
var startAngleRadians = startAngle * Math.PI * 2 / 360;
// Start from the top instead of right, so remove a quarter of the circle
- return index * angleMultiplier - (Math.PI / 2) + startAngleRadians;
+ return index * angleMultiplier + startAngleRadians;
},
getDistanceFromCenterForValue: function(value) {
var me = this;
if (value === null) {
@@ -14155,17 +16019,16 @@
// Take into account half font size + the yPadding of the top value
var scalingFactor = me.drawingArea / (me.max - me.min);
if (me.options.reverse) {
return (me.max - value) * scalingFactor;
- } else {
- return (value - me.min) * scalingFactor;
}
+ return (value - me.min) * scalingFactor;
},
getPointPosition: function(index, distanceFromCenter) {
var me = this;
- var thisAngle = me.getIndexAngle(index);
+ var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
return {
x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
};
},
@@ -14188,12 +16051,10 @@
draw: function() {
var me = this;
var opts = me.options;
var gridLineOpts = opts.gridLines;
var tickOpts = opts.ticks;
- var angleLineOpts = opts.angleLines;
- var pointLabelOpts = opts.pointLabels;
var getValueOrDefault = helpers.getValueOrDefault;
if (opts.display) {
var ctx = me.ctx;
@@ -14209,33 +16070,11 @@
var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
var yHeight = me.yCenter - yCenterOffset;
// Draw circular lines around the scale
if (gridLineOpts.display && index !== 0) {
- ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
- ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
-
- if (opts.lineArc) {
- // Draw circular arcs between the points
- ctx.beginPath();
- ctx.arc(me.xCenter, me.yCenter, yCenterOffset, 0, Math.PI * 2);
- ctx.closePath();
- ctx.stroke();
- } else {
- // Draw straight lines connecting each index
- ctx.beginPath();
- for (var i = 0; i < me.getValueCount(); i++) {
- var pointPosition = me.getPointPosition(i, yCenterOffset);
- if (i === 0) {
- ctx.moveTo(pointPosition.x, pointPosition.y);
- } else {
- ctx.lineTo(pointPosition.x, pointPosition.y);
- }
- }
- ctx.closePath();
- ctx.stroke();
- }
+ drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
}
if (tickOpts.display) {
var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
ctx.font = tickLabelFont;
@@ -14250,82 +16089,30 @@
tickFontSize + tickOpts.backdropPaddingY * 2
);
}
ctx.textAlign = 'center';
- ctx.textBaseline = "middle";
+ ctx.textBaseline = 'middle';
ctx.fillStyle = tickFontColor;
ctx.fillText(label, me.xCenter, yHeight);
}
}
});
if (!opts.lineArc) {
- ctx.lineWidth = angleLineOpts.lineWidth;
- ctx.strokeStyle = angleLineOpts.color;
-
- var outerDistance = me.getDistanceFromCenterForValue(opts.reverse ? me.min : me.max);
-
- // Point Label Font
- var pointLabelFontSize = getValueOrDefault(pointLabelOpts.fontSize, globalDefaults.defaultFontSize);
- var pointLabeFontStyle = getValueOrDefault(pointLabelOpts.fontStyle, globalDefaults.defaultFontStyle);
- var pointLabeFontFamily = getValueOrDefault(pointLabelOpts.fontFamily, globalDefaults.defaultFontFamily);
- var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
-
- for (var i = me.getValueCount() - 1; i >= 0; i--) {
- if (angleLineOpts.display) {
- var outerPosition = me.getPointPosition(i, outerDistance);
- ctx.beginPath();
- ctx.moveTo(me.xCenter, me.yCenter);
- ctx.lineTo(outerPosition.x, outerPosition.y);
- ctx.stroke();
- ctx.closePath();
- }
- // Extra 3px out for some label spacing
- var pointLabelPosition = me.getPointPosition(i, outerDistance + 5);
-
- // Keep this in loop since we may support array properties here
- var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
- ctx.font = pointLabeFont;
- ctx.fillStyle = pointLabelFontColor;
-
- var pointLabels = me.pointLabels;
-
- // Add quarter circle to make degree 0 mean top of circle
- var angleRadians = this.getIndexAngle(i) + (Math.PI / 2);
- var angle = (angleRadians * 360 / (2 * Math.PI)) % 360;
-
- if (angle === 0 || angle === 180) {
- ctx.textAlign = 'center';
- } else if (angle < 180) {
- ctx.textAlign = 'left';
- } else {
- ctx.textAlign = 'right';
- }
-
- // Set the correct text baseline based on outer positioning
- if (angle === 90 || angle === 270) {
- ctx.textBaseline = 'middle';
- } else if (angle > 270 || angle < 90) {
- ctx.textBaseline = 'bottom';
- } else {
- ctx.textBaseline = 'top';
- }
-
- ctx.fillText(pointLabels[i] ? pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y);
- }
+ drawPointLabels(me);
}
}
}
});
- Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig);
+ Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
};
-},{}],44:[function(require,module,exports){
-/*global window: false */
-"use strict";
+},{}],48:[function(require,module,exports){
+/* global window: false */
+'use strict';
var moment = require(6);
moment = typeof(moment) === 'function' ? moment : window.moment;
module.exports = function(Chart) {
@@ -14361,31 +16148,32 @@
maxStep: false
}]
};
var defaultConfig = {
- position: "bottom",
+ position: 'bottom',
time: {
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
format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
unit: false, // false == automatic or override with week, month, year, etc.
round: false, // none, or override with week, month, year, etc.
displayFormat: false, // DEPRECATED
isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
+ minUnit: 'millisecond',
// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
displayFormats: {
- 'millisecond': 'h:mm:ss.SSS a', // 11:20:01.123 AM,
- 'second': 'h:mm:ss a', // 11:20:01 AM
- 'minute': 'h:mm:ss a', // 11:20:01 AM
- 'hour': 'MMM D, hA', // Sept 4, 5PM
- 'day': 'll', // Sep 4 2015
- 'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
- 'month': 'MMM YYYY', // Sept 2015
- 'quarter': '[Q]Q - YYYY', // Q3
- 'year': 'YYYY' // 2015
+ millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
+ second: 'h:mm:ss a', // 11:20:01 AM
+ minute: 'h:mm:ss a', // 11:20:01 AM
+ hour: 'MMM D, hA', // Sept 4, 5PM
+ day: 'll', // Sep 4 2015
+ week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
+ month: 'MMM YYYY', // Sept 2015
+ quarter: '[Q]Q - YYYY', // Q3
+ year: 'YYYY' // 2015
}
},
ticks: {
autoSkip: false
}
@@ -14398,23 +16186,42 @@
}
Chart.Scale.prototype.initialize.call(this);
},
getLabelMoment: function(datasetIndex, index) {
- if (typeof this.labelMoments[datasetIndex] != 'undefined') {
+ if (datasetIndex === null || index === null) {
+ return null;
+ }
+
+ if (typeof this.labelMoments[datasetIndex] !== 'undefined') {
return this.labelMoments[datasetIndex][index];
}
return null;
},
+ getLabelDiff: function(datasetIndex, index) {
+ var me = this;
+ if (datasetIndex === null || index === null) {
+ return null;
+ }
+
+ if (me.labelDiffs === undefined) {
+ me.buildLabelDiffs();
+ }
+
+ if (typeof me.labelDiffs[datasetIndex] !== 'undefined') {
+ return me.labelDiffs[datasetIndex][index];
+ }
+
+ return null;
+ },
getMomentStartOf: function(tick) {
var me = this;
if (me.options.time.unit === 'week' && me.options.time.isoWeekday !== false) {
return tick.clone().startOf('isoWeek').isoWeekday(me.options.time.isoWeekday);
- } else {
- return tick.clone().startOf(me.tickUnit);
}
+ return tick.clone().startOf(me.tickUnit);
},
determineDataLimits: function() {
var me = this;
me.labelMoments = [];
@@ -14480,10 +16287,50 @@
// We will modify these, so clone for later
me.firstTick = (me.firstTick || moment()).clone();
me.lastTick = (me.lastTick || moment()).clone();
},
+ buildLabelDiffs: function() {
+ var me = this;
+ me.labelDiffs = [];
+ var scaleLabelDiffs = [];
+ // Parse common labels once
+ if (me.chart.data.labels && me.chart.data.labels.length > 0) {
+ helpers.each(me.chart.data.labels, function(label) {
+ var labelMoment = me.parseTime(label);
+
+ if (labelMoment.isValid()) {
+ if (me.options.time.round) {
+ labelMoment.startOf(me.options.time.round);
+ }
+ scaleLabelDiffs.push(labelMoment.diff(me.firstTick, me.tickUnit, true));
+ }
+ }, me);
+ }
+
+ helpers.each(me.chart.data.datasets, function(dataset) {
+ var diffsForDataset = [];
+
+ if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) {
+ helpers.each(dataset.data, function(value) {
+ var labelMoment = me.parseTime(me.getRightValue(value));
+
+ if (labelMoment.isValid()) {
+ if (me.options.time.round) {
+ labelMoment.startOf(me.options.time.round);
+ }
+ diffsForDataset.push(labelMoment.diff(me.firstTick, me.tickUnit, true));
+ }
+ }, me);
+ } else {
+ // We have no labels. Use common ones
+ diffsForDataset = scaleLabelDiffs;
+ }
+
+ me.labelDiffs.push(diffsForDataset);
+ }, me);
+ },
buildTicks: function() {
var me = this;
me.ctx.save();
var tickFontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
@@ -14502,22 +16349,22 @@
me.displayFormat = me.options.time.displayFormats[me.tickUnit];
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, 1);
} else {
// Determine the smallest needed unit of the time
- var innerWidth = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
+ var innerWidth = me.isHorizontal() ? me.width : me.height;
// Crude approximation of what the label length might be
var tempFirstLabel = me.tickFormatFunction(me.firstTick, 0, []);
var tickLabelWidth = me.ctx.measureText(tempFirstLabel).width;
var cosRotation = Math.cos(helpers.toRadians(me.options.ticks.maxRotation));
var sinRotation = Math.sin(helpers.toRadians(me.options.ticks.maxRotation));
tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
var labelCapacity = innerWidth / (tickLabelWidth);
// Start as small as possible
- me.tickUnit = 'millisecond';
+ me.tickUnit = me.options.time.minUnit;
me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
me.displayFormat = me.options.time.displayFormats[me.tickUnit];
var unitDefinitionIndex = 0;
var unitDefinition = time.units[unitDefinitionIndex];
@@ -14526,11 +16373,11 @@
while (unitDefinitionIndex < time.units.length) {
// Can we scale this unit. If `false` we can scale infinitely
me.unitScale = 1;
if (helpers.isArray(unitDefinition.steps) && Math.ceil(me.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) {
- // Use one of the prefedined steps
+ // Use one of the predefined steps
for (var idx = 0; idx < unitDefinition.steps.length; ++idx) {
if (unitDefinition.steps[idx] >= Math.ceil(me.scaleSizeInUnits / labelCapacity)) {
me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, unitDefinition.steps[idx]);
break;
}
@@ -14586,46 +16433,48 @@
// first tick. will have been rounded correctly if options.time.min is not specified
me.ticks.push(me.firstTick.clone());
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
- for (var i = 1; i <= me.scaleSizeInUnits; ++i) {
+ for (var i = me.unitScale; i <= me.scaleSizeInUnits; i += me.unitScale) {
var newTick = roundedStart.clone().add(i, me.tickUnit);
// Are we greater than the max time
if (me.options.time.max && newTick.diff(me.lastTick, me.tickUnit, true) >= 0) {
break;
}
- if (i % me.unitScale === 0) {
- me.ticks.push(newTick);
- }
+ me.ticks.push(newTick);
}
// Always show the right tick
var diff = me.ticks[me.ticks.length - 1].diff(me.lastTick, me.tickUnit);
if (diff !== 0 || me.scaleSizeInUnits === 0) {
// 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
// but the last tick was not rounded.
if (me.options.time.max) {
me.ticks.push(me.lastTick.clone());
me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true);
- } else if (me.scaleSizeInUnits === 0) {
- me.ticks.push(me.lastTick.clone());
- me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
+ } else {
+ // me.ticks.push(me.lastTick.clone());
+ // me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
}
}
me.ctx.restore();
+
+ // Invalidate label diffs cache
+ me.labelDiffs = undefined;
},
// Get tooltip label
getLabelForIndex: function(index, datasetIndex) {
var me = this;
var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : '';
+ var value = me.chart.data.datasets[datasetIndex].data[index];
- if (typeof me.chart.data.datasets[datasetIndex].data[0] === 'object') {
- label = me.getRightValue(me.chart.data.datasets[datasetIndex].data[index]);
+ if (value !== null && typeof value === 'object') {
+ label = me.getRightValue(value);
}
// Format nicely
if (me.options.time.tooltipFormat) {
label = me.parseTime(label).format(me.options.time.tooltipFormat);
@@ -14639,52 +16488,54 @@
var tickOpts = this.options.ticks;
var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback);
if (callback) {
return callback(formattedTick, index, ticks);
- } else {
- return formattedTick;
}
+ return formattedTick;
},
convertTicksToLabels: function() {
var me = this;
me.tickMoments = me.ticks;
me.ticks = me.ticks.map(me.tickFormatFunction, me);
},
getPixelForValue: function(value, index, datasetIndex) {
var me = this;
- if (!value || !value.isValid) {
- // not already a moment object
- value = me.parseTime(me.getRightValue(value));
+ var offset = null;
+ if (index !== undefined && datasetIndex !== undefined) {
+ offset = me.getLabelDiff(datasetIndex, index);
}
- var labelMoment = value && value.isValid && value.isValid() ? value : me.getLabelMoment(datasetIndex, index);
- if (labelMoment) {
- var offset = labelMoment.diff(me.firstTick, me.tickUnit, true);
+ if (offset === null) {
+ if (!value || !value.isValid) {
+ // not already a moment object
+ value = me.parseTime(me.getRightValue(value));
+ }
+ if (value && value.isValid && value.isValid()) {
+ offset = value.diff(me.firstTick, me.tickUnit, true);
+ }
+ }
+ if (offset !== null) {
var decimal = offset !== 0 ? offset / me.scaleSizeInUnits : offset;
if (me.isHorizontal()) {
- var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
- var valueOffset = (innerWidth * decimal) + me.paddingLeft;
-
+ var valueOffset = (me.width * decimal);
return me.left + Math.round(valueOffset);
- } else {
- var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
- var heightOffset = (innerHeight * decimal) + me.paddingTop;
-
- return me.top + Math.round(heightOffset);
}
+
+ var heightOffset = (me.height * decimal);
+ return me.top + Math.round(heightOffset);
}
},
getPixelForTick: function(index) {
return this.getPixelForValue(this.tickMoments[index], null, null);
},
getValueForPixel: function(pixel) {
var me = this;
- var innerDimension = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
- var offset = (pixel - (me.isHorizontal() ? me.left + me.paddingLeft : me.top + me.paddingTop)) / innerDimension;
+ var innerDimension = me.isHorizontal() ? me.width : me.height;
+ var offset = (pixel - (me.isHorizontal() ? me.left : me.top)) / innerDimension;
offset *= me.scaleSizeInUnits;
return me.firstTick.clone().add(moment.duration(offset, me.tickUnit).asSeconds(), 'seconds');
},
parseTime: function(label) {
var me = this;
@@ -14702,17 +16553,17 @@
if (label.isValid && label.isValid()) {
return label;
}
// Custom parsing (return an instance of moment)
if (typeof me.options.time.format !== 'string' && me.options.time.format.call) {
- 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");
+ 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');
return me.options.time.format(label);
}
// Moment format parsing
return moment(label, me.options.time.format);
}
});
- Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);
+ Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig);
};
},{"6":6}]},{},[7])(7)
});
\ No newline at end of file