Last active
February 7, 2017 16:02
-
-
Save G3z/c525ec63d84089f628ae8375ca42a26a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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