Make Chart.helpers importable (#4479)

Properly export helpers and remove dependencies to `Chart.helpers`. Helpers can now be accessed from `src/helpers/index.js` (`var helpers = require('path/to/helpers/index')`, instead of `var helpers = Chart.helpers`).
This commit is contained in:
Simon Brunel 2017-07-15 15:13:56 +02:00 committed by GitHub
parent b03ab1ca45
commit 717e8d950a
37 changed files with 638 additions and 623 deletions

View File

@ -3,11 +3,10 @@
*/
var Chart = require('./core/core')();
require('./helpers/helpers.core')(Chart);
require('./helpers/helpers.easing')(Chart);
Chart.helpers = require('./helpers/index');
// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
require('./core/core.helpers')(Chart);
require('./helpers/helpers.time')(Chart);
require('./helpers/helpers.canvas')(Chart);
require('./platforms/platform')(Chart);
require('./core/core.element')(Chart);
@ -66,3 +65,14 @@ module.exports = Chart;
if (typeof window !== 'undefined') {
window.Chart = Chart;
}
// DEPRECATIONS
/**
* Provided for backward compatibility, use Chart.helpers.canvas instead.
* @namespace Chart.canvasHelpers
* @deprecated since version 2.6.0
* @todo remove at version 3
* @private
*/
Chart.canvasHelpers = Chart.helpers.canvas;

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.bar = {
hover: {

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.bubble = {
hover: {

View File

@ -1,9 +1,10 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers,
defaults = Chart.defaults;
var defaults = Chart.defaults;
defaults.doughnut = {
animation: {

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.line = {
showLines: true,
@ -285,13 +285,13 @@ module.exports = function(Chart) {
var ilen = points.length;
var i = 0;
Chart.helpers.canvas.clipArea(chart.ctx, area);
helpers.canvas.clipArea(chart.ctx, area);
if (lineEnabled(me.getDataset(), chart.options)) {
meta.dataset.draw();
}
Chart.helpers.canvas.unclipArea(chart.ctx);
helpers.canvas.unclipArea(chart.ctx);
// Draw the points
for (; i<ilen; ++i) {

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.polarArea = {

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.radar = {
scale: {

View File

@ -1,9 +1,9 @@
/* global window: false */
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.global.animation = {
duration: 1000,

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
var plugins = Chart.plugins;
var platform = Chart.platform;

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];

View File

@ -1,11 +1,10 @@
'use strict';
var color = require('chartjs-color');
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
function interpolate(start, view, model, ease) {
var keys = Object.keys(model);
var i, ilen, key, actual, origin, target, type, c0, c1;

View File

@ -3,9 +3,9 @@
'use strict';
var color = require('chartjs-color');
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
// -- Basic js utility methods

View File

@ -1,7 +1,8 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
/**
* Helper function to get relative position for an event

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
function filterByPosition(array, position) {
return helpers.where(array, function(v) {

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.global.plugins = {};

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.defaults.scale = {
display: true,

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
Chart.scaleService = {
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
/**
* Namespace to hold static tick generation functions

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
/**
* Helper method to merge the opacity into a color

View File

@ -1,9 +1,10 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers,
globalOpts = Chart.defaults.global;
var globalOpts = Chart.defaults.global;
globalOpts.elements.arc = {
backgroundColor: globalOpts.defaultColor,

View File

@ -1,8 +1,9 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
var globalDefaults = Chart.defaults.global;
Chart.defaults.global.elements.line = {

View File

@ -1,9 +1,10 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers,
globalOpts = Chart.defaults.global,
var globalOpts = Chart.defaults.global,
defaultColor = globalOpts.defaultColor;
globalOpts.elements.point = {
@ -64,7 +65,7 @@ module.exports = function(Chart) {
var radius = vm.radius;
var x = vm.x;
var y = vm.y;
var color = Chart.helpers.color;
var color = helpers.color;
var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
var ratio = 0;
@ -94,7 +95,7 @@ module.exports = function(Chart) {
ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
}
Chart.helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
}
});
};

View File

@ -1,58 +1,56 @@
'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
var helpers = require('./helpers.core');
/**
* @namespace Chart.helpers.canvas
*/
var exports = module.exports = {
/**
* 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);
},
/**
* @namespace Chart.helpers.canvas
* Creates a "path" for a rectangle with rounded corners at position (x, y) with a
* given size (width, height) and the same `radius` for all corners.
* @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
* @param {Number} x - The x axis of the coordinate for the rectangle starting point.
* @param {Number} y - The y axis of the coordinate for the rectangle starting point.
* @param {Number} width - The rectangle's width.
* @param {Number} height - The rectangle's height.
* @param {Number} radius - The rounded amount (in pixels) for the four corners.
* @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
*/
helpers.canvas = {
/**
* 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);
},
roundedRect: function(ctx, x, y, width, height, radius) {
if (radius) {
var rx = Math.min(radius, width/2);
var ry = Math.min(radius, height/2);
/**
* Creates a "path" for a rectangle with rounded corners at position (x, y) with a
* given size (width, height) and the same `radius` for all corners.
* @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
* @param {Number} x - The x axis of the coordinate for the rectangle starting point.
* @param {Number} y - The y axis of the coordinate for the rectangle starting point.
* @param {Number} width - The rectangle's width.
* @param {Number} height - The rectangle's height.
* @param {Number} radius - The rounded amount (in pixels) for the four corners.
* @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
*/
roundedRect: function(ctx, x, y, width, height, radius) {
if (radius) {
var rx = Math.min(radius, width/2);
var ry = Math.min(radius, height/2);
ctx.moveTo(x + rx, y);
ctx.lineTo(x + width - rx, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
ctx.lineTo(x + width, y + height - ry);
ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
ctx.lineTo(x + rx, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
ctx.lineTo(x, y + ry);
ctx.quadraticCurveTo(x, y, x + rx, y);
} else {
ctx.rect(x, y, width, height);
}
ctx.moveTo(x + rx, y);
ctx.lineTo(x + width - rx, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
ctx.lineTo(x + width, y + height - ry);
ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
ctx.lineTo(x + rx, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
ctx.lineTo(x, y + ry);
ctx.quadraticCurveTo(x, y, x + rx, y);
} else {
ctx.rect(x, y, width, height);
}
};
},
helpers.canvas.drawPoint = function(ctx, pointStyle, radius, x, y) {
drawPoint: function(ctx, style, radius, x, y) {
var type, edgeLength, xOffset, yOffset, height, size;
if (typeof pointStyle === 'object') {
type = pointStyle.toString();
if (typeof style === 'object') {
type = style.toString();
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2, pointStyle.width, pointStyle.height);
ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
return;
}
}
@ -61,7 +59,7 @@ module.exports = function(Chart) {
return;
}
switch (pointStyle) {
switch (style) {
// Default includes circle
default:
ctx.beginPath();
@ -152,20 +150,20 @@ module.exports = function(Chart) {
}
ctx.stroke();
};
},
helpers.canvas.clipArea = function(ctx, clipArea) {
clipArea: function(ctx, area) {
ctx.save();
ctx.beginPath();
ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top);
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
ctx.clip();
};
},
helpers.canvas.unclipArea = function(ctx) {
unclipArea: function(ctx) {
ctx.restore();
};
},
helpers.canvas.lineTo = function(ctx, previous, target, flip) {
lineTo: function(ctx, previous, target, flip) {
if (target.steppedLine) {
if (target.steppedLine === 'after') {
ctx.lineTo(previous.x, target.y);
@ -188,36 +186,29 @@ module.exports = function(Chart) {
flip? target.controlPointNextY : target.controlPointPreviousY,
target.x,
target.y);
};
/**
* Provided for backward compatibility, use Chart.helpers.canvas instead.
* @namespace Chart.canvasHelpers
* @deprecated since version 2.6.0
* @todo remove at version 3
* @private
*/
Chart.canvasHelpers = helpers.canvas;
/**
* Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
* @namespace Chart.helpers.clear
* @deprecated since version 2.7.0
* @todo remove at version 3
* @private
*/
helpers.clear = helpers.canvas.clear;
/**
* Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
* @namespace Chart.helpers.drawRoundedRectangle
* @deprecated since version 2.7.0
* @todo remove at version 3
* @private
*/
helpers.drawRoundedRectangle = function(ctx) {
ctx.beginPath();
helpers.canvas.roundedRect.apply(this, arguments);
ctx.closePath();
};
}
};
// DEPRECATIONS
/**
* Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
* @namespace Chart.helpers.clear
* @deprecated since version 2.7.0
* @todo remove at version 3
* @private
*/
helpers.clear = exports.clear;
/**
* Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
* @namespace Chart.helpers.drawRoundedRectangle
* @deprecated since version 2.7.0
* @todo remove at version 3
* @private
*/
helpers.drawRoundedRectangle = function(ctx) {
ctx.beginPath();
exports.roundedRect.apply(exports, arguments);
ctx.closePath();
};

View File

@ -1,295 +1,297 @@
'use strict';
module.exports = function(Chart) {
/**
* @namespace Chart.helpers
*/
var helpers = {
/**
* @namespace Chart.helpers
* An empty function that can be used, for example, for optional callback.
*/
var helpers = Chart.helpers = {
/**
* An empty function that can be used, for example, for optional callback.
*/
noop: function() {},
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 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 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 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 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 `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` 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);
},
/**
* 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);
}
},
/**
* 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);
}
/**
* 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 if (helpers.isObject(loopable)) {
keys = Object.keys(loopable);
len = keys.length;
} else {
for (i = 0; i < len; i++) {
fn.call(thisArg, loopable[keys[i]], keys[i]);
fn.call(thisArg, loopable[i], i);
}
}
},
/**
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
* @see http://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;
} 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]);
}
}
},
for (i = 0, ilen=a0.length; i < ilen; ++i) {
v0 = a0[i];
v1 = a1[i];
/**
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
* @see http://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 (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}
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;
},
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);
}
/**
* 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;
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);
}
for (; k<klen; ++k) {
target[keys[k]] = helpers.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|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});
}
};
return source;
},
/**
* Provided for backward compatibility, use Chart.helpers.callback instead.
* @function Chart.helpers.callCallback
* @deprecated since version 2.6.0
* @todo remove at version 3
* 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
*/
helpers.callCallback = helpers.callback;
_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);
}
},
/**
* 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
* Merges source[key] in target[key] only if target[key] is undefined.
* @private
*/
helpers.indexOf = function(array, item, fromIndex) {
return Array.prototype.indexOf.call(array, item, fromIndex);
};
_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);
}
},
/**
* 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
* 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.
*/
helpers.getValueOrDefault = helpers.valueOrDefault;
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;
},
/**
* 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
* 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.
*/
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
mergeIf: function(target, source) {
return helpers.merge(target, source, {merger: helpers._mergerIf});
}
};
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;

View File

@ -1,8 +1,11 @@
'use strict';
var helpers = require('./helpers.core');
/**
* Easing functions adapted from Robert Penner's easing equations.
* http://www.robertpenner.com/easing/
* @namespace Chart.helpers.easingEffects
* @see http://www.robertpenner.com/easing/
*/
var effects = {
linear: function(t) {
@ -231,22 +234,17 @@ var effects = {
}
};
module.exports = function(Chart) {
/**
* @namespace Chart.helpers.easing.effects
*/
Chart.helpers.easing = {
effects: effects
};
/**
* Provided for backward compatibility, use Chart.helpers.easing.effects instead.
* @function Chart.helpers.easingEffects
* @deprecated since version 2.7.0
* @todo remove at version 3
* @private
*/
Chart.helpers.easingEffects = effects;
return Chart.helpers.easing;
module.exports = {
effects: effects
};
// DEPRECATIONS
/**
* Provided for backward compatibility, use Chart.helpers.easing.effects instead.
* @function Chart.helpers.easingEffects
* @deprecated since version 2.7.0
* @todo remove at version 3
* @private
*/
helpers.easingEffects = effects;

View File

@ -3,223 +3,218 @@
var moment = require('moment');
moment = typeof(moment) === 'function' ? moment : window.moment;
module.exports = function(Chart) {
var interval = {
millisecond: {
size: 1,
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
},
second: {
size: 1000,
steps: [1, 2, 5, 10, 30]
},
minute: {
size: 60000,
steps: [1, 2, 5, 10, 30]
},
hour: {
size: 3600000,
steps: [1, 2, 3, 6, 12]
},
day: {
size: 86400000,
steps: [1, 2, 5]
},
week: {
size: 604800000,
maxStep: 4
},
month: {
size: 2.628e9,
maxStep: 3
},
quarter: {
size: 7.884e9,
maxStep: 4
},
year: {
size: 3.154e10,
maxStep: false
}
};
var interval = {
millisecond: {
size: 1,
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
},
second: {
size: 1000,
steps: [1, 2, 5, 10, 30]
},
minute: {
size: 60000,
steps: [1, 2, 5, 10, 30]
},
hour: {
size: 3600000,
steps: [1, 2, 3, 6, 12]
},
day: {
size: 86400000,
steps: [1, 2, 5]
},
week: {
size: 604800000,
maxStep: 4
},
month: {
size: 2.628e9,
maxStep: 3
},
quarter: {
size: 7.884e9,
maxStep: 4
},
year: {
size: 3.154e10,
maxStep: false
/**
* Helper for generating axis labels.
* @param options {ITimeGeneratorOptions} the options for generation
* @param dataRange {IRange} the data range
* @param niceRange {IRange} the pretty range to display
* @return {Number[]} ticks
*/
function generateTicksNiceRange(options, dataRange, niceRange) {
var ticks = [];
if (options.maxTicks) {
var stepSize = options.stepSize;
var startTick = options.min !== undefined ? options.min : niceRange.min;
var majorUnit = options.majorUnit;
var majorUnitStart = majorUnit ? moment(startTick).add(1, majorUnit).startOf(majorUnit) : startTick;
var startRange = majorUnitStart.valueOf() - startTick;
var stepValue = interval[options.unit].size * stepSize;
var startFraction = startRange % stepValue;
var alignedTick = startTick;
if (startFraction && majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) {
alignedTick += startFraction - stepValue;
ticks.push(alignedTick);
} else {
ticks.push(startTick);
}
};
/**
* Helper for generating axis labels.
* @param options {ITimeGeneratorOptions} the options for generation
* @param dataRange {IRange} the data range
* @param niceRange {IRange} the pretty range to display
* @return {Number[]} ticks
*/
function generateTicksNiceRange(options, dataRange, niceRange) {
var ticks = [];
if (options.maxTicks) {
var stepSize = options.stepSize;
var startTick = options.min !== undefined ? options.min : niceRange.min;
var majorUnit = options.majorUnit;
var majorUnitStart = majorUnit ? moment(startTick).add(1, majorUnit).startOf(majorUnit) : startTick;
var startRange = majorUnitStart.valueOf() - startTick;
var stepValue = interval[options.unit].size * stepSize;
var startFraction = startRange % stepValue;
var alignedTick = startTick;
if (startFraction && majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) {
alignedTick += startFraction - stepValue;
ticks.push(alignedTick);
} else {
ticks.push(startTick);
}
var cur = moment(alignedTick);
var realMax = options.max || niceRange.max;
while (cur.add(stepSize, options.unit).valueOf() < realMax) {
ticks.push(cur.valueOf());
}
var cur = moment(alignedTick);
var realMax = options.max || niceRange.max;
while (cur.add(stepSize, options.unit).valueOf() < realMax) {
ticks.push(cur.valueOf());
}
return ticks;
ticks.push(cur.valueOf());
}
return ticks;
}
Chart.helpers = Chart.helpers || {};
/**
* @namespace Chart.helpers.time;
*/
module.exports = {
/**
* Helper function to parse time to a moment object
* @param axis {TimeAxis} the time axis
* @param label {Date|string|number|Moment} The thing to parse
* @return {Moment} parsed time
*/
parseTime: function(axis, label) {
var timeOpts = axis.options.time;
if (typeof timeOpts.parser === 'string') {
return moment(label, timeOpts.parser);
}
if (typeof timeOpts.parser === 'function') {
return timeOpts.parser(label);
}
if (typeof label.getMonth === 'function' || typeof label === 'number') {
// Date objects
return moment(label);
}
if (label.isValid && label.isValid()) {
// Moment support
return label;
}
var format = timeOpts.format;
if (typeof format !== 'string' && format.call) {
// Custom parsing (return an instance of moment)
console.warn('options.time.format is deprecated and replaced by options.time.parser.');
return format(label);
}
// Moment format parsing
return moment(label, format);
},
Chart.helpers.time = {
/**
* Figure out which is the best unit for the scale
* @param minUnit {String} minimum unit to use
* @param min {Number} scale minimum
* @param max {Number} scale maximum
* @return {String} the unit to use
*/
determineUnit: function(minUnit, min, max, maxTicks) {
var units = Object.keys(interval);
var unit;
var numUnits = units.length;
/**
* Helper function to parse time to a moment object
* @param axis {TimeAxis} the time axis
* @param label {Date|string|number|Moment} The thing to parse
* @return {Moment} parsed time
*/
parseTime: function(axis, label) {
var timeOpts = axis.options.time;
if (typeof timeOpts.parser === 'string') {
return moment(label, timeOpts.parser);
for (var i = units.indexOf(minUnit); i < numUnits; i++) {
unit = units[i];
var unitDetails = interval[unit];
var steps = (unitDetails.steps && unitDetails.steps[unitDetails.steps.length - 1]) || unitDetails.maxStep;
if (steps === undefined || Math.ceil((max - min) / (steps * unitDetails.size)) <= maxTicks) {
break;
}
if (typeof timeOpts.parser === 'function') {
return timeOpts.parser(label);
}
if (typeof label.getMonth === 'function' || typeof label === 'number') {
// Date objects
return moment(label);
}
if (label.isValid && label.isValid()) {
// Moment support
return label;
}
var format = timeOpts.format;
if (typeof format !== 'string' && format.call) {
// Custom parsing (return an instance of moment)
console.warn('options.time.format is deprecated and replaced by options.time.parser.');
return format(label);
}
// Moment format parsing
return moment(label, format);
},
/**
* Figure out which is the best unit for the scale
* @param minUnit {String} minimum unit to use
* @param min {Number} scale minimum
* @param max {Number} scale maximum
* @return {String} the unit to use
*/
determineUnit: function(minUnit, min, max, maxTicks) {
var units = Object.keys(interval);
var unit;
var numUnits = units.length;
for (var i = units.indexOf(minUnit); i < numUnits; i++) {
unit = units[i];
var unitDetails = interval[unit];
var steps = (unitDetails.steps && unitDetails.steps[unitDetails.steps.length - 1]) || unitDetails.maxStep;
if (steps === undefined || Math.ceil((max - min) / (steps * unitDetails.size)) <= maxTicks) {
break;
}
}
return unit;
},
/**
* Determine major unit accordingly to passed unit
* @param unit {String} relative unit
* @return {String} major unit
*/
determineMajorUnit: function(unit) {
var units = Object.keys(interval);
var unitIndex = units.indexOf(unit);
while (unitIndex < units.length) {
var majorUnit = units[++unitIndex];
// exclude 'week' and 'quarter' units
if (majorUnit !== 'week' && majorUnit !== 'quarter') {
return majorUnit;
}
}
return null;
},
/**
* Determines how we scale the unit
* @param min {Number} the scale minimum
* @param max {Number} the scale maximum
* @param unit {String} the unit determined by the {@see determineUnit} method
* @return {Number} the axis step size as a multiple of unit
*/
determineStepSize: function(min, max, unit, maxTicks) {
// Using our unit, figure out what we need to scale as
var unitDefinition = interval[unit];
var unitSizeInMilliSeconds = unitDefinition.size;
var sizeInUnits = Math.ceil((max - min) / unitSizeInMilliSeconds);
var multiplier = 1;
var range = max - min;
if (unitDefinition.steps) {
// Have an array of steps
var numSteps = unitDefinition.steps.length;
for (var i = 0; i < numSteps && sizeInUnits > maxTicks; i++) {
multiplier = unitDefinition.steps[i];
sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
}
} else {
while (sizeInUnits > maxTicks && maxTicks > 0) {
++multiplier;
sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
}
}
return multiplier;
},
/**
* @function generateTicks
* @param options {ITimeGeneratorOptions} the options for generation
* @param dataRange {IRange} the data range
* @return {Number[]} ticks
*/
generateTicks: function(options, dataRange) {
var niceMin;
var niceMax;
var isoWeekday = options.timeOpts.isoWeekday;
if (options.unit === 'week' && isoWeekday !== false) {
niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf();
niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday);
if (dataRange.max - niceMax > 0) {
niceMax.add(1, 'week');
}
niceMax = niceMax.valueOf();
} else {
niceMin = moment(dataRange.min).startOf(options.unit).valueOf();
niceMax = moment(dataRange.max).startOf(options.unit);
if (dataRange.max - niceMax > 0) {
niceMax.add(1, options.unit);
}
niceMax = niceMax.valueOf();
}
return generateTicksNiceRange(options, dataRange, {
min: niceMin,
max: niceMax
});
}
};
return unit;
},
/**
* Determine major unit accordingly to passed unit
* @param unit {String} relative unit
* @return {String} major unit
*/
determineMajorUnit: function(unit) {
var units = Object.keys(interval);
var unitIndex = units.indexOf(unit);
while (unitIndex < units.length) {
var majorUnit = units[++unitIndex];
// exclude 'week' and 'quarter' units
if (majorUnit !== 'week' && majorUnit !== 'quarter') {
return majorUnit;
}
}
return null;
},
/**
* Determines how we scale the unit
* @param min {Number} the scale minimum
* @param max {Number} the scale maximum
* @param unit {String} the unit determined by the {@see determineUnit} method
* @return {Number} the axis step size as a multiple of unit
*/
determineStepSize: function(min, max, unit, maxTicks) {
// Using our unit, figure out what we need to scale as
var unitDefinition = interval[unit];
var unitSizeInMilliSeconds = unitDefinition.size;
var sizeInUnits = Math.ceil((max - min) / unitSizeInMilliSeconds);
var multiplier = 1;
var range = max - min;
if (unitDefinition.steps) {
// Have an array of steps
var numSteps = unitDefinition.steps.length;
for (var i = 0; i < numSteps && sizeInUnits > maxTicks; i++) {
multiplier = unitDefinition.steps[i];
sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
}
} else {
while (sizeInUnits > maxTicks && maxTicks > 0) {
++multiplier;
sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
}
}
return multiplier;
},
/**
* @function generateTicks
* @param options {ITimeGeneratorOptions} the options for generation
* @param dataRange {IRange} the data range
* @return {Number[]} ticks
*/
generateTicks: function(options, dataRange) {
var niceMin;
var niceMax;
var isoWeekday = options.timeOpts.isoWeekday;
if (options.unit === 'week' && isoWeekday !== false) {
niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf();
niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday);
if (dataRange.max - niceMax > 0) {
niceMax.add(1, 'week');
}
niceMax = niceMax.valueOf();
} else {
niceMin = moment(dataRange.min).startOf(options.unit).valueOf();
niceMax = moment(dataRange.max).startOf(options.unit);
if (dataRange.max - niceMax > 0) {
niceMax.add(1, options.unit);
}
niceMax = niceMax.valueOf();
}
return generateTicksNiceRange(options, dataRange, {
min: niceMin,
max: niceMax
});
}
};

6
src/helpers/index.js Normal file
View File

@ -0,0 +1,6 @@
'use strict';
module.exports = require('./helpers.core');
module.exports.easing = require('./helpers.easing');
module.exports.canvas = require('./helpers.canvas');
module.exports.time = require('./helpers.time');

View File

@ -1,8 +1,9 @@
'use strict';
var helpers = require('../helpers/index');
// Chart.Platform implementation for targeting a web browser
module.exports = function(Chart) {
var helpers = Chart.helpers;
module.exports = function() {
// DOM event types -> Chart.js event types.
// Note: only events with different types are mapped.

View File

@ -1,8 +1,10 @@
'use strict';
var helpers = require('../helpers/index');
// By default, select the browser (DOM) platform.
// @TODO Make possible to select another platform at build time.
var implementation = require('./platform.dom.js');
var implementation = require('./platform.dom');
module.exports = function(Chart) {
/**
@ -65,5 +67,5 @@ module.exports = function(Chart) {
* @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
*/
Chart.helpers.extend(Chart.platform, implementation(Chart));
helpers.extend(Chart.platform, implementation(Chart));
};

View File

@ -1,5 +1,7 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
/**
* Plugin based on discussion from the following Chart.js issues:
@ -11,7 +13,6 @@ module.exports = function(Chart) {
};
var defaults = Chart.defaults;
var helpers = Chart.helpers;
var mappers = {
dataset: function(source) {
var index = source.fill;

View File

@ -1,8 +1,9 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
var layout = Chart.layoutService;
var noop = helpers.noop;
@ -360,7 +361,7 @@ module.exports = function(Chart) {
var centerY = y + offSet;
// Draw pointStyle as legend symbol
Chart.helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
} else {
// Draw box as legend symbol
if (!isLineWidthZero) {

View File

@ -1,8 +1,9 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
var layout = Chart.layoutService;
var noop = helpers.noop;

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
var defaultConfig = {
position: 'left',

View File

@ -1,9 +1,10 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers,
noop = helpers.noop;
var noop = helpers.noop;
Chart.LinearScaleBase = Chart.Scale.extend({
handleTickRangeOptions: function() {

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = function(Chart) {
var helpers = require('../helpers/index');
var helpers = Chart.helpers;
module.exports = function(Chart) {
var defaultConfig = {
position: 'left',

View File

@ -1,8 +1,9 @@
'use strict';
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
var globalDefaults = Chart.defaults.global;
var defaultConfig = {

View File

@ -4,9 +4,10 @@
var moment = require('moment');
moment = typeof(moment) === 'function' ? moment : window.moment;
var helpers = require('../helpers/index');
module.exports = function(Chart) {
var helpers = Chart.helpers;
var timeHelpers = helpers.time;
var defaultConfig = {