Files
Chart.js/src/controllers/controller.bar.js
Tanner Linsley b22f569e4d Merge branch 'v2.0-dev' into v2.0-dev-refactor
Conflicts:
	samples/line-scale-override.html
	samples/line-x-axis-filter.html
	samples/line.html
	src/controllers/controller.bar.js
	src/core/core.controller.js
	src/scales/scale.category.js
2015-09-24 14:34:26 -06:00

353 lines
11 KiB
JavaScript

(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.bar = {
hover: {
mode: "label"
},
scales: {
xAxes: [{
type: "category",
// Specific to Bar Controller
categoryPercentage: 0.8,
barPercentage: 0.9,
// grid line settings
gridLines: {
offsetGridLines: true,
},
}],
yAxes: [{
type: "linear",
}],
},
};
Chart.controllers.bar = function(chart, datasetIndex) {
this.initialize.call(this, chart, datasetIndex);
};
helpers.extend(Chart.controllers.bar.prototype, {
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
linkScales: function() {
if (!this.getDataset().xAxisID) {
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
}
if (!this.getDataset().yAxisID) {
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
}
},
getDataset: function() {
return this.chart.data.datasets[this.index];
},
getScaleForID: function(scaleID) {
return this.chart.scales[scaleID];
},
// Get the number of datasets that display bars. We use this to correctly calculate the bar width
getBarCount: function getBarCount() {
var barCount = 0;
helpers.each(this.chart.data.datasets, function(dataset) {
if (dataset.type === 'bar') {
++barCount;
} else if (dataset.type === undefined && this.chart.config.type === 'bar') {
++barCount;
}
}, this);
return barCount;
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
});
}, this);
},
addElementAndReset: function(index) {
this.getDataset().metaData = this.getDataset().metaData || [];
var rectangle = new Chart.elements.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
});
var numBars = this.getBarCount();
this.updateElement(rectangle, index, true, numBars);
this.getDataset().metaData.splice(index, 0, rectangle);
},
removeElement: function(index) {
this.getDataset().metaData.splice(index, 1);
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
var numData = this.getDataset().data.length;
var numRectangles = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numRectangles) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numRectangles - numData);
} else if (numData > numRectangles) {
// Add new elements
for (var index = numRectangles; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
update: function update(reset) {
var numBars = this.getBarCount();
helpers.each(this.getDataset().metaData, function(rectangle, index) {
this.updateElement(rectangle, index, reset, numBars);
}, this);
},
updateElement: function updateElement(rectangle, index, reset, numBars) {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var yScalePoint;
if (yScale.min < 0 && yScale.max < 0) {
// all less than 0. use the top
yScalePoint = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
yScalePoint = yScale.getPixelForValue(yScale.min);
} else {
yScalePoint = yScale.getPixelForValue(0);
}
helpers.extend(rectangle, {
// Utility
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: this.calculateBarX(index, this.index),
y: reset ? yScalePoint : this.calculateBarY(index, this.index),
// Tooltip
label: this.chart.data.labels[index],
datasetLabel: this.getDataset().label,
// Appearance
base: this.calculateBarBase(this.index, index),
width: this.calculateBarWidth(numBars),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
},
});
rectangle.pivot();
},
calculateBarBase: function(datasetIndex, index) {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var base = 0;
if (yScale.options.stacked) {
var value = this.chart.data.datasets[datasetIndex].data[index];
if (value < 0) {
for (var i = 0; i < datasetIndex; i++) {
if (this.chart.data.datasets[i].yAxisID === yScale.id) {
base += this.chart.data.datasets[i].data[index] < 0 ? this.chart.data.datasets[i].data[index] : 0;
}
}
} else {
for (var j = 0; j < datasetIndex; j++) {
if (this.chart.data.datasets[j].yAxisID === yScale.id) {
base += this.chart.data.datasets[j].data[index] > 0 ? this.chart.data.datasets[j].data[index] : 0;
}
}
}
return yScale.getPixelForValue(base);
}
base = yScale.getPixelForValue(yScale.min);
if (yScale.beginAtZero || ((yScale.min <= 0 && yScale.max >= 0) || (yScale.min >= 0 && yScale.max <= 0))) {
base = yScale.getPixelForValue(0, 0);
//base += yScale.options.gridLines.lineWidth;
} else if (yScale.min < 0 && yScale.max < 0) {
// All values are negative. Use the top as the base
base = yScale.getPixelForValue(yScale.max);
}
return base;
},
getRuler: function() {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var datasetCount = !this.chart.isCombo ? this.chart.data.datasets.length : helpers.where(this.chart.data.datasets, function(ds) {
return ds.type == 'bar';
}).length;
var tickWidth = (function() {
var min = xScale.getPixelForValue(null, 1) - xScale.getPixelForValue(null, 0);
for (var i = 2; i < this.getDataset().data.length; i++) {
min = Math.min(xScale.getPixelForValue(null, i) - xScale.getPixelForValue(null, i - 1), min);
}
return min;
}).call(this);
var categoryWidth = tickWidth * xScale.options.categoryPercentage;
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
var fullBarWidth = categoryWidth / datasetCount;
var barWidth = fullBarWidth * xScale.options.barPercentage;
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
return {
datasetCount: datasetCount,
tickWidth: tickWidth,
categoryWidth: categoryWidth,
categorySpacing: categorySpacing,
fullBarWidth: fullBarWidth,
barWidth: barWidth,
barSpacing: barSpacing,
};
},
calculateBarWidth: function() {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var ruler = this.getRuler();
if (xScale.options.stacked) {
return ruler.categoryWidth;
}
return ruler.barWidth;
},
calculateBarX: function(index, datasetIndex) {
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var ruler = this.getRuler();
var leftTick = xScale.getPixelForValue(null, index, datasetIndex);
leftTick -= this.chart.isCombo ? (ruler.tickWidth / 2) : 0;
if (yScale.options.stacked) {
return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
}
return leftTick +
(ruler.barWidth / 2) +
ruler.categorySpacing +
(ruler.barWidth * datasetIndex) +
(ruler.barSpacing / 2) +
(ruler.barSpacing * datasetIndex);
},
calculateBarY: function(index, datasetIndex) {
var xScale = this.getScaleForID(this.getDataset().xAxisID);
var yScale = this.getScaleForID(this.getDataset().yAxisID);
var value = this.getDataset().data[index];
if (yScale.options.stacked) {
var sumPos = 0,
sumNeg = 0;
for (var i = 0; i < datasetIndex; i++) {
if (this.chart.data.datasets[i].data[index] < 0) {
sumNeg += this.chart.data.datasets[i].data[index] || 0;
} else {
sumPos += this.chart.data.datasets[i].data[index] || 0;
}
}
if (value < 0) {
return yScale.getPixelForValue(sumNeg + value);
} else {
return yScale.getPixelForValue(sumPos + value);
}
return yScale.getPixelForValue(value);
}
return yScale.getPixelForValue(value);
},
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(rectangle, index) {
rectangle.transition(easingDecimal).draw();
}, this);
},
setHoverStyle: function(rectangle) {
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
var index = rectangle._index;
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString());
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, rectangle._model.borderWidth);
},
removeHoverStyle: function(rectangle) {
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
var index = rectangle._index;
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor);
rectangle._model.borderColor = rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor);
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth);
}
});
}).call(this);