Change spaces to tabs throughout

This commit is contained in:
Evert Timberg 2015-06-13 10:15:21 -04:00
parent a8375d0459
commit b5c69984e3
10 changed files with 2900 additions and 2900 deletions

View File

@ -1,408 +1,408 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var defaultConfig = {
hover: {
mode: "label"
},
scales: {
xAxes: [{
type: "category", // scatter should not use a dataset axis
display: true,
position: "bottom",
id: "x-axis-1", // need an ID so datasets can reference the scale
categorySpacing: 10,
spacing: 1,
// grid line settings
gridLines: {
show: true,
color: "rgba(0, 0, 0, 0.05)",
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true,
zeroLineWidth: 1,
zeroLineColor: "rgba(0,0,0,0.25)",
offsetGridLines: true,
},
// label settings
labels: {
show: true,
template: "<%=value%>",
fontSize: 12,
fontStyle: "normal",
fontColor: "#666",
fontFamily: "Helvetica Neue",
},
}],
yAxes: [{
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: "left",
id: "y-axis-1",
spacing: 1,
// grid line settings
gridLines: {
show: true,
color: "rgba(0, 0, 0, 0.05)",
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true, // draw ticks extending towards the label
zeroLineWidth: 1,
zeroLineColor: "rgba(0,0,0,0.25)",
},
// scale numbers
beginAtZero: false,
override: null,
// label settings
labels: {
show: true,
template: "<%=value%>",
fontSize: 12,
fontStyle: "normal",
fontColor: "#666",
fontFamily: "Helvetica Neue",
}
}],
},
};
Chart.Type.extend({
name: "Bar",
defaults: defaultConfig,
initialize: function() {
var _this = this;
// Events
helpers.bindEvents(this, this.options.events, this.events);
//Create a new bar for each piece of data
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.metaData = [];
helpers.each(dataset.data, function(dataPoint, index) {
dataset.metaData.push(new Chart.Rectangle({
_chart: this.chart,
_datasetIndex: datasetIndex,
_index: index,
}));
}, this);
// The bar chart only supports a single x axis because the x axis is always a dataset axis
dataset.xAxisID = this.options.scales.xAxes[0].id;
if (!dataset.yAxisID) {
dataset.yAxisID = this.options.scales.yAxes[0].id;
}
}, this);
// Build and fit the scale. Needs to happen after the axis IDs have been set
this.buildScale();
// Create tooltip instance exclusively for this chart with some defaults.
this.tooltip = new Chart.Tooltip({
_chart: this.chart,
_data: this.data,
_options: this.options,
}, this);
// Need to fit scales before we reset elements.
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
// So that we animate from the baseline
this.resetElements();
// Update the chart with the latest data.
this.update();
},
resetElements: function() {
// Update the points
this.eachElement(function(bar, index, dataset, datasetIndex) {
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
var yScale = this.scales[this.data.datasets[datasetIndex].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(bar, {
// Utility
_chart: this.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
y: yScalePoint,
// Appearance
base: yScale.calculateBarBase(datasetIndex, index),
width: xScale.calculateBarWidth(this.data.datasets.length),
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.rectangle.backgroundColor),
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.rectangle.borderColor),
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.rectangle.borderWidth),
// Tooltip
label: this.data.labels[index],
datasetLabel: this.data.datasets[datasetIndex].label,
},
});
bar.pivot();
}, this);
},
update: function(animationDuration) {
// Update the scale sizes
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
// Update the points
this.eachElement(function(bar, index, dataset, datasetIndex) {
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
helpers.extend(bar, {
// Utility
_chart: this.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
y: yScale.calculateBarY(datasetIndex, index),
// Appearance
base: yScale.calculateBarBase(datasetIndex, index),
width: xScale.calculateBarWidth(this.data.datasets.length),
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.rectangle.backgroundColor),
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.rectangle.borderColor),
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.rectangle.borderWidth),
// Tooltip
label: this.data.labels[index],
datasetLabel: this.data.datasets[datasetIndex].label,
},
});
bar.pivot();
}, this);
this.render(animationDuration);
},
buildScale: function(labels) {
var self = this;
// Map of scale ID to scale object so we can lookup later
this.scales = {};
// Build the x axis. The line chart only supports a single x axis
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scales.xAxes[0].type);
var xScale = new ScaleClass({
ctx: this.chart.ctx,
options: this.options.scales.xAxes[0],
id: this.options.scales.xAxes[0].id,
data: this.data,
});
this.scales[xScale.id] = xScale;
// Build up all the y scales
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type);
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: yAxisOptions,
data: this.data,
id: yAxisOptions.id,
});
this.scales[scale.id] = scale;
}, this);
},
draw: function(ease) {
var easingDecimal = ease || 1;
this.clear();
// Draw all the scales
helpers.each(this.scales, function(scale) {
scale.draw(this.chartArea);
}, this);
//Draw all the bars for each dataset
this.eachElement(function(bar, index, datasetIndex) {
bar.transition(easingDecimal).draw();
}, this);
// Finally draw the tooltip
this.tooltip.transition(easingDecimal).draw();
},
events: function(e) {
this.lastActive = this.lastActive || [];
// Find Active Elements
if (e.type == 'mouseout') {
this.active = [];
} else {
this.active = function() {
switch (this.options.hover.mode) {
case 'single':
return this.getElementAtEvent(e);
case 'label':
return this.getElementsAtEvent(e);
case 'dataset':
return this.getDatasetAtEvent(e);
default:
return e;
}
}.call(this);
}
// On Hover hook
if (this.options.hover.onHover) {
this.options.hover.onHover.call(this, this.active);
}
if (e.type == 'mouseup' || e.type == 'click') {
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
}
}
var dataset;
var index;
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
index = this.lastActive[0]._index;
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.rectangle.backgroundColor);
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.rectangle.borderColor);
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.rectangle.borderWidth);
break;
case 'label':
for (var i = 0; i < this.lastActive.length; i++) {
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
index = this.lastActive[i]._index;
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.rectangle.backgroundColor);
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.rectangle.borderColor);
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.rectangle.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in hover styling
if (this.active.length && this.options.hover.mode) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.active[0]._datasetIndex];
index = this.active[0]._index;
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[0]._model.borderWidth);
break;
case 'label':
for (var i = 0; i < this.active.length; i++) {
dataset = this.data.datasets[this.active[i]._datasetIndex];
index = this.active[i]._index;
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[i]._model.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in Tooltips
if (this.options.tooltips.enabled) {
// The usual updates
this.tooltip.initialize();
// Active
if (this.active.length) {
this.tooltip._model.opacity = 1;
helpers.extend(this.tooltip, {
_active: this.active,
});
this.tooltip.update();
} else {
// Inactive
this.tooltip._model.opacity = 0;
}
}
this.tooltip.pivot();
// Hover animations
if (!this.animating) {
var changed;
helpers.each(this.active, function(element, index) {
if (element !== this.lastActive[index]) {
changed = true;
}
}, this);
// If entering, leaving, or changing elements, animate the change via pivot
if ((!this.lastActive.length && this.active.length) ||
(this.lastActive.length && !this.active.length) ||
(this.lastActive.length && this.active.length && changed)) {
this.stop();
this.render(this.options.hoverAnimationDuration);
}
}
// Remember Last Active
this.lastActive = this.active;
return this;
},
});
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var defaultConfig = {
hover: {
mode: "label"
},
scales: {
xAxes: [{
type: "category", // scatter should not use a dataset axis
display: true,
position: "bottom",
id: "x-axis-1", // need an ID so datasets can reference the scale
categorySpacing: 10,
spacing: 1,
// grid line settings
gridLines: {
show: true,
color: "rgba(0, 0, 0, 0.05)",
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true,
zeroLineWidth: 1,
zeroLineColor: "rgba(0,0,0,0.25)",
offsetGridLines: true,
},
// label settings
labels: {
show: true,
template: "<%=value%>",
fontSize: 12,
fontStyle: "normal",
fontColor: "#666",
fontFamily: "Helvetica Neue",
},
}],
yAxes: [{
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: "left",
id: "y-axis-1",
spacing: 1,
// grid line settings
gridLines: {
show: true,
color: "rgba(0, 0, 0, 0.05)",
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true, // draw ticks extending towards the label
zeroLineWidth: 1,
zeroLineColor: "rgba(0,0,0,0.25)",
},
// scale numbers
beginAtZero: false,
override: null,
// label settings
labels: {
show: true,
template: "<%=value%>",
fontSize: 12,
fontStyle: "normal",
fontColor: "#666",
fontFamily: "Helvetica Neue",
}
}],
},
};
Chart.Type.extend({
name: "Bar",
defaults: defaultConfig,
initialize: function() {
var _this = this;
// Events
helpers.bindEvents(this, this.options.events, this.events);
//Create a new bar for each piece of data
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.metaData = [];
helpers.each(dataset.data, function(dataPoint, index) {
dataset.metaData.push(new Chart.Rectangle({
_chart: this.chart,
_datasetIndex: datasetIndex,
_index: index,
}));
}, this);
// The bar chart only supports a single x axis because the x axis is always a dataset axis
dataset.xAxisID = this.options.scales.xAxes[0].id;
if (!dataset.yAxisID) {
dataset.yAxisID = this.options.scales.yAxes[0].id;
}
}, this);
// Build and fit the scale. Needs to happen after the axis IDs have been set
this.buildScale();
// Create tooltip instance exclusively for this chart with some defaults.
this.tooltip = new Chart.Tooltip({
_chart: this.chart,
_data: this.data,
_options: this.options,
}, this);
// Need to fit scales before we reset elements.
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
// So that we animate from the baseline
this.resetElements();
// Update the chart with the latest data.
this.update();
},
resetElements: function() {
// Update the points
this.eachElement(function(bar, index, dataset, datasetIndex) {
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
var yScale = this.scales[this.data.datasets[datasetIndex].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(bar, {
// Utility
_chart: this.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
y: yScalePoint,
// Appearance
base: yScale.calculateBarBase(datasetIndex, index),
width: xScale.calculateBarWidth(this.data.datasets.length),
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.rectangle.backgroundColor),
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.rectangle.borderColor),
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.rectangle.borderWidth),
// Tooltip
label: this.data.labels[index],
datasetLabel: this.data.datasets[datasetIndex].label,
},
});
bar.pivot();
}, this);
},
update: function(animationDuration) {
// Update the scale sizes
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
// Update the points
this.eachElement(function(bar, index, dataset, datasetIndex) {
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
helpers.extend(bar, {
// Utility
_chart: this.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
y: yScale.calculateBarY(datasetIndex, index),
// Appearance
base: yScale.calculateBarBase(datasetIndex, index),
width: xScale.calculateBarWidth(this.data.datasets.length),
backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.rectangle.backgroundColor),
borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.rectangle.borderColor),
borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.rectangle.borderWidth),
// Tooltip
label: this.data.labels[index],
datasetLabel: this.data.datasets[datasetIndex].label,
},
});
bar.pivot();
}, this);
this.render(animationDuration);
},
buildScale: function(labels) {
var self = this;
// Map of scale ID to scale object so we can lookup later
this.scales = {};
// Build the x axis. The line chart only supports a single x axis
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scales.xAxes[0].type);
var xScale = new ScaleClass({
ctx: this.chart.ctx,
options: this.options.scales.xAxes[0],
id: this.options.scales.xAxes[0].id,
data: this.data,
});
this.scales[xScale.id] = xScale;
// Build up all the y scales
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type);
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: yAxisOptions,
data: this.data,
id: yAxisOptions.id,
});
this.scales[scale.id] = scale;
}, this);
},
draw: function(ease) {
var easingDecimal = ease || 1;
this.clear();
// Draw all the scales
helpers.each(this.scales, function(scale) {
scale.draw(this.chartArea);
}, this);
//Draw all the bars for each dataset
this.eachElement(function(bar, index, datasetIndex) {
bar.transition(easingDecimal).draw();
}, this);
// Finally draw the tooltip
this.tooltip.transition(easingDecimal).draw();
},
events: function(e) {
this.lastActive = this.lastActive || [];
// Find Active Elements
if (e.type == 'mouseout') {
this.active = [];
} else {
this.active = function() {
switch (this.options.hover.mode) {
case 'single':
return this.getElementAtEvent(e);
case 'label':
return this.getElementsAtEvent(e);
case 'dataset':
return this.getDatasetAtEvent(e);
default:
return e;
}
}.call(this);
}
// On Hover hook
if (this.options.hover.onHover) {
this.options.hover.onHover.call(this, this.active);
}
if (e.type == 'mouseup' || e.type == 'click') {
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
}
}
var dataset;
var index;
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
index = this.lastActive[0]._index;
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.rectangle.backgroundColor);
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.rectangle.borderColor);
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.rectangle.borderWidth);
break;
case 'label':
for (var i = 0; i < this.lastActive.length; i++) {
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
index = this.lastActive[i]._index;
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.rectangle.backgroundColor);
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.rectangle.borderColor);
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.rectangle.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in hover styling
if (this.active.length && this.options.hover.mode) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.active[0]._datasetIndex];
index = this.active[0]._index;
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[0]._model.borderWidth);
break;
case 'label':
for (var i = 0; i < this.active.length; i++) {
dataset = this.data.datasets[this.active[i]._datasetIndex];
index = this.active[i]._index;
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[i]._model.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in Tooltips
if (this.options.tooltips.enabled) {
// The usual updates
this.tooltip.initialize();
// Active
if (this.active.length) {
this.tooltip._model.opacity = 1;
helpers.extend(this.tooltip, {
_active: this.active,
});
this.tooltip.update();
} else {
// Inactive
this.tooltip._model.opacity = 0;
}
}
this.tooltip.pivot();
// Hover animations
if (!this.animating) {
var changed;
helpers.each(this.active, function(element, index) {
if (element !== this.lastActive[index]) {
changed = true;
}
}, this);
// If entering, leaving, or changing elements, animate the change via pivot
if ((!this.lastActive.length && this.active.length) ||
(this.lastActive.length && !this.active.length) ||
(this.lastActive.length && this.active.length && changed)) {
this.stop();
this.render(this.options.hoverAnimationDuration);
}
}
// Remember Last Active
this.lastActive = this.active;
return this;
},
});
}).call(this);

View File

@ -1,365 +1,365 @@
(function() {
"use strict";
var root = this,
Chart = root.Chart,
//Cache a local reference to Chart.helpers
helpers = Chart.helpers;
var defaultConfig = {
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'
},
//The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
};
Chart.Type.extend({
//Passing in a name registers this chart in the Chart namespace
name: "Doughnut",
//Providing a defaults will also register the deafults in the chart namespace
defaults: defaultConfig,
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
//Config is automatically merged by the core of Chart.js, and is available at this.options
initialize: function() {
//Set up tooltip events on the chart
helpers.bindEvents(this, this.options.events, this.events);
//Create a new bar for each piece of data
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.metaData = [];
helpers.each(dataset.data, function(dataPoint, index) {
dataset.metaData.push(new Chart.Arc({
_chart: this.chart,
_datasetIndex: datasetIndex,
_index: index,
_model: {}
}));
}, this);
}, this);
// Create tooltip instance exclusively for this chart with some defaults.
this.tooltip = new Chart.Tooltip({
_chart: this.chart,
_data: this.data,
_options: this.options,
}, this);
this.resetElements();
// Update the chart with the latest data.
this.update();
},
calculateCircumference: function(dataset, value) {
if (dataset.total > 0) {
return (Math.PI * 2) * (value / dataset.total);
} else {
return 0;
}
},
resetElements: function() {
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2;
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
// Update the points
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
// So that calculateCircumference works
dataset.total = 0;
helpers.each(dataset.data, function(value) {
dataset.total += Math.abs(value);
}, this);
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
helpers.each(dataset.metaData, function(slice, index) {
helpers.extend(slice, {
_model: {
x: this.chart.width / 2,
y: this.chart.height / 2,
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
outerRadius: (this.options.animation.animateScale) ? 0 : dataset.outerRadius,
innerRadius: (this.options.animation.animateScale) ? 0 : dataset.innerRadius,
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor),
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth),
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
},
});
slice.pivot();
}, this);
}, this);
},
update: function(animationDuration) {
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2;
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
// Update the points
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.total = 0;
helpers.each(dataset.data, function(value) {
dataset.total += Math.abs(value);
}, this);
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
helpers.each(dataset.metaData, function(slice, index) {
helpers.extend(slice, {
// Utility
_chart: this.chart,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: this.chart.width / 2,
y: this.chart.height / 2,
circumference: this.calculateCircumference(dataset, dataset.data[index]),
outerRadius: dataset.outerRadius,
innerRadius: dataset.innerRadius,
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor),
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth),
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
},
});
if (index === 0) {
slice._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
} else {
slice._model.startAngle = dataset.metaData[index - 1]._model.endAngle;
}
slice._model.endAngle = slice._model.startAngle + slice._model.circumference;
//Check to see if it's the last slice, if not get the next and update its start angle
if (index < dataset.data.length - 1) {
dataset.metaData[index + 1]._model.startAngle = slice._model.endAngle;
}
slice.pivot();
}, this);
}, this);
this.render(animationDuration);
},
draw: function(easeDecimal) {
easeDecimal = easeDecimal || 1;
this.clear();
this.eachElement(function(slice) {
slice.transition(easeDecimal).draw();
}, this);
this.tooltip.transition(easeDecimal).draw();
},
events: function(e) {
this.lastActive = this.lastActive || [];
// Find Active Elements
if (e.type == 'mouseout') {
this.active = [];
} else {
this.active = function() {
switch (this.options.hover.mode) {
case 'single':
return this.getSliceAtEvent(e);
case 'label':
return this.getSlicesAtEvent(e);
case 'dataset':
return this.getDatasetAtEvent(e);
default:
return e;
}
}.call(this);
}
// On Hover hook
if (this.options.hover.onHover) {
this.options.hover.onHover.call(this, this.active);
}
if (e.type == 'mouseup' || e.type == 'click') {
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
}
}
var dataset;
var index;
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
index = this.lastActive[0]._index;
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
break;
case 'label':
for (var i = 0; i < this.lastActive.length; i++) {
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
index = this.lastActive[i]._index;
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in hover styling
if (this.active.length && this.options.hover.mode) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.active[0]._datasetIndex];
index = this.active[0]._index;
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, this.active[0]._model.borderColor);
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, this.active[0]._model.borderWidth);
break;
case 'label':
for (var i = 0; i < this.active.length; i++) {
dataset = this.data.datasets[this.active[i]._datasetIndex];
index = this.active[i]._index;
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, this.active[0]._model.borderColor);
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, this.active[i]._model.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in Tooltips
if (this.options.tooltips.enabled) {
// The usual updates
this.tooltip.initialize();
// Active
if (this.active.length) {
this.tooltip._model.opacity = 1;
helpers.extend(this.tooltip, {
_active: this.active,
});
this.tooltip.update();
} else {
// Inactive
this.tooltip._model.opacity = 0;
}
}
// Hover animations
this.tooltip.pivot();
if (!this.animating) {
var changed;
helpers.each(this.active, function(element, index) {
if (element !== this.lastActive[index]) {
changed = true;
}
}, this);
// If entering, leaving, or changing elements, animate the change via pivot
if ((!this.lastActive.length && this.active.length) ||
(this.lastActive.length && !this.active.length) ||
(this.lastActive.length && this.active.length && changed)) {
this.stop();
this.render(this.options.hover.animationDuration);
}
}
// Remember Last Active
this.lastActive = this.active;
return this;
},
getSliceAtEvent: function(e) {
var elements = [];
var location = helpers.getRelativePosition(e);
this.eachElement(function(slice, index) {
if (slice.inRange(location.x, location.y)) {
elements.push(slice);
}
}, this);
return elements;
},
/*getSlicesAtEvent: function(e) {
var elements = [];
var location = helpers.getRelativePosition(e);
this.eachElement(function(slice, index) {
if (slice.inGroupRange(location.x, location.y)) {
elements.push(slice);
}
}, this);
return elements;
},*/
});
Chart.types.Doughnut.extend({
name: "Pie",
defaults: helpers.merge(defaultConfig, {
cutoutPercentage: 0
})
});
"use strict";
var root = this,
Chart = root.Chart,
//Cache a local reference to Chart.helpers
helpers = Chart.helpers;
var defaultConfig = {
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'
},
//The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
};
Chart.Type.extend({
//Passing in a name registers this chart in the Chart namespace
name: "Doughnut",
//Providing a defaults will also register the deafults in the chart namespace
defaults: defaultConfig,
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
//Config is automatically merged by the core of Chart.js, and is available at this.options
initialize: function() {
//Set up tooltip events on the chart
helpers.bindEvents(this, this.options.events, this.events);
//Create a new bar for each piece of data
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.metaData = [];
helpers.each(dataset.data, function(dataPoint, index) {
dataset.metaData.push(new Chart.Arc({
_chart: this.chart,
_datasetIndex: datasetIndex,
_index: index,
_model: {}
}));
}, this);
}, this);
// Create tooltip instance exclusively for this chart with some defaults.
this.tooltip = new Chart.Tooltip({
_chart: this.chart,
_data: this.data,
_options: this.options,
}, this);
this.resetElements();
// Update the chart with the latest data.
this.update();
},
calculateCircumference: function(dataset, value) {
if (dataset.total > 0) {
return (Math.PI * 2) * (value / dataset.total);
} else {
return 0;
}
},
resetElements: function() {
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2;
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
// Update the points
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
// So that calculateCircumference works
dataset.total = 0;
helpers.each(dataset.data, function(value) {
dataset.total += Math.abs(value);
}, this);
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
helpers.each(dataset.metaData, function(slice, index) {
helpers.extend(slice, {
_model: {
x: this.chart.width / 2,
y: this.chart.height / 2,
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
outerRadius: (this.options.animation.animateScale) ? 0 : dataset.outerRadius,
innerRadius: (this.options.animation.animateScale) ? 0 : dataset.innerRadius,
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor),
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth),
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
},
});
slice.pivot();
}, this);
}, this);
},
update: function(animationDuration) {
this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2;
this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
// Update the points
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.total = 0;
helpers.each(dataset.data, function(value) {
dataset.total += Math.abs(value);
}, this);
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
helpers.each(dataset.metaData, function(slice, index) {
helpers.extend(slice, {
// Utility
_chart: this.chart,
_datasetIndex: datasetIndex,
_index: index,
// Desired view properties
_model: {
x: this.chart.width / 2,
y: this.chart.height / 2,
circumference: this.calculateCircumference(dataset, dataset.data[index]),
outerRadius: dataset.outerRadius,
innerRadius: dataset.innerRadius,
backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor),
hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth),
borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
},
});
if (index === 0) {
slice._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
} else {
slice._model.startAngle = dataset.metaData[index - 1]._model.endAngle;
}
slice._model.endAngle = slice._model.startAngle + slice._model.circumference;
//Check to see if it's the last slice, if not get the next and update its start angle
if (index < dataset.data.length - 1) {
dataset.metaData[index + 1]._model.startAngle = slice._model.endAngle;
}
slice.pivot();
}, this);
}, this);
this.render(animationDuration);
},
draw: function(easeDecimal) {
easeDecimal = easeDecimal || 1;
this.clear();
this.eachElement(function(slice) {
slice.transition(easeDecimal).draw();
}, this);
this.tooltip.transition(easeDecimal).draw();
},
events: function(e) {
this.lastActive = this.lastActive || [];
// Find Active Elements
if (e.type == 'mouseout') {
this.active = [];
} else {
this.active = function() {
switch (this.options.hover.mode) {
case 'single':
return this.getSliceAtEvent(e);
case 'label':
return this.getSlicesAtEvent(e);
case 'dataset':
return this.getDatasetAtEvent(e);
default:
return e;
}
}.call(this);
}
// On Hover hook
if (this.options.hover.onHover) {
this.options.hover.onHover.call(this, this.active);
}
if (e.type == 'mouseup' || e.type == 'click') {
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
}
}
var dataset;
var index;
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
index = this.lastActive[0]._index;
this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
break;
case 'label':
for (var i = 0; i < this.lastActive.length; i++) {
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
index = this.lastActive[i]._index;
this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in hover styling
if (this.active.length && this.options.hover.mode) {
switch (this.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.active[0]._datasetIndex];
index = this.active[0]._index;
this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, this.active[0]._model.borderColor);
this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, this.active[0]._model.borderWidth);
break;
case 'label':
for (var i = 0; i < this.active.length; i++) {
dataset = this.data.datasets[this.active[i]._datasetIndex];
index = this.active[i]._index;
this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, this.active[0]._model.borderColor);
this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, this.active[i]._model.borderWidth);
}
break;
case 'dataset':
break;
default:
// Don't change anything
}
}
// Built in Tooltips
if (this.options.tooltips.enabled) {
// The usual updates
this.tooltip.initialize();
// Active
if (this.active.length) {
this.tooltip._model.opacity = 1;
helpers.extend(this.tooltip, {
_active: this.active,
});
this.tooltip.update();
} else {
// Inactive
this.tooltip._model.opacity = 0;
}
}
// Hover animations
this.tooltip.pivot();
if (!this.animating) {
var changed;
helpers.each(this.active, function(element, index) {
if (element !== this.lastActive[index]) {
changed = true;
}
}, this);
// If entering, leaving, or changing elements, animate the change via pivot
if ((!this.lastActive.length && this.active.length) ||
(this.lastActive.length && !this.active.length) ||
(this.lastActive.length && this.active.length && changed)) {
this.stop();
this.render(this.options.hover.animationDuration);
}
}
// Remember Last Active
this.lastActive = this.active;
return this;
},
getSliceAtEvent: function(e) {
var elements = [];
var location = helpers.getRelativePosition(e);
this.eachElement(function(slice, index) {
if (slice.inRange(location.x, location.y)) {
elements.push(slice);
}
}, this);
return elements;
},
/*getSlicesAtEvent: function(e) {
var elements = [];
var location = helpers.getRelativePosition(e);
this.eachElement(function(slice, index) {
if (slice.inGroupRange(location.x, location.y)) {
elements.push(slice);
}
}, this);
return elements;
},*/
});
Chart.types.Doughnut.extend({
name: "Pie",
defaults: helpers.merge(defaultConfig, {
cutoutPercentage: 0
})
});
}).call(this);

View File

@ -11,117 +11,117 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.animation = {
duration: 1000,
easing: "easeOutQuart",
onProgress: function() {},
onComplete: function() {},
};
Chart.defaults.global.animation = {
duration: 1000,
easing: "easeOutQuart",
onProgress: function() {},
onComplete: function() {},
};
Chart.Animation = Chart.Element.extend({
currentStep: null, // the current animation step
numSteps: 60, // default number of steps
easing: "", // the easing to use for this animation
render: null, // render function used by the animation service
Chart.Animation = Chart.Element.extend({
currentStep: null, // the current animation step
numSteps: 60, // default number of steps
easing: "", // the easing to use for this animation
render: null, // render function used by the animation service
onAnimationProgress: null, // user specified callback to fire on each step of the animation
onAnimationComplete: null, // user specified callback to fire when the animation finishes
});
onAnimationProgress: null, // user specified callback to fire on each step of the animation
onAnimationComplete: null, // user specified callback to fire when the animation finishes
});
Chart.animationService = {
frameDuration: 17,
animations: [],
dropFrames: 0,
addAnimation: function(chartInstance, animationObject, duration) {
Chart.animationService = {
frameDuration: 17,
animations: [],
dropFrames: 0,
addAnimation: function(chartInstance, animationObject, duration) {
if (!duration) {
chartInstance.animating = true;
}
if (!duration) {
chartInstance.animating = true;
}
for (var index = 0; index < this.animations.length; ++index) {
if (this.animations[index].chartInstance === chartInstance) {
// replacing an in progress animation
this.animations[index].animationObject = animationObject;
return;
}
}
for (var index = 0; index < this.animations.length; ++index) {
if (this.animations[index].chartInstance === chartInstance) {
// replacing an in progress animation
this.animations[index].animationObject = animationObject;
return;
}
}
this.animations.push({
chartInstance: chartInstance,
animationObject: animationObject
});
this.animations.push({
chartInstance: chartInstance,
animationObject: animationObject
});
// If there are no animations queued, manually kickstart a digest, for lack of a better word
if (this.animations.length == 1) {
helpers.requestAnimFrame.call(window, this.digestWrapper);
}
},
// Cancel the animation for a given chart instance
cancelAnimation: function(chartInstance) {
var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
return animationWrapper.chartInstance === chartInstance;
});
// If there are no animations queued, manually kickstart a digest, for lack of a better word
if (this.animations.length == 1) {
helpers.requestAnimFrame.call(window, this.digestWrapper);
}
},
// Cancel the animation for a given chart instance
cancelAnimation: function(chartInstance) {
var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
return animationWrapper.chartInstance === chartInstance;
});
if (index) {
this.animations.splice(index, 1);
chartInstance.animating = false;
}
},
// calls startDigest with the proper context
digestWrapper: function() {
Chart.animationService.startDigest.call(Chart.animationService);
},
startDigest: function() {
if (index) {
this.animations.splice(index, 1);
chartInstance.animating = false;
}
},
// calls startDigest with the proper context
digestWrapper: function() {
Chart.animationService.startDigest.call(Chart.animationService);
},
startDigest: function() {
var startTime = Date.now();
var framesToDrop = 0;
var startTime = Date.now();
var framesToDrop = 0;
if (this.dropFrames > 1) {
framesToDrop = Math.floor(this.dropFrames);
this.dropFrames -= framesToDrop;
}
if (this.dropFrames > 1) {
framesToDrop = Math.floor(this.dropFrames);
this.dropFrames -= framesToDrop;
}
for (var i = 0; i < this.animations.length; i++) {
for (var i = 0; i < this.animations.length; i++) {
if (this.animations[i].animationObject.currentStep === null) {
this.animations[i].animationObject.currentStep = 0;
}
if (this.animations[i].animationObject.currentStep === null) {
this.animations[i].animationObject.currentStep = 0;
}
this.animations[i].animationObject.currentStep += 1 + framesToDrop;
if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) {
this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
}
this.animations[i].animationObject.currentStep += 1 + framesToDrop;
if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) {
this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
}
this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps) {
// executed the last frame. Remove the animation.
this.animations[i].chartInstance.animating = false;
this.animations.splice(i, 1);
// Keep the index in place to offset the splice
i--;
}
}
if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps) {
// executed the last frame. Remove the animation.
this.animations[i].chartInstance.animating = false;
this.animations.splice(i, 1);
// Keep the index in place to offset the splice
i--;
}
}
var endTime = Date.now();
var delay = endTime - startTime - this.frameDuration;
var frameDelay = delay / this.frameDuration;
var endTime = Date.now();
var delay = endTime - startTime - this.frameDuration;
var frameDelay = delay / this.frameDuration;
if (frameDelay > 1) {
this.dropFrames += frameDelay;
}
if (frameDelay > 1) {
this.dropFrames += frameDelay;
}
// Do we have more stuff to animate?
if (this.animations.length > 0) {
helpers.requestAnimFrame.call(window, this.digestWrapper);
}
}
};
// Do we have more stuff to animate?
if (this.animations.length > 0) {
helpers.requestAnimFrame.call(window, this.digestWrapper);
}
}
};
}).call(this);

File diff suppressed because it is too large Load Diff

View File

@ -1,302 +1,302 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
// The scale service is used to resize charts along with all of their axes. We make this as
// a service where scales are registered with their respective charts so that changing the
// scales does not require
Chart.scaleService = {
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
// use the new chart options to grab the correct scale
constructors: {},
// Use a registration function so that we can move to an ES6 map when we no longer need to support
// old browsers
registerScaleType: function(type, scaleConstructor) {
this.constructors[type] = scaleConstructor;
},
getScaleConstructor: function(type) {
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
},
// The scale service is used to resize charts along with all of their axes. We make this as
// a service where scales are registered with their respective charts so that changing the
// scales does not require
Chart.scaleService = {
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
// use the new chart options to grab the correct scale
constructors: {},
// Use a registration function so that we can move to an ES6 map when we no longer need to support
// old browsers
registerScaleType: function(type, scaleConstructor) {
this.constructors[type] = scaleConstructor;
},
getScaleConstructor: function(type) {
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
},
// The interesting function
fitScalesForChart: function(chartInstance, width, height) {
var xPadding = width > 30 ? 5 : 2;
var yPadding = height > 30 ? 5 : 2;
// The interesting function
fitScalesForChart: function(chartInstance, width, height) {
var xPadding = width > 30 ? 5 : 2;
var yPadding = height > 30 ? 5 : 2;
if (chartInstance) {
var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "left";
});
var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "right";
});
var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "top";
});
var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "bottom";
});
if (chartInstance) {
var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "left";
});
var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "right";
});
var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "top";
});
var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "bottom";
});
var visibleLeftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "left";
});
var visibleRightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "right";
});
var visibleTopScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "top";
});
var visibleBottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "bottom";
});
var visibleLeftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "left";
});
var visibleRightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "right";
});
var visibleTopScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "top";
});
var visibleBottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "bottom";
});
// // Adjust the padding to take into account displaying labels
// if (topScales.length === 0 || bottomScales.length === 0) {
// var maxFontHeight = 0;
// // Adjust the padding to take into account displaying labels
// if (topScales.length === 0 || bottomScales.length === 0) {
// var maxFontHeight = 0;
// var maxFontHeightFunction = function(scaleInstance) {
// if (scaleInstance.options.labels.show) {
// // Only consider font sizes for axes that actually show labels
// maxFontHeight = Math.max(maxFontHeight, scaleInstance.options.labels.fontSize);
// }
// };
// var maxFontHeightFunction = function(scaleInstance) {
// if (scaleInstance.options.labels.show) {
// // Only consider font sizes for axes that actually show labels
// maxFontHeight = Math.max(maxFontHeight, scaleInstance.options.labels.fontSize);
// }
// };
// helpers.each(leftScales, maxFontHeightFunction);
// helpers.each(rightScales, maxFontHeightFunction);
// helpers.each(leftScales, maxFontHeightFunction);
// helpers.each(rightScales, maxFontHeightFunction);
// if (topScales.length === 0) {
// // Add padding so that we can handle drawing the top nicely
// yPadding += 0.75 * maxFontHeight; // 0.75 since padding added on both sides
// }
// if (topScales.length === 0) {
// // Add padding so that we can handle drawing the top nicely
// yPadding += 0.75 * maxFontHeight; // 0.75 since padding added on both sides
// }
// if (bottomScales.length === 0) {
// // Add padding so that we can handle drawing the bottom nicely
// yPadding += 1.5 * maxFontHeight;
// }
// }
// if (bottomScales.length === 0) {
// // Add padding so that we can handle drawing the bottom nicely
// yPadding += 1.5 * maxFontHeight;
// }
// }
// Essentially we now have any number of scales on each of the 4 sides.
// Our canvas looks like the following.
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
// B1 is the bottom axis
// |------------------------------------------------------|
// | | T1 | |
// |----|-----|-------------------------------------|-----|
// | | | | |
// | L1 | L2 | Chart area | R1 |
// | | | | |
// | | | | |
// |----|-----|-------------------------------------|-----|
// | | B1 | |
// | | | |
// |------------------------------------------------------|
// Essentially we now have any number of scales on each of the 4 sides.
// Our canvas looks like the following.
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
// B1 is the bottom axis
// |------------------------------------------------------|
// | | T1 | |
// |----|-----|-------------------------------------|-----|
// | | | | |
// | L1 | L2 | Chart area | R1 |
// | | | | |
// | | | | |
// |----|-----|-------------------------------------|-----|
// | | B1 | |
// | | | |
// |------------------------------------------------------|
// What we do to find the best sizing, we do the following
// 1. Determine the minimum size of the chart area.
// 2. Split the remaining width equally between each vertical axis
// 3. Split the remaining height equally between each horizontal axis
// 4. Give each scale the maximum size it can be. The scale will return it's minimum size
// 5. Adjust the sizes of each axis based on it's minimum reported size.
// 6. Refit each axis
// 7. Position each axis in the final location
// 8. Tell the chart the final location of the chart area
// What we do to find the best sizing, we do the following
// 1. Determine the minimum size of the chart area.
// 2. Split the remaining width equally between each vertical axis
// 3. Split the remaining height equally between each horizontal axis
// 4. Give each scale the maximum size it can be. The scale will return it's minimum size
// 5. Adjust the sizes of each axis based on it's minimum reported size.
// 6. Refit each axis
// 7. Position each axis in the final location
// 8. Tell the chart the final location of the chart area
// Step 1
var chartWidth = width / 2; // min 50%
var chartHeight = height / 2; // min 50%
// Step 1
var chartWidth = width / 2; // min 50%
var chartHeight = height / 2; // min 50%
chartWidth -= (2 * xPadding);
chartHeight -= (2 * yPadding);
chartWidth -= (2 * xPadding);
chartHeight -= (2 * yPadding);
// Step 2
var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
// Step 2
var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
// Step 3
var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
// Step 3
var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
// Step 4;
var minimumScaleSizes = [];
// Step 4;
var minimumScaleSizes = [];
var verticalScaleMinSizeFunction = function(scaleInstance) {
var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
minimumScaleSizes.push({
horizontal: false,
minSize: minSize,
scale: scaleInstance,
});
};
var verticalScaleMinSizeFunction = function(scaleInstance) {
var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
minimumScaleSizes.push({
horizontal: false,
minSize: minSize,
scale: scaleInstance,
});
};
var horizontalScaleMinSizeFunction = function(scaleInstance) {
var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
minimumScaleSizes.push({
horizontal: true,
minSize: minSize,
scale: scaleInstance,
});
};
var horizontalScaleMinSizeFunction = function(scaleInstance) {
var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
minimumScaleSizes.push({
horizontal: true,
minSize: minSize,
scale: scaleInstance,
});
};
// vertical scales
helpers.each(leftScales, verticalScaleMinSizeFunction);
helpers.each(rightScales, verticalScaleMinSizeFunction);
// vertical scales
helpers.each(leftScales, verticalScaleMinSizeFunction);
helpers.each(rightScales, verticalScaleMinSizeFunction);
// horizontal scales
helpers.each(topScales, horizontalScaleMinSizeFunction);
helpers.each(bottomScales, horizontalScaleMinSizeFunction);
// horizontal scales
helpers.each(topScales, horizontalScaleMinSizeFunction);
helpers.each(bottomScales, horizontalScaleMinSizeFunction);
// Step 5
var maxChartHeight = height - (2 * yPadding);
var maxChartWidth = width - (2 * xPadding);
// Step 5
var maxChartHeight = height - (2 * yPadding);
var maxChartWidth = width - (2 * xPadding);
helpers.each(minimumScaleSizes, function(wrapper) {
if (wrapper.horizontal) {
maxChartHeight -= wrapper.minSize.height;
} else {
maxChartWidth -= wrapper.minSize.width;
}
});
helpers.each(minimumScaleSizes, function(wrapper) {
if (wrapper.horizontal) {
maxChartHeight -= wrapper.minSize.height;
} else {
maxChartWidth -= wrapper.minSize.width;
}
});
// At this point, maxChartHeight and maxChartWidth are the size the chart area could
// be if the axes are drawn at their minimum sizes.
// At this point, maxChartHeight and maxChartWidth are the size the chart area could
// be if the axes are drawn at their minimum sizes.
// Step 6
var verticalScaleFitFunction = function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
// Step 6
var verticalScaleFitFunction = function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
if (wrapper) {
scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
}
};
if (wrapper) {
scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
}
};
var horizontalScaleFitFunction = function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
var horizontalScaleFitFunction = function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
var scaleMargin = {
left: totalLeftWidth,
right: totalRightWidth,
top: 0,
bottom: 0,
};
var scaleMargin = {
left: totalLeftWidth,
right: totalRightWidth,
top: 0,
bottom: 0,
};
if (wrapper) {
scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
}
};
if (wrapper) {
scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
}
};
var totalLeftWidth = xPadding;
var totalRightWidth = xPadding;
var totalTopHeight = yPadding;
var totalBottomHeight = yPadding;
var totalLeftWidth = xPadding;
var totalRightWidth = xPadding;
var totalTopHeight = yPadding;
var totalBottomHeight = yPadding;
helpers.each(leftScales, verticalScaleFitFunction);
helpers.each(rightScales, verticalScaleFitFunction);
helpers.each(leftScales, verticalScaleFitFunction);
helpers.each(rightScales, verticalScaleFitFunction);
// Figure out how much margin is on the left and right of the horizontal axes
helpers.each(leftScales, function(scaleInstance) {
totalLeftWidth += scaleInstance.width;
});
// Figure out how much margin is on the left and right of the horizontal axes
helpers.each(leftScales, function(scaleInstance) {
totalLeftWidth += scaleInstance.width;
});
helpers.each(rightScales, function(scaleInstance) {
totalRightWidth += scaleInstance.width;
});
helpers.each(rightScales, function(scaleInstance) {
totalRightWidth += scaleInstance.width;
});
helpers.each(topScales, horizontalScaleFitFunction);
helpers.each(bottomScales, horizontalScaleFitFunction);
helpers.each(topScales, horizontalScaleFitFunction);
helpers.each(bottomScales, horizontalScaleFitFunction);
helpers.each(topScales, function(scaleInstance) {
totalTopHeight += scaleInstance.height;
});
helpers.each(bottomScales, function(scaleInstance) {
totalBottomHeight += scaleInstance.height;
});
helpers.each(topScales, function(scaleInstance) {
totalTopHeight += scaleInstance.height;
});
helpers.each(bottomScales, function(scaleInstance) {
totalBottomHeight += scaleInstance.height;
});
// Let the left scale know the final margin
helpers.each(leftScales, function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
// Let the left scale know the final margin
helpers.each(leftScales, function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
var scaleMargin = {
left: 0,
right: 0,
top: totalTopHeight,
bottom: totalBottomHeight
};
var scaleMargin = {
left: 0,
right: 0,
top: totalTopHeight,
bottom: totalBottomHeight
};
if (wrapper) {
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
}
});
if (wrapper) {
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
}
});
helpers.each(rightScales, function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
helpers.each(rightScales, function(scaleInstance) {
var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
return wrapper.scale === scaleInstance;
});
var scaleMargin = {
left: 0,
right: 0,
top: totalTopHeight,
bottom: totalBottomHeight
};
var scaleMargin = {
left: 0,
right: 0,
top: totalTopHeight,
bottom: totalBottomHeight
};
if (wrapper) {
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
}
});
if (wrapper) {
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
}
});
// Step 7
// Position the scales
var left = xPadding;
var top = yPadding;
var right = 0;
var bottom = 0;
// Step 7
// Position the scales
var left = xPadding;
var top = yPadding;
var right = 0;
var bottom = 0;
var verticalScalePlacer = function(scaleInstance) {
scaleInstance.left = left;
scaleInstance.right = left + scaleInstance.width;
scaleInstance.top = totalTopHeight;
scaleInstance.bottom = totalTopHeight + maxChartHeight;
var verticalScalePlacer = function(scaleInstance) {
scaleInstance.left = left;
scaleInstance.right = left + scaleInstance.width;
scaleInstance.top = totalTopHeight;
scaleInstance.bottom = totalTopHeight + maxChartHeight;
// Move to next point
left = scaleInstance.right;
};
// Move to next point
left = scaleInstance.right;
};
var horizontalScalePlacer = function(scaleInstance) {
scaleInstance.left = totalLeftWidth;
scaleInstance.right = totalLeftWidth + maxChartWidth;
scaleInstance.top = top;
scaleInstance.bottom = top + scaleInstance.height;
var horizontalScalePlacer = function(scaleInstance) {
scaleInstance.left = totalLeftWidth;
scaleInstance.right = totalLeftWidth + maxChartWidth;
scaleInstance.top = top;
scaleInstance.bottom = top + scaleInstance.height;
// Move to next point
top = scaleInstance.bottom;
};
// Move to next point
top = scaleInstance.bottom;
};
helpers.each(leftScales, verticalScalePlacer);
helpers.each(topScales, horizontalScalePlacer);
helpers.each(leftScales, verticalScalePlacer);
helpers.each(topScales, horizontalScalePlacer);
// Account for chart width and height
left += maxChartWidth;
top += maxChartHeight;
// Account for chart width and height
left += maxChartWidth;
top += maxChartHeight;
helpers.each(rightScales, verticalScalePlacer);
helpers.each(bottomScales, horizontalScalePlacer);
helpers.each(rightScales, verticalScalePlacer);
helpers.each(bottomScales, horizontalScalePlacer);
// Step 8
chartInstance.chartArea = {
left: totalLeftWidth,
top: totalTopHeight,
right: totalLeftWidth + maxChartWidth,
bottom: totalTopHeight + maxChartHeight,
};
}
}
};
// Step 8
chartInstance.chartArea = {
left: totalLeftWidth,
top: totalTopHeight,
right: totalLeftWidth + maxChartWidth,
bottom: totalTopHeight + maxChartHeight,
};
}
}
};
}).call(this);

View File

@ -11,334 +11,334 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.tooltips = {
enabled: true,
custom: null,
backgroundColor: "rgba(0,0,0,0.8)",
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
fontSize: 10,
fontStyle: "normal",
fontColor: "#fff",
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
titleFontSize: 12,
titleFontStyle: "bold",
titleFontColor: "#fff",
yPadding: 6,
xPadding: 6,
caretSize: 8,
cornerRadius: 6,
xOffset: 10,
template: [
'<% if(label){ %>',
'<%=label %>: ',
'<% } %>',
'<%=value %>',
].join(''),
multiTemplate: [
'<%if (datasetLabel){ %>',
'<%=datasetLabel %>: ',
'<% } %>',
'<%=value %>'
].join(''),
multiKeyBackground: '#fff',
};
Chart.defaults.global.tooltips = {
enabled: true,
custom: null,
backgroundColor: "rgba(0,0,0,0.8)",
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
fontSize: 10,
fontStyle: "normal",
fontColor: "#fff",
titleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
titleFontSize: 12,
titleFontStyle: "bold",
titleFontColor: "#fff",
yPadding: 6,
xPadding: 6,
caretSize: 8,
cornerRadius: 6,
xOffset: 10,
template: [
'<% if(label){ %>',
'<%=label %>: ',
'<% } %>',
'<%=value %>',
].join(''),
multiTemplate: [
'<%if (datasetLabel){ %>',
'<%=datasetLabel %>: ',
'<% } %>',
'<%=value %>'
].join(''),
multiKeyBackground: '#fff',
};
Chart.Tooltip = Chart.Element.extend({
initialize: function() {
var options = this._options;
helpers.extend(this, {
_model: {
// Positioning
xPadding: options.tooltips.xPadding,
yPadding: options.tooltips.yPadding,
xOffset: options.tooltips.xOffset,
Chart.Tooltip = Chart.Element.extend({
initialize: function() {
var options = this._options;
helpers.extend(this, {
_model: {
// Positioning
xPadding: options.tooltips.xPadding,
yPadding: options.tooltips.yPadding,
xOffset: options.tooltips.xOffset,
// Labels
textColor: options.tooltips.fontColor,
_fontFamily: options.tooltips.fontFamily,
_fontStyle: options.tooltips.fontStyle,
fontSize: options.tooltips.fontSize,
// Labels
textColor: options.tooltips.fontColor,
_fontFamily: options.tooltips.fontFamily,
_fontStyle: options.tooltips.fontStyle,
fontSize: options.tooltips.fontSize,
// Title
titleTextColor: options.tooltips.titleFontColor,
_titleFontFamily: options.tooltips.titleFontFamily,
_titleFontStyle: options.tooltips.titleFontStyle,
titleFontSize: options.tooltips.titleFontSize,
// Title
titleTextColor: options.tooltips.titleFontColor,
_titleFontFamily: options.tooltips.titleFontFamily,
_titleFontStyle: options.tooltips.titleFontStyle,
titleFontSize: options.tooltips.titleFontSize,
// Appearance
caretHeight: options.tooltips.caretSize,
cornerRadius: options.tooltips.cornerRadius,
backgroundColor: options.tooltips.backgroundColor,
opacity: 0,
legendColorBackground: options.tooltips.multiKeyBackground,
},
});
},
update: function() {
// Appearance
caretHeight: options.tooltips.caretSize,
cornerRadius: options.tooltips.cornerRadius,
backgroundColor: options.tooltips.backgroundColor,
opacity: 0,
legendColorBackground: options.tooltips.multiKeyBackground,
},
});
},
update: function() {
var ctx = this._chart.ctx;
var ctx = this._chart.ctx;
switch (this._options.hover.mode) {
case 'single':
helpers.extend(this._model, {
text: helpers.template(this._options.tooltips.template, {
// These variables are available in the template function. Add others here
element: this._active[0],
value: this._data.datasets[this._active[0]._datasetIndex].data[this._active[0]._index],
label: this._data.labels ? this._data.labels[this._active[0]._index] : '',
}),
});
switch (this._options.hover.mode) {
case 'single':
helpers.extend(this._model, {
text: helpers.template(this._options.tooltips.template, {
// These variables are available in the template function. Add others here
element: this._active[0],
value: this._data.datasets[this._active[0]._datasetIndex].data[this._active[0]._index],
label: this._data.labels ? this._data.labels[this._active[0]._index] : '',
}),
});
var tooltipPosition = this._active[0].tooltipPosition();
helpers.extend(this._model, {
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
caretPadding: tooltipPosition.padding
});
var tooltipPosition = this._active[0].tooltipPosition();
helpers.extend(this._model, {
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
caretPadding: tooltipPosition.padding
});
break;
break;
case 'label':
case 'label':
// Tooltip Content
// Tooltip Content
var dataArray,
dataIndex;
var dataArray,
dataIndex;
var labels = [],
colors = [];
var labels = [],
colors = [];
for (var i = this._data.datasets.length - 1; i >= 0; i--) {
dataArray = this._data.datasets[i].metaData;
dataIndex = helpers.indexOf(dataArray, this._active[0]);
if (dataIndex !== -1) {
break;
}
}
for (var i = this._data.datasets.length - 1; i >= 0; i--) {
dataArray = this._data.datasets[i].metaData;
dataIndex = helpers.indexOf(dataArray, this._active[0]);
if (dataIndex !== -1) {
break;
}
}
var medianPosition = (function(index) {
// Get all the points at that particular index
var elements = [],
dataCollection,
xPositions = [],
yPositions = [],
xMax,
yMax,
xMin,
yMin;
helpers.each(this._data.datasets, function(dataset) {
dataCollection = dataset.metaData;
if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()) {
elements.push(dataCollection[dataIndex]);
}
}, this);
var medianPosition = (function(index) {
// Get all the points at that particular index
var elements = [],
dataCollection,
xPositions = [],
yPositions = [],
xMax,
yMax,
xMin,
yMin;
helpers.each(this._data.datasets, function(dataset) {
dataCollection = dataset.metaData;
if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()) {
elements.push(dataCollection[dataIndex]);
}
}, this);
// Reverse labels if stacked
helpers.each(this._options.stacked ? elements.reverse() : elements, function(element) {
xPositions.push(element._view.x);
yPositions.push(element._view.y);
// Reverse labels if stacked
helpers.each(this._options.stacked ? elements.reverse() : elements, function(element) {
xPositions.push(element._view.x);
yPositions.push(element._view.y);
//Include any colour information about the element
labels.push(helpers.template(this._options.tooltips.multiTemplate, {
// These variables are available in the template function. Add others here
element: element,
datasetLabel: this._data.datasets[element._datasetIndex].label,
value: this._data.datasets[element._datasetIndex].data[element._index],
}));
colors.push({
fill: element._view.backgroundColor,
stroke: element._view.borderColor
});
//Include any colour information about the element
labels.push(helpers.template(this._options.tooltips.multiTemplate, {
// These variables are available in the template function. Add others here
element: element,
datasetLabel: this._data.datasets[element._datasetIndex].label,
value: this._data.datasets[element._datasetIndex].data[element._index],
}));
colors.push({
fill: element._view.backgroundColor,
stroke: element._view.borderColor
});
}, this);
}, this);
yMin = helpers.min(yPositions);
yMax = helpers.max(yPositions);
yMin = helpers.min(yPositions);
yMax = helpers.max(yPositions);
xMin = helpers.min(xPositions);
xMax = helpers.max(xPositions);
xMin = helpers.min(xPositions);
xMax = helpers.max(xPositions);
return {
x: (xMin > this._chart.width / 2) ? xMin : xMax,
y: (yMin + yMax) / 2,
};
}).call(this, dataIndex);
return {
x: (xMin > this._chart.width / 2) ? xMin : xMax,
y: (yMin + yMax) / 2,
};
}).call(this, dataIndex);
// Apply for now
helpers.extend(this._model, {
x: medianPosition.x,
y: medianPosition.y,
labels: labels,
title: this._data.labels && this._data.labels.length ? this._data.labels[this._active[0]._index] : '',
legendColors: colors,
legendBackgroundColor: this._options.tooltips.multiKeyBackground,
});
// Apply for now
helpers.extend(this._model, {
x: medianPosition.x,
y: medianPosition.y,
labels: labels,
title: this._data.labels && this._data.labels.length ? this._data.labels[this._active[0]._index] : '',
legendColors: colors,
legendBackgroundColor: this._options.tooltips.multiKeyBackground,
});
// Calculate Appearance Tweaks
// Calculate Appearance Tweaks
this._model.height = (labels.length * this._model.fontSize) + ((labels.length - 1) * (this._model.fontSize / 2)) + (this._model.yPadding * 2) + this._model.titleFontSize * 1.5;
this._model.height = (labels.length * this._model.fontSize) + ((labels.length - 1) * (this._model.fontSize / 2)) + (this._model.yPadding * 2) + this._model.titleFontSize * 1.5;
var titleWidth = ctx.measureText(this.title).width,
//Label has a legend square as well so account for this.
labelWidth = helpers.longestText(ctx, this.font, labels) + this._model.fontSize + 3,
longestTextWidth = helpers.max([labelWidth, titleWidth]);
var titleWidth = ctx.measureText(this.title).width,
//Label has a legend square as well so account for this.
labelWidth = helpers.longestText(ctx, this.font, labels) + this._model.fontSize + 3,
longestTextWidth = helpers.max([labelWidth, titleWidth]);
this._model.width = longestTextWidth + (this._model.xPadding * 2);
this._model.width = longestTextWidth + (this._model.xPadding * 2);
var halfHeight = this._model.height / 2;
var halfHeight = this._model.height / 2;
//Check to ensure the height will fit on the canvas
if (this._model.y - halfHeight < 0) {
this._model.y = halfHeight;
} else if (this._model.y + halfHeight > this._chart.height) {
this._model.y = this._chart.height - halfHeight;
}
//Check to ensure the height will fit on the canvas
if (this._model.y - halfHeight < 0) {
this._model.y = halfHeight;
} else if (this._model.y + halfHeight > this._chart.height) {
this._model.y = this._chart.height - halfHeight;
}
//Decide whether to align left or right based on position on canvas
if (this._model.x > this._chart.width / 2) {
this._model.x -= this._model.xOffset + this._model.width;
} else {
this._model.x += this._model.xOffset;
}
break;
}
//Decide whether to align left or right based on position on canvas
if (this._model.x > this._chart.width / 2) {
this._model.x -= this._model.xOffset + this._model.width;
} else {
this._model.x += this._model.xOffset;
}
break;
}
return this;
},
draw: function() {
return this;
},
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var ctx = this._chart.ctx;
var vm = this._view;
switch (this._options.hover.mode) {
case 'single':
switch (this._options.hover.mode) {
case 'single':
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
vm.xAlign = "center";
vm.yAlign = "above";
vm.xAlign = "center";
vm.yAlign = "above";
//Distance between the actual element.y position and the start of the tooltip caret
var caretPadding = vm.caretPadding || 2;
//Distance between the actual element.y position and the start of the tooltip caret
var caretPadding = vm.caretPadding || 2;
var tooltipWidth = ctx.measureText(vm.text).width + 2 * vm.xPadding,
tooltipRectHeight = vm.fontSize + 2 * vm.yPadding,
tooltipHeight = tooltipRectHeight + vm.caretHeight + caretPadding;
var tooltipWidth = ctx.measureText(vm.text).width + 2 * vm.xPadding,
tooltipRectHeight = vm.fontSize + 2 * vm.yPadding,
tooltipHeight = tooltipRectHeight + vm.caretHeight + caretPadding;
if (vm.x + tooltipWidth / 2 > this._chart.width) {
vm.xAlign = "left";
} else if (vm.x - tooltipWidth / 2 < 0) {
vm.xAlign = "right";
}
if (vm.x + tooltipWidth / 2 > this._chart.width) {
vm.xAlign = "left";
} else if (vm.x - tooltipWidth / 2 < 0) {
vm.xAlign = "right";
}
if (vm.y - tooltipHeight < 0) {
vm.yAlign = "below";
}
if (vm.y - tooltipHeight < 0) {
vm.yAlign = "below";
}
var tooltipX = vm.x - tooltipWidth / 2,
tooltipY = vm.y - tooltipHeight;
var tooltipX = vm.x - tooltipWidth / 2,
tooltipY = vm.y - tooltipHeight;
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
// Custom Tooltips
if (this._custom) {
this._custom(this._view);
} else {
switch (vm.yAlign) {
case "above":
//Draw a caret above the x/y
ctx.beginPath();
ctx.moveTo(vm.x, vm.y - caretPadding);
ctx.lineTo(vm.x + vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
ctx.lineTo(vm.x - vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
ctx.closePath();
ctx.fill();
break;
case "below":
tooltipY = vm.y + caretPadding + vm.caretHeight;
//Draw a caret below the x/y
ctx.beginPath();
ctx.moveTo(vm.x, vm.y + caretPadding);
ctx.lineTo(vm.x + vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
ctx.lineTo(vm.x - vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
ctx.closePath();
ctx.fill();
break;
}
// Custom Tooltips
if (this._custom) {
this._custom(this._view);
} else {
switch (vm.yAlign) {
case "above":
//Draw a caret above the x/y
ctx.beginPath();
ctx.moveTo(vm.x, vm.y - caretPadding);
ctx.lineTo(vm.x + vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
ctx.lineTo(vm.x - vm.caretHeight, vm.y - (caretPadding + vm.caretHeight));
ctx.closePath();
ctx.fill();
break;
case "below":
tooltipY = vm.y + caretPadding + vm.caretHeight;
//Draw a caret below the x/y
ctx.beginPath();
ctx.moveTo(vm.x, vm.y + caretPadding);
ctx.lineTo(vm.x + vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
ctx.lineTo(vm.x - vm.caretHeight, vm.y + caretPadding + vm.caretHeight);
ctx.closePath();
ctx.fill();
break;
}
switch (vm.xAlign) {
case "left":
tooltipX = vm.x - tooltipWidth + (vm.cornerRadius + vm.caretHeight);
break;
case "right":
tooltipX = vm.x - (vm.cornerRadius + vm.caretHeight);
break;
}
switch (vm.xAlign) {
case "left":
tooltipX = vm.x - tooltipWidth + (vm.cornerRadius + vm.caretHeight);
break;
case "right":
tooltipX = vm.x - (vm.cornerRadius + vm.caretHeight);
break;
}
helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipRectHeight, vm.cornerRadius);
helpers.drawRoundedRectangle(ctx, tooltipX, tooltipY, tooltipWidth, tooltipRectHeight, vm.cornerRadius);
ctx.fill();
ctx.fill();
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(vm.text, tooltipX + tooltipWidth / 2, tooltipY + tooltipRectHeight / 2);
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(vm.text, tooltipX + tooltipWidth / 2, tooltipY + tooltipRectHeight / 2);
}
break;
case 'label':
}
break;
case 'label':
helpers.drawRoundedRectangle(ctx, vm.x, vm.y - vm.height / 2, vm.width, vm.height, vm.cornerRadius);
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
ctx.fill();
ctx.closePath();
helpers.drawRoundedRectangle(ctx, vm.x, vm.y - vm.height / 2, vm.width, vm.height, vm.cornerRadius);
ctx.fillStyle = helpers.color(vm.backgroundColor).alpha(vm.opacity).rgbString();
ctx.fill();
ctx.closePath();
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillStyle = helpers.color(vm.titleTextColor).alpha(vm.opacity).rgbString();
ctx.font = helpers.fontString(vm.fontSize, vm._titleFontStyle, vm._titleFontFamily);
ctx.fillText(vm.title, vm.x + vm.xPadding, this.getLineHeight(0));
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillStyle = helpers.color(vm.titleTextColor).alpha(vm.opacity).rgbString();
ctx.font = helpers.fontString(vm.fontSize, vm._titleFontStyle, vm._titleFontFamily);
ctx.fillText(vm.title, vm.x + vm.xPadding, this.getLineHeight(0));
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
helpers.each(vm.labels, function(label, index) {
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
ctx.fillText(label, vm.x + vm.xPadding + vm.fontSize + 3, this.getLineHeight(index + 1));
ctx.font = helpers.fontString(vm.fontSize, vm._fontStyle, vm._fontFamily);
helpers.each(vm.labels, function(label, index) {
ctx.fillStyle = helpers.color(vm.textColor).alpha(vm.opacity).rgbString();
ctx.fillText(label, vm.x + vm.xPadding + vm.fontSize + 3, this.getLineHeight(index + 1));
//A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
//ctx.clearRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize/2, vm.fontSize, vm.fontSize);
//Instead we'll make a white filled block to put the legendColour palette over.
//A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
//ctx.clearRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize/2, vm.fontSize, vm.fontSize);
//Instead we'll make a white filled block to put the legendColour palette over.
ctx.fillStyle = helpers.color(vm.legendColors[index].stroke).alpha(vm.opacity).rgbString();
ctx.fillRect(vm.x + vm.xPadding - 1, this.getLineHeight(index + 1) - vm.fontSize / 2 - 1, vm.fontSize + 2, vm.fontSize + 2);
ctx.fillStyle = helpers.color(vm.legendColors[index].stroke).alpha(vm.opacity).rgbString();
ctx.fillRect(vm.x + vm.xPadding - 1, this.getLineHeight(index + 1) - vm.fontSize / 2 - 1, vm.fontSize + 2, vm.fontSize + 2);
ctx.fillStyle = helpers.color(vm.legendColors[index].fill).alpha(vm.opacity).rgbString();
ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
ctx.fillStyle = helpers.color(vm.legendColors[index].fill).alpha(vm.opacity).rgbString();
ctx.fillRect(vm.x + vm.xPadding, this.getLineHeight(index + 1) - vm.fontSize / 2, vm.fontSize, vm.fontSize);
}, this);
break;
}
},
getLineHeight: function(index) {
var baseLineHeight = this._view.y - (this._view.height / 2) + this._view.yPadding,
afterTitleIndex = index - 1;
}, this);
break;
}
},
getLineHeight: function(index) {
var baseLineHeight = this._view.y - (this._view.height / 2) + this._view.yPadding,
afterTitleIndex = index - 1;
//If the index is zero, we're getting the title
if (index === 0) {
return baseLineHeight + this._view.titleFontSize / 2;
} else {
return baseLineHeight + ((this._view.fontSize * 1.5 * afterTitleIndex) + this._view.fontSize / 2) + this._view.titleFontSize * 1.5;
}
//If the index is zero, we're getting the title
if (index === 0) {
return baseLineHeight + this._view.titleFontSize / 2;
} else {
return baseLineHeight + ((this._view.fontSize * 1.5 * afterTitleIndex) + this._view.fontSize / 2) + this._view.titleFontSize * 1.5;
}
},
});
},
});
}).call(this);

View File

@ -11,83 +11,83 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.elements.arc = {
backgroundColor: Chart.defaults.global.defaultColor,
borderColor: "#fff",
borderWidth: 2
};
Chart.defaults.global.elements.arc = {
backgroundColor: Chart.defaults.global.defaultColor,
borderColor: "#fff",
borderWidth: 2
};
Chart.Arc = Chart.Element.extend({
inGroupRange: function(mouseX) {
var vm = this._view;
Chart.Arc = Chart.Element.extend({
inGroupRange: function(mouseX) {
var vm = this._view;
if (vm) {
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
} else {
return false;
}
},
inRange: function(chartX, chartY) {
if (vm) {
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
} else {
return false;
}
},
inRange: function(chartX, chartY) {
var vm = this._view;
var vm = this._view;
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
x: chartX,
y: chartY
});
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
x: chartX,
y: chartY
});
// Put into the range of (-PI/2, 3PI/2]
var startAngle = vm.startAngle < (-0.5 * Math.PI) ? vm.startAngle + (2.0 * Math.PI) : vm.startAngle > (1.5 * Math.PI) ? vm.startAngle - (2.0 * Math.PI) : vm.startAngle;
var endAngle = vm.endAngle < (-0.5 * Math.PI) ? vm.endAngle + (2.0 * Math.PI) : vm.endAngle > (1.5 * Math.PI) ? vm.endAngle - (2.0 * Math.PI) : vm.endAngle
// Put into the range of (-PI/2, 3PI/2]
var startAngle = vm.startAngle < (-0.5 * Math.PI) ? vm.startAngle + (2.0 * Math.PI) : vm.startAngle > (1.5 * Math.PI) ? vm.startAngle - (2.0 * Math.PI) : vm.startAngle;
var endAngle = vm.endAngle < (-0.5 * Math.PI) ? vm.endAngle + (2.0 * Math.PI) : vm.endAngle > (1.5 * Math.PI) ? vm.endAngle - (2.0 * Math.PI) : vm.endAngle
//Check if within the range of the open/close angle
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
//Check if within the range of the open/close angle
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
return (betweenAngles && withinRadius);
//Ensure within the outside of the arc centre, but inside arc outer
},
tooltipPosition: function() {
var vm = this._view;
return (betweenAngles && withinRadius);
//Ensure within the outside of the arc centre, but inside arc outer
},
tooltipPosition: function() {
var vm = this._view;
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
return {
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
};
},
draw: function() {
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
return {
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
};
},
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var ctx = this._chart.ctx;
var vm = this._view;
ctx.beginPath();
ctx.beginPath();
ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle);
ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle);
ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true);
ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true);
ctx.closePath();
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
ctx.closePath();
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
ctx.fillStyle = vm.backgroundColor;
ctx.fillStyle = vm.backgroundColor;
ctx.fill();
ctx.lineJoin = 'bevel';
ctx.fill();
ctx.lineJoin = 'bevel';
if (vm.borderWidth) {
ctx.stroke();
}
}
});
if (vm.borderWidth) {
ctx.stroke();
}
}
});
}).call(this);

View File

@ -11,180 +11,180 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.elements.line = {
tension: 0.4,
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 3,
borderColor: Chart.defaults.global.defaultColor,
fill: true, // do we fill in the area between the line and its base axis
skipNull: true,
drawNull: false,
};
Chart.defaults.global.elements.line = {
tension: 0.4,
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 3,
borderColor: Chart.defaults.global.defaultColor,
fill: true, // do we fill in the area between the line and its base axis
skipNull: true,
drawNull: false,
};
Chart.Line = Chart.Element.extend({
draw: function() {
Chart.Line = Chart.Element.extend({
draw: function() {
var vm = this._view;
var ctx = this._chart.ctx;
var first = this._children[0];
var last = this._children[this._children.length - 1];
var vm = this._view;
var ctx = this._chart.ctx;
var first = this._children[0];
var last = this._children[this._children.length - 1];
// Draw the background first (so the border is always on top)
helpers.each(this._children, function(point, index) {
var previous = this.previousPoint(point, this._children, index);
var next = this.nextPoint(point, this._children, index);
// Draw the background first (so the border is always on top)
helpers.each(this._children, function(point, index) {
var previous = this.previousPoint(point, this._children, index);
var next = this.nextPoint(point, this._children, index);
// First point only
if (index === 0) {
ctx.moveTo(point._view.x, point._view.y);
return;
}
// First point only
if (index === 0) {
ctx.moveTo(point._view.x, point._view.y);
return;
}
// Start Skip and drag along scale baseline
if (point._view.skip && vm.skipNull && !this._loop) {
ctx.lineTo(previous._view.x, point._view.y);
ctx.moveTo(next._view.x, point._view.y);
}
// End Skip Stright line from the base line
else if (previous._view.skip && vm.skipNull && !this._loop) {
ctx.moveTo(point._view.x, previous._view.y);
ctx.lineTo(point._view.x, point._view.y);
}
// Start Skip and drag along scale baseline
if (point._view.skip && vm.skipNull && !this._loop) {
ctx.lineTo(previous._view.x, point._view.y);
ctx.moveTo(next._view.x, point._view.y);
}
// End Skip Stright line from the base line
else if (previous._view.skip && vm.skipNull && !this._loop) {
ctx.moveTo(point._view.x, previous._view.y);
ctx.lineTo(point._view.x, point._view.y);
}
if (previous._view.skip && vm.skipNull) {
ctx.moveTo(point._view.x, point._view.y);
}
// Normal Bezier Curve
else {
if (vm.tension > 0) {
ctx.bezierCurveTo(
previous._view.controlPointNextX,
previous._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
} else {
ctx.lineTo(point._view.x, point._view.y);
}
}
}, this);
if (previous._view.skip && vm.skipNull) {
ctx.moveTo(point._view.x, point._view.y);
}
// Normal Bezier Curve
else {
if (vm.tension > 0) {
ctx.bezierCurveTo(
previous._view.controlPointNextX,
previous._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
} else {
ctx.lineTo(point._view.x, point._view.y);
}
}
}, this);
// For radial scales, loop back around to the first point
if (this._loop) {
if (vm.tension > 0 && !first._view.skip) {
// For radial scales, loop back around to the first point
if (this._loop) {
if (vm.tension > 0 && !first._view.skip) {
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
first._view.controlPointPreviousX,
first._view.controlPointPreviousY,
first._view.x,
first._view.y
);
} else {
ctx.lineTo(first._view.x, first._view.y);
}
}
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
first._view.controlPointPreviousX,
first._view.controlPointPreviousY,
first._view.x,
first._view.y
);
} else {
ctx.lineTo(first._view.x, first._view.y);
}
}
// If we had points and want to fill this line, do so.
if (this._children.length > 0 && vm.fill) {
//Round off the line by going to the base of the chart, back to the start, then fill.
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
ctx.closePath();
ctx.fill();
}
// If we had points and want to fill this line, do so.
if (this._children.length > 0 && vm.fill) {
//Round off the line by going to the base of the chart, back to the start, then fill.
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
ctx.closePath();
ctx.fill();
}
// Now draw the line between all the points with any borders
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.defaultColor;
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.beginPath();
// Now draw the line between all the points with any borders
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.defaultColor;
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.beginPath();
helpers.each(this._children, function(point, index) {
var previous = this.previousPoint(point, this._children, index);
var next = this.nextPoint(point, this._children, index);
helpers.each(this._children, function(point, index) {
var previous = this.previousPoint(point, this._children, index);
var next = this.nextPoint(point, this._children, index);
// First point only
if (index === 0) {
ctx.moveTo(point._view.x, point._view.y);
return;
}
// First point only
if (index === 0) {
ctx.moveTo(point._view.x, point._view.y);
return;
}
// Start Skip and drag along scale baseline
if (point._view.skip && vm.skipNull && !this._loop) {
ctx.moveTo(previous._view.x, point._view.y);
ctx.moveTo(next._view.x, point._view.y);
return;
}
// End Skip Stright line from the base line
if (previous._view.skip && vm.skipNull && !this._loop) {
ctx.moveTo(point._view.x, previous._view.y);
ctx.moveTo(point._view.x, point._view.y);
return;
}
// Start Skip and drag along scale baseline
if (point._view.skip && vm.skipNull && !this._loop) {
ctx.moveTo(previous._view.x, point._view.y);
ctx.moveTo(next._view.x, point._view.y);
return;
}
// End Skip Stright line from the base line
if (previous._view.skip && vm.skipNull && !this._loop) {
ctx.moveTo(point._view.x, previous._view.y);
ctx.moveTo(point._view.x, point._view.y);
return;
}
if (previous._view.skip && vm.skipNull) {
ctx.moveTo(point._view.x, point._view.y);
return;
}
// Normal Bezier Curve
if (vm.tension > 0) {
ctx.bezierCurveTo(
previous._view.controlPointNextX,
previous._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
} else {
ctx.lineTo(point._view.x, point._view.y);
}
}, this);
if (previous._view.skip && vm.skipNull) {
ctx.moveTo(point._view.x, point._view.y);
return;
}
// Normal Bezier Curve
if (vm.tension > 0) {
ctx.bezierCurveTo(
previous._view.controlPointNextX,
previous._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
} else {
ctx.lineTo(point._view.x, point._view.y);
}
}, this);
if (this._loop && !first._view.skip) {
if (vm.tension > 0) {
if (this._loop && !first._view.skip) {
if (vm.tension > 0) {
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
first._view.controlPointPreviousX,
first._view.controlPointPreviousY,
first._view.x,
first._view.y
);
} else {
ctx.lineTo(first._view.x, first._view.y);
}
}
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
first._view.controlPointPreviousX,
first._view.controlPointPreviousY,
first._view.x,
first._view.y
);
} else {
ctx.lineTo(first._view.x, first._view.y);
}
}
ctx.stroke();
ctx.stroke();
},
nextPoint: function(point, collection, index) {
if (this.loop) {
return collection[index + 1] || collection[0];
}
return collection[index + 1] || collection[collection.length - 1];
},
previousPoint: function(point, collection, index) {
if (this.loop) {
return collection[index - 1] || collection[collection.length - 1];
}
return collection[index - 1] || collection[0];
},
});
},
nextPoint: function(point, collection, index) {
if (this.loop) {
return collection[index + 1] || collection[0];
}
return collection[index + 1] || collection[collection.length - 1];
},
previousPoint: function(point, collection, index) {
if (this.loop) {
return collection[index - 1] || collection[collection.length - 1];
}
return collection[index - 1] || collection[0];
},
});
}).call(this);

View File

@ -11,74 +11,74 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.elements.point = {
radius: 3,
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
// Hover
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
};
Chart.defaults.global.elements.point = {
radius: 3,
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
// Hover
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1,
};
Chart.Point = Chart.Element.extend({
inRange: function(mouseX, mouseY) {
var vm = this._view;
var hoverRange = vm.hitRadius + vm.radius;
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
},
inGroupRange: function(mouseX) {
var vm = this._view;
Chart.Point = Chart.Element.extend({
inRange: function(mouseX, mouseY) {
var vm = this._view;
var hoverRange = vm.hitRadius + vm.radius;
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
},
inGroupRange: function(mouseX) {
var vm = this._view;
if (vm) {
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2));
} else {
return false;
}
},
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
y: vm.y,
padding: vm.radius + vm.borderWidth
};
},
draw: function() {
if (vm) {
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2));
} else {
return false;
}
},
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
y: vm.y,
padding: vm.radius + vm.borderWidth
};
},
draw: function() {
var vm = this._view;
var ctx = this._chart.ctx;
var vm = this._view;
var ctx = this._chart.ctx;
if (vm.skip) {
return;
}
if (vm.skip) {
return;
}
if (vm.radius > 0 || vm.borderWidth > 0) {
if (vm.radius > 0 || vm.borderWidth > 0) {
ctx.beginPath();
ctx.beginPath();
ctx.arc(vm.x, vm.y, vm.radius || Chart.defaults.global.elements.point.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.arc(vm.x, vm.y, vm.radius || Chart.defaults.global.elements.point.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.point.borderWidth;
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.point.borderWidth;
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
ctx.fill();
ctx.stroke();
}
}
});
ctx.fill();
ctx.stroke();
}
}
});
}).call(this);

View File

@ -11,85 +11,85 @@
(function() {
"use strict";
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
Chart.defaults.global.elements.rectangle = {
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 0,
borderColor: Chart.defaults.global.defaultColor,
};
Chart.defaults.global.elements.rectangle = {
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 0,
borderColor: Chart.defaults.global.defaultColor,
};
Chart.Rectangle = Chart.Element.extend({
draw: function() {
Chart.Rectangle = Chart.Element.extend({
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var ctx = this._chart.ctx;
var vm = this._view;
var halfWidth = vm.width / 2,
leftX = vm.x - halfWidth,
rightX = vm.x + halfWidth,
top = vm.base - (vm.base - vm.y),
halfStroke = vm.borderWidth / 2;
var halfWidth = vm.width / 2,
leftX = vm.x - halfWidth,
rightX = vm.x + halfWidth,
top = vm.base - (vm.base - vm.y),
halfStroke = vm.borderWidth / 2;
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if (vm.borderWidth) {
leftX += halfStroke;
rightX -= halfStroke;
top += halfStroke;
}
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if (vm.borderWidth) {
leftX += halfStroke;
rightX -= halfStroke;
top += halfStroke;
}
ctx.beginPath();
ctx.beginPath();
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
// It'd be nice to keep this class totally generic to any rectangle
// and simply specify which border to miss out.
ctx.moveTo(leftX, vm.base);
ctx.lineTo(leftX, top);
ctx.lineTo(rightX, top);
ctx.lineTo(rightX, vm.base);
ctx.fill();
if (vm.borderWidth) {
ctx.stroke();
}
},
height: function() {
var vm = this._view;
return vm.base - vm.y;
},
inRange: function(mouseX, mouseY) {
var vm = this._view;
if (vm.y < vm.base) {
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
} else {
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
}
},
inGroupRange: function(mouseX) {
var vm = this._view;
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
},
tooltipPosition: function() {
var vm = this._view;
if (vm.y < vm.base) {
return {
x: vm.x,
y: vm.y
};
} else {
return {
x: vm.x,
y: vm.base
};
}
},
});
// It'd be nice to keep this class totally generic to any rectangle
// and simply specify which border to miss out.
ctx.moveTo(leftX, vm.base);
ctx.lineTo(leftX, top);
ctx.lineTo(rightX, top);
ctx.lineTo(rightX, vm.base);
ctx.fill();
if (vm.borderWidth) {
ctx.stroke();
}
},
height: function() {
var vm = this._view;
return vm.base - vm.y;
},
inRange: function(mouseX, mouseY) {
var vm = this._view;
if (vm.y < vm.base) {
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
} else {
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
}
},
inGroupRange: function(mouseX) {
var vm = this._view;
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
},
tooltipPosition: function() {
var vm = this._view;
if (vm.y < vm.base) {
return {
x: vm.x,
y: vm.y
};
} else {
return {
x: vm.x,
y: vm.base
};
}
},
});
}).call(this);