İlk temizlik tamamlandı bir önceki projeden
This commit is contained in:
@@ -0,0 +1,431 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var deprecated = helpers._deprecated;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
defaults._set('bar', {
|
||||
hover: {
|
||||
mode: 'label'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'category',
|
||||
offset: true,
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}],
|
||||
|
||||
yAxes: [{
|
||||
type: 'linear'
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
defaults._set('global', {
|
||||
datasets: {
|
||||
bar: {
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
|
||||
* @private
|
||||
*/
|
||||
function computeMinSampleSize(scale, pixels) {
|
||||
var min = scale._length;
|
||||
var prev, curr, i, ilen;
|
||||
|
||||
for (i = 1, ilen = pixels.length; i < ilen; ++i) {
|
||||
min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
|
||||
}
|
||||
|
||||
for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) {
|
||||
curr = scale.getPixelForTick(i);
|
||||
min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
|
||||
* uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
|
||||
* mode currently always generates bars equally sized (until we introduce scriptable options?).
|
||||
* @private
|
||||
*/
|
||||
function computeFitCategoryTraits(index, ruler, options) {
|
||||
var thickness = options.barThickness;
|
||||
var count = ruler.stackCount;
|
||||
var curr = ruler.pixels[index];
|
||||
var min = helpers.isNullOrUndef(thickness)
|
||||
? computeMinSampleSize(ruler.scale, ruler.pixels)
|
||||
: -1;
|
||||
var size, ratio;
|
||||
|
||||
if (helpers.isNullOrUndef(thickness)) {
|
||||
size = min * options.categoryPercentage;
|
||||
ratio = options.barPercentage;
|
||||
} else {
|
||||
// When bar thickness is enforced, category and bar percentages are ignored.
|
||||
// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
|
||||
// and deprecate barPercentage since this value is ignored when thickness is absolute.
|
||||
size = thickness * count;
|
||||
ratio = 1;
|
||||
}
|
||||
|
||||
return {
|
||||
chunk: size / count,
|
||||
ratio: ratio,
|
||||
start: curr - (size / 2)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an "optimal" category that globally arranges bars side by side (no gap when
|
||||
* percentage options are 1), based on the previous and following categories. This mode
|
||||
* generates bars with different widths when data are not evenly spaced.
|
||||
* @private
|
||||
*/
|
||||
function computeFlexCategoryTraits(index, ruler, options) {
|
||||
var pixels = ruler.pixels;
|
||||
var curr = pixels[index];
|
||||
var prev = index > 0 ? pixels[index - 1] : null;
|
||||
var next = index < pixels.length - 1 ? pixels[index + 1] : null;
|
||||
var percent = options.categoryPercentage;
|
||||
var start, size;
|
||||
|
||||
if (prev === null) {
|
||||
// first data: its size is double based on the next point or,
|
||||
// if it's also the last data, we use the scale size.
|
||||
prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
|
||||
}
|
||||
|
||||
if (next === null) {
|
||||
// last data: its size is also double based on the previous point.
|
||||
next = curr + curr - prev;
|
||||
}
|
||||
|
||||
start = curr - (curr - Math.min(prev, next)) / 2 * percent;
|
||||
size = Math.abs(next - prev) / 2 * percent;
|
||||
|
||||
return {
|
||||
chunk: size / ruler.stackCount,
|
||||
ratio: options.barPercentage,
|
||||
start: start
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
dataElementType: elements.Rectangle,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_dataElementOptions: [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderSkipped',
|
||||
'borderWidth',
|
||||
'barPercentage',
|
||||
'barThickness',
|
||||
'categoryPercentage',
|
||||
'maxBarThickness',
|
||||
'minBarLength'
|
||||
],
|
||||
|
||||
initialize: function() {
|
||||
var me = this;
|
||||
var meta, scaleOpts;
|
||||
|
||||
DatasetController.prototype.initialize.apply(me, arguments);
|
||||
|
||||
meta = me.getMeta();
|
||||
meta.stack = me.getDataset().stack;
|
||||
meta.bar = true;
|
||||
|
||||
scaleOpts = me._getIndexScale().options;
|
||||
deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage');
|
||||
deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness');
|
||||
deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage');
|
||||
deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength');
|
||||
deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness');
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var rects = me.getMeta().data;
|
||||
var i, ilen;
|
||||
|
||||
me._ruler = me.getRuler();
|
||||
|
||||
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._resolveDataElementOptions(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]
|
||||
};
|
||||
|
||||
if (helpers.isArray(dataset.data[index])) {
|
||||
rectangle._model.borderSkipped = null;
|
||||
}
|
||||
|
||||
me._updateElementGeometry(rectangle, index, reset, options);
|
||||
|
||||
rectangle.pivot();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateElementGeometry: function(rectangle, index, reset, options) {
|
||||
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, options);
|
||||
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options);
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the stacks based on groups and bar visibility.
|
||||
* @param {number} [last] - The dataset index
|
||||
* @returns {string[]} The list of stack IDs
|
||||
* @private
|
||||
*/
|
||||
_getStacks: function(last) {
|
||||
var me = this;
|
||||
var scale = me._getIndexScale();
|
||||
var metasets = scale._getMatchingVisibleMetas(me._type);
|
||||
var stacked = scale.options.stacked;
|
||||
var ilen = metasets.length;
|
||||
var stacks = [];
|
||||
var i, meta;
|
||||
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
meta = metasets[i];
|
||||
// stacked | meta.stack
|
||||
// | found | not found | undefined
|
||||
// false | x | x | x
|
||||
// true | | x |
|
||||
// undefined | | x | x
|
||||
if (stacked === false || stacks.indexOf(meta.stack) === -1 ||
|
||||
(stacked === undefined && meta.stack === undefined)) {
|
||||
stacks.push(meta.stack);
|
||||
}
|
||||
if (meta.index === last) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 pixels = [];
|
||||
var i, ilen;
|
||||
|
||||
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
|
||||
pixels.push(scale.getPixelForValue(null, i, me.index));
|
||||
}
|
||||
|
||||
return {
|
||||
pixels: pixels,
|
||||
start: scale._startPixel,
|
||||
end: scale._endPixel,
|
||||
stackCount: me.getStackCount(),
|
||||
scale: scale
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Note: pixel values are not clamped to the scale area.
|
||||
* @private
|
||||
*/
|
||||
calculateBarValuePixels: function(datasetIndex, index, options) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var scale = me._getValueScale();
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var datasets = chart.data.datasets;
|
||||
var metasets = scale._getMatchingVisibleMetas(me._type);
|
||||
var value = scale._parseValue(datasets[datasetIndex].data[index]);
|
||||
var minBarLength = options.minBarLength;
|
||||
var stacked = scale.options.stacked;
|
||||
var stack = me.getMeta().stack;
|
||||
var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;
|
||||
var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;
|
||||
var ilen = metasets.length;
|
||||
var i, imeta, ivalue, base, head, size, stackLength;
|
||||
|
||||
if (stacked || (stacked === undefined && stack !== undefined)) {
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
imeta = metasets[i];
|
||||
|
||||
if (imeta.index === datasetIndex) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (imeta.stack === stack) {
|
||||
stackLength = scale._parseValue(datasets[imeta.index].data[index]);
|
||||
ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;
|
||||
|
||||
if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {
|
||||
start += ivalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base = scale.getPixelForValue(start);
|
||||
head = scale.getPixelForValue(start + length);
|
||||
size = head - base;
|
||||
|
||||
if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
|
||||
size = minBarLength;
|
||||
if (length >= 0 && !isHorizontal || length < 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, options) {
|
||||
var me = this;
|
||||
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(
|
||||
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) {
|
||||
var val = scale._parseValue(dataset.data[i]);
|
||||
if (!isNaN(val.min) && !isNaN(val.max)) {
|
||||
rects[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveDataElementOptions: function() {
|
||||
var me = this;
|
||||
var values = helpers.extend({}, DatasetController.prototype._resolveDataElementOptions.apply(me, arguments));
|
||||
var indexOpts = me._getIndexScale().options;
|
||||
var valueOpts = me._getValueScale().options;
|
||||
|
||||
values.barPercentage = valueOrDefault(indexOpts.barPercentage, values.barPercentage);
|
||||
values.barThickness = valueOrDefault(indexOpts.barThickness, values.barThickness);
|
||||
values.categoryPercentage = valueOrDefault(indexOpts.categoryPercentage, values.categoryPercentage);
|
||||
values.maxBarThickness = valueOrDefault(indexOpts.maxBarThickness, values.maxBarThickness);
|
||||
values.minBarLength = valueOrDefault(valueOpts.minBarLength, values.minBarLength);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,172 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var resolve = helpers.options.resolve;
|
||||
|
||||
defaults._set('bubble', {
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear', // bubble should probably use a linear scale by default
|
||||
position: 'bottom',
|
||||
id: 'x-axis-0' // need an ID so datasets can reference the scale
|
||||
}],
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
position: 'left',
|
||||
id: 'y-axis-0'
|
||||
}]
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function() {
|
||||
// Title doesn't make sense for scatter since we format the data as a point
|
||||
return '';
|
||||
},
|
||||
label: function(item, data) {
|
||||
var datasetLabel = data.datasets[item.datasetIndex].label || '';
|
||||
var dataPoint = data.datasets[item.datasetIndex].data[item.index];
|
||||
return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
dataElementType: elements.Point,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_dataElementOptions: [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'hoverBackgroundColor',
|
||||
'hoverBorderColor',
|
||||
'hoverBorderWidth',
|
||||
'hoverRadius',
|
||||
'hitRadius',
|
||||
'pointStyle',
|
||||
'rotation'
|
||||
],
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var points = meta.data;
|
||||
|
||||
// Update Points
|
||||
helpers.each(points, function(point, index) {
|
||||
me.updateElement(point, index, reset);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @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._resolveDataElementOptions(point, index);
|
||||
var data = me.getDataset().data[index];
|
||||
var dsIndex = me.index;
|
||||
|
||||
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._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,
|
||||
};
|
||||
|
||||
point.pivot();
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
setHoverStyle: function(point) {
|
||||
var model = point._model;
|
||||
var options = point._options;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||||
model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = options.radius + options.hoverRadius;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveDataElementOptions: function(point, index) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var dataset = me.getDataset();
|
||||
var custom = point.custom || {};
|
||||
var data = dataset.data[index] || {};
|
||||
var values = DatasetController.prototype._resolveDataElementOptions.apply(me, arguments);
|
||||
|
||||
// Scriptable options
|
||||
var context = {
|
||||
chart: chart,
|
||||
dataIndex: index,
|
||||
dataset: dataset,
|
||||
datasetIndex: me.index
|
||||
};
|
||||
|
||||
// In case values were cached (and thus frozen), we need to clone the values
|
||||
if (me._cachedDataOpts === values) {
|
||||
values = helpers.extend({}, values);
|
||||
}
|
||||
|
||||
// Custom radius resolution
|
||||
values.radius = resolve([
|
||||
custom.radius,
|
||||
data.r,
|
||||
me._config.radius,
|
||||
chart.options.elements.point.radius
|
||||
], context, index);
|
||||
|
||||
return values;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,390 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
var PI = Math.PI;
|
||||
var DOUBLE_PI = PI * 2;
|
||||
var HALF_PI = PI / 2;
|
||||
|
||||
defaults._set('doughnut', {
|
||||
animation: {
|
||||
// Boolean - Whether we animate the rotation of the Doughnut
|
||||
animateRotate: true,
|
||||
// Boolean - Whether we animate scaling the Doughnut from the centre
|
||||
animateScale: false
|
||||
},
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
legendCallback: function(chart) {
|
||||
var list = document.createElement('ul');
|
||||
var data = chart.data;
|
||||
var datasets = data.datasets;
|
||||
var labels = data.labels;
|
||||
var i, ilen, listItem, listItemSpan;
|
||||
|
||||
list.setAttribute('class', chart.id + '-legend');
|
||||
if (datasets.length) {
|
||||
for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
|
||||
listItem = list.appendChild(document.createElement('li'));
|
||||
listItemSpan = listItem.appendChild(document.createElement('span'));
|
||||
listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
|
||||
if (labels[i]) {
|
||||
listItem.appendChild(document.createTextNode(labels[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.outerHTML;
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
generateLabels: function(chart) {
|
||||
var data = chart.data;
|
||||
if (data.labels.length && data.datasets.length) {
|
||||
return data.labels.map(function(label, i) {
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
var style = meta.controller.getStyle(i);
|
||||
|
||||
return {
|
||||
text: label,
|
||||
fillStyle: style.backgroundColor,
|
||||
strokeStyle: style.borderColor,
|
||||
lineWidth: style.borderWidth,
|
||||
hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
|
||||
|
||||
// Extra data used for toggling the correct item
|
||||
index: i
|
||||
};
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function(e, legendItem) {
|
||||
var index = legendItem.index;
|
||||
var chart = this.chart;
|
||||
var i, ilen, meta;
|
||||
|
||||
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
// toggle visibility of index if exists
|
||||
if (meta.data[index]) {
|
||||
meta.data[index].hidden = !meta.data[index].hidden;
|
||||
}
|
||||
}
|
||||
|
||||
chart.update();
|
||||
}
|
||||
},
|
||||
|
||||
// The percentage of the chart that we cut out of the middle.
|
||||
cutoutPercentage: 50,
|
||||
|
||||
// The rotation of the chart, where the first data arc begins.
|
||||
rotation: -HALF_PI,
|
||||
|
||||
// The total circumference of the chart.
|
||||
circumference: DOUBLE_PI,
|
||||
|
||||
// Need to override these to give a nice default
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function() {
|
||||
return '';
|
||||
},
|
||||
label: function(tooltipItem, data) {
|
||||
var dataLabel = data.labels[tooltipItem.index];
|
||||
var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
|
||||
if (helpers.isArray(dataLabel)) {
|
||||
// show value on first line of multiline label
|
||||
// need to clone because we are changing the value
|
||||
dataLabel = dataLabel.slice();
|
||||
dataLabel[0] += value;
|
||||
} else {
|
||||
dataLabel += value;
|
||||
}
|
||||
|
||||
return dataLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
dataElementType: elements.Arc,
|
||||
|
||||
linkScales: helpers.noop,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_dataElementOptions: [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'borderAlign',
|
||||
'hoverBackgroundColor',
|
||||
'hoverBorderColor',
|
||||
'hoverBorderWidth',
|
||||
],
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
return ringIndex;
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var ratioX = 1;
|
||||
var ratioY = 1;
|
||||
var offsetX = 0;
|
||||
var offsetY = 0;
|
||||
var meta = me.getMeta();
|
||||
var arcs = meta.data;
|
||||
var cutout = opts.cutoutPercentage / 100 || 0;
|
||||
var circumference = opts.circumference;
|
||||
var chartWeight = me._getRingWeight(me.index);
|
||||
var maxWidth, maxHeight, i, ilen;
|
||||
|
||||
// If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
|
||||
if (circumference < DOUBLE_PI) {
|
||||
var startAngle = opts.rotation % DOUBLE_PI;
|
||||
startAngle += startAngle >= PI ? -DOUBLE_PI : startAngle < -PI ? DOUBLE_PI : 0;
|
||||
var endAngle = startAngle + circumference;
|
||||
var startX = Math.cos(startAngle);
|
||||
var startY = Math.sin(startAngle);
|
||||
var endX = Math.cos(endAngle);
|
||||
var endY = Math.sin(endAngle);
|
||||
var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI;
|
||||
var contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= DOUBLE_PI + HALF_PI;
|
||||
var contains180 = startAngle === -PI || endAngle >= PI;
|
||||
var contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
|
||||
var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
|
||||
var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
|
||||
var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
|
||||
var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
|
||||
ratioX = (maxX - minX) / 2;
|
||||
ratioY = (maxY - minY) / 2;
|
||||
offsetX = -(maxX + minX) / 2;
|
||||
offsetY = -(maxY + minY) / 2;
|
||||
}
|
||||
|
||||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||||
arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
|
||||
}
|
||||
|
||||
chart.borderWidth = me.getMaxBorderWidth();
|
||||
maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
|
||||
maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
|
||||
chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
|
||||
chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
|
||||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
|
||||
chart.offsetX = offsetX * chart.outerRadius;
|
||||
chart.offsetY = offsetY * chart.outerRadius;
|
||||
|
||||
meta.total = me.calculateTotal();
|
||||
|
||||
me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
|
||||
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);
|
||||
|
||||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||||
me.updateElement(arcs[i], i, 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 / DOUBLE_PI);
|
||||
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
|
||||
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
|
||||
var options = arc._options || {};
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderWidth: options.borderWidth,
|
||||
borderAlign: options.borderAlign,
|
||||
x: centerX + chart.offsetX,
|
||||
y: centerY + chart.offsetY,
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle,
|
||||
circumference: circumference,
|
||||
outerRadius: outerRadius,
|
||||
innerRadius: innerRadius,
|
||||
label: helpers.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
|
||||
}
|
||||
});
|
||||
|
||||
var model = arc._model;
|
||||
|
||||
// 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 DOUBLE_PI * (Math.abs(value) / total);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
// gets the max border or hover width to properly scale pie charts
|
||||
getMaxBorderWidth: function(arcs) {
|
||||
var me = this;
|
||||
var max = 0;
|
||||
var chart = me.chart;
|
||||
var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
|
||||
|
||||
if (!arcs) {
|
||||
// Find the outmost visible dataset
|
||||
for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
|
||||
if (chart.isDatasetVisible(i)) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
arcs = meta.data;
|
||||
if (i !== me.index) {
|
||||
controller = meta.controller;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!arcs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||||
arc = arcs[i];
|
||||
if (controller) {
|
||||
controller._configure();
|
||||
options = controller._resolveDataElementOptions(arc, i);
|
||||
} else {
|
||||
options = arc._options;
|
||||
}
|
||||
if (options.borderAlign !== 'inner') {
|
||||
borderWidth = options.borderWidth;
|
||||
hoverWidth = options.hoverBorderWidth;
|
||||
|
||||
max = borderWidth > max ? borderWidth : max;
|
||||
max = hoverWidth > max ? hoverWidth : max;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
setHoverStyle: function(arc) {
|
||||
var model = arc._model;
|
||||
var options = arc._options;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
|
||||
arc.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
};
|
||||
|
||||
model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||||
model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
|
||||
* @private
|
||||
*/
|
||||
_getRingWeightOffset: function(datasetIndex) {
|
||||
var ringWeightOffset = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; ++i) {
|
||||
if (this.chart.isDatasetVisible(i)) {
|
||||
ringWeightOffset += this._getRingWeight(i);
|
||||
}
|
||||
}
|
||||
|
||||
return ringWeightOffset;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getRingWeight: function(dataSetIndex) {
|
||||
return Math.max(valueOrDefault(this.chart.data.datasets[dataSetIndex].weight, 1), 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the sum of all visibile data set weights. This value can be 0.
|
||||
* @private
|
||||
*/
|
||||
_getVisibleDatasetWeightTotal: function() {
|
||||
return this._getRingWeightOffset(this.chart.data.datasets.length);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
'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',
|
||||
offset: true,
|
||||
gridLines: {
|
||||
offsetGridLines: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
elements: {
|
||||
rectangle: {
|
||||
borderSkipped: 'left'
|
||||
}
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
mode: 'index',
|
||||
axis: 'y'
|
||||
}
|
||||
});
|
||||
|
||||
defaults._set('global', {
|
||||
datasets: {
|
||||
horizontalBar: {
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BarController.extend({
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getValueScaleId: function() {
|
||||
return this.getMeta().xAxisID;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getIndexScaleId: function() {
|
||||
return this.getMeta().yAxisID;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,371 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var resolve = helpers.options.resolve;
|
||||
var isPointInArea = helpers.canvas._isPointInArea;
|
||||
|
||||
defaults._set('line', {
|
||||
showLines: true,
|
||||
spanGaps: false,
|
||||
|
||||
hover: {
|
||||
mode: 'label'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'category',
|
||||
id: 'x-axis-0'
|
||||
}],
|
||||
yAxes: [{
|
||||
type: 'linear',
|
||||
id: 'y-axis-0'
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
function scaleClip(scale, halfBorderWidth) {
|
||||
var tickOpts = scale && scale.options.ticks || {};
|
||||
var reverse = tickOpts.reverse;
|
||||
var min = tickOpts.min === undefined ? halfBorderWidth : 0;
|
||||
var max = tickOpts.max === undefined ? halfBorderWidth : 0;
|
||||
return {
|
||||
start: reverse ? max : min,
|
||||
end: reverse ? min : max
|
||||
};
|
||||
}
|
||||
|
||||
function defaultClip(xScale, yScale, borderWidth) {
|
||||
var halfBorderWidth = borderWidth / 2;
|
||||
var x = scaleClip(xScale, halfBorderWidth);
|
||||
var y = scaleClip(yScale, halfBorderWidth);
|
||||
|
||||
return {
|
||||
top: y.end,
|
||||
right: x.end,
|
||||
bottom: y.start,
|
||||
left: x.start
|
||||
};
|
||||
}
|
||||
|
||||
function toClip(value) {
|
||||
var t, r, b, l;
|
||||
|
||||
if (helpers.isObject(value)) {
|
||||
t = value.top;
|
||||
r = value.right;
|
||||
b = value.bottom;
|
||||
l = value.left;
|
||||
} else {
|
||||
t = r = b = l = value;
|
||||
}
|
||||
|
||||
return {
|
||||
top: t,
|
||||
right: r,
|
||||
bottom: b,
|
||||
left: l
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
datasetElementType: elements.Line,
|
||||
|
||||
dataElementType: elements.Point,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_datasetElementOptions: [
|
||||
'backgroundColor',
|
||||
'borderCapStyle',
|
||||
'borderColor',
|
||||
'borderDash',
|
||||
'borderDashOffset',
|
||||
'borderJoinStyle',
|
||||
'borderWidth',
|
||||
'cubicInterpolationMode',
|
||||
'fill'
|
||||
],
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_dataElementOptions: {
|
||||
backgroundColor: 'pointBackgroundColor',
|
||||
borderColor: 'pointBorderColor',
|
||||
borderWidth: 'pointBorderWidth',
|
||||
hitRadius: 'pointHitRadius',
|
||||
hoverBackgroundColor: 'pointHoverBackgroundColor',
|
||||
hoverBorderColor: 'pointHoverBorderColor',
|
||||
hoverBorderWidth: 'pointHoverBorderWidth',
|
||||
hoverRadius: 'pointHoverRadius',
|
||||
pointStyle: 'pointStyle',
|
||||
radius: 'pointRadius',
|
||||
rotation: 'pointRotation'
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var line = meta.dataset;
|
||||
var points = meta.data || [];
|
||||
var options = me.chart.options;
|
||||
var config = me._config;
|
||||
var showLine = me._showLine = valueOrDefault(config.showLine, options.showLines);
|
||||
var i, ilen;
|
||||
|
||||
me._xScale = me.getScaleForId(meta.xAxisID);
|
||||
me._yScale = me.getScaleForId(meta.yAxisID);
|
||||
|
||||
// Update Line
|
||||
if (showLine) {
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if (config.tension !== undefined && config.lineTension === undefined) {
|
||||
config.lineTension = config.tension;
|
||||
}
|
||||
|
||||
// Utility
|
||||
line._scale = me._yScale;
|
||||
line._datasetIndex = me.index;
|
||||
// Data
|
||||
line._children = points;
|
||||
// Model
|
||||
line._model = me._resolveDatasetElementOptions(line);
|
||||
|
||||
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();
|
||||
}
|
||||
},
|
||||
|
||||
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 xScale = me._xScale;
|
||||
var yScale = me._yScale;
|
||||
var lineModel = meta.dataset._model;
|
||||
var x, y;
|
||||
|
||||
var options = me._resolveDataElementOptions(point, index);
|
||||
|
||||
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._options = options;
|
||||
point._datasetIndex = datasetIndex;
|
||||
point._index = index;
|
||||
|
||||
// Desired view properties
|
||||
point._model = {
|
||||
x: x,
|
||||
y: y,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
// Appearance
|
||||
radius: options.radius,
|
||||
pointStyle: options.pointStyle,
|
||||
rotation: options.rotation,
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderWidth: options.borderWidth,
|
||||
tension: valueOrDefault(custom.tension, lineModel ? lineModel.tension : 0),
|
||||
steppedLine: lineModel ? lineModel.steppedLine : false,
|
||||
// Tooltip
|
||||
hitRadius: options.hitRadius
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveDatasetElementOptions: function(element) {
|
||||
var me = this;
|
||||
var config = me._config;
|
||||
var custom = element.custom || {};
|
||||
var options = me.chart.options;
|
||||
var lineOptions = options.elements.line;
|
||||
var values = DatasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
|
||||
|
||||
// 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
|
||||
values.spanGaps = valueOrDefault(config.spanGaps, options.spanGaps);
|
||||
values.tension = valueOrDefault(config.lineTension, lineOptions.tension);
|
||||
values.steppedLine = resolve([custom.steppedLine, config.steppedLine, lineOptions.stepped]);
|
||||
values.clip = toClip(valueOrDefault(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth)));
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
calculatePointY: function(value, index, datasetIndex) {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var yScale = me._yScale;
|
||||
var sumPos = 0;
|
||||
var sumNeg = 0;
|
||||
var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen;
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
rightValue = +yScale.getRightValue(value);
|
||||
metasets = chart._getSortedVisibleDatasetMetas();
|
||||
ilen = metasets.length;
|
||||
|
||||
for (i = 0; i < ilen; ++i) {
|
||||
dsMeta = metasets[i];
|
||||
if (dsMeta.index === datasetIndex) {
|
||||
break;
|
||||
}
|
||||
|
||||
ds = chart.data.datasets[dsMeta.index];
|
||||
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) {
|
||||
stackedRightValue = +yScale.getRightValue(ds.data[index]);
|
||||
if (stackedRightValue < 0) {
|
||||
sumNeg += stackedRightValue || 0;
|
||||
} else {
|
||||
sumPos += stackedRightValue || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rightValue < 0) {
|
||||
return yScale.getPixelForValue(sumNeg + rightValue);
|
||||
}
|
||||
return yScale.getPixelForValue(sumPos + rightValue);
|
||||
}
|
||||
return yScale.getPixelForValue(value);
|
||||
},
|
||||
|
||||
updateBezierControlPoints: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var meta = me.getMeta();
|
||||
var lineModel = meta.dataset._model;
|
||||
var area = chart.chartArea;
|
||||
var points = meta.data || [];
|
||||
var i, ilen, model, controlPoints;
|
||||
|
||||
// Only consider points that are drawn in case the spanGaps option is used
|
||||
if (lineModel.spanGaps) {
|
||||
points = points.filter(function(pt) {
|
||||
return !pt._model.skip;
|
||||
});
|
||||
}
|
||||
|
||||
function capControlPoint(pt, min, max) {
|
||||
return Math.max(Math.min(pt, max), min);
|
||||
}
|
||||
|
||||
if (lineModel.cubicInterpolationMode === 'monotone') {
|
||||
helpers.splineCurveMonotone(points);
|
||||
} else {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
model = points[i]._model;
|
||||
controlPoints = helpers.splineCurve(
|
||||
helpers.previousItem(points, i)._model,
|
||||
model,
|
||||
helpers.nextItem(points, i)._model,
|
||||
lineModel.tension
|
||||
);
|
||||
model.controlPointPreviousX = controlPoints.previous.x;
|
||||
model.controlPointPreviousY = controlPoints.previous.y;
|
||||
model.controlPointNextX = controlPoints.next.x;
|
||||
model.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (chart.options.elements.line.capBezierPoints) {
|
||||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||||
model = points[i]._model;
|
||||
if (isPointInArea(model, area)) {
|
||||
if (i > 0 && isPointInArea(points[i - 1]._model, area)) {
|
||||
model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
|
||||
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
|
||||
}
|
||||
if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {
|
||||
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 canvas = chart.canvas;
|
||||
var i = 0;
|
||||
var ilen = points.length;
|
||||
var clip;
|
||||
|
||||
if (me._showLine) {
|
||||
clip = meta.dataset._model.clip;
|
||||
|
||||
helpers.canvas.clipArea(chart.ctx, {
|
||||
left: clip.left === false ? 0 : area.left - clip.left,
|
||||
right: clip.right === false ? canvas.width : area.right + clip.right,
|
||||
top: clip.top === false ? 0 : area.top - clip.top,
|
||||
bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom
|
||||
});
|
||||
|
||||
meta.dataset.draw();
|
||||
|
||||
helpers.canvas.unclipArea(chart.ctx);
|
||||
}
|
||||
|
||||
// Draw the points
|
||||
for (; i < ilen; ++i) {
|
||||
points[i].draw(area);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
setHoverStyle: function(point) {
|
||||
var model = point._model;
|
||||
var options = point._options;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||||
model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = valueOrDefault(options.hoverRadius, options.radius);
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
@@ -0,0 +1,291 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var resolve = helpers.options.resolve;
|
||||
|
||||
defaults._set('polarArea', {
|
||||
scale: {
|
||||
type: 'radialLinear',
|
||||
angleLines: {
|
||||
display: false
|
||||
},
|
||||
gridLines: {
|
||||
circular: true
|
||||
},
|
||||
pointLabels: {
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
},
|
||||
|
||||
// Boolean - Whether to animate the rotation of the chart
|
||||
animation: {
|
||||
animateRotate: true,
|
||||
animateScale: true
|
||||
},
|
||||
|
||||
startAngle: -0.5 * Math.PI,
|
||||
legendCallback: function(chart) {
|
||||
var list = document.createElement('ul');
|
||||
var data = chart.data;
|
||||
var datasets = data.datasets;
|
||||
var labels = data.labels;
|
||||
var i, ilen, listItem, listItemSpan;
|
||||
|
||||
list.setAttribute('class', chart.id + '-legend');
|
||||
if (datasets.length) {
|
||||
for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
|
||||
listItem = list.appendChild(document.createElement('li'));
|
||||
listItemSpan = listItem.appendChild(document.createElement('span'));
|
||||
listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
|
||||
if (labels[i]) {
|
||||
listItem.appendChild(document.createTextNode(labels[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.outerHTML;
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
generateLabels: function(chart) {
|
||||
var data = chart.data;
|
||||
if (data.labels.length && data.datasets.length) {
|
||||
return data.labels.map(function(label, i) {
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
var style = meta.controller.getStyle(i);
|
||||
|
||||
return {
|
||||
text: label,
|
||||
fillStyle: style.backgroundColor,
|
||||
strokeStyle: style.borderColor,
|
||||
lineWidth: style.borderWidth,
|
||||
hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
|
||||
|
||||
// Extra data used for toggling the correct item
|
||||
index: i
|
||||
};
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function(e, legendItem) {
|
||||
var index = legendItem.index;
|
||||
var chart = this.chart;
|
||||
var i, ilen, meta;
|
||||
|
||||
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
meta.data[index].hidden = !meta.data[index].hidden;
|
||||
}
|
||||
|
||||
chart.update();
|
||||
}
|
||||
},
|
||||
|
||||
// Need to override these to give a nice default
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function() {
|
||||
return '';
|
||||
},
|
||||
label: function(item, data) {
|
||||
return data.labels[item.index] + ': ' + item.yLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
|
||||
dataElementType: elements.Arc,
|
||||
|
||||
linkScales: helpers.noop,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_dataElementOptions: [
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'borderAlign',
|
||||
'hoverBackgroundColor',
|
||||
'hoverBorderColor',
|
||||
'hoverBorderWidth',
|
||||
],
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getIndexScaleId: function() {
|
||||
return this.chart.scale.id;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getValueScaleId: function() {
|
||||
return this.chart.scale.id;
|
||||
},
|
||||
|
||||
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 arcs = meta.data;
|
||||
var i, ilen, angle;
|
||||
|
||||
me._updateRadius();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||||
arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
|
||||
me.updateElement(arcs[i], i, reset);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateRadius: function() {
|
||||
var me = this;
|
||||
var chart = me.chart;
|
||||
var chartArea = chart.chartArea;
|
||||
var opts = chart.options;
|
||||
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
||||
|
||||
chart.outerRadius = Math.max(minSize / 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]);
|
||||
var options = arc._options || {};
|
||||
|
||||
helpers.extend(arc, {
|
||||
// Utility
|
||||
_datasetIndex: me.index,
|
||||
_index: index,
|
||||
_scale: scale,
|
||||
|
||||
// Desired view properties
|
||||
_model: {
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderWidth: options.borderWidth,
|
||||
borderAlign: options.borderAlign,
|
||||
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])
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
setHoverStyle: function(arc) {
|
||||
var model = arc._model;
|
||||
var options = arc._options;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
arc.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
};
|
||||
|
||||
model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||||
model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
},
|
||||
|
||||
/**
|
||||
* @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 resolve([
|
||||
me.chart.options.elements.arc.angle,
|
||||
(2 * Math.PI) / count
|
||||
], context, index);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,217 @@
|
||||
'use strict';
|
||||
|
||||
var DatasetController = require('../core/core.datasetController');
|
||||
var defaults = require('../core/core.defaults');
|
||||
var elements = require('../elements/index');
|
||||
var helpers = require('../helpers/index');
|
||||
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
|
||||
defaults._set('radar', {
|
||||
spanGaps: false,
|
||||
scale: {
|
||||
type: 'radialLinear'
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
fill: 'start',
|
||||
tension: 0 // no bezier in radar
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DatasetController.extend({
|
||||
datasetElementType: elements.Line,
|
||||
|
||||
dataElementType: elements.Point,
|
||||
|
||||
linkScales: helpers.noop,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_datasetElementOptions: [
|
||||
'backgroundColor',
|
||||
'borderWidth',
|
||||
'borderColor',
|
||||
'borderCapStyle',
|
||||
'borderDash',
|
||||
'borderDashOffset',
|
||||
'borderJoinStyle',
|
||||
'fill'
|
||||
],
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_dataElementOptions: {
|
||||
backgroundColor: 'pointBackgroundColor',
|
||||
borderColor: 'pointBorderColor',
|
||||
borderWidth: 'pointBorderWidth',
|
||||
hitRadius: 'pointHitRadius',
|
||||
hoverBackgroundColor: 'pointHoverBackgroundColor',
|
||||
hoverBorderColor: 'pointHoverBorderColor',
|
||||
hoverBorderWidth: 'pointHoverBorderWidth',
|
||||
hoverRadius: 'pointHoverRadius',
|
||||
pointStyle: 'pointStyle',
|
||||
radius: 'pointRadius',
|
||||
rotation: 'pointRotation'
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getIndexScaleId: function() {
|
||||
return this.chart.scale.id;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getValueScaleId: function() {
|
||||
return this.chart.scale.id;
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var line = meta.dataset;
|
||||
var points = meta.data || [];
|
||||
var scale = me.chart.scale;
|
||||
var config = me._config;
|
||||
var i, ilen;
|
||||
|
||||
// Compatibility: If the properties are defined with only the old name, use those values
|
||||
if (config.tension !== undefined && config.lineTension === undefined) {
|
||||
config.lineTension = config.tension;
|
||||
}
|
||||
|
||||
// Utility
|
||||
line._scale = scale;
|
||||
line._datasetIndex = me.index;
|
||||
// Data
|
||||
line._children = points;
|
||||
line._loop = true;
|
||||
// Model
|
||||
line._model = me._resolveDatasetElementOptions(line);
|
||||
|
||||
line.pivot();
|
||||
|
||||
// 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 pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
|
||||
var options = me._resolveDataElementOptions(point, index);
|
||||
var lineModel = me.getMeta().dataset._model;
|
||||
var x = reset ? scale.xCenter : pointPosition.x;
|
||||
var y = reset ? scale.yCenter : pointPosition.y;
|
||||
|
||||
// Utility
|
||||
point._scale = scale;
|
||||
point._options = options;
|
||||
point._datasetIndex = me.index;
|
||||
point._index = index;
|
||||
|
||||
// Desired view properties
|
||||
point._model = {
|
||||
x: x, // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: y,
|
||||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||||
// Appearance
|
||||
radius: options.radius,
|
||||
pointStyle: options.pointStyle,
|
||||
rotation: options.rotation,
|
||||
backgroundColor: options.backgroundColor,
|
||||
borderColor: options.borderColor,
|
||||
borderWidth: options.borderWidth,
|
||||
tension: valueOrDefault(custom.tension, lineModel ? lineModel.tension : 0),
|
||||
|
||||
// Tooltip
|
||||
hitRadius: options.hitRadius
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resolveDatasetElementOptions: function() {
|
||||
var me = this;
|
||||
var config = me._config;
|
||||
var options = me.chart.options;
|
||||
var values = DatasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
|
||||
|
||||
values.spanGaps = valueOrDefault(config.spanGaps, options.spanGaps);
|
||||
values.tension = valueOrDefault(config.lineTension, options.elements.line.tension);
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
updateBezierControlPoints: function() {
|
||||
var me = this;
|
||||
var meta = me.getMeta();
|
||||
var area = me.chart.chartArea;
|
||||
var points = meta.data || [];
|
||||
var i, ilen, 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
var model = point._model;
|
||||
var options = point._options;
|
||||
var getHoverColor = helpers.getHoverColor;
|
||||
|
||||
point.$previousStyle = {
|
||||
backgroundColor: model.backgroundColor,
|
||||
borderColor: model.borderColor,
|
||||
borderWidth: model.borderWidth,
|
||||
radius: model.radius
|
||||
};
|
||||
|
||||
model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||||
model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = valueOrDefault(options.hoverRadius, options.radius);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
var LineController = require('./controller.line');
|
||||
var defaults = require('../core/core.defaults');
|
||||
|
||||
defaults._set('scatter', {
|
||||
hover: {
|
||||
mode: 'single'
|
||||
},
|
||||
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x-axis-1', // need an ID so datasets can reference the scale
|
||||
type: 'linear', // scatter should not use a category axis
|
||||
position: 'bottom'
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'y-axis-1',
|
||||
type: 'linear',
|
||||
position: 'left'
|
||||
}]
|
||||
},
|
||||
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: function() {
|
||||
return ''; // doesn't make sense for scatter since data are formatted as a point
|
||||
},
|
||||
label: function(item) {
|
||||
return '(' + item.xLabel + ', ' + item.yLabel + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
defaults._set('global', {
|
||||
datasets: {
|
||||
scatter: {
|
||||
showLine: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Scatter charts use line controllers
|
||||
module.exports = LineController;
|
||||
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
var bar = require('./controller.bar');
|
||||
var bubble = require('./controller.bubble');
|
||||
var doughnut = require('./controller.doughnut');
|
||||
var horizontalBar = require('./controller.horizontalBar');
|
||||
var line = require('./controller.line');
|
||||
var polarArea = require('./controller.polarArea');
|
||||
var pie = require('./controller.pie');
|
||||
var radar = require('./controller.radar');
|
||||
var scatter = require('./controller.scatter');
|
||||
|
||||
// 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]`).
|
||||
|
||||
module.exports = {
|
||||
bar: bar,
|
||||
bubble: bubble,
|
||||
doughnut: doughnut,
|
||||
horizontalBar: horizontalBar,
|
||||
line: line,
|
||||
polarArea: polarArea,
|
||||
pie: pie,
|
||||
radar: radar,
|
||||
scatter: scatter
|
||||
};
|
||||
Reference in New Issue
Block a user