mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
349 lines
9.9 KiB
JavaScript
349 lines
9.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @namespace Chart.helpers
|
|
*/
|
|
var helpers = {
|
|
/**
|
|
* An empty function that can be used, for example, for optional callback.
|
|
*/
|
|
noop: function() {},
|
|
|
|
/**
|
|
* Returns a unique id, sequentially generated from a global variable.
|
|
* @returns {Number}
|
|
* @function
|
|
*/
|
|
uid: (function() {
|
|
var id = 0;
|
|
return function() {
|
|
return id++;
|
|
};
|
|
}()),
|
|
|
|
/**
|
|
* Returns true if `value` is neither null nor undefined, else returns false.
|
|
* @param {*} value - The value to test.
|
|
* @returns {Boolean}
|
|
* @since 2.7.0
|
|
*/
|
|
isNullOrUndef: function(value) {
|
|
return value === null || typeof value === 'undefined';
|
|
},
|
|
|
|
/**
|
|
* Returns true if `value` is an array, else returns false.
|
|
* @param {*} value - The value to test.
|
|
* @returns {Boolean}
|
|
* @function
|
|
*/
|
|
isArray: Array.isArray ? Array.isArray : function(value) {
|
|
return Object.prototype.toString.call(value) === '[object Array]';
|
|
},
|
|
|
|
/**
|
|
* Returns true if `value` is an object (excluding null), else returns false.
|
|
* @param {*} value - The value to test.
|
|
* @returns {Boolean}
|
|
* @since 2.7.0
|
|
*/
|
|
isObject: function(value) {
|
|
return value !== null && Object.prototype.toString.call(value) === '[object Object]';
|
|
},
|
|
|
|
/**
|
|
* Returns true if `value` is a finite number, else returns false
|
|
* @param {*} value - The value to test.
|
|
* @returns {Boolean}
|
|
*/
|
|
isFinite: function(value) {
|
|
return (typeof value === 'number' || value instanceof Number) && isFinite(value);
|
|
},
|
|
|
|
/**
|
|
* Returns `value` if defined, else returns `defaultValue`.
|
|
* @param {*} value - The value to return if defined.
|
|
* @param {*} defaultValue - The value to return if `value` is undefined.
|
|
* @returns {*}
|
|
*/
|
|
valueOrDefault: function(value, defaultValue) {
|
|
return typeof value === 'undefined' ? defaultValue : value;
|
|
},
|
|
|
|
/**
|
|
* Returns value at the given `index` in array if defined, else returns `defaultValue`.
|
|
* @param {Array} value - The array to lookup for value at `index`.
|
|
* @param {Number} index - The index in `value` to lookup for value.
|
|
* @param {*} defaultValue - The value to return if `value[index]` is undefined.
|
|
* @returns {*}
|
|
*/
|
|
valueAtIndexOrDefault: function(value, index, defaultValue) {
|
|
return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
|
|
},
|
|
|
|
/**
|
|
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
|
|
* value returned by `fn`. If `fn` is not a function, this method returns undefined.
|
|
* @param {Function} fn - The function to call.
|
|
* @param {Array|undefined|null} args - The arguments with which `fn` should be called.
|
|
* @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
|
|
* @returns {*}
|
|
*/
|
|
callback: function(fn, args, thisArg) {
|
|
if (fn && typeof fn.call === 'function') {
|
|
return fn.apply(thisArg, args);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Note(SB) for performance sake, this method should only be used when loopable type
|
|
* is unknown or in none intensive code (not called often and small loopable). Else
|
|
* it's preferable to use a regular for() loop and save extra function calls.
|
|
* @param {Object|Array} loopable - The object or array to be iterated.
|
|
* @param {Function} fn - The function to call for each item.
|
|
* @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
|
|
* @param {Boolean} [reverse] - If true, iterates backward on the loopable.
|
|
*/
|
|
each: function(loopable, fn, thisArg, reverse) {
|
|
var i, len, keys;
|
|
if (helpers.isArray(loopable)) {
|
|
len = loopable.length;
|
|
if (reverse) {
|
|
for (i = len - 1; i >= 0; i--) {
|
|
fn.call(thisArg, loopable[i], i);
|
|
}
|
|
} else {
|
|
for (i = 0; i < len; i++) {
|
|
fn.call(thisArg, loopable[i], i);
|
|
}
|
|
}
|
|
} else if (helpers.isObject(loopable)) {
|
|
keys = Object.keys(loopable);
|
|
len = keys.length;
|
|
for (i = 0; i < len; i++) {
|
|
fn.call(thisArg, loopable[keys[i]], keys[i]);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
|
|
* @see https://stackoverflow.com/a/14853974
|
|
* @param {Array} a0 - The array to compare
|
|
* @param {Array} a1 - The array to compare
|
|
* @returns {Boolean}
|
|
*/
|
|
arrayEquals: function(a0, a1) {
|
|
var i, ilen, v0, v1;
|
|
|
|
if (!a0 || !a1 || a0.length !== a1.length) {
|
|
return false;
|
|
}
|
|
|
|
for (i = 0, ilen = a0.length; i < ilen; ++i) {
|
|
v0 = a0[i];
|
|
v1 = a1[i];
|
|
|
|
if (v0 instanceof Array && v1 instanceof Array) {
|
|
if (!helpers.arrayEquals(v0, v1)) {
|
|
return false;
|
|
}
|
|
} else if (v0 !== v1) {
|
|
// NOTE: two different object instances will never be equal: {x:20} != {x:20}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Returns a deep copy of `source` without keeping references on objects and arrays.
|
|
* @param {*} source - The value to clone.
|
|
* @returns {*}
|
|
*/
|
|
clone: function(source) {
|
|
if (helpers.isArray(source)) {
|
|
return source.map(helpers.clone);
|
|
}
|
|
|
|
if (helpers.isObject(source)) {
|
|
var target = {};
|
|
var keys = Object.keys(source);
|
|
var klen = keys.length;
|
|
var k = 0;
|
|
|
|
for (; k < klen; ++k) {
|
|
target[keys[k]] = helpers.clone(source[keys[k]]);
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
return source;
|
|
},
|
|
|
|
/**
|
|
* The default merger when Chart.helpers.merge is called without merger option.
|
|
* Note(SB): this method is also used by configMerge and scaleMerge as fallback.
|
|
* @private
|
|
*/
|
|
_merger: function(key, target, source, options) {
|
|
var tval = target[key];
|
|
var sval = source[key];
|
|
|
|
if (helpers.isObject(tval) && helpers.isObject(sval)) {
|
|
helpers.merge(tval, sval, options);
|
|
} else {
|
|
target[key] = helpers.clone(sval);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Merges source[key] in target[key] only if target[key] is undefined.
|
|
* @private
|
|
*/
|
|
_mergerIf: function(key, target, source) {
|
|
var tval = target[key];
|
|
var sval = source[key];
|
|
|
|
if (helpers.isObject(tval) && helpers.isObject(sval)) {
|
|
helpers.mergeIf(tval, sval);
|
|
} else if (!target.hasOwnProperty(key)) {
|
|
target[key] = helpers.clone(sval);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Recursively deep copies `source` properties into `target` with the given `options`.
|
|
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
|
|
* @param {Object} target - The target object in which all sources are merged into.
|
|
* @param {Object|Array(Object)} source - Object(s) to merge into `target`.
|
|
* @param {Object} [options] - Merging options:
|
|
* @param {Function} [options.merger] - The merge method (key, target, source, options)
|
|
* @returns {Object} The `target` object.
|
|
*/
|
|
merge: function(target, source, options) {
|
|
var sources = helpers.isArray(source) ? source : [source];
|
|
var ilen = sources.length;
|
|
var merge, i, keys, klen, k;
|
|
|
|
if (!helpers.isObject(target)) {
|
|
return target;
|
|
}
|
|
|
|
options = options || {};
|
|
merge = options.merger || helpers._merger;
|
|
|
|
for (i = 0; i < ilen; ++i) {
|
|
source = sources[i];
|
|
if (!helpers.isObject(source)) {
|
|
continue;
|
|
}
|
|
|
|
keys = Object.keys(source);
|
|
for (k = 0, klen = keys.length; k < klen; ++k) {
|
|
merge(keys[k], target, source, options);
|
|
}
|
|
}
|
|
|
|
return target;
|
|
},
|
|
|
|
/**
|
|
* Recursively deep copies `source` properties into `target` *only* if not defined in target.
|
|
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
|
|
* @param {Object} target - The target object in which all sources are merged into.
|
|
* @param {Object|Array(Object)} source - Object(s) to merge into `target`.
|
|
* @returns {Object} The `target` object.
|
|
*/
|
|
mergeIf: function(target, source) {
|
|
return helpers.merge(target, source, {merger: helpers._mergerIf});
|
|
},
|
|
|
|
/**
|
|
* Applies the contents of two or more objects together into the first object.
|
|
* @param {Object} target - The target object in which all objects are merged into.
|
|
* @param {Object} arg1 - Object containing additional properties to merge in target.
|
|
* @param {Object} argN - Additional objects containing properties to merge in target.
|
|
* @returns {Object} The `target` object.
|
|
*/
|
|
extend: function(target) {
|
|
var setFn = function(value, key) {
|
|
target[key] = value;
|
|
};
|
|
for (var i = 1, ilen = arguments.length; i < ilen; ++i) {
|
|
helpers.each(arguments[i], setFn);
|
|
}
|
|
return target;
|
|
},
|
|
|
|
/**
|
|
* Basic javascript inheritance based on the model created in Backbone.js
|
|
*/
|
|
inherits: function(extensions) {
|
|
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 = me.prototype;
|
|
ChartElement.prototype = new Surrogate();
|
|
ChartElement.extend = helpers.inherits;
|
|
|
|
if (extensions) {
|
|
helpers.extend(ChartElement.prototype, extensions);
|
|
}
|
|
|
|
ChartElement.__super__ = me.prototype;
|
|
return ChartElement;
|
|
}
|
|
};
|
|
|
|
module.exports = helpers;
|
|
|
|
// DEPRECATIONS
|
|
|
|
/**
|
|
* Provided for backward compatibility, use Chart.helpers.callback instead.
|
|
* @function Chart.helpers.callCallback
|
|
* @deprecated since version 2.6.0
|
|
* @todo remove at version 3
|
|
* @private
|
|
*/
|
|
helpers.callCallback = helpers.callback;
|
|
|
|
/**
|
|
* Provided for backward compatibility, use Array.prototype.indexOf instead.
|
|
* Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
|
|
* @function Chart.helpers.indexOf
|
|
* @deprecated since version 2.7.0
|
|
* @todo remove at version 3
|
|
* @private
|
|
*/
|
|
helpers.indexOf = function(array, item, fromIndex) {
|
|
return Array.prototype.indexOf.call(array, item, fromIndex);
|
|
};
|
|
|
|
/**
|
|
* Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
|
|
* @function Chart.helpers.getValueOrDefault
|
|
* @deprecated since version 2.7.0
|
|
* @todo remove at version 3
|
|
* @private
|
|
*/
|
|
helpers.getValueOrDefault = helpers.valueOrDefault;
|
|
|
|
/**
|
|
* Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
|
|
* @function Chart.helpers.getValueAtIndexOrDefault
|
|
* @deprecated since version 2.7.0
|
|
* @todo remove at version 3
|
|
* @private
|
|
*/
|
|
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
|