Merge pull request #2612 from simonbrunel/refactor

Refactor and clean up
This commit is contained in:
Evert Timberg 2016-05-24 21:30:10 -04:00
commit e3943174cc
12 changed files with 292 additions and 485 deletions

View File

@ -13,12 +13,10 @@ require('./core/core.scaleService')(Chart);
require('./core/core.title')(Chart);
require('./core/core.tooltip')(Chart);
require('./controllers/controller.bar')(Chart);
require('./controllers/controller.bubble')(Chart);
require('./controllers/controller.doughnut')(Chart);
require('./controllers/controller.line')(Chart);
require('./controllers/controller.polarArea')(Chart);
require('./controllers/controller.radar')(Chart);
require('./elements/element.arc')(Chart);
require('./elements/element.line')(Chart);
require('./elements/element.point')(Chart);
require('./elements/element.rectangle')(Chart);
require('./scales/scale.category')(Chart);
require('./scales/scale.linear')(Chart);
@ -26,10 +24,14 @@ require('./scales/scale.logarithmic')(Chart);
require('./scales/scale.radialLinear')(Chart);
require('./scales/scale.time')(Chart);
require('./elements/element.arc')(Chart);
require('./elements/element.line')(Chart);
require('./elements/element.point')(Chart);
require('./elements/element.rectangle')(Chart);
// Controllers must be loaded after elements
// See Chart.core.datasetController.dataElementType
require('./controllers/controller.bar')(Chart);
require('./controllers/controller.bubble')(Chart);
require('./controllers/controller.doughnut')(Chart);
require('./controllers/controller.line')(Chart);
require('./controllers/controller.polarArea')(Chart);
require('./controllers/controller.radar')(Chart);
require('./charts/Chart.Bar')(Chart);
require('./charts/Chart.Bubble')(Chart);

View File

@ -29,12 +29,16 @@ module.exports = function(Chart) {
};
Chart.controllers.bar = Chart.DatasetController.extend({
dataElementType: Chart.elements.Rectangle,
initialize: function(chart, datasetIndex) {
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
// Use this to indicate that this is a bar dataset.
this.getMeta().bar = true;
},
// Get the number of datasets that display bars. We use this to correctly calculate the bar width
getBarCount: function getBarCount() {
var barCount = 0;
@ -47,79 +51,39 @@ module.exports = function(Chart) {
return barCount;
},
addElements: function() {
var meta = this.getMeta();
helpers.each(this.getDataset().data, function(value, index) {
meta.data[index] = meta.data[index] || new Chart.elements.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index) {
var rectangle = new Chart.elements.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
var numBars = this.getBarCount();
// Add to the points array and reset it
this.getMeta().data.splice(index, 0, rectangle);
this.updateElement(rectangle, index, true, numBars);
},
update: function update(reset) {
var numBars = this.getBarCount();
helpers.each(this.getMeta().data, function(rectangle, index) {
this.updateElement(rectangle, index, reset, numBars);
this.updateElement(rectangle, index, reset);
}, this);
},
updateElement: function updateElement(rectangle, index, reset, numBars) {
updateElement: function updateElement(rectangle, index, reset) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.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);
}
var scaleBase = yScale.getBasePixel();
var rectangleElementOptions = this.chart.options.elements.rectangle;
var custom = rectangle.custom || {};
var dataset = this.getDataset();
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),
y: reset ? scaleBase : this.calculateBarY(index, this.index),
// Tooltip
label: this.chart.data.labels[index],
datasetLabel: dataset.label,
// Appearance
base: reset ? yScalePoint : this.calculateBarBase(this.index, index),
base: reset ? scaleBase : this.calculateBarBase(this.index, index),
width: this.calculateBarWidth(index),
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
@ -132,9 +96,7 @@ module.exports = function(Chart) {
calculateBarBase: function(datasetIndex, index) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var base = 0;
if (yScale.options.stacked) {
@ -163,24 +125,12 @@ module.exports = function(Chart) {
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;
return yScale.getBasePixel();
},
getRuler: function(index) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var datasetCount = this.getBarCount();
var tickWidth;
@ -237,7 +187,6 @@ module.exports = function(Chart) {
calculateBarX: function(index, datasetIndex) {
var meta = this.getMeta();
var yScale = this.getScaleForId(meta.yAxisID);
var xScale = this.getScaleForId(meta.xAxisID);
var barIndex = this.getBarIndex(datasetIndex);
@ -259,9 +208,7 @@ module.exports = function(Chart) {
calculateBarY: function(index, datasetIndex) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var value = this.getDataset().data[index];
if (yScale.options.stacked) {
@ -387,24 +334,13 @@ module.exports = function(Chart) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var xScalePoint;
if (xScale.min < 0 && xScale.max < 0) {
// all less than 0. use the right
xScalePoint = xScale.getPixelForValue(xScale.max);
} else if (xScale.min > 0 && xScale.max > 0) {
xScalePoint = xScale.getPixelForValue(xScale.min);
} else {
xScalePoint = xScale.getPixelForValue(0);
}
var scaleBase = xScale.getBasePixel();
var custom = rectangle.custom || {};
var dataset = this.getDataset();
var rectangleElementOptions = this.chart.options.elements.rectangle;
helpers.extend(rectangle, {
// Utility
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
@ -412,7 +348,7 @@ module.exports = function(Chart) {
// Desired view properties
_model: {
x: reset ? xScalePoint : this.calculateBarX(index, this.index),
x: reset ? scaleBase : this.calculateBarX(index, this.index),
y: this.calculateBarY(index, this.index),
// Tooltip
@ -420,7 +356,7 @@ module.exports = function(Chart) {
datasetLabel: dataset.label,
// Appearance
base: reset ? xScalePoint : this.calculateBarBase(this.index, index),
base: reset ? scaleBase : this.calculateBarBase(this.index, index),
height: this.calculateBarHeight(index),
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
@ -506,8 +442,6 @@ module.exports = function(Chart) {
calculateBarBase: function (datasetIndex, index) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var base = 0;
if (xScale.options.stacked) {
@ -535,21 +469,11 @@ module.exports = function(Chart) {
return xScale.getPixelForValue(base);
}
base = xScale.getPixelForValue(xScale.min);
if (xScale.beginAtZero || ((xScale.min <= 0 && xScale.max >= 0) || (xScale.min >= 0 && xScale.max <= 0))) {
base = xScale.getPixelForValue(0, 0);
} else if (xScale.min < 0 && xScale.max < 0) {
// All values are negative. Use the right as the base
base = xScale.getPixelForValue(xScale.max);
}
return base;
return xScale.getBasePixel();
},
getRuler: function (index) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var datasetCount = this.getBarCount();
@ -592,8 +516,6 @@ module.exports = function(Chart) {
calculateBarX: function (index, datasetIndex) {
var meta = this.getMeta();
var xScale = this.getScaleForId(meta.xAxisID);
var yScale = this.getScaleForId(meta.yAxisID);
var value = this.getDataset().data[index];
if (xScale.options.stacked) {
@ -626,7 +548,6 @@ module.exports = function(Chart) {
calculateBarY: function (index, datasetIndex) {
var meta = this.getMeta();
var yScale = this.getScaleForId(meta.yAxisID);
var xScale = this.getScaleForId(meta.xAxisID);
var barIndex = this.getBarIndex(datasetIndex);
var ruler = this.getRuler(index);

View File

@ -37,74 +37,32 @@ module.exports = function(Chart) {
}
};
Chart.controllers.bubble = Chart.DatasetController.extend({
addElements: function() {
var meta = this.getMeta();
helpers.each(this.getDataset().data, function(value, index) {
meta.data[index] = meta.data[index] || new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index) {
var point = new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
// Add to the points array and reset it
this.getMeta().data.splice(index, 0, point);
this.updateElement(point, index, true);
},
dataElementType: Chart.elements.Point,
update: function update(reset) {
var meta = this.getMeta();
var points = meta.data;
var yScale = this.getScaleForId(meta.yAxisID);
var xScale = this.getScaleForId(meta.xAxisID);
var scaleBase;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
// Update Points
helpers.each(points, function(point, index) {
this.updateElement(point, index, reset);
}, this);
},
updateElement: function(point, index, reset) {
var meta = this.getMeta();
var yScale = this.getScaleForId(meta.yAxisID);
var xScale = this.getScaleForId(meta.xAxisID);
var scaleBase;
var yScale = this.getScaleForId(meta.yAxisID);
var custom = point.custom || {};
var dataset = this.getDataset();
var data = dataset.data[index];
var pointElementOptions = this.chart.options.elements.point;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
helpers.extend(point, {
// Utility
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
@ -113,7 +71,7 @@ module.exports = function(Chart) {
// Desired view properties
_model: {
x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(data, index, this.index, this.chart.isCombo),
y: reset ? scaleBase : yScale.getPixelForValue(data, index, this.index),
y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, this.index),
// Appearance
radius: reset ? 0 : custom.radius ? custom.radius : this.getRadius(data),
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, pointElementOptions.backgroundColor),
@ -135,17 +93,6 @@ module.exports = function(Chart) {
return value.r || this.chart.options.elements.point.radius;
},
draw: function(ease) {
var easingDecimal = ease || 1;
// Transition and Draw the Points
helpers.each(this.getMeta().data, function(point, index) {
point.transition(easingDecimal);
point.draw();
});
},
setHoverStyle: function(point) {
// Point
var dataset = this.chart.data.datasets[point._datasetIndex];

View File

@ -113,40 +113,11 @@ module.exports = function(Chart) {
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
// no scales for doughnut
dataElementType: Chart.elements.Arc,
linkScales: helpers.noop,
addElements: function() {
var _this = this;
var meta = this.getMeta(),
data = meta.data;
helpers.each(_this.getDataset().data, function(value, index) {
data[index] = data[index] || new Chart.elements.Arc({
_chart: _this.chart.chart,
_datasetIndex: _this.index,
_index: index
});
});
},
addElementAndReset: function(index, colorForNewElement) {
var _this = this;
var arc = new Chart.elements.Arc({
_chart: _this.chart.chart,
_datasetIndex: _this.index,
_index: index
}),
ds = _this.getDataset();
if (colorForNewElement && helpers.isArray(ds.backgroundColor)) {
ds.backgroundColor.splice(index, 0, colorForNewElement);
}
// Add to the points array and reset it
_this.getMeta().data.splice(index, 0, arc);
_this.updateElement(arc, index, true);
},
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
getRingIndex: function getRingIndex(datasetIndex) {
var ringIndex = 0;
@ -232,7 +203,6 @@ module.exports = function(Chart) {
helpers.extend(arc, {
// Utility
_chart: chart.chart,
_datasetIndex: _this.index,
_index: index,

View File

@ -24,40 +24,16 @@ module.exports = function(Chart) {
};
Chart.controllers.line = Chart.DatasetController.extend({
addElements: function() {
var me = this;
var meta = me.getMeta();
var data = me.getDataset().data || [];
var value, i, ilen;
meta.dataset = meta.dataset || new Chart.elements.Line({
_chart: me.chart.chart,
_datasetIndex: me.index,
_points: meta.data
});
datasetElementType: Chart.elements.Line,
for (i=0, ilen=data.length; i<ilen; ++i) {
value = data[i];
meta.data[i] = meta.data[i] || new Chart.elements.Point({
_chart: me.chart.chart,
_datasetIndex: me.index,
_index: i
});
}
},
dataElementType: Chart.elements.Point,
addElementAndReset: function(index) {
var me = this;
var options = me.chart.options;
var point = new Chart.elements.Point({
_chart: me.chart.chart,
_datasetIndex: me.index,
_index: index
});
// Add to the points array and reset it
me.getMeta().data.splice(index, 0, point);
me.updateElement(point, index, true);
Chart.DatasetController.prototype.addElementAndReset.call(me, index);
// Make sure bezier control points are updated
if (options.showLines && options.elements.line.tension !== 0) {
@ -72,17 +48,8 @@ module.exports = function(Chart) {
var points = meta.data || [];
var options = me.chart.options;
var lineElementOptions = options.elements.line;
var yScale = me.getScaleForId(meta.yAxisID);
var xScale = me.getScaleForId(meta.xAxisID);
var scaleBase, i, ilen, dataset, custom;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
var scale = me.getScaleForId(meta.yAxisID);
var i, ilen, dataset, custom;
// Update Line
if (options.showLines) {
@ -95,7 +62,7 @@ module.exports = function(Chart) {
}
// Utility
line._scale = yScale;
line._scale = scale;
line._datasetIndex = me.index;
// Data
line._children = points;
@ -112,9 +79,9 @@ module.exports = function(Chart) {
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
// Scale
scaleTop: yScale.top,
scaleBottom: yScale.bottom,
scaleZero: scaleBase
scaleTop: scale.top,
scaleBottom: scale.bottom,
scaleZero: scale.getBasePixel()
};
line.pivot();
@ -188,15 +155,7 @@ module.exports = function(Chart) {
var yScale = me.getScaleForId(meta.yAxisID);
var xScale = me.getScaleForId(meta.xAxisID);
var pointOptions = me.chart.options.elements.point;
var scaleBase, x, y;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
var x, y;
// Compatibility: If the properties are defined with only the old name, use those values
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
@ -207,10 +166,9 @@ module.exports = function(Chart) {
}
x = xScale.getPixelForValue(value, index, datasetIndex, me.chart.isCombo);
y = reset ? scaleBase : me.calculatePointY(value, index, datasetIndex, me.chart.isCombo);
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex, me.chart.isCombo);
// Utility
point._chart = me.chart.chart;
point._xScale = xScale;
point._yScale = yScale;
point._datasetIndex = datasetIndex;
@ -237,7 +195,6 @@ module.exports = function(Chart) {
var me = this;
var chart = me.chart;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
var yScale = me.getScaleForId(meta.yAxisID);
var sumPos = 0;
var sumNeg = 0;

View File

@ -100,34 +100,11 @@ module.exports = function(Chart) {
};
Chart.controllers.polarArea = Chart.DatasetController.extend({
dataElementType: Chart.elements.Arc,
linkScales: helpers.noop,
addElements: function() {
var _this = this;
var meta = this.getMeta();
var data = meta.data;
helpers.each(_this.getDataset().data, function(value, index) {
data[index] = data[index] || new Chart.elements.Arc({
_chart: _this.chart.chart,
_datasetIndex: _this.index,
_index: index
});
});
},
addElementAndReset: function(index) {
var _this = this;
var arc = new Chart.elements.Arc({
_chart: _this.chart.chart,
_datasetIndex: _this.index,
_index: index
});
// Add to the points array and reset it
_this.getMeta().data.splice(index, 0, arc);
_this.updateElement(arc, index, true);
},
update: function update(reset) {
var _this = this;
var chart = _this.chart;
@ -198,7 +175,6 @@ module.exports = function(Chart) {
helpers.extend(arc, {
// Utility
_chart: chart.chart,
_datasetIndex: _this.index,
_index: index,
_scale: scale,

View File

@ -4,7 +4,6 @@ module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.radar = {
scale: {
type: "radialLinear"
@ -17,42 +16,15 @@ module.exports = function(Chart) {
};
Chart.controllers.radar = Chart.DatasetController.extend({
linkScales: function() {
// No need. Single scale only
},
addElements: function() {
var meta = this.getMeta();
datasetElementType: Chart.elements.Line,
meta.dataset = meta.dataset || new Chart.elements.Line({
_chart: this.chart.chart,
_datasetIndex: this.index,
_points: meta.data,
_loop: true
});
dataElementType: Chart.elements.Point,
linkScales: helpers.noop,
helpers.each(this.getDataset().data, function(value, index) {
meta.data[index] = meta.data[index] || new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
_model: {
x: 0, //xScale.getPixelForValue(null, index, true),
y: 0 //this.chartArea.bottom,
}
});
}, this);
},
addElementAndReset: function(index) {
var point = new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
// Add to the points array and reset it
this.getMeta().data.splice(index, 0, point);
this.updateElement(point, index, true);
Chart.DatasetController.prototype.addElementAndReset.call(this, index);
// Make sure bezier control points are updated
this.updateBezierControlPoints();
@ -65,21 +37,10 @@ module.exports = function(Chart) {
var custom = line.custom || {};
var dataset = this.getDataset();
var lineElementOptions = this.chart.options.elements.line;
var scale = this.chart.scale;
var scaleBase;
if (scale.min < 0 && scale.max < 0) {
scaleBase = scale.getPointPositionForValue(0, scale.max);
} else if (scale.min > 0 && scale.max > 0) {
scaleBase = scale.getPointPositionForValue(0, scale.min);
} else {
scaleBase = scale.getPointPositionForValue(0, 0);
}
// Compatibility: If the properties are defined with only the old name, use those values
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined))
{
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
dataset.lineTension = dataset.tension;
}
@ -88,6 +49,7 @@ module.exports = function(Chart) {
_datasetIndex: this.index,
// Data
_children: points,
_loop: true,
// Model
_model: {
// Appearance
@ -104,7 +66,7 @@ module.exports = function(Chart) {
// Scale
scaleTop: scale.top,
scaleBottom: scale.bottom,
scaleZero: scaleBase
scaleZero: scale.getBasePosition()
}
});

View File

@ -98,82 +98,71 @@ module.exports = function(Chart) {
return this;
},
ensureScalesHaveIDs: function ensureScalesHaveIDs() {
var defaultXAxisID = 'x-axis-';
var defaultYAxisID = 'y-axis-';
var options = this.options;
var scalesOptions = options.scales || {};
var scaleOptions = options.scale;
if (this.options.scales) {
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
xAxisOptions.id = xAxisOptions.id || (defaultXAxisID + index);
});
}
helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
});
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
// Build the y axes
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
yAxisOptions.id = yAxisOptions.id || (defaultYAxisID + index);
});
}
helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
});
if (scaleOptions) {
scaleOptions.id = scaleOptions.id || 'scale';
}
},
/**
* Builds a map of scale ID to scale object for future lookup.
*/
buildScales: function buildScales() {
// Map of scale ID to scale object so we can lookup later
this.scales = {};
var me = this;
var options = me.options;
var scales = me.scales = {};
var items = [];
// Build the x axes
if (this.options.scales) {
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
var xType = helpers.getValueOrDefault(xAxisOptions.type, 'category');
var ScaleClass = Chart.scaleService.getScaleConstructor(xType);
if (ScaleClass) {
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: xAxisOptions,
chart: this,
id: xAxisOptions.id
});
this.scales[scale.id] = scale;
}
}, this);
}
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
// Build the y axes
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
var yType = helpers.getValueOrDefault(yAxisOptions.type, 'linear');
var ScaleClass = Chart.scaleService.getScaleConstructor(yType);
if (ScaleClass) {
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: yAxisOptions,
chart: this,
id: yAxisOptions.id
});
this.scales[scale.id] = scale;
}
}, this);
}
if (options.scales) {
items = items.concat(
(options.scales.xAxes || []).map(function(xAxisOptions) {
return { options: xAxisOptions, dtype: 'category' }; }),
(options.scales.yAxes || []).map(function(yAxisOptions) {
return { options: yAxisOptions, dtype: 'linear' }; }));
}
if (this.options.scale) {
// Build radial axes
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scale.type);
if (ScaleClass) {
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: this.options.scale,
chart: this
});
this.scale = scale;
this.scales.radialScale = scale;
}
if (options.scale) {
items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true });
}
helpers.each(items, function(item, index) {
var scaleOptions = item.options;
var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype);
var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
if (!scaleClass) {
return;
}
var scale = new scaleClass({
id: scaleOptions.id,
options: scaleOptions,
ctx: me.chart.ctx,
chart: me
});
scales[scale.id] = scale;
// TODO(SB): I think we should be able to remove this custom case (options.scale)
// and consider it as a regular scale part of the "scales"" map only! This would
// make the logic easier and remove some useless? custom code.
if (item.isDefault) {
me.scale = scale;
}
});
Chart.scaleService.addScalesToLayout(this);
},
@ -399,6 +388,20 @@ module.exports = function(Chart) {
return elementsArray;
},
getElementsAtEventForMode: function(e, mode) {
var me = this;
switch (mode) {
case 'single':
return me.getElementAtEvent(e);
case 'label':
return me.getElementsAtEvent(e);
case 'dataset':
return me.getDatasetAtEvent(e);
default:
return e;
}
},
getDatasetAtEvent: function(e) {
var elementsArray = this.getElementAtEvent(e);
@ -495,140 +498,106 @@ module.exports = function(Chart) {
this.eventHandler(evt);
});
},
updateHoverStyle: function(elements, mode, enabled) {
var method = enabled? 'setHoverStyle' : 'removeHoverStyle';
var element, i, ilen;
switch (mode) {
case 'single':
elements = [ elements[0] ];
break;
case 'label':
case 'dataset':
// elements = elements;
break;
default:
// unsupported mode
return;
}
for (i=0, ilen=elements.length; i<ilen; ++i) {
element = elements[i];
if (element) {
this.getDatasetMeta(element._datasetIndex).controller[method](element);
}
}
},
eventHandler: function eventHandler(e) {
this.lastActive = this.lastActive || [];
this.lastTooltipActive = this.lastTooltipActive || [];
var me = this;
var tooltip = me.tooltip;
var options = me.options || {};
var hoverOptions = options.hover;
var tooltipsOptions = options.tooltips;
me.lastActive = me.lastActive || [];
me.lastTooltipActive = me.lastTooltipActive || [];
// Find Active Elements for hover and tooltips
if (e.type === 'mouseout') {
this.active = [];
this.tooltipActive = [];
me.active = [];
me.tooltipActive = [];
} else {
var _this = this;
var getItemsForMode = function(mode) {
switch (mode) {
case 'single':
return _this.getElementAtEvent(e);
case 'label':
return _this.getElementsAtEvent(e);
case 'dataset':
return _this.getDatasetAtEvent(e);
default:
return e;
}
};
this.active = getItemsForMode(this.options.hover.mode);
this.tooltipActive = getItemsForMode(this.options.tooltips.mode);
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode);
me.tooltipActive = me.getElementsAtEventForMode(e, tooltipsOptions.mode);
}
// On Hover hook
if (this.options.hover.onHover) {
this.options.hover.onHover.call(this, this.active);
if (hoverOptions.onHover) {
hoverOptions.onHover.call(me, me.active);
}
if (e.type === 'mouseup' || e.type === 'click') {
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
if (options.onClick) {
options.onClick.call(me, e, me.active);
}
if (this.legend && this.legend.handleEvent) {
this.legend.handleEvent(e);
if (me.legend && me.legend.handleEvent) {
me.legend.handleEvent(e);
}
}
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
var lastActive;
switch (this.options.hover.mode) {
case 'single':
lastActive = this.lastActive[0];
this.getDatasetMeta(lastActive._datasetIndex).controller.removeHoverStyle(lastActive, lastActive._datasetIndex, lastActive._index);
break;
case 'label':
case 'dataset':
for (var i = 0; i < this.lastActive.length; i++) {
lastActive = this.lastActive[i];
if (lastActive)
this.getDatasetMeta(lastActive._datasetIndex).controller.removeHoverStyle(lastActive, lastActive._datasetIndex, lastActive._index);
}
break;
default:
// Don't change anything
}
if (me.lastActive.length) {
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
}
// Built in hover styling
if (this.active.length && this.options.hover.mode) {
var active;
switch (this.options.hover.mode) {
case 'single':
active = this.active[0];
this.getDatasetMeta(active._datasetIndex).controller.setHoverStyle(active);
break;
case 'label':
case 'dataset':
for (var j = 0; j < this.active.length; j++) {
active = this.active[j];
if (active)
this.getDatasetMeta(active._datasetIndex).controller.setHoverStyle(active);
}
break;
default:
// Don't change anything
}
if (me.active.length && hoverOptions.mode) {
me.updateHoverStyle(me.active, hoverOptions.mode, true);
}
var tooltip = this.tooltip;
// Built in Tooltips
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
// The usual updates
if (tooltipsOptions.enabled || tooltipsOptions.custom) {
tooltip.initialize();
tooltip._active = this.tooltipActive;
tooltip._active = me.tooltipActive;
tooltip.update(true);
}
// Hover animations
tooltip.pivot();
if (!this.animating) {
var changed;
helpers.each(this.active, function(element, index) {
if (element !== this.lastActive[index]) {
changed = true;
}
}, this);
helpers.each(this.tooltipActive, function(element, index) {
if (element !== this.lastTooltipActive[index]) {
changed = true;
}
}, this);
if (!me.animating) {
// If entering, leaving, or changing elements, animate the change via pivot
if ((this.lastActive.length !== this.active.length) ||
(this.lastTooltipActive.length !== this.tooltipActive.length) ||
changed) {
if (!helpers.arrayEquals(me.active, me.lastActive) ||
!helpers.arrayEquals(me.tooltipActive, me.lastTooltipActive)) {
this.stop();
me.stop();
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
if (tooltipsOptions.enabled || tooltipsOptions.custom) {
tooltip.update(true);
}
// We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more
// memory than necessary.
this.render(this.options.hover.animationDuration, true);
// We only need to render at this point. Updating will cause scales to be
// recomputed generating flicker & using more memory than necessary.
me.render(hoverOptions.animationDuration, true);
}
}
// Remember Last Actives
this.lastActive = this.active;
this.lastTooltipActive = this.tooltipActive;
return this;
me.lastActive = me.active;
me.lastTooltipActive = me.tooltipActive;
return me;
}
});
};

View File

@ -11,12 +11,26 @@ module.exports = function(Chart) {
};
helpers.extend(Chart.DatasetController.prototype, {
/**
* Element type used to generate a meta dataset (e.g. Chart.element.Line).
* @type {Chart.core.element}
*/
datasetElementType: null,
/**
* Element type used to generate a meta data (e.g. Chart.element.Point).
* @type {Chart.core.element}
*/
dataElementType: null,
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
@ -49,6 +63,46 @@ module.exports = function(Chart) {
this.update(true);
},
createMetaDataset: function() {
var me = this;
var type = me.datasetElementType;
return type && new type({
_chart: me.chart.chart,
_datasetIndex: me.index
});
},
createMetaData: function(index) {
var me = this;
var type = me.dataElementType;
return type && new type({
_chart: me.chart.chart,
_datasetIndex: me.index,
_index: index
});
},
addElements: function() {
var me = this;
var meta = me.getMeta();
var data = me.getDataset().data || [];
var metaData = meta.data;
var i, ilen;
for (i=0, ilen=data.length; i<ilen; ++i) {
metaData[i] = metaData[i] || me.createMetaData(meta, i);
}
meta.dataset = meta.dataset || me.createMetaDataset();
},
addElementAndReset: function(index) {
var me = this;
var element = me.createMetaData(index);
me.getMeta().data.splice(index, 0, element);
me.updateElement(element, index, true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var meta = this.getMeta(),
@ -68,15 +122,15 @@ module.exports = function(Chart) {
}
},
// Controllers should implement the following
addElements: noop,
addElementAndReset: noop,
update: noop,
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getMeta().data, function(element, index) {
element.transition(easingDecimal).draw();
});
},
removeHoverStyle: function(element, elementOpts) {
var dataset = this.chart.data.datasets[element._datasetIndex],
index = element._index,
@ -89,6 +143,7 @@ module.exports = function(Chart) {
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
},
setHoverStyle: function(element) {
var dataset = this.chart.data.datasets[element._datasetIndex],
index = element._index,
@ -101,8 +156,7 @@ module.exports = function(Chart) {
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
},
update: noop
}
});
Chart.DatasetController.extend = helpers.inherits;

View File

@ -925,6 +925,30 @@ module.exports = function(Chart) {
}
return Array.isArray(obj);
};
//! @see http://stackoverflow.com/a/14853974
helpers.arrayEquals = function(a0, a1) {
var i, ilen, v0, v1;
if (!a0 || !a1 || a0.length != a1.length) {
return false;
}
for (i = 0, ilen=a0.length; i < ilen; ++i) {
v0 = a0[i];
v1 = a1[i];
if (v0 instanceof Array && v1 instanceof Array) {
if (!helpers.arrayEquals(v0, v1)) {
return false;
}
} else if (v0 != v1) {
// NOTE: two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
};
helpers.pushAllIfDefined = function(element, array) {
if (typeof element === "undefined") {
return;

View File

@ -451,6 +451,18 @@ module.exports = function(Chart) {
}
},
getBasePixel: function() {
var me = this;
var min = me.min;
var max = me.max;
return me.getPixelForValue(
me.beginAtZero? 0:
min < 0 && max < 0? max :
min > 0 && max > 0? min :
0);
},
// Actualy draw the scale on the canvas
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
draw: function(chartArea) {

View File

@ -321,6 +321,19 @@ module.exports = function(Chart) {
getPointPositionForValue: function(index, value) {
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
},
getBasePosition: function() {
var me = this;
var min = me.min;
var max = me.max;
return me.getPointPositionForValue(0,
me.beginAtZero? 0:
min < 0 && max < 0? max :
min > 0 && max > 0? min :
0);
},
draw: function() {
if (this.options.display) {
var ctx = this.ctx;