mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-06 04:09:08 +02:00
Convert src/helpers to use ES6 exports (#6797)
* Canvas helpers exported * Easing helpers use exports * Math helper uses export * Linting * RTL options use export * Allow object spread operator * Rest of helpers use `export`
This commit is contained in:
parent
1f3cf3c28d
commit
5a99dff5f7
@ -5,7 +5,7 @@ env:
|
||||
node: true
|
||||
|
||||
parserOptions:
|
||||
ecmaVersion: 6
|
||||
ecmaVersion: 2018
|
||||
sourceType: 'module'
|
||||
|
||||
plugins: ['html']
|
||||
|
@ -10,193 +10,191 @@ const TWO_THIRDS_PI = PI * 2 / 3;
|
||||
/**
|
||||
* @namespace Chart.helpers.canvas
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* Returns the aligned pixel value to avoid anti-aliasing blur
|
||||
* @param {Chart} chart - The chart instance.
|
||||
* @param {number} pixel - A pixel value.
|
||||
* @param {number} width - The width of the element.
|
||||
* @returns {number} The aligned pixel value.
|
||||
* @private
|
||||
*/
|
||||
_alignPixel: function(chart, pixel, width) {
|
||||
const devicePixelRatio = chart.currentDevicePixelRatio;
|
||||
const halfWidth = width / 2;
|
||||
return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
|
||||
},
|
||||
/**
|
||||
* Returns the aligned pixel value to avoid anti-aliasing blur
|
||||
* @param {Chart} chart - The chart instance.
|
||||
* @param {number} pixel - A pixel value.
|
||||
* @param {number} width - The width of the element.
|
||||
* @returns {number} The aligned pixel value.
|
||||
* @private
|
||||
*/
|
||||
export function _alignPixel(chart, pixel, width) {
|
||||
const devicePixelRatio = chart.currentDevicePixelRatio;
|
||||
const halfWidth = width / 2;
|
||||
return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the entire canvas associated to the given `chart`.
|
||||
* @param {Chart} chart - The chart for which to clear the canvas.
|
||||
*/
|
||||
clear: function(chart) {
|
||||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||||
},
|
||||
/**
|
||||
* Clears the entire canvas associated to the given `chart`.
|
||||
* @param {Chart} chart - The chart for which to clear the canvas.
|
||||
*/
|
||||
export function clear(chart) {
|
||||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||||
}
|
||||
|
||||
drawPoint: function(ctx, style, radius, x, y, rotation) {
|
||||
var type, xOffset, yOffset, size, cornerRadius;
|
||||
var rad = (rotation || 0) * RAD_PER_DEG;
|
||||
export function drawPoint(ctx, style, radius, x, y, rotation) {
|
||||
var type, xOffset, yOffset, size, cornerRadius;
|
||||
var rad = (rotation || 0) * RAD_PER_DEG;
|
||||
|
||||
if (style && typeof style === 'object') {
|
||||
type = style.toString();
|
||||
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(rad);
|
||||
ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
|
||||
ctx.restore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNaN(radius) || radius <= 0) {
|
||||
if (style && typeof style === 'object') {
|
||||
type = style.toString();
|
||||
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(rad);
|
||||
ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
|
||||
ctx.restore();
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
switch (style) {
|
||||
// Default includes circle
|
||||
default:
|
||||
ctx.arc(x, y, radius, 0, DOUBLE_PI);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'triangle':
|
||||
ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||||
rad += TWO_THIRDS_PI;
|
||||
ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||||
rad += TWO_THIRDS_PI;
|
||||
ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'rectRounded':
|
||||
// NOTE: the rounded rect implementation changed to use `arc` instead of
|
||||
// `quadraticCurveTo` since it generates better results when rect is
|
||||
// almost a circle. 0.516 (instead of 0.5) produces results with visually
|
||||
// closer proportion to the previous impl and it is inscribed in the
|
||||
// circle with `radius`. For more details, see the following PRs:
|
||||
// https://github.com/chartjs/Chart.js/issues/5597
|
||||
// https://github.com/chartjs/Chart.js/issues/5858
|
||||
cornerRadius = radius * 0.516;
|
||||
size = radius - cornerRadius;
|
||||
xOffset = Math.cos(rad + QUARTER_PI) * size;
|
||||
yOffset = Math.sin(rad + QUARTER_PI) * size;
|
||||
ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
|
||||
ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
|
||||
ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
|
||||
ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'rect':
|
||||
if (!rotation) {
|
||||
size = Math.SQRT1_2 * radius;
|
||||
ctx.rect(x - size, y - size, 2 * size, 2 * size);
|
||||
break;
|
||||
}
|
||||
rad += QUARTER_PI;
|
||||
/* falls through */
|
||||
case 'rectRot':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'crossRot':
|
||||
rad += QUARTER_PI;
|
||||
/* falls through */
|
||||
case 'cross':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
break;
|
||||
case 'star':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
rad += QUARTER_PI;
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
break;
|
||||
case 'line':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
break;
|
||||
case 'dash':
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the point is inside the rectangle
|
||||
* @param {object} point - The point to test
|
||||
* @param {object} area - The rectangle
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
_isPointInArea: function(point, area) {
|
||||
var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
|
||||
|
||||
return point.x > area.left - epsilon && point.x < area.right + epsilon &&
|
||||
point.y > area.top - epsilon && point.y < area.bottom + epsilon;
|
||||
},
|
||||
|
||||
clipArea: function(ctx, area) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
|
||||
ctx.clip();
|
||||
},
|
||||
|
||||
unclipArea: function(ctx) {
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_steppedLineTo: function(ctx, previous, target, flip, mode) {
|
||||
if (mode === 'middle') {
|
||||
const midpoint = (previous.x + target.x) / 2.0;
|
||||
ctx.lineTo(midpoint, flip ? target.y : previous.y);
|
||||
ctx.lineTo(midpoint, flip ? previous.y : target.y);
|
||||
} else if ((mode === 'after' && !flip) || (mode !== 'after' && flip)) {
|
||||
ctx.lineTo(previous.x, target.y);
|
||||
} else {
|
||||
ctx.lineTo(target.x, previous.y);
|
||||
}
|
||||
ctx.lineTo(target.x, target.y);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_bezierCurveTo: function(ctx, previous, target, flip) {
|
||||
ctx.bezierCurveTo(
|
||||
flip ? previous.controlPointPreviousX : previous.controlPointNextX,
|
||||
flip ? previous.controlPointPreviousY : previous.controlPointNextY,
|
||||
flip ? target.controlPointNextX : target.controlPointPreviousX,
|
||||
flip ? target.controlPointNextY : target.controlPointPreviousY,
|
||||
target.x,
|
||||
target.y);
|
||||
}
|
||||
};
|
||||
|
||||
if (isNaN(radius) || radius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
switch (style) {
|
||||
// Default includes circle
|
||||
default:
|
||||
ctx.arc(x, y, radius, 0, DOUBLE_PI);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'triangle':
|
||||
ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||||
rad += TWO_THIRDS_PI;
|
||||
ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||||
rad += TWO_THIRDS_PI;
|
||||
ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'rectRounded':
|
||||
// NOTE: the rounded rect implementation changed to use `arc` instead of
|
||||
// `quadraticCurveTo` since it generates better results when rect is
|
||||
// almost a circle. 0.516 (instead of 0.5) produces results with visually
|
||||
// closer proportion to the previous impl and it is inscribed in the
|
||||
// circle with `radius`. For more details, see the following PRs:
|
||||
// https://github.com/chartjs/Chart.js/issues/5597
|
||||
// https://github.com/chartjs/Chart.js/issues/5858
|
||||
cornerRadius = radius * 0.516;
|
||||
size = radius - cornerRadius;
|
||||
xOffset = Math.cos(rad + QUARTER_PI) * size;
|
||||
yOffset = Math.sin(rad + QUARTER_PI) * size;
|
||||
ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
|
||||
ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
|
||||
ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
|
||||
ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'rect':
|
||||
if (!rotation) {
|
||||
size = Math.SQRT1_2 * radius;
|
||||
ctx.rect(x - size, y - size, 2 * size, 2 * size);
|
||||
break;
|
||||
}
|
||||
rad += QUARTER_PI;
|
||||
/* falls through */
|
||||
case 'rectRot':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
ctx.closePath();
|
||||
break;
|
||||
case 'crossRot':
|
||||
rad += QUARTER_PI;
|
||||
/* falls through */
|
||||
case 'cross':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
break;
|
||||
case 'star':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
rad += QUARTER_PI;
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
ctx.moveTo(x + yOffset, y - xOffset);
|
||||
ctx.lineTo(x - yOffset, y + xOffset);
|
||||
break;
|
||||
case 'line':
|
||||
xOffset = Math.cos(rad) * radius;
|
||||
yOffset = Math.sin(rad) * radius;
|
||||
ctx.moveTo(x - xOffset, y - yOffset);
|
||||
ctx.lineTo(x + xOffset, y + yOffset);
|
||||
break;
|
||||
case 'dash':
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the point is inside the rectangle
|
||||
* @param {object} point - The point to test
|
||||
* @param {object} area - The rectangle
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
export function _isPointInArea(point, area) {
|
||||
var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
|
||||
|
||||
return point.x > area.left - epsilon && point.x < area.right + epsilon &&
|
||||
point.y > area.top - epsilon && point.y < area.bottom + epsilon;
|
||||
}
|
||||
|
||||
export function clipArea(ctx, area) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
|
||||
ctx.clip();
|
||||
}
|
||||
|
||||
export function unclipArea(ctx) {
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function _steppedLineTo(ctx, previous, target, flip, mode) {
|
||||
if (mode === 'middle') {
|
||||
const midpoint = (previous.x + target.x) / 2.0;
|
||||
ctx.lineTo(midpoint, flip ? target.y : previous.y);
|
||||
ctx.lineTo(midpoint, flip ? previous.y : target.y);
|
||||
} else if ((mode === 'after' && !flip) || (mode !== 'after' && flip)) {
|
||||
ctx.lineTo(previous.x, target.y);
|
||||
} else {
|
||||
ctx.lineTo(target.x, previous.y);
|
||||
}
|
||||
ctx.lineTo(target.x, target.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function _bezierCurveTo(ctx, previous, target, flip) {
|
||||
ctx.bezierCurveTo(
|
||||
flip ? previous.controlPointPreviousX : previous.controlPointNextX,
|
||||
flip ? previous.controlPointPreviousY : previous.controlPointNextY,
|
||||
flip ? target.controlPointNextX : target.controlPointPreviousX,
|
||||
flip ? target.controlPointNextY : target.controlPointPreviousY,
|
||||
target.x,
|
||||
target.y);
|
||||
}
|
||||
|
@ -3,342 +3,343 @@
|
||||
/**
|
||||
* @namespace Chart.helpers
|
||||
*/
|
||||
var helpers = {
|
||||
/**
|
||||
* An empty function that can be used, for example, for optional callback.
|
||||
*/
|
||||
noop: function() {},
|
||||
/**
|
||||
* An empty function that can be used, for example, for optional callback.
|
||||
*/
|
||||
export function noop() {}
|
||||
|
||||
/**
|
||||
* Returns a unique id, sequentially generated from a global variable.
|
||||
* @returns {number}
|
||||
* @function
|
||||
*/
|
||||
uid: (function() {
|
||||
var id = 0;
|
||||
return function() {
|
||||
return id++;
|
||||
};
|
||||
}()),
|
||||
/**
|
||||
* Returns a unique id, sequentially generated from a global variable.
|
||||
* @returns {number}
|
||||
* @function
|
||||
*/
|
||||
export const 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 (including typed arrays), else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {boolean}
|
||||
* @function
|
||||
*/
|
||||
isArray: function(value) {
|
||||
if (Array.isArray && Array.isArray(value)) {
|
||||
return true;
|
||||
}
|
||||
var type = Object.prototype.toString.call(value);
|
||||
if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if `value` is neither null nor undefined, else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {boolean}
|
||||
* @since 2.7.0
|
||||
*/
|
||||
export function isNullOrUndef(value) {
|
||||
return value === null || typeof value === 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if `value` is an array (including typed arrays), else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {boolean}
|
||||
* @function
|
||||
*/
|
||||
export function isArray(value) {
|
||||
if (Array.isArray && Array.isArray(value)) {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
|
||||
* @param {Array} a0 - The array to compare
|
||||
* @param {Array} a1 - The array to compare
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_elementsEqual: function(a0, a1) {
|
||||
let 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.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
var type = Object.prototype.toString.call(value);
|
||||
if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
|
||||
return true;
|
||||
},
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
/**
|
||||
* Returns true if `value` is an object (excluding null), else returns false.
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {boolean}
|
||||
* @since 2.7.0
|
||||
*/
|
||||
export function isObject(value) {
|
||||
return value !== null && Object.prototype.toString.call(value) === '[object Object]';
|
||||
}
|
||||
|
||||
if (helpers.isObject(source)) {
|
||||
var target = {};
|
||||
var keys = Object.keys(source);
|
||||
var klen = keys.length;
|
||||
var k = 0;
|
||||
/**
|
||||
* Returns true if `value` is a finite number, else returns false
|
||||
* @param {*} value - The value to test.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isNumberFinite = (value) => {
|
||||
return (typeof value === 'number' || value instanceof Number) && isFinite(value);
|
||||
};
|
||||
export {
|
||||
isNumberFinite as isFinite,
|
||||
};
|
||||
|
||||
for (; k < klen; ++k) {
|
||||
target[keys[k]] = helpers.clone(source[keys[k]]);
|
||||
/**
|
||||
* 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 {*}
|
||||
*/
|
||||
export function valueOrDefault(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 {*}
|
||||
*/
|
||||
export function valueAtIndexOrDefault(value, index, defaultValue) {
|
||||
return valueOrDefault(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 {*}
|
||||
*/
|
||||
export function callback(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.
|
||||
*/
|
||||
export function each(loopable, fn, thisArg, reverse) {
|
||||
var i, len, keys;
|
||||
if (isArray(loopable)) {
|
||||
len = loopable.length;
|
||||
if (reverse) {
|
||||
for (i = len - 1; i >= 0; i--) {
|
||||
fn.call(thisArg, loopable[i], i);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
return source;
|
||||
},
|
||||
|
||||
/**
|
||||
* The default merger when Chart.helpers.merge is called without merger option.
|
||||
* Note(SB): also used by mergeConfig and mergeScaleConfig 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 (!Object.prototype.hasOwnProperty.call(target, 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|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;
|
||||
for (i = 0; i < len; i++) {
|
||||
fn.call(thisArg, loopable[i], i);
|
||||
}
|
||||
}
|
||||
} else if (isObject(loopable)) {
|
||||
keys = Object.keys(loopable);
|
||||
len = keys.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
fn.call(thisArg, loopable[keys[i]], keys[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys = Object.keys(source);
|
||||
for (k = 0, klen = keys.length; k < klen; ++k) {
|
||||
merge(keys[k], target, source, options);
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
export function arrayEquals(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 (!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 true if the `a0` and `a1` arrays have the same content, else returns false.
|
||||
* @param {Array} a0 - The array to compare
|
||||
* @param {Array} a1 - The array to compare
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function _elementsEqual(a0, a1) {
|
||||
let 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.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of `source` without keeping references on objects and arrays.
|
||||
* @param {*} source - The value to clone.
|
||||
* @returns {*}
|
||||
*/
|
||||
export function clone(source) {
|
||||
if (isArray(source)) {
|
||||
return source.map(clone);
|
||||
}
|
||||
|
||||
if (isObject(source)) {
|
||||
var target = {};
|
||||
var keys = Object.keys(source);
|
||||
var klen = keys.length;
|
||||
var k = 0;
|
||||
|
||||
for (; k < klen; ++k) {
|
||||
target[keys[k]] = clone(source[keys[k]]);
|
||||
}
|
||||
|
||||
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|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});
|
||||
},
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: Object.assign || function(target) {
|
||||
return helpers.merge(target, [].slice.call(arguments, 1), {
|
||||
merger: function(key, dst, src) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* The default merger when Chart.helpers.merge is called without merger option.
|
||||
* Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
|
||||
* @private
|
||||
*/
|
||||
export function _merger(key, target, source, options) {
|
||||
var tval = target[key];
|
||||
var sval = source[key];
|
||||
|
||||
/**
|
||||
* Basic javascript inheritance based on the model created in Backbone.js
|
||||
*/
|
||||
inherits: function(extensions) {
|
||||
var me = this;
|
||||
var ChartElement = (extensions && Object.prototype.hasOwnProperty.call(extensions, 'constructor')) ? extensions.constructor : function() {
|
||||
return me.apply(this, arguments);
|
||||
};
|
||||
if (isObject(tval) && isObject(sval)) {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
merge(tval, sval, options);
|
||||
} else {
|
||||
target[key] = clone(sval);
|
||||
}
|
||||
}
|
||||
|
||||
var Surrogate = function() {
|
||||
this.constructor = ChartElement;
|
||||
};
|
||||
/**
|
||||
* 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|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.
|
||||
*/
|
||||
export function merge(target, source, options) {
|
||||
var sources = isArray(source) ? source : [source];
|
||||
var ilen = sources.length;
|
||||
var merger, i, keys, klen, k;
|
||||
|
||||
Surrogate.prototype = me.prototype;
|
||||
ChartElement.prototype = new Surrogate();
|
||||
ChartElement.extend = helpers.inherits;
|
||||
if (!isObject(target)) {
|
||||
return target;
|
||||
}
|
||||
|
||||
if (extensions) {
|
||||
helpers.extend(ChartElement.prototype, extensions);
|
||||
options = options || {};
|
||||
merger = options.merger || _merger;
|
||||
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
source = sources[i];
|
||||
if (!isObject(source)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ChartElement.__super__ = me.prototype;
|
||||
return ChartElement;
|
||||
},
|
||||
|
||||
_deprecated: function(scope, value, previous, current) {
|
||||
if (value !== undefined) {
|
||||
console.warn(scope + ': "' + previous +
|
||||
'" is deprecated. Please use "' + current + '" instead');
|
||||
keys = Object.keys(source);
|
||||
for (k = 0, klen = keys.length; k < klen; ++k) {
|
||||
merger(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|object[]} source - Object(s) to merge into `target`.
|
||||
* @returns {object} The `target` object.
|
||||
*/
|
||||
export function mergeIf(target, source) {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
return merge(target, source, {merger: _mergerIf});
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges source[key] in target[key] only if target[key] is undefined.
|
||||
* @private
|
||||
*/
|
||||
export function _mergerIf(key, target, source) {
|
||||
var tval = target[key];
|
||||
var sval = source[key];
|
||||
|
||||
if (isObject(tval) && isObject(sval)) {
|
||||
mergeIf(tval, sval);
|
||||
} else if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
||||
target[key] = clone(sval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export const extend = Object.assign || function(target) {
|
||||
return merge(target, [].slice.call(arguments, 1), {
|
||||
merger: function(key, dst, src) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = helpers;
|
||||
/**
|
||||
* Basic javascript inheritance based on the model created in Backbone.js
|
||||
*/
|
||||
export function inherits(extensions) {
|
||||
var me = this;
|
||||
var ChartElement = (extensions && Object.prototype.hasOwnProperty.call(extensions, '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 = inherits;
|
||||
|
||||
if (extensions) {
|
||||
extend(ChartElement.prototype, extensions);
|
||||
}
|
||||
|
||||
ChartElement.__super__ = me.prototype;
|
||||
return ChartElement;
|
||||
}
|
||||
|
||||
export function _deprecated(scope, value, previous, current) {
|
||||
if (value !== undefined) {
|
||||
console.warn(scope + ': "' + previous +
|
||||
'" is deprecated. Please use "' + current + '" instead');
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @namespace Chart.helpers.easing.effects
|
||||
* @see http://www.robertpenner.com/easing/
|
||||
*/
|
||||
var effects = {
|
||||
export const effects = {
|
||||
linear: function(t) {
|
||||
return t;
|
||||
},
|
||||
@ -231,7 +231,3 @@ var effects = {
|
||||
return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
effects: effects
|
||||
};
|
||||
|
@ -4,41 +4,37 @@
|
||||
* @alias Chart.helpers.math
|
||||
* @namespace
|
||||
*/
|
||||
var exports = {
|
||||
/**
|
||||
* Returns an array of factors sorted from 1 to sqrt(value)
|
||||
* @private
|
||||
*/
|
||||
_factorize: function(value) {
|
||||
var result = [];
|
||||
var sqrt = Math.sqrt(value);
|
||||
var i;
|
||||
/**
|
||||
* Returns an array of factors sorted from 1 to sqrt(value)
|
||||
* @private
|
||||
*/
|
||||
export function _factorize(value) {
|
||||
var result = [];
|
||||
var sqrt = Math.sqrt(value);
|
||||
var i;
|
||||
|
||||
for (i = 1; i < sqrt; i++) {
|
||||
if (value % i === 0) {
|
||||
result.push(i);
|
||||
result.push(value / i);
|
||||
}
|
||||
for (i = 1; i < sqrt; i++) {
|
||||
if (value % i === 0) {
|
||||
result.push(i);
|
||||
result.push(value / i);
|
||||
}
|
||||
if (sqrt === (sqrt | 0)) { // if value is a square number
|
||||
result.push(sqrt);
|
||||
}
|
||||
|
||||
result.sort(function(a, b) {
|
||||
return a - b;
|
||||
}).pop();
|
||||
return result;
|
||||
},
|
||||
|
||||
log10: Math.log10 || function(x) {
|
||||
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
|
||||
// Check for whole powers of 10,
|
||||
// which due to floating point rounding error should be corrected.
|
||||
var powerOf10 = Math.round(exponent);
|
||||
var isPowerOf10 = x === Math.pow(10, powerOf10);
|
||||
|
||||
return isPowerOf10 ? powerOf10 : exponent;
|
||||
}
|
||||
};
|
||||
if (sqrt === (sqrt | 0)) { // if value is a square number
|
||||
result.push(sqrt);
|
||||
}
|
||||
|
||||
module.exports = exports;
|
||||
result.sort(function(a, b) {
|
||||
return a - b;
|
||||
}).pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
export const log10 = Math.log10 || function(x) {
|
||||
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
|
||||
// Check for whole powers of 10,
|
||||
// which due to floating point rounding error should be corrected.
|
||||
var powerOf10 = Math.round(exponent);
|
||||
var isPowerOf10 = x === Math.pow(10, powerOf10);
|
||||
|
||||
return isPowerOf10 ? powerOf10 : exponent;
|
||||
};
|
||||
|
@ -1,9 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var defaults = require('../core/core.defaults');
|
||||
var helpers = require('./helpers.core');
|
||||
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
import defaults from '../core/core.defaults';
|
||||
import {isNullOrUndef, isArray, isObject, valueOrDefault} from './helpers.core';
|
||||
|
||||
/**
|
||||
* Converts the given font object into a CSS font string.
|
||||
@ -12,7 +10,7 @@ var valueOrDefault = helpers.valueOrDefault;
|
||||
* @private
|
||||
*/
|
||||
function toFontString(font) {
|
||||
if (!font || helpers.isNullOrUndef(font.size) || helpers.isNullOrUndef(font.family)) {
|
||||
if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -26,122 +24,120 @@ function toFontString(font) {
|
||||
* @alias Chart.helpers.options
|
||||
* @namespace
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* Converts the given line height `value` in pixels for a specific font `size`.
|
||||
* @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
|
||||
* @param {number} size - The font size (in pixels) used to resolve relative `value`.
|
||||
* @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
|
||||
* @since 2.7.0
|
||||
*/
|
||||
toLineHeight: function(value, size) {
|
||||
var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
|
||||
if (!matches || matches[1] === 'normal') {
|
||||
return size * 1.2;
|
||||
/**
|
||||
* Converts the given line height `value` in pixels for a specific font `size`.
|
||||
* @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
|
||||
* @param {number} size - The font size (in pixels) used to resolve relative `value`.
|
||||
* @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
|
||||
* @since 2.7.0
|
||||
*/
|
||||
export function toLineHeight(value, size) {
|
||||
var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
|
||||
if (!matches || matches[1] === 'normal') {
|
||||
return size * 1.2;
|
||||
}
|
||||
|
||||
value = +matches[2];
|
||||
|
||||
switch (matches[3]) {
|
||||
case 'px':
|
||||
return value;
|
||||
case '%':
|
||||
value /= 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return size * value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value into a padding object with pre-computed width/height.
|
||||
* @param {number|object} value - If a number, set the value to all TRBL component,
|
||||
* else, if and object, use defined properties and sets undefined ones to 0.
|
||||
* @returns {object} The padding values (top, right, bottom, left, width, height)
|
||||
* @since 2.7.0
|
||||
*/
|
||||
export function toPadding(value) {
|
||||
var t, r, b, l;
|
||||
|
||||
if (isObject(value)) {
|
||||
t = +value.top || 0;
|
||||
r = +value.right || 0;
|
||||
b = +value.bottom || 0;
|
||||
l = +value.left || 0;
|
||||
} else {
|
||||
t = r = b = l = +value || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
top: t,
|
||||
right: r,
|
||||
bottom: b,
|
||||
left: l,
|
||||
height: t + b,
|
||||
width: l + r
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses font options and returns the font object.
|
||||
* @param {object} options - A object that contains font options to be parsed.
|
||||
* @return {object} The font object.
|
||||
* @todo Support font.* options and renamed to toFont().
|
||||
* @private
|
||||
*/
|
||||
export function _parseFont(options) {
|
||||
var globalDefaults = defaults.global;
|
||||
var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
|
||||
var font = {
|
||||
family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),
|
||||
lineHeight: toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),
|
||||
size: size,
|
||||
style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),
|
||||
weight: null,
|
||||
string: ''
|
||||
};
|
||||
|
||||
font.string = toFontString(font);
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given `inputs` sequentially and returns the first defined value.
|
||||
* @param {Array} inputs - An array of values, falling back to the last value.
|
||||
* @param {object} [context] - If defined and the current value is a function, the value
|
||||
* is called with `context` as first argument and the result becomes the new input.
|
||||
* @param {number} [index] - If defined and the current value is an array, the value
|
||||
* at `index` become the new input.
|
||||
* @param {object} [info] - object to return information about resolution in
|
||||
* @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable.
|
||||
* @since 2.7.0
|
||||
*/
|
||||
export function resolve(inputs, context, index, info) {
|
||||
var cacheable = true;
|
||||
var i, ilen, value;
|
||||
|
||||
for (i = 0, ilen = inputs.length; i < ilen; ++i) {
|
||||
value = inputs[i];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
value = +matches[2];
|
||||
|
||||
switch (matches[3]) {
|
||||
case 'px':
|
||||
if (context !== undefined && typeof value === 'function') {
|
||||
value = value(context);
|
||||
cacheable = false;
|
||||
}
|
||||
if (index !== undefined && isArray(value)) {
|
||||
value = value[index];
|
||||
cacheable = false;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
if (info && !cacheable) {
|
||||
info.cacheable = false;
|
||||
}
|
||||
return value;
|
||||
case '%':
|
||||
value /= 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return size * value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the given value into a padding object with pre-computed width/height.
|
||||
* @param {number|object} value - If a number, set the value to all TRBL component,
|
||||
* else, if and object, use defined properties and sets undefined ones to 0.
|
||||
* @returns {object} The padding values (top, right, bottom, left, width, height)
|
||||
* @since 2.7.0
|
||||
*/
|
||||
toPadding: function(value) {
|
||||
var t, r, b, l;
|
||||
|
||||
if (helpers.isObject(value)) {
|
||||
t = +value.top || 0;
|
||||
r = +value.right || 0;
|
||||
b = +value.bottom || 0;
|
||||
l = +value.left || 0;
|
||||
} else {
|
||||
t = r = b = l = +value || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
top: t,
|
||||
right: r,
|
||||
bottom: b,
|
||||
left: l,
|
||||
height: t + b,
|
||||
width: l + r
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses font options and returns the font object.
|
||||
* @param {object} options - A object that contains font options to be parsed.
|
||||
* @return {object} The font object.
|
||||
* @todo Support font.* options and renamed to toFont().
|
||||
* @private
|
||||
*/
|
||||
_parseFont: function(options) {
|
||||
var globalDefaults = defaults.global;
|
||||
var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
|
||||
var font = {
|
||||
family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),
|
||||
lineHeight: helpers.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),
|
||||
size: size,
|
||||
style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),
|
||||
weight: null,
|
||||
string: ''
|
||||
};
|
||||
|
||||
font.string = toFontString(font);
|
||||
return font;
|
||||
},
|
||||
|
||||
/**
|
||||
* Evaluates the given `inputs` sequentially and returns the first defined value.
|
||||
* @param {Array} inputs - An array of values, falling back to the last value.
|
||||
* @param {object} [context] - If defined and the current value is a function, the value
|
||||
* is called with `context` as first argument and the result becomes the new input.
|
||||
* @param {number} [index] - If defined and the current value is an array, the value
|
||||
* at `index` become the new input.
|
||||
* @param {object} [info] - object to return information about resolution in
|
||||
* @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable.
|
||||
* @since 2.7.0
|
||||
*/
|
||||
resolve: function(inputs, context, index, info) {
|
||||
var cacheable = true;
|
||||
var i, ilen, value;
|
||||
|
||||
for (i = 0, ilen = inputs.length; i < ilen; ++i) {
|
||||
value = inputs[i];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (context !== undefined && typeof value === 'function') {
|
||||
value = value(context);
|
||||
cacheable = false;
|
||||
}
|
||||
if (index !== undefined && helpers.isArray(value)) {
|
||||
value = value[index];
|
||||
cacheable = false;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
if (info && !cacheable) {
|
||||
info.cacheable = false;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -42,11 +42,11 @@ var getLtrAdapter = function() {
|
||||
};
|
||||
};
|
||||
|
||||
var getAdapter = function(rtl, rectX, width) {
|
||||
const getAdapter = function(rtl, rectX, width) {
|
||||
return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter();
|
||||
};
|
||||
|
||||
var overrideTextDirection = function(ctx, direction) {
|
||||
const overrideTextDirection = function(ctx, direction) {
|
||||
var style, original;
|
||||
if (direction === 'ltr' || direction === 'rtl') {
|
||||
style = ctx.canvas.style;
|
||||
@ -60,7 +60,7 @@ var overrideTextDirection = function(ctx, direction) {
|
||||
}
|
||||
};
|
||||
|
||||
var restoreTextDirection = function(ctx) {
|
||||
const restoreTextDirection = function(ctx) {
|
||||
var original = ctx.prevTextDirection;
|
||||
if (original !== undefined) {
|
||||
delete ctx.prevTextDirection;
|
||||
@ -68,8 +68,8 @@ var restoreTextDirection = function(ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getRtlAdapter: getAdapter,
|
||||
overrideTextDirection: overrideTextDirection,
|
||||
restoreTextDirection: restoreTextDirection,
|
||||
export {
|
||||
getAdapter as getRtlAdapter,
|
||||
overrideTextDirection,
|
||||
restoreTextDirection
|
||||
};
|
||||
|
@ -1,8 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./helpers.core');
|
||||
module.exports.easing = require('./helpers.easing');
|
||||
module.exports.canvas = require('./helpers.canvas');
|
||||
module.exports.options = require('./helpers.options');
|
||||
module.exports.math = require('./helpers.math');
|
||||
module.exports.rtl = require('./helpers.rtl');
|
||||
import * as coreHelpers from './helpers.core';
|
||||
import * as canvas from './helpers.canvas';
|
||||
import * as easing from './helpers.easing';
|
||||
import * as options from './helpers.options';
|
||||
import * as math from './helpers.math';
|
||||
import * as rtl from './helpers.rtl';
|
||||
|
||||
export default {
|
||||
...coreHelpers,
|
||||
canvas,
|
||||
easing,
|
||||
options,
|
||||
math,
|
||||
rtl,
|
||||
};
|
||||
|
@ -37,10 +37,5 @@ describe('Chart namespace', function() {
|
||||
it('should be an object', function() {
|
||||
expect(Chart.helpers instanceof Object).toBeTruthy();
|
||||
});
|
||||
it('should contains "helpers" namespaces', function() {
|
||||
expect(Chart.helpers.easing instanceof Object).toBeTruthy();
|
||||
expect(Chart.helpers.canvas instanceof Object).toBeTruthy();
|
||||
expect(Chart.helpers.options instanceof Object).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user