mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Make Chart.controllers.* importable (#5871)
`controllers.*.js` and `core.datasetController.js` are now importable (no more function export), that's why there is so many changes mainly due to one indentation level removed. Split code for `bar/horizontalBar` and `doughnut/pie` in separate files, added a global controllers import (`src/controllers/index.js`) and add tests to check that all dataset controllers are correctly registered under `chart.controllers.{type}`.
This commit is contained in:
parent
c84d249035
commit
be8d78a900
13
src/chart.js
13
src/chart.js
@ -10,6 +10,8 @@ require('./core/core.helpers')(Chart);
|
||||
|
||||
Chart.Animation = require('./core/core.animation');
|
||||
Chart.animationService = require('./core/core.animations');
|
||||
Chart.controllers = require('./controllers/index');
|
||||
Chart.DatasetController = require('./core/core.datasetController');
|
||||
Chart.defaults = require('./core/core.defaults');
|
||||
Chart.Element = require('./core/core.element');
|
||||
Chart.elements = require('./elements/index');
|
||||
@ -23,7 +25,6 @@ Chart.Ticks = require('./core/core.ticks');
|
||||
Chart.Tooltip = require('./core/core.tooltip');
|
||||
|
||||
require('./core/core.controller')(Chart);
|
||||
require('./core/core.datasetController')(Chart);
|
||||
|
||||
require('./scales/scale.linearbase')(Chart);
|
||||
require('./scales/scale.category')(Chart);
|
||||
@ -32,16 +33,6 @@ require('./scales/scale.logarithmic')(Chart);
|
||||
require('./scales/scale.radialLinear')(Chart);
|
||||
require('./scales/scale.time')(Chart);
|
||||
|
||||
// Controllers must be loaded after elements
|
||||
// See Chart.core.datasetController.dataElementType
|
||||
require('./controllers/controller.bar')(Chart);
|
||||
require('./controllers/controller.bubble')(Chart);
|
||||
require('./controllers/controller.doughnut')(Chart);
|
||||
require('./controllers/controller.line')(Chart);
|
||||
require('./controllers/controller.polarArea')(Chart);
|
||||
require('./controllers/controller.radar')(Chart);
|
||||
require('./controllers/controller.scatter')(Chart);
|
||||
|
||||
// Loading built-in plugins
|
||||
var plugins = require('./plugins');
|
||||
for (var k in plugins) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
@ -12,15 +13,9 @@ defaults._set('bar', {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'category',
|
||||
|
||||
// Specific to Bar Controller
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
|
||||
// offset settings
|
||||
offset: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
@ -32,69 +27,6 @@ defaults._set('bar', {
|
||||
}
|
||||
});
|
||||
|
||||
defaults._set('horizontalBar', {
|
||||
hover: {
|
||||
mode: 'index',
|
||||
axis: 'y'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}],
|
||||
|
||||
yAxes: [{
|
||||
position: 'left',
|
||||
type: 'category',
|
||||
|
||||
// Specific to Horizontal Bar Controller
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
|
||||
// offset settings
|
||||
offset: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
elements: {
|
||||
rectangle: {
|
||||
borderSkipped: 'left'
|
||||
}
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function(item, data) {
|
||||
// Pick first xLabel for now
|
||||
var title = '';
|
||||
|
||||
if (item.length > 0) {
|
||||
if (item[0].yLabel) {
|
||||
title = item[0].yLabel;
|
||||
} else if (data.labels.length > 0 && item[0].index < data.labels.length) {
|
||||
title = data.labels[item[0].index];
|
||||
}
|
||||
}
|
||||
|
||||
return title;
|
||||
},
|
||||
|
||||
label: function(item, data) {
|
||||
var datasetLabel = data.datasets[item.datasetIndex].label || '';
|
||||
return datasetLabel + ': ' + item.xLabel;
|
||||
}
|
||||
},
|
||||
mode: 'index',
|
||||
axis: 'y'
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
|
||||
* @private
|
||||
@ -182,349 +114,330 @@ function computeFlexCategoryTraits(index, ruler, options) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(Chart) {
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
Chart.controllers.bar = Chart.DatasetController.extend({
|
||||
dataElementType: elements.Rectangle,
|
||||
|
||||
dataElementType: elements.Rectangle,
|
||||
initialize: function() {
|
||||
var me = this;
|
||||
var meta;
|
||||
|
||||
initialize: function() {
|
||||
var me = this;
|
||||
var meta;
|
||||
DatasetController.prototype.initialize.apply(me, arguments);
|
||||
|
||||
Chart.DatasetController.prototype.initialize.apply(me, arguments);
|
||||
meta = me.getMeta();
|
||||
meta.stack = me.getDataset().stack;
|
||||
meta.bar = true;
|
||||
},
|
||||
|
||||
meta = me.getMeta();
|
||||
meta.stack = me.getDataset().stack;
|
||||
meta.bar = true;
|
||||
},
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var rects = me.getMeta().data;
|
||||
var i, ilen;
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var rects = me.getMeta().data;
|
||||
var i, ilen;
|
||||
me._ruler = me.getRuler();
|
||||
|
||||
me._ruler = me.getRuler();
|
||||
for (i = 0, ilen = rects.length; i < ilen; ++i) {
|
||||
me.updateElement(rects[i], i, reset);
|
||||
}
|
||||
},
|
||||
|
||||
for (i = 0, ilen = rects.length; i < ilen; ++i) {
|
||||
me.updateElement(rects[i], i, reset);
|
||||
updateElement: function(rectangle, index, reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var dataset = me.getDataset();
|
||||
var options = me._resolveElementOptions(rectangle, index);
|
||||
|
||||
rectangle._xScale = me.getScaleForId(meta.xAxisID);
|
||||
rectangle._yScale = me.getScaleForId(meta.yAxisID);
|
||||
rectangle._datasetIndex = me.index;
|
||||
rectangle._index = index;
|
||||
rectangle._model = {
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderSkipped: options.borderSkipped,
|
||||
borderWidth: options.borderWidth,
|
||||
datasetLabel: dataset.label,
|
||||
label: me.chart.data.labels[index]
|
||||
};
|
||||
|
||||
me._updateElementGeometry(rectangle, index, reset);
|
||||
|
||||
rectangle.pivot();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateElementGeometry: function(rectangle, index, reset) {
|
||||
var me = this;
|
||||
var model = rectangle._model;
|
||||
var vscale = me.getValueScale();
|
||||
var base = vscale.getBasePixel();
|
||||
var horizontal = vscale.isHorizontal();
|
||||
var ruler = me._ruler || me.getRuler();
|
||||
var vpixels = me.calculateBarValuePixels(me.index, index);
|
||||
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
|
||||
|
||||
model.horizontal = horizontal;
|
||||
model.base = reset ? base : vpixels.base;
|
||||
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
|
||||
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
|
||||
model.height = horizontal ? ipixels.size : undefined;
|
||||
model.width = horizontal ? undefined : ipixels.size;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getValueScaleId: function() {
|
||||
return this.getMeta().yAxisID;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getIndexScaleId: function() {
|
||||
return this.getMeta().xAxisID;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getValueScale: function() {
|
||||
return this.getScaleForId(this.getValueScaleId());
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getIndexScale: function() {
|
||||
return this.getScaleForId(this.getIndexScaleId());
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the stacks based on groups and bar visibility.
|
||||
* @param {Number} [last] - The dataset index
|
||||
* @returns {Array} The stack list
|
||||
* @private
|
||||
*/
|
||||
_getStacks: function(last) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var scale = me.getIndexScale();
|
||||
var stacked = scale.options.stacked;
|
||||
var ilen = last === undefined ? chart.data.datasets.length : last + 1;
|
||||
var stacks = [];
|
||||
var i, meta;
|
||||
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
if (meta.bar && chart.isDatasetVisible(i) &&
|
||||
(stacked === false ||
|
||||
(stacked === true && stacks.indexOf(meta.stack) === -1) ||
|
||||
(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
|
||||
stacks.push(meta.stack);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
updateElement: function(rectangle, index, reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var dataset = me.getDataset();
|
||||
var options = me._resolveElementOptions(rectangle, index);
|
||||
return stacks;
|
||||
},
|
||||
|
||||
rectangle._xScale = me.getScaleForId(meta.xAxisID);
|
||||
rectangle._yScale = me.getScaleForId(meta.yAxisID);
|
||||
rectangle._datasetIndex = me.index;
|
||||
rectangle._index = index;
|
||||
rectangle._model = {
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderSkipped: options.borderSkipped,
|
||||
borderWidth: options.borderWidth,
|
||||
datasetLabel: dataset.label,
|
||||
label: me.chart.data.labels[index]
|
||||
};
|
||||
/**
|
||||
* Returns the effective number of stacks based on groups and bar visibility.
|
||||
* @private
|
||||
*/
|
||||
getStackCount: function() {
|
||||
return this._getStacks().length;
|
||||
},
|
||||
|
||||
me._updateElementGeometry(rectangle, index, reset);
|
||||
/**
|
||||
* Returns the stack index for the given dataset based on groups and bar visibility.
|
||||
* @param {Number} [datasetIndex] - The dataset index
|
||||
* @param {String} [name] - The stack name to find
|
||||
* @returns {Number} The stack index
|
||||
* @private
|
||||
*/
|
||||
getStackIndex: function(datasetIndex, name) {
|
||||
var stacks = this._getStacks(datasetIndex);
|
||||
var index = (name !== undefined)
|
||||
? stacks.indexOf(name)
|
||||
: -1; // indexOf returns -1 if element is not present
|
||||
|
||||
rectangle.pivot();
|
||||
},
|
||||
return (index === -1)
|
||||
? stacks.length - 1
|
||||
: index;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateElementGeometry: function(rectangle, index, reset) {
|
||||
var me = this;
|
||||
var model = rectangle._model;
|
||||
var vscale = me.getValueScale();
|
||||
var base = vscale.getBasePixel();
|
||||
var horizontal = vscale.isHorizontal();
|
||||
var ruler = me._ruler || me.getRuler();
|
||||
var vpixels = me.calculateBarValuePixels(me.index, index);
|
||||
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getRuler: function() {
|
||||
var me = this;
|
||||
var scale = me.getIndexScale();
|
||||
var stackCount = me.getStackCount();
|
||||
var datasetIndex = me.index;
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var start = isHorizontal ? scale.left : scale.top;
|
||||
var end = start + (isHorizontal ? scale.width : scale.height);
|
||||
var pixels = [];
|
||||
var i, ilen, min;
|
||||
|
||||
model.horizontal = horizontal;
|
||||
model.base = reset ? base : vpixels.base;
|
||||
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
|
||||
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
|
||||
model.height = horizontal ? ipixels.size : undefined;
|
||||
model.width = horizontal ? undefined : ipixels.size;
|
||||
},
|
||||
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
|
||||
pixels.push(scale.getPixelForValue(null, i, datasetIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getValueScaleId: function() {
|
||||
return this.getMeta().yAxisID;
|
||||
},
|
||||
min = helpers.isNullOrUndef(scale.options.barThickness)
|
||||
? computeMinSampleSize(scale, pixels)
|
||||
: -1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getIndexScaleId: function() {
|
||||
return this.getMeta().xAxisID;
|
||||
},
|
||||
return {
|
||||
min: min,
|
||||
pixels: pixels,
|
||||
start: start,
|
||||
end: end,
|
||||
stackCount: stackCount,
|
||||
scale: scale
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getValueScale: function() {
|
||||
return this.getScaleForId(this.getValueScaleId());
|
||||
},
|
||||
/**
|
||||
* Note: pixel values are not clamped to the scale area.
|
||||
* @private
|
||||
*/
|
||||
calculateBarValuePixels: function(datasetIndex, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var scale = me.getValueScale();
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var datasets = chart.data.datasets;
|
||||
var value = scale.getRightValue(datasets[datasetIndex].data[index]);
|
||||
var minBarLength = scale.options.minBarLength;
|
||||
var stacked = scale.options.stacked;
|
||||
var stack = meta.stack;
|
||||
var start = 0;
|
||||
var i, imeta, ivalue, base, head, size;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getIndexScale: function() {
|
||||
return this.getScaleForId(this.getIndexScaleId());
|
||||
},
|
||||
if (stacked || (stacked === undefined && stack !== undefined)) {
|
||||
for (i = 0; i < datasetIndex; ++i) {
|
||||
imeta = chart.getDatasetMeta(i);
|
||||
|
||||
/**
|
||||
* Returns the stacks based on groups and bar visibility.
|
||||
* @param {Number} [last] - The dataset index
|
||||
* @returns {Array} The stack list
|
||||
* @private
|
||||
*/
|
||||
_getStacks: function(last) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var scale = me.getIndexScale();
|
||||
var stacked = scale.options.stacked;
|
||||
var ilen = last === undefined ? chart.data.datasets.length : last + 1;
|
||||
var stacks = [];
|
||||
var i, meta;
|
||||
if (imeta.bar &&
|
||||
imeta.stack === stack &&
|
||||
imeta.controller.getValueScaleId() === scale.id &&
|
||||
chart.isDatasetVisible(i)) {
|
||||
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
if (meta.bar && chart.isDatasetVisible(i) &&
|
||||
(stacked === false ||
|
||||
(stacked === true && stacks.indexOf(meta.stack) === -1) ||
|
||||
(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
|
||||
stacks.push(meta.stack);
|
||||
}
|
||||
}
|
||||
|
||||
return stacks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the effective number of stacks based on groups and bar visibility.
|
||||
* @private
|
||||
*/
|
||||
getStackCount: function() {
|
||||
return this._getStacks().length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the stack index for the given dataset based on groups and bar visibility.
|
||||
* @param {Number} [datasetIndex] - The dataset index
|
||||
* @param {String} [name] - The stack name to find
|
||||
* @returns {Number} The stack index
|
||||
* @private
|
||||
*/
|
||||
getStackIndex: function(datasetIndex, name) {
|
||||
var stacks = this._getStacks(datasetIndex);
|
||||
var index = (name !== undefined)
|
||||
? stacks.indexOf(name)
|
||||
: -1; // indexOf returns -1 if element is not present
|
||||
|
||||
return (index === -1)
|
||||
? stacks.length - 1
|
||||
: index;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getRuler: function() {
|
||||
var me = this;
|
||||
var scale = me.getIndexScale();
|
||||
var stackCount = me.getStackCount();
|
||||
var datasetIndex = me.index;
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var start = isHorizontal ? scale.left : scale.top;
|
||||
var end = start + (isHorizontal ? scale.width : scale.height);
|
||||
var pixels = [];
|
||||
var i, ilen, min;
|
||||
|
||||
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
|
||||
pixels.push(scale.getPixelForValue(null, i, datasetIndex));
|
||||
}
|
||||
|
||||
min = helpers.isNullOrUndef(scale.options.barThickness)
|
||||
? computeMinSampleSize(scale, pixels)
|
||||
: -1;
|
||||
|
||||
return {
|
||||
min: min,
|
||||
pixels: pixels,
|
||||
start: start,
|
||||
end: end,
|
||||
stackCount: stackCount,
|
||||
scale: scale
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Note: pixel values are not clamped to the scale area.
|
||||
* @private
|
||||
*/
|
||||
calculateBarValuePixels: function(datasetIndex, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var scale = me.getValueScale();
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var datasets = chart.data.datasets;
|
||||
var value = scale.getRightValue(datasets[datasetIndex].data[index]);
|
||||
var minBarLength = scale.options.minBarLength;
|
||||
var stacked = scale.options.stacked;
|
||||
var stack = meta.stack;
|
||||
var start = 0;
|
||||
var i, imeta, ivalue, base, head, size;
|
||||
|
||||
if (stacked || (stacked === undefined && stack !== undefined)) {
|
||||
for (i = 0; i < datasetIndex; ++i) {
|
||||
imeta = chart.getDatasetMeta(i);
|
||||
|
||||
if (imeta.bar &&
|
||||
imeta.stack === stack &&
|
||||
imeta.controller.getValueScaleId() === scale.id &&
|
||||
chart.isDatasetVisible(i)) {
|
||||
|
||||
ivalue = scale.getRightValue(datasets[i].data[index]);
|
||||
if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
|
||||
start += ivalue;
|
||||
}
|
||||
ivalue = scale.getRightValue(datasets[i].data[index]);
|
||||
if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
|
||||
start += ivalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base = scale.getPixelForValue(start);
|
||||
head = scale.getPixelForValue(start + value);
|
||||
size = (head - base) / 2;
|
||||
|
||||
if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
|
||||
size = minBarLength;
|
||||
if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) {
|
||||
head = base - minBarLength;
|
||||
} else {
|
||||
head = base + minBarLength;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
size: size,
|
||||
base: base,
|
||||
head: head,
|
||||
center: head + size / 2
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
calculateBarIndexPixels: function(datasetIndex, index, ruler) {
|
||||
var me = this;
|
||||
var options = ruler.scale.options;
|
||||
var range = options.barThickness === 'flex'
|
||||
? computeFlexCategoryTraits(index, ruler, options)
|
||||
: computeFitCategoryTraits(index, ruler, options);
|
||||
|
||||
var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
|
||||
var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
|
||||
var size = Math.min(
|
||||
helpers.valueOrDefault(options.maxBarThickness, Infinity),
|
||||
range.chunk * range.ratio);
|
||||
|
||||
return {
|
||||
base: center - size / 2,
|
||||
head: center + size / 2,
|
||||
center: center,
|
||||
size: size
|
||||
};
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var scale = me.getValueScale();
|
||||
var rects = me.getMeta().data;
|
||||
var dataset = me.getDataset();
|
||||
var ilen = rects.length;
|
||||
var i = 0;
|
||||
|
||||
helpers.canvas.clipArea(chart.ctx, chart.chartArea);
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
if (!isNaN(scale.getRightValue(dataset.data[i]))) {
|
||||
rects[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveElementOptions: function(rectangle, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var datasets = chart.data.datasets;
|
||||
var dataset = datasets[me.index];
|
||||
var custom = rectangle.custom || {};
|
||||
var options = chart.options.elements.rectangle;
|
||||
var resolve = helpers.options.resolve;
|
||||
var values = {};
|
||||
var i, ilen, key;
|
||||
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
var keys = [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderSkipped',
|
||||
'borderWidth'
|
||||
];
|
||||
|
||||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||||
key = keys[i];
|
||||
values[key] = resolve([
|
||||
custom[key],
|
||||
dataset[key],
|
||||
options[key]
|
||||
], context, index);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
});
|
||||
|
||||
Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getValueScaleId: function() {
|
||||
return this.getMeta().xAxisID;
|
||||
},
|
||||
base = scale.getPixelForValue(start);
|
||||
head = scale.getPixelForValue(start + value);
|
||||
size = (head - base) / 2;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getIndexScaleId: function() {
|
||||
return this.getMeta().yAxisID;
|
||||
if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
|
||||
size = minBarLength;
|
||||
if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) {
|
||||
head = base - minBarLength;
|
||||
} else {
|
||||
head = base + minBarLength;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
size: size,
|
||||
base: base,
|
||||
head: head,
|
||||
center: head + size / 2
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
calculateBarIndexPixels: function(datasetIndex, index, ruler) {
|
||||
var me = this;
|
||||
var options = ruler.scale.options;
|
||||
var range = options.barThickness === 'flex'
|
||||
? computeFlexCategoryTraits(index, ruler, options)
|
||||
: computeFitCategoryTraits(index, ruler, options);
|
||||
|
||||
var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
|
||||
var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
|
||||
var size = Math.min(
|
||||
helpers.valueOrDefault(options.maxBarThickness, Infinity),
|
||||
range.chunk * range.ratio);
|
||||
|
||||
return {
|
||||
base: center - size / 2,
|
||||
head: center + size / 2,
|
||||
center: center,
|
||||
size: size
|
||||
};
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var scale = me.getValueScale();
|
||||
var rects = me.getMeta().data;
|
||||
var dataset = me.getDataset();
|
||||
var ilen = rects.length;
|
||||
var i = 0;
|
||||
|
||||
helpers.canvas.clipArea(chart.ctx, chart.chartArea);
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
if (!isNaN(scale.getRightValue(dataset.data[i]))) {
|
||||
rects[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveElementOptions: function(rectangle, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var datasets = chart.data.datasets;
|
||||
var dataset = datasets[me.index];
|
||||
var custom = rectangle.custom || {};
|
||||
var options = chart.options.elements.rectangle;
|
||||
var resolve = helpers.options.resolve;
|
||||
var values = {};
|
||||
var i, ilen, key;
|
||||
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
var keys = [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderSkipped',
|
||||
'borderWidth'
|
||||
];
|
||||
|
||||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||||
key = keys[i];
|
||||
values[key] = resolve([
|
||||
custom[key],
|
||||
dataset[key],
|
||||
options[key]
|
||||
], context, index);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
@ -37,140 +38,136 @@ defaults._set('bubble', {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
dataElementType: elements.Point,
|
||||
|
||||
module.exports = function(Chart) {
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var points = meta.data;
|
||||
|
||||
Chart.controllers.bubble = Chart.DatasetController.extend({
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
dataElementType: elements.Point,
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
me.updateElement(point, index, reset);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var points = meta.data;
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
updateElement: function(point, index, reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var custom = point.custom || {};
|
||||
var xScale = me.getScaleForId(meta.xAxisID);
|
||||
var yScale = me.getScaleForId(meta.yAxisID);
|
||||
var options = me._resolveElementOptions(point, index);
|
||||
var data = me.getDataset().data[index];
|
||||
var dsIndex = me.index;
|
||||
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
me.updateElement(point, index, reset);
|
||||
});
|
||||
},
|
||||
var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
|
||||
var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
updateElement: function(point, index, reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var custom = point.custom || {};
|
||||
var xScale = me.getScaleForId(meta.xAxisID);
|
||||
var yScale = me.getScaleForId(meta.yAxisID);
|
||||
var options = me._resolveElementOptions(point, index);
|
||||
var data = me.getDataset().data[index];
|
||||
var dsIndex = me.index;
|
||||
point._xScale = xScale;
|
||||
point._yScale = yScale;
|
||||
point._options = options;
|
||||
point._datasetIndex = dsIndex;
|
||||
point._index = index;
|
||||
point._model = {
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderWidth: options.borderWidth,
|
||||
hitRadius: options.hitRadius,
|
||||
pointStyle: options.pointStyle,
|
||||
rotation: options.rotation,
|
||||
radius: reset ? 0 : options.radius,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
|
||||
var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
|
||||
var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
|
||||
point.pivot();
|
||||
},
|
||||
|
||||
point._xScale = xScale;
|
||||
point._yScale = yScale;
|
||||
point._options = options;
|
||||
point._datasetIndex = dsIndex;
|
||||
point._index = index;
|
||||
point._model = {
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderWidth: options.borderWidth,
|
||||
hitRadius: options.hitRadius,
|
||||
pointStyle: options.pointStyle,
|
||||
rotation: options.rotation,
|
||||
radius: reset ? 0 : options.radius,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
setHoverStyle: function(point) {
|
||||
var model = point._model;
|
||||
var options = point._options;
|
||||
|
||||
point.pivot();
|
||||
},
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
setHoverStyle: function(point) {
|
||||
var model = point._model;
|
||||
var options = point._options;
|
||||
model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
|
||||
model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
|
||||
model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = options.radius + options.hoverRadius;
|
||||
},
|
||||
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveElementOptions: function(point, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var datasets = chart.data.datasets;
|
||||
var dataset = datasets[me.index];
|
||||
var custom = point.custom || {};
|
||||
var options = chart.options.elements.point;
|
||||
var resolve = helpers.options.resolve;
|
||||
var data = dataset.data[index];
|
||||
var values = {};
|
||||
var i, ilen, key;
|
||||
|
||||
model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
|
||||
model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
|
||||
model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = options.radius + options.hoverRadius;
|
||||
},
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveElementOptions: function(point, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var datasets = chart.data.datasets;
|
||||
var dataset = datasets[me.index];
|
||||
var custom = point.custom || {};
|
||||
var options = chart.options.elements.point;
|
||||
var resolve = helpers.options.resolve;
|
||||
var data = dataset.data[index];
|
||||
var values = {};
|
||||
var i, ilen, key;
|
||||
var keys = [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'hoverBackgroundColor',
|
||||
'hoverBorderColor',
|
||||
'hoverBorderWidth',
|
||||
'hoverRadius',
|
||||
'hitRadius',
|
||||
'pointStyle',
|
||||
'rotation'
|
||||
];
|
||||
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
var keys = [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'hoverBackgroundColor',
|
||||
'hoverBorderColor',
|
||||
'hoverBorderWidth',
|
||||
'hoverRadius',
|
||||
'hitRadius',
|
||||
'pointStyle',
|
||||
'rotation'
|
||||
];
|
||||
|
||||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||||
key = keys[i];
|
||||
values[key] = resolve([
|
||||
custom[key],
|
||||
dataset[key],
|
||||
options[key]
|
||||
], context, index);
|
||||
}
|
||||
|
||||
// Custom radius resolution
|
||||
values.radius = resolve([
|
||||
custom.radius,
|
||||
data ? data.r : undefined,
|
||||
dataset.radius,
|
||||
options.radius
|
||||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||||
key = keys[i];
|
||||
values[key] = resolve([
|
||||
custom[key],
|
||||
dataset[key],
|
||||
options[key]
|
||||
], context, index);
|
||||
|
||||
return values;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Custom radius resolution
|
||||
values.radius = resolve([
|
||||
custom.radius,
|
||||
data ? data.r : undefined,
|
||||
dataset.radius,
|
||||
options.radius
|
||||
], context, index);
|
||||
|
||||
return values;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
@ -118,184 +119,176 @@ defaults._set('doughnut', {
|
||||
}
|
||||
});
|
||||
|
||||
defaults._set('pie', helpers.clone(defaults.doughnut));
|
||||
defaults._set('pie', {
|
||||
cutoutPercentage: 0
|
||||
});
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
module.exports = function(Chart) {
|
||||
dataElementType: elements.Arc,
|
||||
|
||||
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
|
||||
linkScales: helpers.noop,
|
||||
|
||||
dataElementType: elements.Arc,
|
||||
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
|
||||
getRingIndex: function(datasetIndex) {
|
||||
var ringIndex = 0;
|
||||
|
||||
linkScales: helpers.noop,
|
||||
|
||||
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
|
||||
getRingIndex: function(datasetIndex) {
|
||||
var ringIndex = 0;
|
||||
|
||||
for (var j = 0; j < datasetIndex; ++j) {
|
||||
if (this.chart.isDatasetVisible(j)) {
|
||||
++ringIndex;
|
||||
}
|
||||
for (var j = 0; j < datasetIndex; ++j) {
|
||||
if (this.chart.isDatasetVisible(j)) {
|
||||
++ringIndex;
|
||||
}
|
||||
|
||||
return ringIndex;
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var arcOpts = opts.elements.arc;
|
||||
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
|
||||
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
|
||||
var minSize = Math.min(availableWidth, availableHeight);
|
||||
var offset = {x: 0, y: 0};
|
||||
var meta = me.getMeta();
|
||||
var cutoutPercentage = opts.cutoutPercentage;
|
||||
var circumference = opts.circumference;
|
||||
|
||||
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
|
||||
if (circumference < Math.PI * 2.0) {
|
||||
var startAngle = opts.rotation % (Math.PI * 2.0);
|
||||
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
|
||||
var endAngle = startAngle + circumference;
|
||||
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
|
||||
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
|
||||
var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
|
||||
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
|
||||
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
|
||||
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
|
||||
var cutout = cutoutPercentage / 100.0;
|
||||
var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
|
||||
var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
|
||||
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
|
||||
minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
|
||||
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
|
||||
}
|
||||
|
||||
chart.borderWidth = me.getMaxBorderWidth(meta.data);
|
||||
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
|
||||
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
|
||||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
|
||||
chart.offsetX = offset.x * chart.outerRadius;
|
||||
chart.offsetY = offset.y * chart.outerRadius;
|
||||
|
||||
meta.total = me.calculateTotal();
|
||||
|
||||
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
|
||||
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
|
||||
|
||||
helpers.each(meta.data, function(arc, index) {
|
||||
me.updateElement(arc, index, reset);
|
||||
});
|
||||
},
|
||||
|
||||
updateElement: function(arc, index, reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var animationOpts = opts.animation;
|
||||
var centerX = (chartArea.left + chartArea.right) / 2;
|
||||
var centerY = (chartArea.top + chartArea.bottom) / 2;
|
||||
var startAngle = opts.rotation; // non reset case handled later
|
||||
var endAngle = opts.rotation; // non reset case handled later
|
||||
var dataset = me.getDataset();
|
||||
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
|
||||
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
|
||||
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
|
||||
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: centerX + chart.offsetX,
|
||||
y: centerY + chart.offsetY,
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
circumference: circumference,
|
||||
outerRadius: outerRadius,
|
||||
innerRadius: innerRadius,
|
||||
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
|
||||
}
|
||||
});
|
||||
|
||||
var model = arc._model;
|
||||
|
||||
// Resets the visual styles
|
||||
var custom = arc.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var elementOpts = this.chart.options.elements.arc;
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
|
||||
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
|
||||
|
||||
// Set correct angles if not resetting
|
||||
if (!reset || !animationOpts.animateRotate) {
|
||||
if (index === 0) {
|
||||
model.startAngle = opts.rotation;
|
||||
} else {
|
||||
model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
|
||||
}
|
||||
|
||||
model.endAngle = model.startAngle + model.circumference;
|
||||
}
|
||||
|
||||
arc.pivot();
|
||||
},
|
||||
|
||||
calculateTotal: function() {
|
||||
var dataset = this.getDataset();
|
||||
var meta = this.getMeta();
|
||||
var total = 0;
|
||||
var value;
|
||||
|
||||
helpers.each(meta.data, function(element, index) {
|
||||
value = dataset.data[index];
|
||||
if (!isNaN(value) && !element.hidden) {
|
||||
total += Math.abs(value);
|
||||
}
|
||||
});
|
||||
|
||||
/* if (total === 0) {
|
||||
total = NaN;
|
||||
}*/
|
||||
|
||||
return total;
|
||||
},
|
||||
|
||||
calculateCircumference: function(value) {
|
||||
var total = this.getMeta().total;
|
||||
if (total > 0 && !isNaN(value)) {
|
||||
return (Math.PI * 2.0) * (Math.abs(value) / total);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
// gets the max border or hover width to properly scale pie charts
|
||||
getMaxBorderWidth: function(arcs) {
|
||||
var max = 0;
|
||||
var index = this.index;
|
||||
var length = arcs.length;
|
||||
var borderWidth;
|
||||
var hoverWidth;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
|
||||
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
|
||||
|
||||
max = borderWidth > max ? borderWidth : max;
|
||||
max = hoverWidth > max ? hoverWidth : max;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return ringIndex;
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var arcOpts = opts.elements.arc;
|
||||
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
|
||||
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
|
||||
var minSize = Math.min(availableWidth, availableHeight);
|
||||
var offset = {x: 0, y: 0};
|
||||
var meta = me.getMeta();
|
||||
var cutoutPercentage = opts.cutoutPercentage;
|
||||
var circumference = opts.circumference;
|
||||
|
||||
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
|
||||
if (circumference < Math.PI * 2.0) {
|
||||
var startAngle = opts.rotation % (Math.PI * 2.0);
|
||||
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
|
||||
var endAngle = startAngle + circumference;
|
||||
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
|
||||
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
|
||||
var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
|
||||
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
|
||||
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
|
||||
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
|
||||
var cutout = cutoutPercentage / 100.0;
|
||||
var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
|
||||
var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
|
||||
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
|
||||
minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
|
||||
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
|
||||
}
|
||||
|
||||
chart.borderWidth = me.getMaxBorderWidth(meta.data);
|
||||
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
|
||||
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
|
||||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
|
||||
chart.offsetX = offset.x * chart.outerRadius;
|
||||
chart.offsetY = offset.y * chart.outerRadius;
|
||||
|
||||
meta.total = me.calculateTotal();
|
||||
|
||||
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
|
||||
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
|
||||
|
||||
helpers.each(meta.data, function(arc, index) {
|
||||
me.updateElement(arc, index, reset);
|
||||
});
|
||||
},
|
||||
|
||||
updateElement: function(arc, index, reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var animationOpts = opts.animation;
|
||||
var centerX = (chartArea.left + chartArea.right) / 2;
|
||||
var centerY = (chartArea.top + chartArea.bottom) / 2;
|
||||
var startAngle = opts.rotation; // non reset case handled later
|
||||
var endAngle = opts.rotation; // non reset case handled later
|
||||
var dataset = me.getDataset();
|
||||
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
|
||||
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
|
||||
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
|
||||
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: centerX + chart.offsetX,
|
||||
y: centerY + chart.offsetY,
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
circumference: circumference,
|
||||
outerRadius: outerRadius,
|
||||
innerRadius: innerRadius,
|
||||
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
|
||||
}
|
||||
});
|
||||
|
||||
var model = arc._model;
|
||||
|
||||
// Resets the visual styles
|
||||
var custom = arc.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var elementOpts = this.chart.options.elements.arc;
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
|
||||
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
|
||||
|
||||
// Set correct angles if not resetting
|
||||
if (!reset || !animationOpts.animateRotate) {
|
||||
if (index === 0) {
|
||||
model.startAngle = opts.rotation;
|
||||
} else {
|
||||
model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
|
||||
}
|
||||
|
||||
model.endAngle = model.startAngle + model.circumference;
|
||||
}
|
||||
|
||||
arc.pivot();
|
||||
},
|
||||
|
||||
calculateTotal: function() {
|
||||
var dataset = this.getDataset();
|
||||
var meta = this.getMeta();
|
||||
var total = 0;
|
||||
var value;
|
||||
|
||||
helpers.each(meta.data, function(element, index) {
|
||||
value = dataset.data[index];
|
||||
if (!isNaN(value) && !element.hidden) {
|
||||
total += Math.abs(value);
|
||||
}
|
||||
});
|
||||
|
||||
/* if (total === 0) {
|
||||
total = NaN;
|
||||
}*/
|
||||
|
||||
return total;
|
||||
},
|
||||
|
||||
calculateCircumference: function(value) {
|
||||
var total = this.getMeta().total;
|
||||
if (total > 0 && !isNaN(value)) {
|
||||
return (Math.PI * 2.0) * (Math.abs(value) / total);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
// gets the max border or hover width to properly scale pie charts
|
||||
getMaxBorderWidth: function(arcs) {
|
||||
var max = 0;
|
||||
var index = this.index;
|
||||
var length = arcs.length;
|
||||
var borderWidth;
|
||||
var hoverWidth;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
|
||||
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
|
||||
|
||||
max = borderWidth > max ? borderWidth : max;
|
||||
max = hoverWidth > max ? hoverWidth : max;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
});
|
||||
|
||||
79
src/controllers/controller.horizontalBar.js
Normal file
79
src/controllers/controller.horizontalBar.js
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var BarController = require('./controller.bar');
|
||||
var defaults = require('../core/core.defaults');
|
||||
|
||||
defaults._set('horizontalBar', {
|
||||
hover: {
|
||||
mode: 'index',
|
||||
axis: 'y'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}],
|
||||
|
||||
yAxes: [{
|
||||
type: 'category',
|
||||
position: 'left',
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
offset: true,
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
elements: {
|
||||
rectangle: {
|
||||
borderSkipped: 'left'
|
||||
}
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function(item, data) {
|
||||
// Pick first xLabel for now
|
||||
var title = '';
|
||||
|
||||
if (item.length > 0) {
|
||||
if (item[0].yLabel) {
|
||||
title = item[0].yLabel;
|
||||
} else if (data.labels.length > 0 && item[0].index < data.labels.length) {
|
||||
title = data.labels[item[0].index];
|
||||
}
|
||||
}
|
||||
|
||||
return title;
|
||||
},
|
||||
|
||||
label: function(item, data) {
|
||||
var datasetLabel = data.datasets[item.datasetIndex].label || '';
|
||||
return datasetLabel + ': ' + item.xLabel;
|
||||
}
|
||||
},
|
||||
mode: 'index',
|
||||
axis: 'y'
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BarController.extend({
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getValueScaleId: function() {
|
||||
return this.getMeta().xAxisID;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getIndexScaleId: function() {
|
||||
return this.getMeta().yAxisID;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
@ -24,321 +25,318 @@ defaults._set('line', {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
function lineEnabled(dataset, options) {
|
||||
return helpers.valueOrDefault(dataset.showLine, options.showLines);
|
||||
}
|
||||
|
||||
function lineEnabled(dataset, options) {
|
||||
return helpers.valueOrDefault(dataset.showLine, options.showLines);
|
||||
}
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
Chart.controllers.line = Chart.DatasetController.extend({
|
||||
datasetElementType: elements.Line,
|
||||
|
||||
datasetElementType: elements.Line,
|
||||
dataElementType: elements.Point,
|
||||
|
||||
dataElementType: elements.Point,
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var line = meta.dataset;
|
||||
var points = meta.data || [];
|
||||
var options = me.chart.options;
|
||||
var lineElementOptions = options.elements.line;
|
||||
var scale = me.getScaleForId(meta.yAxisID);
|
||||
var i, ilen, custom;
|
||||
var dataset = me.getDataset();
|
||||
var showLine = lineEnabled(dataset, options);
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var line = meta.dataset;
|
||||
var points = meta.data || [];
|
||||
var options = me.chart.options;
|
||||
var lineElementOptions = options.elements.line;
|
||||
var scale = me.getScaleForId(meta.yAxisID);
|
||||
var i, ilen, custom;
|
||||
var dataset = me.getDataset();
|
||||
var showLine = lineEnabled(dataset, options);
|
||||
|
||||
// Update Line
|
||||
if (showLine) {
|
||||
custom = line.custom || {};
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
|
||||
dataset.lineTension = dataset.tension;
|
||||
}
|
||||
|
||||
// Utility
|
||||
line._scale = scale;
|
||||
line._datasetIndex = me.index;
|
||||
// Data
|
||||
line._children = points;
|
||||
// Model
|
||||
line._model = {
|
||||
// Appearance
|
||||
// The default behavior of lines is to break at null values, according
|
||||
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
|
||||
// This option gives lines the ability to span gaps
|
||||
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
|
||||
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
|
||||
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
|
||||
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
|
||||
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
|
||||
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
|
||||
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
|
||||
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
|
||||
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
|
||||
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
|
||||
steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
|
||||
cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
|
||||
};
|
||||
|
||||
line.pivot();
|
||||
}
|
||||
|
||||
// Update Points
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
me.updateElement(points[i], i, reset);
|
||||
}
|
||||
|
||||
if (showLine && line._model.tension !== 0) {
|
||||
me.updateBezierControlPoints();
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
points[i].pivot();
|
||||
}
|
||||
},
|
||||
|
||||
getPointBackgroundColor: function(point, index) {
|
||||
var backgroundColor = this.chart.options.elements.point.backgroundColor;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (custom.backgroundColor) {
|
||||
backgroundColor = custom.backgroundColor;
|
||||
} else if (dataset.pointBackgroundColor) {
|
||||
backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
|
||||
} else if (dataset.backgroundColor) {
|
||||
backgroundColor = dataset.backgroundColor;
|
||||
}
|
||||
|
||||
return backgroundColor;
|
||||
},
|
||||
|
||||
getPointBorderColor: function(point, index) {
|
||||
var borderColor = this.chart.options.elements.point.borderColor;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (custom.borderColor) {
|
||||
borderColor = custom.borderColor;
|
||||
} else if (dataset.pointBorderColor) {
|
||||
borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
|
||||
} else if (dataset.borderColor) {
|
||||
borderColor = dataset.borderColor;
|
||||
}
|
||||
|
||||
return borderColor;
|
||||
},
|
||||
|
||||
getPointBorderWidth: function(point, index) {
|
||||
var borderWidth = this.chart.options.elements.point.borderWidth;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (!isNaN(custom.borderWidth)) {
|
||||
borderWidth = custom.borderWidth;
|
||||
} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
|
||||
borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
|
||||
} else if (!isNaN(dataset.borderWidth)) {
|
||||
borderWidth = dataset.borderWidth;
|
||||
}
|
||||
|
||||
return borderWidth;
|
||||
},
|
||||
|
||||
getPointRotation: function(point, index) {
|
||||
var pointRotation = this.chart.options.elements.point.rotation;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (!isNaN(custom.rotation)) {
|
||||
pointRotation = custom.rotation;
|
||||
} else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) {
|
||||
pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation);
|
||||
}
|
||||
return pointRotation;
|
||||
},
|
||||
|
||||
updateElement: function(point, index, reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var custom = point.custom || {};
|
||||
var dataset = me.getDataset();
|
||||
var datasetIndex = me.index;
|
||||
var value = dataset.data[index];
|
||||
var yScale = me.getScaleForId(meta.yAxisID);
|
||||
var xScale = me.getScaleForId(meta.xAxisID);
|
||||
var pointOptions = me.chart.options.elements.point;
|
||||
var x, y;
|
||||
// Update Line
|
||||
if (showLine) {
|
||||
custom = line.custom || {};
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
|
||||
dataset.pointRadius = dataset.radius;
|
||||
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
|
||||
dataset.lineTension = dataset.tension;
|
||||
}
|
||||
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
|
||||
dataset.pointHitRadius = dataset.hitRadius;
|
||||
}
|
||||
|
||||
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
|
||||
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
|
||||
|
||||
// Utility
|
||||
point._xScale = xScale;
|
||||
point._yScale = yScale;
|
||||
point._datasetIndex = datasetIndex;
|
||||
point._index = index;
|
||||
|
||||
// Desired view properties
|
||||
point._model = {
|
||||
x: x,
|
||||
y: y,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
line._scale = scale;
|
||||
line._datasetIndex = me.index;
|
||||
// Data
|
||||
line._children = points;
|
||||
// Model
|
||||
line._model = {
|
||||
// Appearance
|
||||
radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
|
||||
pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
|
||||
rotation: me.getPointRotation(point, index),
|
||||
backgroundColor: me.getPointBackgroundColor(point, index),
|
||||
borderColor: me.getPointBorderColor(point, index),
|
||||
borderWidth: me.getPointBorderWidth(point, index),
|
||||
tension: meta.dataset._model ? meta.dataset._model.tension : 0,
|
||||
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
|
||||
// Tooltip
|
||||
hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
|
||||
// The default behavior of lines is to break at null values, according
|
||||
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
|
||||
// This option gives lines the ability to span gaps
|
||||
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
|
||||
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
|
||||
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
|
||||
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
|
||||
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
|
||||
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
|
||||
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
|
||||
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
|
||||
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
|
||||
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
|
||||
steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
|
||||
cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
|
||||
};
|
||||
},
|
||||
|
||||
calculatePointY: function(value, index, datasetIndex) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var yScale = me.getScaleForId(meta.yAxisID);
|
||||
var sumPos = 0;
|
||||
var sumNeg = 0;
|
||||
var i, ds, dsMeta;
|
||||
line.pivot();
|
||||
}
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
for (i = 0; i < datasetIndex; i++) {
|
||||
ds = chart.data.datasets[i];
|
||||
dsMeta = chart.getDatasetMeta(i);
|
||||
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
|
||||
var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
|
||||
if (stackedRightValue < 0) {
|
||||
sumNeg += stackedRightValue || 0;
|
||||
} else {
|
||||
sumPos += stackedRightValue || 0;
|
||||
}
|
||||
// Update Points
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
me.updateElement(points[i], i, reset);
|
||||
}
|
||||
|
||||
if (showLine && line._model.tension !== 0) {
|
||||
me.updateBezierControlPoints();
|
||||
}
|
||||
|
||||
// Now pivot the point for animation
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
points[i].pivot();
|
||||
}
|
||||
},
|
||||
|
||||
getPointBackgroundColor: function(point, index) {
|
||||
var backgroundColor = this.chart.options.elements.point.backgroundColor;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (custom.backgroundColor) {
|
||||
backgroundColor = custom.backgroundColor;
|
||||
} else if (dataset.pointBackgroundColor) {
|
||||
backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
|
||||
} else if (dataset.backgroundColor) {
|
||||
backgroundColor = dataset.backgroundColor;
|
||||
}
|
||||
|
||||
return backgroundColor;
|
||||
},
|
||||
|
||||
getPointBorderColor: function(point, index) {
|
||||
var borderColor = this.chart.options.elements.point.borderColor;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (custom.borderColor) {
|
||||
borderColor = custom.borderColor;
|
||||
} else if (dataset.pointBorderColor) {
|
||||
borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
|
||||
} else if (dataset.borderColor) {
|
||||
borderColor = dataset.borderColor;
|
||||
}
|
||||
|
||||
return borderColor;
|
||||
},
|
||||
|
||||
getPointBorderWidth: function(point, index) {
|
||||
var borderWidth = this.chart.options.elements.point.borderWidth;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (!isNaN(custom.borderWidth)) {
|
||||
borderWidth = custom.borderWidth;
|
||||
} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
|
||||
borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
|
||||
} else if (!isNaN(dataset.borderWidth)) {
|
||||
borderWidth = dataset.borderWidth;
|
||||
}
|
||||
|
||||
return borderWidth;
|
||||
},
|
||||
|
||||
getPointRotation: function(point, index) {
|
||||
var pointRotation = this.chart.options.elements.point.rotation;
|
||||
var dataset = this.getDataset();
|
||||
var custom = point.custom || {};
|
||||
|
||||
if (!isNaN(custom.rotation)) {
|
||||
pointRotation = custom.rotation;
|
||||
} else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) {
|
||||
pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation);
|
||||
}
|
||||
return pointRotation;
|
||||
},
|
||||
|
||||
updateElement: function(point, index, reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var custom = point.custom || {};
|
||||
var dataset = me.getDataset();
|
||||
var datasetIndex = me.index;
|
||||
var value = dataset.data[index];
|
||||
var yScale = me.getScaleForId(meta.yAxisID);
|
||||
var xScale = me.getScaleForId(meta.xAxisID);
|
||||
var pointOptions = me.chart.options.elements.point;
|
||||
var x, y;
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
|
||||
dataset.pointRadius = dataset.radius;
|
||||
}
|
||||
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
|
||||
dataset.pointHitRadius = dataset.hitRadius;
|
||||
}
|
||||
|
||||
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
|
||||
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
|
||||
|
||||
// Utility
|
||||
point._xScale = xScale;
|
||||
point._yScale = yScale;
|
||||
point._datasetIndex = datasetIndex;
|
||||
point._index = index;
|
||||
|
||||
// Desired view properties
|
||||
point._model = {
|
||||
x: x,
|
||||
y: y,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
// Appearance
|
||||
radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
|
||||
pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
|
||||
rotation: me.getPointRotation(point, index),
|
||||
backgroundColor: me.getPointBackgroundColor(point, index),
|
||||
borderColor: me.getPointBorderColor(point, index),
|
||||
borderWidth: me.getPointBorderWidth(point, index),
|
||||
tension: meta.dataset._model ? meta.dataset._model.tension : 0,
|
||||
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
|
||||
// Tooltip
|
||||
hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
|
||||
};
|
||||
},
|
||||
|
||||
calculatePointY: function(value, index, datasetIndex) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var yScale = me.getScaleForId(meta.yAxisID);
|
||||
var sumPos = 0;
|
||||
var sumNeg = 0;
|
||||
var i, ds, dsMeta;
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
for (i = 0; i < datasetIndex; i++) {
|
||||
ds = chart.data.datasets[i];
|
||||
dsMeta = chart.getDatasetMeta(i);
|
||||
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
|
||||
var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
|
||||
if (stackedRightValue < 0) {
|
||||
sumNeg += stackedRightValue || 0;
|
||||
} else {
|
||||
sumPos += stackedRightValue || 0;
|
||||
}
|
||||
}
|
||||
|
||||
var rightValue = Number(yScale.getRightValue(value));
|
||||
if (rightValue < 0) {
|
||||
return yScale.getPixelForValue(sumNeg + rightValue);
|
||||
}
|
||||
return yScale.getPixelForValue(sumPos + rightValue);
|
||||
}
|
||||
|
||||
return yScale.getPixelForValue(value);
|
||||
},
|
||||
|
||||
updateBezierControlPoints: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var area = me.chart.chartArea;
|
||||
var points = (meta.data || []);
|
||||
var i, ilen, point, model, controlPoints;
|
||||
|
||||
// Only consider points that are drawn in case the spanGaps option is used
|
||||
if (meta.dataset._model.spanGaps) {
|
||||
points = points.filter(function(pt) {
|
||||
return !pt._model.skip;
|
||||
});
|
||||
var rightValue = Number(yScale.getRightValue(value));
|
||||
if (rightValue < 0) {
|
||||
return yScale.getPixelForValue(sumNeg + rightValue);
|
||||
}
|
||||
return yScale.getPixelForValue(sumPos + rightValue);
|
||||
}
|
||||
|
||||
function capControlPoint(pt, min, max) {
|
||||
return Math.max(Math.min(pt, max), min);
|
||||
return yScale.getPixelForValue(value);
|
||||
},
|
||||
|
||||
updateBezierControlPoints: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var area = me.chart.chartArea;
|
||||
var points = (meta.data || []);
|
||||
var i, ilen, point, model, controlPoints;
|
||||
|
||||
// Only consider points that are drawn in case the spanGaps option is used
|
||||
if (meta.dataset._model.spanGaps) {
|
||||
points = points.filter(function(pt) {
|
||||
return !pt._model.skip;
|
||||
});
|
||||
}
|
||||
|
||||
function capControlPoint(pt, min, max) {
|
||||
return Math.max(Math.min(pt, max), min);
|
||||
}
|
||||
|
||||
if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
|
||||
helpers.splineCurveMonotone(points);
|
||||
} else {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
point = points[i];
|
||||
model = point._model;
|
||||
controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(points, i)._model,
|
||||
model,
|
||||
helpers.nextItem(points, i)._model,
|
||||
meta.dataset._model.tension
|
||||
);
|
||||
model.controlPointPreviousX = controlPoints.previous.x;
|
||||
model.controlPointPreviousY = controlPoints.previous.y;
|
||||
model.controlPointNextX = controlPoints.next.x;
|
||||
model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
|
||||
helpers.splineCurveMonotone(points);
|
||||
} else {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
point = points[i];
|
||||
model = point._model;
|
||||
controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(points, i)._model,
|
||||
model,
|
||||
helpers.nextItem(points, i)._model,
|
||||
meta.dataset._model.tension
|
||||
);
|
||||
model.controlPointPreviousX = controlPoints.previous.x;
|
||||
model.controlPointPreviousY = controlPoints.previous.y;
|
||||
model.controlPointNextX = controlPoints.next.x;
|
||||
model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
if (me.chart.options.elements.line.capBezierPoints) {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
model = points[i]._model;
|
||||
model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
|
||||
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
|
||||
model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
|
||||
model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
if (me.chart.options.elements.line.capBezierPoints) {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
model = points[i]._model;
|
||||
model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
|
||||
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
|
||||
model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
|
||||
model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
|
||||
}
|
||||
}
|
||||
},
|
||||
draw: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var points = meta.data || [];
|
||||
var area = chart.chartArea;
|
||||
var ilen = points.length;
|
||||
var halfBorderWidth;
|
||||
var i = 0;
|
||||
|
||||
draw: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var points = meta.data || [];
|
||||
var area = chart.chartArea;
|
||||
var ilen = points.length;
|
||||
var halfBorderWidth;
|
||||
var i = 0;
|
||||
if (lineEnabled(me.getDataset(), chart.options)) {
|
||||
halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;
|
||||
|
||||
if (lineEnabled(me.getDataset(), chart.options)) {
|
||||
halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;
|
||||
helpers.canvas.clipArea(chart.ctx, {
|
||||
left: area.left,
|
||||
right: area.right,
|
||||
top: area.top - halfBorderWidth,
|
||||
bottom: area.bottom + halfBorderWidth
|
||||
});
|
||||
|
||||
helpers.canvas.clipArea(chart.ctx, {
|
||||
left: area.left,
|
||||
right: area.right,
|
||||
top: area.top - halfBorderWidth,
|
||||
bottom: area.bottom + halfBorderWidth
|
||||
});
|
||||
meta.dataset.draw();
|
||||
|
||||
meta.dataset.draw();
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
}
|
||||
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
}
|
||||
// Draw the points
|
||||
for (; i < ilen; ++i) {
|
||||
points[i].draw(area);
|
||||
}
|
||||
},
|
||||
|
||||
// Draw the points
|
||||
for (; i < ilen; ++i) {
|
||||
points[i].draw(area);
|
||||
}
|
||||
},
|
||||
setHoverStyle: function(element) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||||
var index = element._index;
|
||||
var custom = element.custom || {};
|
||||
var model = element._model;
|
||||
|
||||
setHoverStyle: function(element) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||||
var index = element._index;
|
||||
var custom = element.custom || {};
|
||||
var model = element._model;
|
||||
element.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
element.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
|
||||
model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
},
|
||||
});
|
||||
};
|
||||
model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
|
||||
model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
}
|
||||
});
|
||||
|
||||
13
src/controllers/controller.pie.js
Normal file
13
src/controllers/controller.pie.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var DoughnutController = require('./controller.doughnut');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
defaults._set('pie', helpers.clone(defaults.doughnut));
|
||||
defaults._set('pie', {
|
||||
cutoutPercentage: 0
|
||||
});
|
||||
|
||||
// Pie charts are Doughnut chart with different defaults
|
||||
module.exports = DoughnutController;
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
@ -108,148 +109,145 @@ defaults._set('polarArea', {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
Chart.controllers.polarArea = Chart.DatasetController.extend({
|
||||
dataElementType: elements.Arc,
|
||||
|
||||
dataElementType: elements.Arc,
|
||||
linkScales: helpers.noop,
|
||||
|
||||
linkScales: helpers.noop,
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var dataset = me.getDataset();
|
||||
var meta = me.getMeta();
|
||||
var start = me.chart.options.startAngle || 0;
|
||||
var starts = me._starts = [];
|
||||
var angles = me._angles = [];
|
||||
var i, ilen, angle;
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var dataset = me.getDataset();
|
||||
var meta = me.getMeta();
|
||||
var start = me.chart.options.startAngle || 0;
|
||||
var starts = me._starts = [];
|
||||
var angles = me._angles = [];
|
||||
var i, ilen, angle;
|
||||
me._updateRadius();
|
||||
|
||||
me._updateRadius();
|
||||
meta.count = me.countVisibleElements();
|
||||
|
||||
meta.count = me.countVisibleElements();
|
||||
|
||||
for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
|
||||
starts[i] = start;
|
||||
angle = me._computeAngle(i);
|
||||
angles[i] = angle;
|
||||
start += angle;
|
||||
}
|
||||
|
||||
helpers.each(meta.data, function(arc, index) {
|
||||
me.updateElement(arc, index, reset);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateRadius: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var arcOpts = opts.elements.arc;
|
||||
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
||||
|
||||
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
|
||||
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
|
||||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
|
||||
|
||||
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
|
||||
me.innerRadius = me.outerRadius - chart.radiusLength;
|
||||
},
|
||||
|
||||
updateElement: function(arc, index, reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var dataset = me.getDataset();
|
||||
var opts = chart.options;
|
||||
var animationOpts = opts.animation;
|
||||
var scale = chart.scale;
|
||||
var labels = chart.data.labels;
|
||||
|
||||
var centerX = scale.xCenter;
|
||||
var centerY = scale.yCenter;
|
||||
|
||||
// var negHalfPI = -0.5 * Math.PI;
|
||||
var datasetStartAngle = opts.startAngle;
|
||||
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
|
||||
var startAngle = me._starts[index];
|
||||
var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
|
||||
|
||||
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
_scale: scale,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
innerRadius: 0,
|
||||
outerRadius: reset ? resetRadius : distance,
|
||||
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
|
||||
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
|
||||
label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
|
||||
}
|
||||
});
|
||||
|
||||
// Apply border and fill style
|
||||
var elementOpts = this.chart.options.elements.arc;
|
||||
var custom = arc.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var model = arc._model;
|
||||
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
|
||||
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
|
||||
|
||||
arc.pivot();
|
||||
},
|
||||
|
||||
countVisibleElements: function() {
|
||||
var dataset = this.getDataset();
|
||||
var meta = this.getMeta();
|
||||
var count = 0;
|
||||
|
||||
helpers.each(meta.data, function(element, index) {
|
||||
if (!isNaN(dataset.data[index]) && !element.hidden) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_computeAngle: function(index) {
|
||||
var me = this;
|
||||
var count = this.getMeta().count;
|
||||
var dataset = me.getDataset();
|
||||
var meta = me.getMeta();
|
||||
|
||||
if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: me.chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
return helpers.options.resolve([
|
||||
me.chart.options.elements.arc.angle,
|
||||
(2 * Math.PI) / count
|
||||
], context, index);
|
||||
for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
|
||||
starts[i] = start;
|
||||
angle = me._computeAngle(i);
|
||||
angles[i] = angle;
|
||||
start += angle;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
helpers.each(meta.data, function(arc, index) {
|
||||
me.updateElement(arc, index, reset);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateRadius: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var arcOpts = opts.elements.arc;
|
||||
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
||||
|
||||
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
|
||||
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
|
||||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
|
||||
|
||||
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
|
||||
me.innerRadius = me.outerRadius - chart.radiusLength;
|
||||
},
|
||||
|
||||
updateElement: function(arc, index, reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var dataset = me.getDataset();
|
||||
var opts = chart.options;
|
||||
var animationOpts = opts.animation;
|
||||
var scale = chart.scale;
|
||||
var labels = chart.data.labels;
|
||||
|
||||
var centerX = scale.xCenter;
|
||||
var centerY = scale.yCenter;
|
||||
|
||||
// var negHalfPI = -0.5 * Math.PI;
|
||||
var datasetStartAngle = opts.startAngle;
|
||||
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
|
||||
var startAngle = me._starts[index];
|
||||
var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
|
||||
|
||||
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
_scale: scale,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
innerRadius: 0,
|
||||
outerRadius: reset ? resetRadius : distance,
|
||||
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
|
||||
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
|
||||
label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
|
||||
}
|
||||
});
|
||||
|
||||
// Apply border and fill style
|
||||
var elementOpts = this.chart.options.elements.arc;
|
||||
var custom = arc.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var model = arc._model;
|
||||
|
||||
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
|
||||
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
|
||||
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
|
||||
|
||||
arc.pivot();
|
||||
},
|
||||
|
||||
countVisibleElements: function() {
|
||||
var dataset = this.getDataset();
|
||||
var meta = this.getMeta();
|
||||
var count = 0;
|
||||
|
||||
helpers.each(meta.data, function(element, index) {
|
||||
if (!isNaN(dataset.data[index]) && !element.hidden) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_computeAngle: function(index) {
|
||||
var me = this;
|
||||
var count = this.getMeta().count;
|
||||
var dataset = me.getDataset();
|
||||
var meta = me.getMeta();
|
||||
|
||||
if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: me.chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
return helpers.options.resolve([
|
||||
me.chart.options.elements.arc.angle,
|
||||
(2 * Math.PI) / count
|
||||
], context, index);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
@ -15,158 +16,157 @@ defaults._set('radar', {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
Chart.controllers.radar = Chart.DatasetController.extend({
|
||||
datasetElementType: elements.Line,
|
||||
|
||||
datasetElementType: elements.Line,
|
||||
dataElementType: elements.Point,
|
||||
|
||||
dataElementType: elements.Point,
|
||||
linkScales: helpers.noop,
|
||||
|
||||
linkScales: helpers.noop,
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var line = meta.dataset;
|
||||
var points = meta.data || [];
|
||||
var custom = line.custom || {};
|
||||
var dataset = me.getDataset();
|
||||
var lineElementOptions = me.chart.options.elements.line;
|
||||
var scale = me.chart.scale;
|
||||
var i, ilen;
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var line = meta.dataset;
|
||||
var points = meta.data || [];
|
||||
var custom = line.custom || {};
|
||||
var dataset = me.getDataset();
|
||||
var lineElementOptions = me.chart.options.elements.line;
|
||||
var scale = me.chart.scale;
|
||||
var i, ilen;
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
|
||||
dataset.lineTension = dataset.tension;
|
||||
}
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
|
||||
dataset.lineTension = dataset.tension;
|
||||
helpers.extend(meta.dataset, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_scale: scale,
|
||||
// Data
|
||||
_children: points,
|
||||
_loop: true,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
|
||||
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
|
||||
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
|
||||
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
|
||||
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
|
||||
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
|
||||
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
|
||||
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
|
||||
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
|
||||
}
|
||||
});
|
||||
|
||||
helpers.extend(meta.dataset, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_scale: scale,
|
||||
// Data
|
||||
_children: points,
|
||||
_loop: true,
|
||||
// Model
|
||||
_model: {
|
||||
// Appearance
|
||||
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
|
||||
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
|
||||
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
|
||||
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
|
||||
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
|
||||
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
|
||||
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
|
||||
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
|
||||
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
|
||||
}
|
||||
});
|
||||
meta.dataset.pivot();
|
||||
|
||||
meta.dataset.pivot();
|
||||
// Update Points
|
||||
for (i = 0, ilen = points.length; i < ilen; i++) {
|
||||
me.updateElement(points[i], i, reset);
|
||||
}
|
||||
|
||||
// Update Points
|
||||
for (i = 0, ilen = points.length; i < ilen; i++) {
|
||||
me.updateElement(points[i], i, reset);
|
||||
// Update bezier control points
|
||||
me.updateBezierControlPoints();
|
||||
|
||||
// Now pivot the point for animation
|
||||
for (i = 0, ilen = points.length; i < ilen; i++) {
|
||||
points[i].pivot();
|
||||
}
|
||||
},
|
||||
|
||||
updateElement: function(point, index, reset) {
|
||||
var me = this;
|
||||
var custom = point.custom || {};
|
||||
var dataset = me.getDataset();
|
||||
var scale = me.chart.scale;
|
||||
var pointElementOptions = me.chart.options.elements.point;
|
||||
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
|
||||
dataset.pointRadius = dataset.radius;
|
||||
}
|
||||
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
|
||||
dataset.pointHitRadius = dataset.hitRadius;
|
||||
}
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
_scale: scale,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: reset ? scale.yCenter : pointPosition.y,
|
||||
|
||||
// Appearance
|
||||
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
|
||||
radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
|
||||
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
|
||||
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
|
||||
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
|
||||
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
|
||||
rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation),
|
||||
|
||||
// Tooltip
|
||||
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
|
||||
}
|
||||
});
|
||||
|
||||
// Update bezier control points
|
||||
me.updateBezierControlPoints();
|
||||
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
|
||||
},
|
||||
|
||||
// Now pivot the point for animation
|
||||
for (i = 0, ilen = points.length; i < ilen; i++) {
|
||||
points[i].pivot();
|
||||
}
|
||||
},
|
||||
updateElement: function(point, index, reset) {
|
||||
var me = this;
|
||||
var custom = point.custom || {};
|
||||
var dataset = me.getDataset();
|
||||
var scale = me.chart.scale;
|
||||
var pointElementOptions = me.chart.options.elements.point;
|
||||
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
|
||||
updateBezierControlPoints: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var area = me.chart.chartArea;
|
||||
var points = meta.data || [];
|
||||
var i, ilen, model, controlPoints;
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
|
||||
dataset.pointRadius = dataset.radius;
|
||||
}
|
||||
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
|
||||
dataset.pointHitRadius = dataset.hitRadius;
|
||||
}
|
||||
function capControlPoint(pt, min, max) {
|
||||
return Math.max(Math.min(pt, max), min);
|
||||
}
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
_scale: scale,
|
||||
for (i = 0, ilen = points.length; i < ilen; i++) {
|
||||
model = points[i]._model;
|
||||
controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(points, i, true)._model,
|
||||
model,
|
||||
helpers.nextItem(points, i, true)._model,
|
||||
model.tension
|
||||
);
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: reset ? scale.yCenter : pointPosition.y,
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
|
||||
model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
|
||||
model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
|
||||
model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
|
||||
}
|
||||
},
|
||||
|
||||
// Appearance
|
||||
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
|
||||
radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
|
||||
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
|
||||
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
|
||||
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
|
||||
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
|
||||
rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation),
|
||||
setHoverStyle: function(point) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var custom = point.custom || {};
|
||||
var index = point._index;
|
||||
var model = point._model;
|
||||
|
||||
// Tooltip
|
||||
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
|
||||
}
|
||||
});
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
|
||||
},
|
||||
updateBezierControlPoints: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var area = me.chart.chartArea;
|
||||
var points = meta.data || [];
|
||||
var i, ilen, model, controlPoints;
|
||||
|
||||
function capControlPoint(pt, min, max) {
|
||||
return Math.max(Math.min(pt, max), min);
|
||||
}
|
||||
|
||||
for (i = 0, ilen = points.length; i < ilen; i++) {
|
||||
model = points[i]._model;
|
||||
controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(points, i, true)._model,
|
||||
model,
|
||||
helpers.nextItem(points, i, true)._model,
|
||||
model.tension
|
||||
);
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
|
||||
model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
|
||||
model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
|
||||
model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
|
||||
}
|
||||
},
|
||||
|
||||
setHoverStyle: function(point) {
|
||||
// Point
|
||||
var dataset = this.chart.data.datasets[point._datasetIndex];
|
||||
var custom = point.custom || {};
|
||||
var index = point._index;
|
||||
var model = point._model;
|
||||
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
|
||||
},
|
||||
});
|
||||
};
|
||||
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var LineController = require('./controller.line');
|
||||
var defaults = require('../core/core.defaults');
|
||||
|
||||
defaults._set('scatter', {
|
||||
@ -34,9 +35,5 @@ defaults._set('scatter', {
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
// Scatter charts use line controllers
|
||||
Chart.controllers.scatter = Chart.controllers.line;
|
||||
|
||||
};
|
||||
// Scatter charts use line controllers
|
||||
module.exports = LineController;
|
||||
|
||||
18
src/controllers/index.js
Normal file
18
src/controllers/index.js
Normal file
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
// NOTE export a map in which the key represents the controller type, not
|
||||
// the class, and so must be CamelCase in order to be correctly retrieved
|
||||
// by the controller in core.controller.js (`controllers[meta.type]`).
|
||||
|
||||
/* eslint-disable global-require */
|
||||
module.exports = {
|
||||
bar: require('./controller.bar'),
|
||||
bubble: require('./controller.bubble'),
|
||||
doughnut: require('./controller.doughnut'),
|
||||
horizontalBar: require('./controller.horizontalBar'),
|
||||
line: require('./controller.line'),
|
||||
polarArea: require('./controller.polarArea'),
|
||||
pie: require('./controller.pie'),
|
||||
radar: require('./controller.radar'),
|
||||
scatter: require('./controller.scatter')
|
||||
};
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
var Animation = require('./core.animation');
|
||||
var animations = require('./core.animations');
|
||||
var controllers = require('../controllers/index');
|
||||
var defaults = require('./core.defaults');
|
||||
var helpers = require('../helpers/index');
|
||||
var Interaction = require('./core.interaction');
|
||||
@ -20,9 +21,6 @@ module.exports = function(Chart) {
|
||||
// Destroy method on the chart will remove the instance of the chart from this reference.
|
||||
Chart.instances = {};
|
||||
|
||||
// Controllers available for dataset visualization eg. bar, line, slice, etc.
|
||||
Chart.controllers = {};
|
||||
|
||||
/**
|
||||
* Initializes the given config with global and chart default values.
|
||||
*/
|
||||
@ -337,7 +335,7 @@ module.exports = function(Chart) {
|
||||
meta.controller.updateIndex(datasetIndex);
|
||||
meta.controller.linkScales();
|
||||
} else {
|
||||
var ControllerClass = Chart.controllers[meta.type];
|
||||
var ControllerClass = controllers[meta.type];
|
||||
if (ControllerClass === undefined) {
|
||||
throw new Error('"' + meta.type + '" is not a chart type.');
|
||||
}
|
||||
|
||||
@ -2,328 +2,325 @@
|
||||
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
module.exports = function(Chart) {
|
||||
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
|
||||
|
||||
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
|
||||
|
||||
/**
|
||||
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
|
||||
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are
|
||||
* called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
|
||||
*/
|
||||
function listenArrayEvents(array, listener) {
|
||||
if (array._chartjs) {
|
||||
array._chartjs.listeners.push(listener);
|
||||
return;
|
||||
}
|
||||
|
||||
Object.defineProperty(array, '_chartjs', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: {
|
||||
listeners: [listener]
|
||||
}
|
||||
});
|
||||
|
||||
arrayEvents.forEach(function(key) {
|
||||
var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
|
||||
var base = array[key];
|
||||
|
||||
Object.defineProperty(array, key, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var res = base.apply(this, args);
|
||||
|
||||
helpers.each(array._chartjs.listeners, function(object) {
|
||||
if (typeof object[method] === 'function') {
|
||||
object[method].apply(object, args);
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
|
||||
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are
|
||||
* called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
|
||||
*/
|
||||
function listenArrayEvents(array, listener) {
|
||||
if (array._chartjs) {
|
||||
array._chartjs.listeners.push(listener);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given array event listener and cleanup extra attached properties (such as
|
||||
* the _chartjs stub and overridden methods) if array doesn't have any more listeners.
|
||||
*/
|
||||
function unlistenArrayEvents(array, listener) {
|
||||
var stub = array._chartjs;
|
||||
if (!stub) {
|
||||
return;
|
||||
}
|
||||
|
||||
var listeners = stub.listeners;
|
||||
var index = listeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
|
||||
if (listeners.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
arrayEvents.forEach(function(key) {
|
||||
delete array[key];
|
||||
});
|
||||
|
||||
delete array._chartjs;
|
||||
}
|
||||
|
||||
// Base class for all dataset controllers (line, bar, etc)
|
||||
Chart.DatasetController = function(chart, datasetIndex) {
|
||||
this.initialize(chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(Chart.DatasetController.prototype, {
|
||||
|
||||
/**
|
||||
* Element type used to generate a meta dataset (e.g. Chart.element.Line).
|
||||
* @type {Chart.core.element}
|
||||
*/
|
||||
datasetElementType: null,
|
||||
|
||||
/**
|
||||
* Element type used to generate a meta data (e.g. Chart.element.Point).
|
||||
* @type {Chart.core.element}
|
||||
*/
|
||||
dataElementType: null,
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
var me = this;
|
||||
me.chart = chart;
|
||||
me.index = datasetIndex;
|
||||
me.linkScales();
|
||||
me.addElements();
|
||||
},
|
||||
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var dataset = me.getDataset();
|
||||
|
||||
if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {
|
||||
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
|
||||
}
|
||||
if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {
|
||||
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getMeta: function() {
|
||||
return this.chart.getDatasetMeta(this.index);
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy: function() {
|
||||
if (this._data) {
|
||||
unlistenArrayEvents(this._data, this);
|
||||
}
|
||||
},
|
||||
|
||||
createMetaDataset: function() {
|
||||
var me = this;
|
||||
var type = me.datasetElementType;
|
||||
return type && new type({
|
||||
_chart: me.chart,
|
||||
_datasetIndex: me.index
|
||||
});
|
||||
},
|
||||
|
||||
createMetaData: function(index) {
|
||||
var me = this;
|
||||
var type = me.dataElementType;
|
||||
return type && new type({
|
||||
_chart: me.chart,
|
||||
_datasetIndex: me.index,
|
||||
_index: index
|
||||
});
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var data = me.getDataset().data || [];
|
||||
var metaData = meta.data;
|
||||
var i, ilen;
|
||||
|
||||
for (i = 0, ilen = data.length; i < ilen; ++i) {
|
||||
metaData[i] = metaData[i] || me.createMetaData(i);
|
||||
}
|
||||
|
||||
meta.dataset = meta.dataset || me.createMetaDataset();
|
||||
},
|
||||
|
||||
addElementAndReset: function(index) {
|
||||
var element = this.createMetaData(index);
|
||||
this.getMeta().data.splice(index, 0, element);
|
||||
this.updateElement(element, index, true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function() {
|
||||
var me = this;
|
||||
var dataset = me.getDataset();
|
||||
var data = dataset.data || (dataset.data = []);
|
||||
|
||||
// In order to correctly handle data addition/deletion animation (an thus simulate
|
||||
// real-time charts), we need to monitor these data modifications and synchronize
|
||||
// the internal meta data accordingly.
|
||||
if (me._data !== data) {
|
||||
if (me._data) {
|
||||
// This case happens when the user replaced the data array instance.
|
||||
unlistenArrayEvents(me._data, me);
|
||||
}
|
||||
|
||||
listenArrayEvents(data, me);
|
||||
me._data = data;
|
||||
}
|
||||
|
||||
// Re-sync meta data in case the user replaced the data array or if we missed
|
||||
// any updates and so make sure that we handle number of datapoints changing.
|
||||
me.resyncElements();
|
||||
},
|
||||
|
||||
update: helpers.noop,
|
||||
|
||||
transition: function(easingValue) {
|
||||
var meta = this.getMeta();
|
||||
var elements = meta.data || [];
|
||||
var ilen = elements.length;
|
||||
var i = 0;
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
elements[i].transition(easingValue);
|
||||
}
|
||||
|
||||
if (meta.dataset) {
|
||||
meta.dataset.transition(easingValue);
|
||||
}
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
var meta = this.getMeta();
|
||||
var elements = meta.data || [];
|
||||
var ilen = elements.length;
|
||||
var i = 0;
|
||||
|
||||
if (meta.dataset) {
|
||||
meta.dataset.draw();
|
||||
}
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
elements[i].draw();
|
||||
}
|
||||
},
|
||||
|
||||
removeHoverStyle: function(element) {
|
||||
helpers.merge(element._model, element.$previousStyle || {});
|
||||
delete element.$previousStyle;
|
||||
},
|
||||
|
||||
setHoverStyle: function(element) {
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||||
var index = element._index;
|
||||
var custom = element.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
var model = element._model;
|
||||
|
||||
element.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth
|
||||
};
|
||||
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
resyncElements: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var data = me.getDataset().data;
|
||||
var numMeta = meta.data.length;
|
||||
var numData = data.length;
|
||||
|
||||
if (numData < numMeta) {
|
||||
meta.data.splice(numData, numMeta - numData);
|
||||
} else if (numData > numMeta) {
|
||||
me.insertElements(numMeta, numData - numMeta);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
insertElements: function(start, count) {
|
||||
for (var i = 0; i < count; ++i) {
|
||||
this.addElementAndReset(start + i);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataPush: function() {
|
||||
this.insertElements(this.getDataset().data.length - 1, arguments.length);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataPop: function() {
|
||||
this.getMeta().data.pop();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataShift: function() {
|
||||
this.getMeta().data.shift();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataSplice: function(start, count) {
|
||||
this.getMeta().data.splice(start, count);
|
||||
this.insertElements(start, arguments.length - 2);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataUnshift: function() {
|
||||
this.insertElements(0, arguments.length);
|
||||
Object.defineProperty(array, '_chartjs', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: {
|
||||
listeners: [listener]
|
||||
}
|
||||
});
|
||||
|
||||
Chart.DatasetController.extend = helpers.inherits;
|
||||
arrayEvents.forEach(function(key) {
|
||||
var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
|
||||
var base = array[key];
|
||||
|
||||
Object.defineProperty(array, key, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var res = base.apply(this, args);
|
||||
|
||||
helpers.each(array._chartjs.listeners, function(object) {
|
||||
if (typeof object[method] === 'function') {
|
||||
object[method].apply(object, args);
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given array event listener and cleanup extra attached properties (such as
|
||||
* the _chartjs stub and overridden methods) if array doesn't have any more listeners.
|
||||
*/
|
||||
function unlistenArrayEvents(array, listener) {
|
||||
var stub = array._chartjs;
|
||||
if (!stub) {
|
||||
return;
|
||||
}
|
||||
|
||||
var listeners = stub.listeners;
|
||||
var index = listeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
|
||||
if (listeners.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
arrayEvents.forEach(function(key) {
|
||||
delete array[key];
|
||||
});
|
||||
|
||||
delete array._chartjs;
|
||||
}
|
||||
|
||||
// Base class for all dataset controllers (line, bar, etc)
|
||||
var DatasetController = module.exports = function(chart, datasetIndex) {
|
||||
this.initialize(chart, datasetIndex);
|
||||
};
|
||||
|
||||
helpers.extend(DatasetController.prototype, {
|
||||
|
||||
/**
|
||||
* Element type used to generate a meta dataset (e.g. Chart.element.Line).
|
||||
* @type {Chart.core.element}
|
||||
*/
|
||||
datasetElementType: null,
|
||||
|
||||
/**
|
||||
* Element type used to generate a meta data (e.g. Chart.element.Point).
|
||||
* @type {Chart.core.element}
|
||||
*/
|
||||
dataElementType: null,
|
||||
|
||||
initialize: function(chart, datasetIndex) {
|
||||
var me = this;
|
||||
me.chart = chart;
|
||||
me.index = datasetIndex;
|
||||
me.linkScales();
|
||||
me.addElements();
|
||||
},
|
||||
|
||||
updateIndex: function(datasetIndex) {
|
||||
this.index = datasetIndex;
|
||||
},
|
||||
|
||||
linkScales: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var dataset = me.getDataset();
|
||||
|
||||
if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {
|
||||
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
|
||||
}
|
||||
if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {
|
||||
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
getDataset: function() {
|
||||
return this.chart.data.datasets[this.index];
|
||||
},
|
||||
|
||||
getMeta: function() {
|
||||
return this.chart.getDatasetMeta(this.index);
|
||||
},
|
||||
|
||||
getScaleForId: function(scaleID) {
|
||||
return this.chart.scales[scaleID];
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.update(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy: function() {
|
||||
if (this._data) {
|
||||
unlistenArrayEvents(this._data, this);
|
||||
}
|
||||
},
|
||||
|
||||
createMetaDataset: function() {
|
||||
var me = this;
|
||||
var type = me.datasetElementType;
|
||||
return type && new type({
|
||||
_chart: me.chart,
|
||||
_datasetIndex: me.index
|
||||
});
|
||||
},
|
||||
|
||||
createMetaData: function(index) {
|
||||
var me = this;
|
||||
var type = me.dataElementType;
|
||||
return type && new type({
|
||||
_chart: me.chart,
|
||||
_datasetIndex: me.index,
|
||||
_index: index
|
||||
});
|
||||
},
|
||||
|
||||
addElements: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var data = me.getDataset().data || [];
|
||||
var metaData = meta.data;
|
||||
var i, ilen;
|
||||
|
||||
for (i = 0, ilen = data.length; i < ilen; ++i) {
|
||||
metaData[i] = metaData[i] || me.createMetaData(i);
|
||||
}
|
||||
|
||||
meta.dataset = meta.dataset || me.createMetaDataset();
|
||||
},
|
||||
|
||||
addElementAndReset: function(index) {
|
||||
var element = this.createMetaData(index);
|
||||
this.getMeta().data.splice(index, 0, element);
|
||||
this.updateElement(element, index, true);
|
||||
},
|
||||
|
||||
buildOrUpdateElements: function() {
|
||||
var me = this;
|
||||
var dataset = me.getDataset();
|
||||
var data = dataset.data || (dataset.data = []);
|
||||
|
||||
// In order to correctly handle data addition/deletion animation (an thus simulate
|
||||
// real-time charts), we need to monitor these data modifications and synchronize
|
||||
// the internal meta data accordingly.
|
||||
if (me._data !== data) {
|
||||
if (me._data) {
|
||||
// This case happens when the user replaced the data array instance.
|
||||
unlistenArrayEvents(me._data, me);
|
||||
}
|
||||
|
||||
listenArrayEvents(data, me);
|
||||
me._data = data;
|
||||
}
|
||||
|
||||
// Re-sync meta data in case the user replaced the data array or if we missed
|
||||
// any updates and so make sure that we handle number of datapoints changing.
|
||||
me.resyncElements();
|
||||
},
|
||||
|
||||
update: helpers.noop,
|
||||
|
||||
transition: function(easingValue) {
|
||||
var meta = this.getMeta();
|
||||
var elements = meta.data || [];
|
||||
var ilen = elements.length;
|
||||
var i = 0;
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
elements[i].transition(easingValue);
|
||||
}
|
||||
|
||||
if (meta.dataset) {
|
||||
meta.dataset.transition(easingValue);
|
||||
}
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
var meta = this.getMeta();
|
||||
var elements = meta.data || [];
|
||||
var ilen = elements.length;
|
||||
var i = 0;
|
||||
|
||||
if (meta.dataset) {
|
||||
meta.dataset.draw();
|
||||
}
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
elements[i].draw();
|
||||
}
|
||||
},
|
||||
|
||||
removeHoverStyle: function(element) {
|
||||
helpers.merge(element._model, element.$previousStyle || {});
|
||||
delete element.$previousStyle;
|
||||
},
|
||||
|
||||
setHoverStyle: function(element) {
|
||||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||||
var index = element._index;
|
||||
var custom = element.custom || {};
|
||||
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
var model = element._model;
|
||||
|
||||
element.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth
|
||||
};
|
||||
|
||||
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
|
||||
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
|
||||
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
resyncElements: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var data = me.getDataset().data;
|
||||
var numMeta = meta.data.length;
|
||||
var numData = data.length;
|
||||
|
||||
if (numData < numMeta) {
|
||||
meta.data.splice(numData, numMeta - numData);
|
||||
} else if (numData > numMeta) {
|
||||
me.insertElements(numMeta, numData - numMeta);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
insertElements: function(start, count) {
|
||||
for (var i = 0; i < count; ++i) {
|
||||
this.addElementAndReset(start + i);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataPush: function() {
|
||||
this.insertElements(this.getDataset().data.length - 1, arguments.length);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataPop: function() {
|
||||
this.getMeta().data.pop();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataShift: function() {
|
||||
this.getMeta().data.shift();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataSplice: function(start, count) {
|
||||
this.getMeta().data.splice(start, count);
|
||||
this.insertElements(start, arguments.length - 2);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onDataUnshift: function() {
|
||||
this.insertElements(0, arguments.length);
|
||||
}
|
||||
});
|
||||
|
||||
DatasetController.extend = helpers.inherits;
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
describe('Chart.controllers.bar', function() {
|
||||
describe('auto', jasmine.fixture.specs('controller.bar'));
|
||||
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.bar).toBe('function');
|
||||
expect(typeof Chart.controllers.horizontalBar).toBe('function');
|
||||
});
|
||||
|
||||
it('should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'bar',
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
describe('Chart.controllers.bubble', function() {
|
||||
describe('auto', jasmine.fixture.specs('controller.bubble'));
|
||||
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.bubble).toBe('function');
|
||||
});
|
||||
|
||||
it('should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'bubble',
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
describe('Chart.controllers.doughnut', function() {
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.doughnut).toBe('function');
|
||||
expect(Chart.controllers.doughnut).toBe(Chart.controllers.pie);
|
||||
});
|
||||
|
||||
it('should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'doughnut',
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
describe('Chart.controllers.line', function() {
|
||||
describe('auto', jasmine.fixture.specs('controller.line'));
|
||||
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.line).toBe('function');
|
||||
});
|
||||
|
||||
it('should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
describe('auto', jasmine.fixture.specs('controller.polarArea'));
|
||||
|
||||
describe('Chart.controllers.polarArea', function() {
|
||||
describe('auto', jasmine.fixture.specs('controller.polarArea'));
|
||||
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.polarArea).toBe('function');
|
||||
});
|
||||
|
||||
it('should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'polarArea',
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
describe('Chart.controllers.radar', function() {
|
||||
describe('auto', jasmine.fixture.specs('controller.radar'));
|
||||
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.radar).toBe('function');
|
||||
});
|
||||
|
||||
it('Should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'radar',
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
describe('Chart.controllers.scatter', function() {
|
||||
it('should be registered as dataset controller', function() {
|
||||
expect(typeof Chart.controllers.scatter).toBe('function');
|
||||
});
|
||||
|
||||
describe('showLines option', function() {
|
||||
it('should not draw a line if undefined', function() {
|
||||
var chart = window.acquireChart({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user