/**
* @private
*/
Ext.define('Ext.chart.Util', {
singleton: true,
/**
* @private
* Given a `range` array of two (min/max) numbers and an arbitrary array of numbers
* (`data`), updates the given `range`, if the range of `data` exceeds it.
* Typically, one would start with the `[NaN, NaN]` range array instance, call the
* method on multiple datasets with that range instance, then validate it with
* {@link #validateRange}.
* @param {Number[]} range
* @param {Number[]} data
*/
expandRange: function(range, data) {
var length = data.length,
min = range[0],
max = range[1],
i, value;
for (i = 0; i < length; i++) {
value = data[i];
// `null` is a "finite" number in JavaScript
// and greater than any negative number.
if (value == null || !isFinite(value)) {
continue;
}
if (value < min || !isFinite(min)) {
min = value;
}
if (value > max || !isFinite(max)) {
max = value;
}
}
range[0] = min;
range[1] = max;
},
defaultRange: [0, 1],
/**
* @private
* Makes sure the range exists, is not zero, and its min/max values are finite numbers.
* If this is not the case, the values from the provided `defaultRange`
* are used.
*
* The range to validate. Never modified.
* @param {Number[]} range
* The default range to use, if the given range is not a valid data structure,
* if both values are infinities, or if both values are the same and dangerously
* close to either infinity (which makes expansion of the range by the value of
* `padding` impossible).
* If only a single value is infinity, the other value will be derived
* from the finite value by incrementing/decrementing it by the span
* of the default range towards the infinity.
* For example, if the `defaultRange` is `[0, 1]`, we have:
*
* [5, Infinity] --> [5, 6]
* [3, -Infinity] --> [2, 3]
* [-Infinity, -5] --> [-6, -5]
* [-3, -Infinity] --> [-4, -3]
*
* @param {Number[]} [defaultRange=[0, 1]]
* A non-negative padding to use in case of identical min/max.
* Note that the range span is not guaranteed to be `padding * 2` in this case,
* if min/max are close to MIN_SAFE_INTEGER/MAX_SAFE_INTEGER.
* @param {Number} [padding=0.5]
* @return {Number[]}
*/
validateRange: function(range, defaultRange, padding) {
var isFin0, isFin1;
defaultRange = defaultRange || this.defaultRange.slice();
if (!(padding === 0 || padding > 0)) {
padding = 0.5;
}
if (!range || range.length !== 2) {
return defaultRange;
}
range = [range[0], range[1]];
if (!range[0]) {
range[0] = 0;
}
if (!range[1]) {
range[1] = 0;
}
if (padding && range[0] === range[1]) {
range = [
range[0] - padding,
range[0] + padding
];
// In case the range values are at Infinity, the expansion above by the value
// of 'padding' won't do us much good, so we still have to fall back to the
// 'defaultRange'.
if (range[0] === range[1]) {
return defaultRange;
}
}
// Same sign infinities are ruled out at this point.
isFin0 = isFinite(range[0]);
isFin1 = isFinite(range[1]);
if (!isFin0 && !isFin1) {
return defaultRange;
}
// Different sign infinities are ruled out at this point.
if (isFin0 && !isFin1) {
range[1] = range[0] +
Ext.Number.sign(range[1]) * (defaultRange[1] - defaultRange[0]);
}
else if (isFin1 && !isFin0) {
range[0] = range[1] +
Ext.Number.sign(range[0]) * (defaultRange[1] - defaultRange[0]);
}
// All infinities are ruled out at this point.
return [
Math.min(range[0], range[1]),
Math.max(range[0], range[1])
];
},
applyAnimation: function(animation, oldAnimation) {
if (!animation) {
animation = {
duration: 0
};
}
else if (animation === true) {
animation = {
easing: 'easeInOut',
duration: 500
};
}
return oldAnimation ? Ext.apply({}, animation, oldAnimation) : animation;
}
});