Move some of the "core" and "canvas" utils in `helpers.core.js` and `helpers.canvas.js` and introduce the new `isNullOrUndef` and `isObject` helpers. Deprecate `indexOf` and rename `drawRoundedRectangle` to `roundedRect` which now creates a simple `rect` path if radius is 0. Write missing unit tests for the moved helpers.
/dist
/docs/index.md
/gh-pages
+/jsdoc
/node_modules
.DS_Store
.idea
*/
var Chart = require('./core/core.js')();
+require('./helpers/helpers.core')(Chart);
require('./core/core.helpers')(Chart);
require('./helpers/helpers.time')(Chart);
+require('./helpers/helpers.canvas')(Chart);
require('./platforms/platform.js')(Chart);
-require('./core/core.canvasHelpers')(Chart);
require('./core/core.element')(Chart);
require('./core/core.plugin.js')(Chart);
require('./core/core.animation')(Chart);
var ilen = points.length;
var i = 0;
- Chart.canvasHelpers.clipArea(chart.ctx, area);
+ Chart.helpers.canvas.clipArea(chart.ctx, area);
if (lineEnabled(me.getDataset(), chart.options)) {
meta.dataset.draw();
}
- Chart.canvasHelpers.unclipArea(chart.ctx);
+ Chart.helpers.canvas.unclipArea(chart.ctx);
// Draw the points
for (; i<ilen; ++i) {
},
clear: function() {
- helpers.clear(this);
+ helpers.canvas.clear(this);
return this;
},
me.clear();
- if (easingValue === undefined || easingValue === null) {
+ if (helpers.isNullOrUndef(easingValue)) {
easingValue = 1;
}
if (canvas) {
me.unbindEvents();
- helpers.clear(me);
+ helpers.canvas.clear(me);
platform.releaseContext(me.ctx);
me.canvas = null;
me.ctx = null;
var color = require('chartjs-color');
module.exports = function(Chart) {
- // Global Chart helpers object for utility methods and classes
- var helpers = Chart.helpers = {};
+ var helpers = Chart.helpers;
// -- Basic js utility methods
- helpers.each = function(loopable, callback, self, reverse) {
- // Check to see if null or undefined firstly.
- var i, len;
- if (helpers.isArray(loopable)) {
- len = loopable.length;
- if (reverse) {
- for (i = len - 1; i >= 0; i--) {
- callback.call(self, loopable[i], i);
- }
- } else {
- for (i = 0; i < len; i++) {
- callback.call(self, loopable[i], i);
- }
- }
- } else if (typeof loopable === 'object') {
- var keys = Object.keys(loopable);
- len = keys.length;
- for (i = 0; i < len; i++) {
- callback.call(self, loopable[keys[i]], keys[i]);
- }
- }
- };
helpers.clone = function(obj) {
var objClone = {};
helpers.each(obj, function(value, key) {
return base;
};
- helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
- if (value === undefined || value === null) {
- return defaultValue;
- }
-
- if (helpers.isArray(value)) {
- return index < value.length ? value[index] : defaultValue;
- }
- return value;
- };
- helpers.getValueOrDefault = function(value, defaultValue) {
- return value === undefined ? defaultValue : value;
- };
- helpers.indexOf = Array.prototype.indexOf?
- function(array, item) {
- return array.indexOf(item);
- }:
- function(array, item) {
- for (var i = 0, ilen = array.length; i < ilen; ++i) {
- if (array[i] === item) {
- return i;
- }
- }
- return -1;
- };
helpers.where = function(collection, filterCallback) {
if (helpers.isArray(collection) && Array.prototype.filter) {
return collection.filter(filterCallback);
};
helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
// Default to start of the array
- if (startIndex === undefined || startIndex === null) {
+ if (helpers.isNullOrUndef(startIndex)) {
startIndex = -1;
}
for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
};
helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
// Default to end of the array
- if (startIndex === undefined || startIndex === null) {
+ if (helpers.isNullOrUndef(startIndex)) {
startIndex = arrayToSearch.length;
}
for (var i = startIndex - 1; i >= 0; i--) {
return ChartElement;
};
- helpers.noop = function() {};
- helpers.uid = (function() {
- var id = 0;
- return function() {
- return id++;
- };
- }());
// -- Math methods
helpers.isNumber = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
canvas.style.width = width + 'px';
};
// -- Canvas methods
- helpers.clear = function(chart) {
- chart.ctx.clearRect(0, 0, chart.width, chart.height);
- };
helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
};
});
return numberOfLines;
};
- helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
- ctx.beginPath();
- ctx.moveTo(x + radius, y);
- ctx.lineTo(x + width - radius, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
- ctx.lineTo(x + width, y + height - radius);
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
- ctx.lineTo(x + radius, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
- ctx.lineTo(x, y + radius);
- ctx.quadraticCurveTo(x, y, x + radius, y);
- ctx.closePath();
- };
helpers.color = !color?
function(value) {
return color(value);
};
- helpers.isArray = Array.isArray?
- function(obj) {
- return Array.isArray(obj);
- } :
- function(obj) {
- return Object.prototype.toString.call(obj) === '[object Array]';
- };
- // ! @see http://stackoverflow.com/a/14853974
- helpers.arrayEquals = function(a0, a1) {
- var i, ilen, v0, v1;
-
- if (!a0 || !a1 || a0.length !== a1.length) {
- return false;
- }
-
- for (i = 0, ilen=a0.length; i < ilen; ++i) {
- v0 = a0[i];
- v1 = a1[i];
-
- if (v0 instanceof Array && v1 instanceof Array) {
- if (!helpers.arrayEquals(v0, v1)) {
- return false;
- }
- } else if (v0 !== v1) {
- // NOTE: two different object instances will never be equal: {x:20} != {x:20}
- return false;
- }
- }
-
- return true;
- };
- helpers.callback = function(fn, args, thisArg) {
- if (fn && typeof fn.call === 'function') {
- return fn.apply(thisArg, args);
- }
- };
helpers.getHoverColor = function(colorValue) {
/* global CanvasPattern */
return (colorValue instanceof CanvasPattern) ?
colorValue :
helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
};
-
- /**
- * Provided for backward compatibility, use Chart.helpers#callback instead.
- * @function Chart.helpers#callCallback
- * @deprecated since version 2.6.0
- * @todo remove at version 3
- */
- helpers.callCallback = helpers.callback;
};
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
getRightValue: function(rawValue) {
// Null and undefined values first
- if (rawValue === null || typeof(rawValue) === 'undefined') {
+ if (helpers.isNullOrUndef(rawValue)) {
return NaN;
}
// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
helpers.each(me.ticks, function(tick, index) {
var label = (tick && tick.value) || tick;
// If the callback returned a null or undefined value, do not draw this line
- if (label === undefined || label === null) {
+ if (helpers.isNullOrUndef(label)) {
return;
}
// Since we always show the last tick,we need may need to hide the last shown one before
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length);
- if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
+ if (shouldSkip && !isLastTick || helpers.isNullOrUndef(label)) {
return;
}
ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
}
- Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
+ Chart.helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
}
});
};
'use strict';
module.exports = function(Chart) {
- // Global Chart canvas helpers object for drawing items to canvas
- var helpers = Chart.canvasHelpers = {};
+ var helpers = Chart.helpers;
- helpers.drawPoint = function(ctx, pointStyle, radius, x, y) {
+ /**
+ * @namespace Chart.helpers.canvas
+ */
+ 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);
+ },
+
+ /**
+ * 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 handler `radius` as top-left, top-right, bottom-right, bottom-left array/object?
+ * @todo clamp `radius` to the maximum "correct" value.
+ */
+ roundedRect: function(ctx, x, y, width, height, radius) {
+ if (radius) {
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + width - radius, y);
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+ ctx.lineTo(x + radius, y + height);
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
+ ctx.lineTo(x, y + radius);
+ ctx.quadraticCurveTo(x, y, x + radius, y);
+ } else {
+ ctx.rect(x, y, width, height);
+ }
+ }
+ };
+
+ helpers.canvas.drawPoint = function(ctx, pointStyle, radius, x, y) {
var type, edgeLength, xOffset, yOffset, height, size;
if (typeof pointStyle === 'object') {
var leftX = x - offset;
var topY = y - offset;
var sideSize = Math.SQRT2 * radius;
- Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2);
+ ctx.beginPath();
+ this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
+ ctx.closePath();
ctx.fill();
break;
case 'rectRot':
ctx.stroke();
};
- helpers.clipArea = function(ctx, clipArea) {
+ helpers.canvas.clipArea = function(ctx, clipArea) {
ctx.save();
ctx.beginPath();
ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top);
ctx.clip();
};
- helpers.unclipArea = function(ctx) {
+ helpers.canvas.unclipArea = function(ctx) {
ctx.restore();
};
- helpers.lineTo = function(ctx, previous, target, flip) {
+ helpers.canvas.lineTo = function(ctx, previous, target, flip) {
if (target.steppedLine) {
if (target.steppedLine === 'after') {
ctx.lineTo(previous.x, target.y);
target.y);
};
- Chart.helpers.canvas = helpers;
+ /**
+ * 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();
+ };
};
--- /dev/null
+'use strict';
+
+module.exports = function(Chart) {
+ /**
+ * @namespace Chart.helpers
+ */
+ var helpers = Chart.helpers = {
+ /**
+ * An empty function that can be used, for example, for optional callback.
+ */
+ noop: function() {},
+
+ /**
+ * Returns a unique id, sequentially generated from a global variable.
+ * @returns {Number}
+ * @function
+ */
+ uid: (function() {
+ var id = 0;
+ return function() {
+ return id++;
+ };
+ }()),
+
+ /**
+ * Returns true if `value` is neither null nor undefined, else returns false.
+ * @param {*} value - The value to test.
+ * @returns {Boolean}
+ * @since 2.7.0
+ */
+ isNullOrUndef: function(value) {
+ return value === null || typeof value === 'undefined';
+ },
+
+ /**
+ * Returns true if `value` is an array, else returns false.
+ * @param {*} value - The value to test.
+ * @returns {Boolean}
+ * @function
+ */
+ isArray: Array.isArray? Array.isArray : function(value) {
+ return Object.prototype.toString.call(value) === '[object Array]';
+ },
+
+ /**
+ * Returns true if `value` is an object (excluding null), else returns false.
+ * @param {*} value - The value to test.
+ * @returns {Boolean}
+ * @since 2.7.0
+ */
+ isObject: function(value) {
+ return value !== null && Object.prototype.toString.call(value) === '[object Object]';
+ },
+
+ /**
+ * Returns `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 {*}
+ */
+ getValueOrDefault: 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 {*}
+ */
+ getValueAtIndexOrDefault: function(value, index, defaultValue) {
+ if (helpers.isNullOrUndef(value)) {
+ return defaultValue;
+ }
+
+ if (helpers.isArray(value)) {
+ value = value[index];
+ return typeof value === 'undefined'? defaultValue : value;
+ }
+
+ return value;
+ },
+
+ /**
+ * 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 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;
+ }
+
+ 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;
+ }
+ };
+
+ /**
+ * 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);
+ };
+};
var initial = canvas._chartjs.initial;
['height', 'width'].forEach(function(prop) {
var value = initial[prop];
- if (value === undefined || value === null) {
+ if (helpers.isNullOrUndef(value)) {
canvas.removeAttribute(prop);
} else {
canvas.setAttribute(prop, value);
var centerY = y + offSet;
// Draw pointStyle as legend symbol
- Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
+ Chart.helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
} else {
// Draw box as legend symbol
if (!isLineWidthZero) {
module.exports = function(Chart) {
- var helpers = Chart.helpers;
// Default config for a category scale
var defaultConfig = {
position: 'bottom'
if (me.options.ticks.min !== undefined) {
// user specified min value
- findIndex = helpers.indexOf(labels, me.options.ticks.min);
+ findIndex = labels.indexOf(me.options.ticks.min);
me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
}
if (me.options.ticks.max !== undefined) {
// user specified max value
- findIndex = helpers.indexOf(labels, me.options.ticks.max);
+ findIndex = labels.indexOf(me.options.ticks.max);
me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
}
},
moveTo: function() {},
quadraticCurveTo: function() {},
+ rect: function() {},
restore: function() {},
rotate: function() {},
save: function() {},
helpers = window.Chart.helpers;
});
- it('should iterate over an array and pass the extra data to that function', function() {
- var testData = [0, 9, 'abc'];
- var scope = {}; // fake out the scope and ensure that 'this' is the correct thing
-
- helpers.each(testData, function(item, index) {
- expect(item).not.toBe(undefined);
- expect(index).not.toBe(undefined);
-
- expect(testData[index]).toBe(item);
- expect(this).toBe(scope);
- }, scope);
-
- // Reverse iteration
- var iterated = [];
- helpers.each(testData, function(item, index) {
- expect(item).not.toBe(undefined);
- expect(index).not.toBe(undefined);
-
- expect(testData[index]).toBe(item);
- expect(this).toBe(scope);
-
- iterated.push(item);
- }, scope, true);
-
- expect(iterated.slice().reverse()).toEqual(testData);
- });
-
- it('should iterate over properties in an object', function() {
- var testData = {
- myProp1: 'abc',
- myProp2: 276,
- myProp3: ['a', 'b']
- };
-
- helpers.each(testData, function(value, key) {
- if (key === 'myProp1') {
- expect(value).toBe('abc');
- } else if (key === 'myProp2') {
- expect(value).toBe(276);
- } else if (key === 'myProp3') {
- expect(value).toEqual(['a', 'b']);
- } else {
- expect(false).toBe(true);
- }
- });
- });
-
- it('should not error when iterating over a null object', function() {
- expect(function() {
- helpers.each(undefined);
- }).not.toThrow();
- });
-
it('should clone an object', function() {
var testData = {
myProp1: 'abc',
expect(merged.scales.yAxes[2].ticks.callback).toEqual(jasmine.any(Function));
});
- it('should get value or default', function() {
- expect(helpers.getValueAtIndexOrDefault(98, 0, 56)).toBe(98);
- expect(helpers.getValueAtIndexOrDefault(0, 0, 56)).toBe(0);
- expect(helpers.getValueAtIndexOrDefault(undefined, undefined, 56)).toBe(56);
- expect(helpers.getValueAtIndexOrDefault([1, 2, 3], 1, 100)).toBe(2);
- expect(helpers.getValueAtIndexOrDefault([1, 2, 3], 3, 100)).toBe(100);
- });
-
it('should filter an array', function() {
var data = [-10, 0, 6, 0, 7];
var callback = function(item) {
expect(helpers.previousItem(testData, 1, true)).toEqual(0);
});
- it('should clear a canvas', function() {
- var context = window.createMockContext();
- helpers.clear({
- width: 100,
- height: 150,
- ctx: context
- });
-
- expect(context.getCalls()).toEqual([{
- name: 'clearRect',
- args: [0, 0, 100, 150]
- }]);
- });
-
it('should return the width of the longest text in an Array and 2D Array', function() {
var context = window.createMockContext();
var font = "normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif";
expect(helpers.numberOfLabelLines(arrayOfThings3)).toEqual(3);
});
- it('should draw a rounded rectangle', function() {
- var context = window.createMockContext();
- helpers.drawRoundedRectangle(context, 10, 20, 30, 40, 5);
-
- expect(context.getCalls()).toEqual([{
- name: 'beginPath',
- args: []
- }, {
- name: 'moveTo',
- args: [15, 20]
- }, {
- name: 'lineTo',
- args: [35, 20]
- }, {
- name: 'quadraticCurveTo',
- args: [40, 20, 40, 25]
- }, {
- name: 'lineTo',
- args: [40, 55]
- }, {
- name: 'quadraticCurveTo',
- args: [40, 60, 35, 60]
- }, {
- name: 'lineTo',
- args: [15, 60]
- }, {
- name: 'quadraticCurveTo',
- args: [10, 60, 10, 55]
- }, {
- name: 'lineTo',
- args: [10, 25]
- }, {
- name: 'quadraticCurveTo',
- args: [10, 20, 15, 20]
- }, {
- name: 'closePath',
- args: []
- }]);
- });
-
it ('should get the maximum width and height for a node', function() {
// Create div with fixed size as a test bed
var div = document.createElement('div');
}]);
var drawRoundedRectangleSpy = jasmine.createSpy('drawRoundedRectangle');
- var drawRoundedRectangle = Chart.helpers.drawRoundedRectangle;
+ var drawRoundedRectangle = Chart.helpers.canvas.roundedRect;
var offset = point._view.radius / Math.SQRT2;
- Chart.helpers.drawRoundedRectangle = drawRoundedRectangleSpy;
+ Chart.helpers.canvas.roundedRect = drawRoundedRectangleSpy;
mockContext.resetCalls();
point._view.pointStyle = 'rectRounded';
point.draw();
})
);
- Chart.helpers.drawRoundedRectangle = drawRoundedRectangle;
+ Chart.helpers.canvas.roundedRect = drawRoundedRectangle;
mockContext.resetCalls();
point._view.pointStyle = 'rectRot';
point.draw();
describe('Deprecations', function() {
describe('Version 2.7.0', function() {
describe('Chart.Controller.update(duration, lazy)', function() {
- beforeEach(function() {
- this.chart = acquireChart({
+ it('should add an animation with the provided options', function() {
+ var chart = acquireChart({
type: 'doughnut',
options: {
animation: {
}
});
- this.addAnimationSpy = spyOn(Chart.animationService, 'addAnimation');
- });
+ spyOn(Chart.animationService, 'addAnimation');
- it('should add an animation with the provided options', function() {
- this.chart.update(800, false);
+ chart.update(800, false);
- expect(this.addAnimationSpy).toHaveBeenCalledWith(
- this.chart,
+ expect(Chart.animationService.addAnimation).toHaveBeenCalledWith(
+ chart,
jasmine.objectContaining({easing: 'linear'}),
800,
false
});
describe('Chart.Controller.render(duration, lazy)', function() {
- beforeEach(function() {
- this.chart = acquireChart({
+ it('should add an animation with the provided options', function() {
+ var chart = acquireChart({
type: 'doughnut',
options: {
animation: {
}
});
- this.addAnimationSpy = spyOn(Chart.animationService, 'addAnimation');
- });
+ spyOn(Chart.animationService, 'addAnimation');
- it('should add an animation with the provided options', function() {
- this.chart.render(800, true);
+ chart.render(800, true);
- expect(this.addAnimationSpy).toHaveBeenCalledWith(
- this.chart,
+ expect(Chart.animationService.addAnimation).toHaveBeenCalledWith(
+ chart,
jasmine.objectContaining({easing: 'linear'}),
800,
true
);
});
});
+
+ describe('Chart.helpers.indexOf', function() {
+ it('should be defined and a function', function() {
+ expect(Chart.helpers.indexOf).toBeDefined();
+ expect(typeof Chart.helpers.indexOf).toBe('function');
+ });
+ it('should returns the correct index', function() {
+ expect(Chart.helpers.indexOf([1, 2, 42], 42)).toBe(2);
+ expect(Chart.helpers.indexOf([1, 2, 42], 3)).toBe(-1);
+ expect(Chart.helpers.indexOf([1, 42, 2, 42], 42, 2)).toBe(3);
+ expect(Chart.helpers.indexOf([1, 42, 2, 42], 3, 2)).toBe(-1);
+ });
+ });
+
+ describe('Chart.helpers.clear', function() {
+ it('should be defined and an alias of Chart.helpers.canvas.clear', function() {
+ expect(Chart.helpers.clear).toBeDefined();
+ expect(Chart.helpers.clear).toBe(Chart.helpers.canvas.clear);
+ });
+ });
+
+ describe('Chart.helpers.drawRoundedRectangle', function() {
+ it('should be defined and a function', function() {
+ expect(Chart.helpers.drawRoundedRectangle).toBeDefined();
+ expect(typeof Chart.helpers.drawRoundedRectangle).toBe('function');
+ });
+ it('should call Chart.helpers.canvas.roundedRect', function() {
+ var ctx = window.createMockContext();
+ spyOn(Chart.helpers.canvas, 'roundedRect');
+
+ Chart.helpers.drawRoundedRectangle(ctx, 10, 20, 30, 40, 5);
+
+ var calls = ctx.getCalls();
+ expect(calls[0]).toEqual({name: 'beginPath', args: []});
+ expect(calls[calls.length-1]).toEqual({name: 'closePath', args: []});
+ expect(Chart.helpers.canvas.roundedRect).toHaveBeenCalledWith(ctx, 10, 20, 30, 40, 5);
+ });
+ });
});
describe('Version 2.6.0', function() {
});
});
});
+
+ describe('Chart.helpers.callCallback', function() {
+ it('should be defined and an alias of Chart.helpers.callback', function() {
+ expect(Chart.helpers.callCallback).toBeDefined();
+ expect(Chart.helpers.callCallback).toBe(Chart.helpers.callback);
+ });
+ });
});
describe('Version 2.5.0', function() {
--- /dev/null
+'use strict';
+
+describe('Chart.helpers.canvas', function() {
+ var helpers = Chart.helpers;
+
+ describe('clear', function() {
+ it('should clear the chart canvas', function() {
+ var chart = acquireChart({}, {
+ canvas: {
+ style: 'width: 150px; height: 245px'
+ }
+ });
+
+ spyOn(chart.ctx, 'clearRect');
+
+ helpers.canvas.clear(chart);
+
+ expect(chart.ctx.clearRect.calls.count()).toBe(1);
+ expect(chart.ctx.clearRect.calls.first().object).toBe(chart.ctx);
+ expect(chart.ctx.clearRect.calls.first().args).toEqual([0, 0, 150, 245]);
+ });
+ });
+
+ describe('roundedRect', function() {
+ it('should create a rounded rectangle path', function() {
+ var context = window.createMockContext();
+
+ helpers.canvas.roundedRect(context, 10, 20, 30, 40, 5);
+
+ expect(context.getCalls()).toEqual([
+ {name: 'moveTo', args: [15, 20]},
+ {name: 'lineTo', args: [35, 20]},
+ {name: 'quadraticCurveTo', args: [40, 20, 40, 25]},
+ {name: 'lineTo', args: [40, 55]},
+ {name: 'quadraticCurveTo', args: [40, 60, 35, 60]},
+ {name: 'lineTo', args: [15, 60]},
+ {name: 'quadraticCurveTo', args: [10, 60, 10, 55]},
+ {name: 'lineTo', args: [10, 25]},
+ {name: 'quadraticCurveTo', args: [10, 20, 15, 20]}
+ ]);
+ });
+ it('should optimize path if radius is 0', function() {
+ var context = window.createMockContext();
+
+ helpers.canvas.roundedRect(context, 10, 20, 30, 40, 0);
+
+ expect(context.getCalls()).toEqual([{name: 'rect', args: [10, 20, 30, 40]}]);
+ });
+ });
+});
--- /dev/null
+'use strict';
+
+describe('Chart.helpers.core', function() {
+ var helpers = Chart.helpers;
+
+ describe('noop', function() {
+ it('should be callable', function() {
+ expect(helpers.noop).toBeDefined();
+ expect(typeof helpers.noop).toBe('function');
+ expect(typeof helpers.noop.call).toBe('function');
+ });
+ it('should returns "undefined"', function() {
+ expect(helpers.noop(42)).not.toBeDefined();
+ expect(helpers.noop.call(this, 42)).not.toBeDefined();
+ });
+ });
+
+ describe('isArray', function() {
+ it('should return true if value is an array', function() {
+ expect(helpers.isArray([])).toBeTruthy();
+ expect(helpers.isArray([42])).toBeTruthy();
+ expect(helpers.isArray(new Array())).toBeTruthy();
+ expect(helpers.isArray(Array.prototype)).toBeTruthy();
+ });
+ it('should return false if value is not an array', function() {
+ expect(helpers.isArray()).toBeFalsy();
+ expect(helpers.isArray({})).toBeFalsy();
+ expect(helpers.isArray(undefined)).toBeFalsy();
+ expect(helpers.isArray(null)).toBeFalsy();
+ expect(helpers.isArray(true)).toBeFalsy();
+ expect(helpers.isArray(false)).toBeFalsy();
+ expect(helpers.isArray(42)).toBeFalsy();
+ expect(helpers.isArray('Array')).toBeFalsy();
+ expect(helpers.isArray({__proto__: Array.prototype})).toBeFalsy();
+ });
+ });
+
+ describe('isObject', function() {
+ it('should return true if value is an object', function() {
+ expect(helpers.isObject({})).toBeTruthy();
+ expect(helpers.isObject({a: 42})).toBeTruthy();
+ expect(helpers.isObject(new Object())).toBeTruthy();
+ });
+ it('should return false if value is not an object', function() {
+ expect(helpers.isObject()).toBeFalsy();
+ expect(helpers.isObject(undefined)).toBeFalsy();
+ expect(helpers.isObject(null)).toBeFalsy();
+ expect(helpers.isObject(true)).toBeFalsy();
+ expect(helpers.isObject(false)).toBeFalsy();
+ expect(helpers.isObject(42)).toBeFalsy();
+ expect(helpers.isObject('Object')).toBeFalsy();
+ expect(helpers.isObject([])).toBeFalsy();
+ expect(helpers.isObject([42])).toBeFalsy();
+ expect(helpers.isObject(new Array())).toBeFalsy();
+ expect(helpers.isObject(new Date())).toBeFalsy();
+ });
+ });
+
+ describe('isNullOrUndef', function() {
+ it('should return true if value is null/undefined', function() {
+ expect(helpers.isNullOrUndef(null)).toBeTruthy();
+ expect(helpers.isNullOrUndef(undefined)).toBeTruthy();
+ });
+ it('should return false if value is not null/undefined', function() {
+ expect(helpers.isNullOrUndef(true)).toBeFalsy();
+ expect(helpers.isNullOrUndef(false)).toBeFalsy();
+ expect(helpers.isNullOrUndef('')).toBeFalsy();
+ expect(helpers.isNullOrUndef('String')).toBeFalsy();
+ expect(helpers.isNullOrUndef(0)).toBeFalsy();
+ expect(helpers.isNullOrUndef([])).toBeFalsy();
+ expect(helpers.isNullOrUndef({})).toBeFalsy();
+ expect(helpers.isNullOrUndef([42])).toBeFalsy();
+ expect(helpers.isNullOrUndef(new Date())).toBeFalsy();
+ });
+ });
+
+ describe('getValueOrDefault', function() {
+ it('should return value if defined', function() {
+ var object = {};
+ var array = [];
+
+ expect(helpers.getValueOrDefault(null, 42)).toBe(null);
+ expect(helpers.getValueOrDefault(false, 42)).toBe(false);
+ expect(helpers.getValueOrDefault(object, 42)).toBe(object);
+ expect(helpers.getValueOrDefault(array, 42)).toBe(array);
+ expect(helpers.getValueOrDefault('', 42)).toBe('');
+ expect(helpers.getValueOrDefault(0, 42)).toBe(0);
+ });
+ it('should return default if undefined', function() {
+ expect(helpers.getValueOrDefault(undefined, 42)).toBe(42);
+ expect(helpers.getValueOrDefault({}.foo, 42)).toBe(42);
+ });
+ });
+
+ describe('getValueAtIndexOrDefault', function() {
+ it('should return the passed value if not an array', function() {
+ expect(helpers.getValueAtIndexOrDefault(0, 0, 42)).toBe(0);
+ expect(helpers.getValueAtIndexOrDefault('', 0, 42)).toBe('');
+ expect(helpers.getValueAtIndexOrDefault(false, 0, 42)).toBe(false);
+ expect(helpers.getValueAtIndexOrDefault(98, 0, 42)).toBe(98);
+ });
+ it('should return the default value if the passed value is null or undefined', function() {
+ expect(helpers.getValueAtIndexOrDefault(null, 0, 42)).toBe(42);
+ expect(helpers.getValueAtIndexOrDefault(undefined, 0, 42)).toBe(42);
+ });
+ it('should return the value at index if defined', function() {
+ expect(helpers.getValueAtIndexOrDefault([1, false, 'foo'], 1, 42)).toBe(false);
+ expect(helpers.getValueAtIndexOrDefault([1, false, 'foo'], 2, 42)).toBe('foo');
+ });
+ it('should return the default value if value at index is undefined', function() {
+ expect(helpers.getValueAtIndexOrDefault([1, false, 'foo'], 3, 42)).toBe(42);
+ expect(helpers.getValueAtIndexOrDefault([1, undefined, 'foo'], 1, 42)).toBe(42);
+ });
+ });
+
+ describe('callback', function() {
+ it('should return undefined if fn is not a function', function() {
+ expect(helpers.callback()).not.toBeDefined();
+ expect(helpers.callback(null)).not.toBeDefined();
+ expect(helpers.callback(42)).not.toBeDefined();
+ expect(helpers.callback([])).not.toBeDefined();
+ expect(helpers.callback({})).not.toBeDefined();
+ });
+ it('should call fn with the given args', function() {
+ var spy = jasmine.createSpy('spy');
+ helpers.callback(spy);
+ helpers.callback(spy, []);
+ helpers.callback(spy, ['foo']);
+ helpers.callback(spy, [42, 'bar']);
+
+ expect(spy.calls.argsFor(0)).toEqual([]);
+ expect(spy.calls.argsFor(1)).toEqual([]);
+ expect(spy.calls.argsFor(2)).toEqual(['foo']);
+ expect(spy.calls.argsFor(3)).toEqual([42, 'bar']);
+ });
+ it('should call fn with the given scope', function() {
+ var spy = jasmine.createSpy('spy');
+ var scope = {};
+
+ helpers.callback(spy);
+ helpers.callback(spy, [], null);
+ helpers.callback(spy, [], undefined);
+ helpers.callback(spy, [], scope);
+
+ expect(spy.calls.all()[0].object).toBe(window);
+ expect(spy.calls.all()[1].object).toBe(window);
+ expect(spy.calls.all()[2].object).toBe(window);
+ expect(spy.calls.all()[3].object).toBe(scope);
+ });
+ it('should return the value returned by fn', function() {
+ expect(helpers.callback(helpers.noop, [41])).toBe(undefined);
+ expect(helpers.callback(function(i) {
+ return i+1;
+ }, [41])).toBe(42);
+ });
+ });
+
+ describe('each', function() {
+ it('should iterate over an array forward if reverse === false', function() {
+ var scope = {};
+ var scopes = [];
+ var items = [];
+ var keys = [];
+
+ helpers.each(['foo', 'bar', 42], function(item, key) {
+ scopes.push(this);
+ items.push(item);
+ keys.push(key);
+ }, scope);
+
+ expect(scopes).toEqual([scope, scope, scope]);
+ expect(items).toEqual(['foo', 'bar', 42]);
+ expect(keys).toEqual([0, 1, 2]);
+ });
+ it('should iterate over an array backward if reverse === true', function() {
+ var scope = {};
+ var scopes = [];
+ var items = [];
+ var keys = [];
+
+ helpers.each(['foo', 'bar', 42], function(item, key) {
+ scopes.push(this);
+ items.push(item);
+ keys.push(key);
+ }, scope, true);
+
+ expect(scopes).toEqual([scope, scope, scope]);
+ expect(items).toEqual([42, 'bar', 'foo']);
+ expect(keys).toEqual([2, 1, 0]);
+ });
+ it('should iterate over object properties', function() {
+ var scope = {};
+ var scopes = [];
+ var items = [];
+
+ helpers.each({a: 'foo', b: 'bar', c: 42}, function(item, key) {
+ scopes.push(this);
+ items[key] = item;
+ }, scope);
+
+ expect(scopes).toEqual([scope, scope, scope]);
+ expect(items).toEqual(jasmine.objectContaining({a: 'foo', b: 'bar', c: 42}));
+ });
+ it('should not throw when called with a non iterable object', function() {
+ expect(function() {
+ helpers.each(undefined);
+ }).not.toThrow();
+ expect(function() {
+ helpers.each(null);
+ }).not.toThrow();
+ expect(function() {
+ helpers.each(42);
+ }).not.toThrow();
+ });
+ });
+
+ describe('arrayEquals', function() {
+ it('should return false if arrays are not the same', function() {
+ expect(helpers.arrayEquals([], [42])).toBeFalsy();
+ expect(helpers.arrayEquals([42], ['42'])).toBeFalsy();
+ expect(helpers.arrayEquals([1, 2, 3], [1, 2, 3, 4])).toBeFalsy();
+ expect(helpers.arrayEquals(['foo', 'bar'], ['bar', 'foo'])).toBeFalsy();
+ expect(helpers.arrayEquals([1, 2, 3], [1, 2, 'foo'])).toBeFalsy();
+ expect(helpers.arrayEquals([1, 2, [3, 4]], [1, 2, [3, 'foo']])).toBeFalsy();
+ expect(helpers.arrayEquals([{a: 42}], [{a: 42}])).toBeFalsy();
+ });
+ it('should return false if arrays are not the same', function() {
+ var o0 = {};
+ var o1 = {};
+ var o2 = {};
+
+ expect(helpers.arrayEquals([], [])).toBeTruthy();
+ expect(helpers.arrayEquals([1, 2, 3], [1, 2, 3])).toBeTruthy();
+ expect(helpers.arrayEquals(['foo', 'bar'], ['foo', 'bar'])).toBeTruthy();
+ expect(helpers.arrayEquals([true, false, true], [true, false, true])).toBeTruthy();
+ expect(helpers.arrayEquals([o0, o1, o2], [o0, o1, o2])).toBeTruthy();
+ });
+ });
+});