İlk temizlik tamamlandı bir önceki projeden

This commit is contained in:
Ümit Tunç
2026-04-28 21:14:25 +03:00
commit f80443aec0
10000 changed files with 959965 additions and 0 deletions
@@ -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;
+27
View File
@@ -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
};