Skip to content

Instantly share code, notes, and snippets.

@G3z
Last active February 7, 2017 16:02
Show Gist options
  • Save G3z/c525ec63d84089f628ae8375ca42a26a to your computer and use it in GitHub Desktop.
Save G3z/c525ec63d84089f628ae8375ca42a26a to your computer and use it in GitHub Desktop.
module.exports = function(Chart) {
"use strict";
var helpers = Chart.helpers;
var defaultConfig = {
geometry: 'vertical', // horizontal/vertical
range: {
startValue: -150,
endValue: 300
}, // startValue/endValue
axisColor: '#c0c4cf',
axisWidth: 6, // set to 0 so no axis line is displayed
majorTicks: {},
minorTicks: {},
tickLabels: {},
scaleColorRanges: [],
dataset: {
value: 0,
indicator: 'range', // 'range' or 'point' indicator
shape: 'circle', // for point indicator available options - 'circle', 'rect', 'tringle'
width: 8,
height: 5, // for point indicator
offset: 10, // for range indicator from center of the axis line
color: '#51f40b',
colorRanges: [{
startpoint: 0,
breakpoint: 20,
color: '#6154ab'
}, {
startpoint: 20,
breakpoint: 70,
color: '#74f40b'
}, {
startpoint: 70,
breakpoint: 100,
color: '#fd0902'
}], // for the range, array with breakpoints and colors
tooltipRanges: [{
startpoint: 0,
breakpoint: 20,
tooltip: 'low'
}, {
startpoint: 20,
breakpoint: 70,
tooltip: 'normal'
}, {
startpoint: 70,
breakpoint: 100,
tooltip: 'high'
}],
img: '',
label: ''
},
padding: {
top: 0,
bottom: 0,
left: 0,
right: 0
},
multiTooltipTitles: 'Total'
};
// Linear guage scale class
Chart.LinearScale = Chart.Element.extend({
initialize: function() {
// Calculate size of lable
this.textDimention = function(val) {
var width = 0;
var height = this.fontSize;
var str = val + "";
width = (height / 1.5) * str.length;
return {
width: width,
height: height
};
};
// Prepare values for ticks
// Major ticks
if (typeof(this.majorTicks) == 'object' && this.majorTicks !== null && this.majorTicks.interval > 0) {
var majorVals = [];
if (this.majorTicks.customValues.length > 0) {
majorVals = this.majorTicks.customValues;
} else {
var interval = this.majorTicks.interval;
var numOfMajor = (this.range.endValue - this.range.startValue) / interval;
for (var i = 0; i < numOfMajor - 1; i++) {
majorVals.push(this.range.startValue + (interval * (i + 1)));
}
}
this.majorVals = majorVals;
}
// Minor ticks
if (typeof(this.minorTicks) == 'object' && this.minorTicks !== null && this.minorTicks.interval > 0) {
var minorVals = [];
if (this.minorTicks.customValues.length > 0) {
minorVals = this.minorTicks.customValues;
} else {
var interval = this.minorTicks.interval;
var numOfMinor = (this.range.endValue - this.range.startValue) / interval;
for (var i = 0; i < numOfMinor - 1; i++) {
minorVals.push(this.range.startValue + (interval * (i + 1)));
}
}
this.minorVals = minorVals;
}
// Labels of ticks
if (typeof(this.tickLabels) == 'object' && this.tickLabels !== null && this.tickLabels.interval > 0) {
var labelVals = [];
if (this.tickLabels.customValues.length > 0) {
labelVals = this.tickLabels.customValues;
} else {
var interval = this.tickLabels.interval;
var numOfLabels = ((this.range.endValue - this.range.startValue) / interval) + 1;
for (var i = 0; i < numOfLabels; i++) {
labelVals.push(this.range.startValue + (interval * i));
}
}
this.labelVals = labelVals;
}
// Horizontal orientation
if (this.geometry == 'horizontal') {
this.scalePoint = function(val) {
var displayW = this.width - this.padding.left - this.padding.right;
var rangeH = this.range.endValue - this.range.startValue;
var factor = displayW / rangeH;
return Math.round((val * factor) + this.padding.left - (this.range.startValue * factor));
};
this.base = this.height / 2; // center of chart located at the center of canvas
} else {
this.scalePoint = function(val) {
var displayH = this.height - this.padding.top - this.padding.bottom;
var rangeH = this.range.endValue - this.range.startValue;
var factor = displayH / rangeH;
return Math.round(this.height - (val * factor - (this.range.startValue * factor)) - this.padding.bottom);
};
this.base = this.width / 2; // center of chart located at the center of canvas
}
},
update: function(newProps) {
helpers.extend(this, newProps);
this.initialize();
},
draw: function() {
var ctx = this.ctx;
ctx.textBaseline = "alphabetic";
ctx.textAlign = "start";
// Horizontal orientation
if (this.geometry == 'horizontal') {
// Draw scale background
ctx.beginPath();
ctx.fillStyle = this.axisColor;
ctx.rect(this.padding.left, this.base - this.axisWidth / 2,
this.width - this.padding.left - this.padding.right, this.axisWidth);
ctx.fill();
ctx.closePath();
// Draw scale color ranges
if (typeof(this.scaleColorRanges) == 'object' && this.scaleColorRanges.length > 0) {
helpers.each(this.scaleColorRanges, function(d, ind) {
var width = this.scalePoint(d.end) - this.scalePoint(d.start);
var height = this.axisWidth;
ctx.beginPath();
ctx.fillStyle = d.color;
ctx.rect(
this.scalePoint(d.start),
this.base - (height / 2),
width,
height
);
ctx.fill();
}, this);
}
// Draw scale minor ticks
ctx.beginPath();
if (typeof(this.minorVals) == 'object' && this.minorVals.length > 0) {
ctx.fillStyle = this.minorTicks.color;
ctx.strokeStyle = this.minorTicks.color;
ctx.lineWidth = this.minorTicks.height;
for (var v = 0; v < this.minorVals.length; v++) {
var val = this.minorVals[v];
ctx.moveTo(this.scalePoint(val) - (this.minorTicks.height / 2),
this.base - (this.minorTicks.width / 2) + this.minorTicks.offset);
ctx.lineTo(this.scalePoint(val) - (this.minorTicks.height / 2), (this.base - (this.minorTicks.width / 2) + this.minorTicks.offset) + this.minorTicks.width);
ctx.stroke();
}
}
ctx.closePath();
// Draw scale major ticks
ctx.beginPath();
if (typeof(this.majorVals) == 'object' && this.majorVals.length > 0) {
ctx.fillStyle = this.majorTicks.color;
ctx.strokeStyle = this.majorTicks.color;
ctx.lineWidth = this.majorTicks.height;
for (var v = 0; v < this.majorVals.length; v++) {
var val = this.majorVals[v];
ctx.moveTo(this.scalePoint(val) - (this.majorTicks.height / 2),
this.base - (this.majorTicks.width / 2) + this.majorTicks.offset);
ctx.lineTo(this.scalePoint(val) - (this.majorTicks.height / 2), (this.base - (this.majorTicks.width / 2) + this.majorTicks.offset) + this.majorTicks.width);
ctx.stroke();
}
}
ctx.closePath();
// Draw scale labels
ctx.beginPath();
if (typeof(this.labelVals) == 'object' && this.labelVals.length > 0) {
ctx.fillStyle = this.tickLabels.color;
ctx.font = this.font;
for (var v = 0; v < this.labelVals.length; v++) {
var val = this.labelVals[v];
if (this.showLabels) {
var text = val + this.tickLabels.units;
ctx.fillText(text,
this.scalePoint(val) - this.textDimention(text).width / 2,
this.base + (this.tickLabels.offset > 0 ? 0 : this.textDimention(text).height) - this.tickLabels.offset
);
}
}
}
ctx.closePath();
} else {
// Draw scale background
ctx.beginPath();
ctx.fillStyle = this.axisColor;
ctx.rect(this.base - this.axisWidth / 2, this.padding.top, this.axisWidth, this.height - this.padding.top - this.padding.bottom);
ctx.fill();
ctx.closePath();
// Draw scale color ranges
if (typeof(this.scaleColorRanges) == 'object' && this.scaleColorRanges.length > 0) {
helpers.each(this.scaleColorRanges, function(d, ind) {
var width = this.axisWidth;
var height = this.scalePoint(d.start) - this.scalePoint(d.end);
ctx.beginPath();
ctx.fillStyle = d.color;
ctx.rect(
this.base - (width / 2),
this.scalePoint(d.end),
width,
height
);
ctx.fill();
}, this);
}
// Draw scale minor ticks
ctx.beginPath();
if (typeof(this.minorVals) == 'object' && this.minorVals.length > 0) {
ctx.fillStyle = this.minorTicks.color;
ctx.strokeStyle = this.minorTicks.color;
ctx.lineWidth = this.minorTicks.height;
for (var v = 0; v < this.minorVals.length; v++) {
var val = this.minorVals[v];
ctx.moveTo(this.base - (this.minorTicks.width / 2) + this.minorTicks.offset,
this.scalePoint(val) - (this.minorTicks.height / 2));
ctx.lineTo((this.base - (this.minorTicks.width / 2) + this.minorTicks.offset) + this.minorTicks.width,
this.scalePoint(val) - (this.minorTicks.height / 2));
ctx.stroke();
}
}
ctx.closePath();
// Draw scale major ticks
ctx.beginPath();
if (typeof(this.majorVals) == 'object' && this.majorVals.length > 0) {
ctx.fillStyle = this.majorTicks.color;
ctx.strokeStyle = this.majorTicks.color;
ctx.lineWidth = this.majorTicks.height;
for (var v = 0; v < this.majorVals.length; v++) {
var val = this.majorVals[v];
ctx.moveTo(this.base - (this.majorTicks.width / 2) + this.majorTicks.offset,
this.scalePoint(val) - (this.majorTicks.height / 2));
ctx.lineTo((this.base - (this.majorTicks.width / 2) + this.majorTicks.offset) + this.majorTicks.width,
this.scalePoint(val) - (this.majorTicks.height / 2));
ctx.stroke();
}
}
ctx.closePath();
// Draw scale labels
ctx.beginPath();
if (typeof(this.labelVals) == 'object' && this.labelVals.length > 0) {
ctx.fillStyle = this.tickLabels.color;
ctx.font = this.font;
for (var v = 0; v < this.labelVals.length; v++) {
var val = this.labelVals[v];
if (this.showLabels) {
var text = val + this.tickLabels.units;
ctx.fillText(text,
this.base - (this.tickLabels.offset > 0 ? 0 : this.textDimention(text).width) + this.tickLabels.offset,
this.scalePoint(val) + this.textDimention(text).height / 4
);
}
}
}
ctx.closePath();
}
}
});
Chart.Type.extend({
name: "Linear",
defaults: defaultConfig,
initialize: function(data) {
//Expose options as a scope variable here so we can access it in the ScaleClass
var options = this.options;
var padding = this.padding = {};
padding.top = this.options.scaleFontSize + this.options.padding.top;
padding.bottom = this.options.scaleFontSize + this.options.padding.bottom;
padding.left = this.options.scaleFontSize * 2 + this.options.padding.left;
padding.right = this.options.scaleFontSize * 2 + this.options.padding.right;
this.RangeClass = Chart.Element.extend({
strokeWidth: this.options.barStrokeWidth,
showStroke: this.options.barShowStroke,
ctx: this.chart.ctx,
rangeColorImage: null,
generateImage: function(colors, widths) {
var width = 0;
for (var i = 0; i < widths.length; i++)
width += widths[i];
var canvas = document.createElement('canvas'),
c = canvas.getContext('2d');
canvas.width = width;
canvas.height = 1;
var gw2 = widths[0];
var grd = c.createLinearGradient(0, 0, width, 0);
grd.addColorStop(0, colors[0]);
for (var k = 0; k < colors.length; k++) {
if ((k + 1) < colors.length) {
gw2 += widths[k + 1] / 2;
grd.addColorStop(gw2 / width, colors[k + 1]);
} else grd.addColorStop(1, colors[k]);
this.ctx.closePath();
if ((k + 1) < colors.length)
gw2 += widths[k + 1] / 2;
}
c.fillStyle = grd;
c.fillRect(0, 0, width, 20);
var imgd = c.getImageData(0, 0, canvas.width, 1);
return imgd;
},
getColor: function() {
var startColor = this.fillColor;
var out = 0;
var rc = 0;
var gc = 0;
var bc = 0;
var ac = 1;
// If image data did not filled yet
if (this.rangeColorImage === null) {
var colors = [];
var widths = [];
//colors.push(startColor);
helpers.each(this.colorRanges, function(cl, i) {
if (i === 0)
widths.push((cl.breakpoint - this.axisRange.startValue) * this.scaleValue);
else
widths.push((cl.breakpoint - this.colorRanges[i - 1].breakpoint) * this.scaleValue);
colors.push(cl.color);
}, this);
this.rangeColorImages = this.generateImage(colors, widths);
}
var start = this.axisRange.startValue;
var k = Math.ceil((this.value - start) * this.scaleValue);
rc = this.rangeColorImages.data[k * 4 + 0];
gc = this.rangeColorImages.data[k * 4 + 1];
bc = this.rangeColorImages.data[k * 4 + 2];
ac = this.rangeColorImages.data[k * 4 + 3];
return 'RGBA(' + rc + ', ' + gc + ', ' + bc + ', ' + ac + ')';
},
draw: function() {
var ctx = this.ctx,
leftX, leftY, rightX, rightY;
if (this.img !== null) {
if (this.geometry == 'horizontal') {
var x = this.leftX = this.startPoint + ((this.value - this.startValue) * this.scaleValue) - this.width / 2,
y = this.leftY = this.base - this.width + this.offset * (-1),
width = this.width,
height = this.height;
} else {
var y = this.leftX = this.startPoint - ((this.value - this.startValue) * this.scaleValue) - this.height / 2,
x = this.leftY = this.base + this.offset,
width = this.width,
height = this.height;
}
this.x = x + this.width / 2;
this.y = y + this.height / 2;
this.rightX = this.leftX + this.width;
this.rightY = this.leftY + this.height;
ctx.drawImage(this.img, 0, 0, this.img.width, this.img.height, x, y, width, height);
return;
}
var startColor = this.fillColor;
// Implement color ranges
if (typeof(this.colorRanges) == 'object' && this.colorRanges.length > 0) {
startColor = this.getColor();
}
ctx.beginPath();
ctx.fillStyle = startColor;
ctx.strokeStyle = this.strokeColor;
ctx.lineWidth = 1;
if (this.indicator == 'range') {
if (this.geometry == 'horizontal') {
leftX = this.leftX = this.startPoint;
leftY = this.leftY = this.base - (this.width / 2) + this.offset * (-1);
rightX = this.rightX = this.startPoint + ((this.value - this.startValue) * this.scaleValue);
rightY = this.rightY = this.base + (this.width / 2) + this.offset * (-1);
// tooltips position
this.x = rightX;
this.y = this.base - this.offset;
} else {
leftX = this.leftX = this.base - (this.width / 2) + this.offset;
leftY = this.leftY = this.startPoint;
rightX = this.rightX = this.base + (this.width / 2) + this.offset;
rightY = this.rightY = this.startPoint - ((this.value - this.startValue) * this.scaleValue);
// tooltips position
this.x = this.base + this.offset;
this.y = rightY;
}
ctx.moveTo(leftX, leftY);
ctx.lineTo(rightX, leftY);
ctx.lineTo(rightX, rightY);
ctx.lineTo(leftX, rightY);
}
if (this.indicator == 'point') {
if (this.shape == 'circle') {
if (this.geometry == 'horizontal') {
var x = this.x = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y = this.y = this.base - (this.height / 2) + this.offset * (-1),
r = this.width / 2,
sAngle = 0,
eAngle = helpers.radians(360),
counterclockwise = false;
} else {
var y = this.y = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x = this.x = this.base + (this.width / 2) + this.offset,
r = this.width / 2,
sAngle = 0,
eAngle = helpers.radians(360),
counterclockwise = false;
}
this.leftX = x - this.height / 2;
this.rightX = x + this.height / 2;
this.leftY = y - this.height / 2;
this.rightY = y + this.height / 2;
ctx.arc(x, y, r, sAngle, eAngle, counterclockwise);
}
if (this.shape == 'rect') {
if (this.geometry == 'horizontal') {
var x = this.leftX = this.startPoint + ((this.value - this.startValue) * this.scaleValue) - this.width / 2,
y = this.leftY = this.base - this.width + this.offset * (-1),
width = this.width,
height = this.height;
} else {
var y = this.leftY = this.startPoint - ((this.value - this.startValue) * this.scaleValue) - this.height / 2,
x = this.leftX = this.base + this.offset,
width = this.width,
height = this.height;
}
this.x = x + this.width / 2;
this.y = y + this.height / 2;
this.rightX = this.leftX + this.width;
this.rightY = this.leftY + this.height;
ctx.rect(x, y, width, height);
}
if (this.shape == 'triangle') {
if (this.geometry == 'horizontal') {
var x1 = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y1 = this.base + this.offset * (-1),
x2 = this.leftX = x1 - this.width / 2,
y2 = this.leftY = y1 - this.height,
x3 = x2 + this.width,
y3 = y2;
} else {
var y1 = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x1 = this.leftX = this.base + this.offset,
x2 = x1 + this.width,
y2 = this.leftY = y1 - this.height / 2,
x3 = x2,
y3 = y2 + this.height;
}
this.x = this.leftX + this.width / 2;
this.y = this.leftY + this.height / 2;
this.rightX = this.leftX + this.width;
this.rightY = this.leftY + this.height;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
}
if (this.shape == 'inverted-triangle') {
if (this.geometry == 'horizontal') {
var x1 = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y1 = this.leftY = this.base + this.offset * (-1) - this.height,
x2 = this.leftX = x1 - this.width / 2,
y2 = y1 + this.height,
x3 = x2 + this.width,
y3 = y2;
} else {
var y1 = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x1 = this.base + this.offset + this.width,
x2 = this.leftX = x1 - this.width,
y2 = this.leftY = y1 - this.height / 2,
x3 = x2,
y3 = y2 + this.height;
}
this.x = this.leftX + this.width / 2;
this.y = this.leftY + this.height / 2;
this.rightX = this.leftX + this.width;
this.rightY = this.leftY + this.height;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
}
if (this.shape == 'bowtie') {
if (this.geometry == 'horizontal') {
var local_height = this.height / 2;
var local_width = this.width;
var local_base = this.base - local_height;
var x1 = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y1 = this.leftY = local_base + this.offset * (-1) + local_height,
x2 = this.leftX = x1 - local_width / 2,
y2 = y1 + local_height,
x3 = x2 + local_width,
y3 = y2;
var x11 = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y11 = this.base + this.offset * (-1),
x21 = x11 - local_width / 2,
y21 = y11 - local_height,
x31 = x21 + local_width,
y31 = y21;
} else {
var local_width = this.width / 2;
var local_base = this.base - local_width;
var y1 = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x1 = local_base + this.offset + local_width,
x2 = this.leftX = x1 - local_width,
y2 = this.leftY = y1 - this.height / 2,
x3 = x2,
y3 = y2 + this.height;
var y11 = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x11 = this.base + this.offset,
x21 = x11 + local_width,
y21 = y11 - this.height / 2,
x31 = x21,
y31 = y21 + this.height;
}
this.x = this.leftX + this.width / 2;
this.y = this.leftY + this.height / 2;
this.rightX = this.leftX + this.width;
this.rightY = this.leftY + this.height;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.lineTo(x11, y11);
ctx.lineTo(x21, y21);
ctx.lineTo(x31, y31);
}
if (this.shape == 'diamond') {
if (this.geometry == 'horizontal') {
var local_height = this.height / 2;
var local_width = this.width;
var local_base = this.base - local_height;
var x1 = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y1 = this.leftY = local_base + this.offset * (-1),
x2 = this.leftX = x1 - local_width / 2,
y2 = y1 + local_height,
x3 = x2 + local_width,
y3 = y2;
var x11 = this.startPoint + ((this.value - this.startValue) * this.scaleValue),
y11 = this.base + this.offset * (-1) + local_height,
x21 = x11 - local_width / 2,
y21 = y11 - local_height,
x31 = x21 + local_width,
y31 = y21;
} else {
var local_width = this.width / 2;
var local_base = this.base - local_width;
var y1 = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x1 = this.base + this.offset + local_width,
x2 = x1 - local_width,
y2 = this.leftY = y1 - this.height / 2,
x3 = x2,
y3 = y2 + this.height;
var y11 = this.startPoint - ((this.value - this.startValue) * this.scaleValue),
x11 = this.leftX = local_base + this.offset,
x21 = x11 + local_width,
y21 = y11 - this.height / 2,
x31 = x21,
y31 = y21 + this.height;
}
this.x = this.leftX + this.width / 2;
this.y = this.leftY + this.height / 2;
this.rightX = this.leftX + this.width;
this.rightY = this.leftY + this.height;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.lineTo(x11, y11);
ctx.lineTo(x21, y21);
ctx.lineTo(x31, y31);
}
}
ctx.fill();
},
inRange: function(chartX, chartY) {
if (this.geometry == 'horizontal') {
return ((chartX >= this.leftX && chartX <= this.rightX) || (chartX >= this.rightX && chartX <= this.leftX)) &&
(chartY <= this.rightY && chartY >= this.leftY);
} else {
return (chartX >= this.leftX && chartX <= this.rightX) && ((chartY >= this.rightY && chartY <= this.leftY) ||
(chartY >= this.leftY && chartY <= this.rightY));
}
}
});
this.startPoint = options.range.startValue > 0 ? options.range.startValue : 0;
this.startPoint = options.range.endValue <= 0 ? options.range.startValue : this.startPoint;
this.startValue = this.startPoint;
this.scaleValue = 0;
this.base = 0;
if (options.geometry == 'horizontal') {
this.scaleValue = (this.chart.width - (this.padding.left + this.padding.right)) / (options.range.endValue - options.range.startValue);
this.startPoint = this.padding.left + (Math.abs(options.range.startValue * this.scaleValue) - (Math.abs(this.startPoint) * this.scaleValue));
this.base = this.chart.height / 2;
} else {
this.scaleValue = (this.chart.height - (this.padding.top + this.padding.bottom)) / (options.range.endValue - options.range.startValue);
this.startPoint = this.chart.height - (this.padding.bottom +
(Math.abs(options.range.startValue * this.scaleValue) - (Math.abs(this.startPoint) * this.scaleValue)));
this.base = this.chart.width / 2;
}
this.datasets = [];
//Iterate through each of the datasets, and build this into a property of the chart
if (typeof(data) !== 'undefined' && data !== null && data.length > 0) {
helpers.each(data, function(dataset, datasetIndex) {
this.addData(dataset);
}, this);
}
//Set up tooltip events on the chart
if (this.options.showTooltips) {
helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
helpers.each(this.datasets, function(data) {
data.bar.restore(['fillColor', 'strokeColor']);
});
helpers.each(activeBars, function(activeBar) {
activeBar.fillColor = activeBar.highlightFill;
activeBar.strokeColor = activeBar.highlightStroke;
});
//this.update();
this.showTooltip(activeBars);
});
}
this.buildScale();
this.render();
},
getBarsAtEvent: function(e) {
var barsArray = [],
eventPosition = helpers.getRelativePosition(e),
barIndex;
for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
if (this.datasets[datasetIndex].bar.inRange(eventPosition.x, eventPosition.y)) {
barsArray.push(this.datasets[datasetIndex].bar);
}
}
return barsArray;
},
buildScale: function() {
var o = this.options;
// Major ticks defaults
var majorTicks = {
interval: o.majorTicks.interval || 0,
customValues: o.majorTicks.customValues || [],
width: o.majorTicks.width || 6,
height: o.majorTicks.height || 0.5,
offset: o.majorTicks.offset || 0,
color: o.majorTicks.color || '#fff'
}
// Minor ticks defaults
var minorTicks = {
interval: o.minorTicks.interval || 0,
customValues: o.minorTicks.customValues || [],
width: o.minorTicks.width || 6,
height: o.minorTicks.height || 0.5,
offset: o.minorTicks.offset || 0,
color: o.minorTicks.color || '#fff'
}
// Tick labels defaults
var tickLabels = {
units: o.tickLabels.units || '',
interval: o.tickLabels.interval || 0,
customValues: o.tickLabels.customValues || [],
offset: o.tickLabels.offset || -9,
color: o.tickLabels.color || '#777b80'
}
var scaleOptions = {
templateString: o.scaleLabel,
height: this.chart.height,
width: this.chart.width,
ctx: this.chart.ctx,
textColor: o.scaleFontColor,
fontSize: o.scaleFontSize,
fontStyle: o.scaleFontStyle,
fontFamily: o.scaleFontFamily,
integersOnly: o.scaleIntegersOnly,
font: helpers.fontString(o.scaleFontSize, o.scaleFontStyle, o.scaleFontFamily),
padding: this.padding,
showLabels: o.scaleShowLabels,
display: o.showScale,
axisWidth: o.axisWidth,
axisColor: o.axisColor,
geometry: o.geometry,
majorTicks: majorTicks,
minorTicks: minorTicks,
tickLabels: tickLabels,
range: o.range,
scaleColorRanges: o.scaleColorRanges || []
}
this.scale = new Chart.LinearScale(scaleOptions);
},
update: function() {
var options = this.options;
this.startPoint = options.range.startValue > 0 ? options.range.startValue : 0;
this.startPoint = options.range.endValue <= 0 ? options.range.startValue : this.startPoint;
this.startValue = this.startPoint;
this.scaleValue = 0;
this.base = 0;
if (options.geometry == 'horizontal') {
this.scaleValue = (this.chart.width - (this.padding.left + this.padding.right)) / (options.range.endValue - options.range.startValue);
this.startPoint = this.padding.left + (Math.abs(options.range.startValue * this.scaleValue) - (Math.abs(this.startPoint) * this.scaleValue));
this.base = this.chart.height / 2;
} else {
this.scaleValue = (this.chart.height - (this.padding.top + this.padding.bottom)) / (options.range.endValue - options.range.startValue);
this.startPoint = this.chart.height - (this.padding.bottom +
(Math.abs(options.range.startValue * this.scaleValue) - (Math.abs(this.startPoint) * this.scaleValue)));
this.base = this.chart.width / 2;
}
this.scale.update();
var newBarProps = helpers.extend({
base: this.base,
scaleValue: this.scaleValue,
startPoint: this.startPoint
});
helpers.each(this.datasets, function(d) {
d.bar.update(newBarProps);
});
// Reset any highlight colours before updating.
helpers.each(this.activeElements, function(activeElement) {
activeElement.restore(['fillColor', 'strokeColor']);
});
helpers.each(this.datasets, function(d, datasetIndex) {
d.bar.save();
}, this);
this.render();
},
addData: function(dataset) {
var options = this.options;
var imbuffer = new Image();
if (typeof(dataset.img) !== 'undefined')
imbuffer.src = dataset.img;
var label = dataset.label || null,
fillColor = dataset.color || options.dataset.color,
strokeColor = dataset.color || options.dataset.color,
highlightFill = dataset.highlightFill || options.dataset.color,
highlightStroke = dataset.highlightStroke || options.dataset.color,
indicator = dataset.indicator || options.dataset.indicator,
shape = dataset.shape || options.dataset.shape,
width = typeof(dataset.width) !== 'undefined' ? dataset.width : options.dataset.width,
height = typeof(dataset.height) !== 'undefined' ? dataset.height : options.dataset.height,
offset = typeof(dataset.offset) !== 'undefined' ? dataset.offset : options.dataset.offset,
colorRanges = dataset.colorRanges || [],
tooltipRanges = dataset.tooltipRanges || [],
img = typeof(dataset.img) !== 'undefined' ? imbuffer : null;
var datasetObject = {
bar: new this.RangeClass({
label: label,
fillColor: fillColor,
strokeColor: strokeColor,
highlightFill: highlightFill,
highlightStroke: highlightStroke,
value: this.startValue,
startValue: this.startValue,
indicator: indicator,
shape: shape,
width: width,
height: height,
offset: offset,
colorRanges: colorRanges,
tooltipRanges: tooltipRanges,
img: img,
startPoint: this.startPoint,
scaleValue: this.scaleValue,
geometry: options.geometry,
axisRange: options.range,
base: this.base,
units: options.tickLabels.units || ''
}),
value: dataset.value
}
this.datasets.push(datasetObject);
},
removeData: function() {
this.datasets.shift();
this.update();
},
draw: function(ease) {
var self = this;
var easingDecimal = ease || 1;
this.clear();
var ctx = this.chart.ctx;
this.scale.draw(easingDecimal);
if (this.datasets.length > 0) {
helpers.each(this.datasets, function(data, datasetIndex) {
data.bar.transition({
value: data.value
}, easingDecimal).draw();
}, this);
}
},
reflow: function() {
var newScaleProps = helpers.extend({
height: this.chart.height,
width: this.chart.width
});
this.scale.update(newScaleProps);
this.update();
},
showTooltip: function(ChartElements, forceRedraw) {
// Overload original function
// Tooltip ranges implementation function
function rangeTooltip(templFunc, element) {
var tooltip = '';
var tooltipSet = false;
if (typeof(element.tooltipRanges) == 'object' && element.tooltipRanges.length && element.tooltipRanges.length > 0) {
for (var i = 0; i < element.tooltipRanges.length; i++) {
var r = element.tooltipRanges[i];
if (element.value >= r.startpoint && element.value < r.breakpoint) {
tooltip = r.tooltip;
tooltipSet = true;
break;
}
}
}
if (!tooltipSet) {
tooltip = helpers.template(templFunc, element) + element.units;
}
return tooltip;
}
// Only redraw the chart if we've actually changed what we're hovering on.
if (typeof this.activeElements === 'undefined') this.activeElements = [];
var isChanged = (function(Elements) {
var changed = false;
if (Elements.length !== this.activeElements.length) {
changed = true;
return changed;
}
helpers.each(Elements, function(element, index) {
if (element !== this.activeElements[index]) {
changed = true;
}
}, this);
return changed;
}).call(this, ChartElements);
if (!isChanged && !forceRedraw) {
return;
} else {
this.activeElements = ChartElements;
}
this.draw();
if (this.options.customTooltips) {
this.options.customTooltips(false);
}
if (ChartElements.length > 0) {
// If we have multiple datasets, show a MultiTooltip for all of the data points at that index
if (this.datasets && ChartElements.length > 1) {
var dataArray,
dataIndex;
var tooltipLabels = [],
tooltipColors = [],
medianPosition = (function() {
// Get all the points at that particular index
var Elements = [],
dataCollection,
xPositions = [],
yPositions = [],
xMax,
yMax,
xMin,
yMin;
helpers.each(ChartElements, function(chartElement) {
if (chartElement.hasValue()) {
Elements.push(chartElement);
}
}, this);
helpers.each(Elements, function(element) {
xPositions.push(element.x);
yPositions.push(element.y);
//Include any colour information about the element
tooltipLabels.push(rangeTooltip(this.options.multiTooltipTemplate, element));
tooltipColors.push({
fill: element._saved.fillColor || element.fillColor,
stroke: element._saved.strokeColor || element.strokeColor
});
}, this);
yMin = helpers.min(yPositions);
yMax = helpers.max(yPositions);
xMin = helpers.min(xPositions);
xMax = helpers.max(xPositions);
return {
x: (xMin > this.chart.width / 2) ? xMin : xMax,
y: (yMin + yMax) / 2
};
}).call(this);
new Chart.MultiTooltip({
x: medianPosition.x,
y: medianPosition.y,
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
xOffset: this.options.tooltipXOffset,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
titleTextColor: this.options.tooltipTitleFontColor,
titleFontFamily: this.options.tooltipTitleFontFamily,
titleFontStyle: this.options.tooltipTitleFontStyle,
titleFontSize: this.options.tooltipTitleFontSize,
cornerRadius: this.options.tooltipCornerRadius,
labels: tooltipLabels,
legendColors: tooltipColors,
legendColorBackground: this.options.multiTooltipKeyBackground,
title: this.options.multiTooltipTitles,
chart: this.chart,
ctx: this.chart.ctx,
custom: this.options.customTooltips
}).draw();
} else {
helpers.each(ChartElements, function(Element) {
var tooltipPosition = Element.tooltipPosition();
new Chart.Tooltip({
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
caretHeight: this.options.tooltipCaretSize,
cornerRadius: this.options.tooltipCornerRadius,
text: rangeTooltip(this.options.tooltipTemplate, Element),
chart: this.chart,
custom: this.options.customTooltips
}).draw();
}, this);
}
}
return this;
}
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment