in site/js/quokka.js [158:487]
function quokkaLines(id, titles, values, options, sums) {
var canvas = document.getElementById(id);
var ctx=canvas.getContext("2d");
// clear the canvas first
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.lineWidth = 0.25;
ctx.strokeStyle = "#000000";
var lwidth = 250;
var lheight = 75;
var rectwidth = canvas.width - lwidth - 50;
var stack = options ? options.stack : false;
var curve = options ? options.curve : false;
var title = options ? options.title : null;
var spots = options ? options.points : false;
var noX = options ? options.nox : false;
var verts = options ? options.verts : true;
if (noX) {
lheight = 0;
}
// Draw the stamp
base_image = new Image();
base_image.src = '/images/logo_large.png';
base_image.onload = function(){
ctx.globalAlpha = 0.04
ctx.drawImage(base_image, (canvas.width/2) - 128 - (lwidth/2), (canvas.height/2) - 128);
ctx.globalAlpha = 1
}
// calc rectwidth if titles are large
var nlwidth = 0
for (var k in titles) {
ctx.font="12px Arial";
ctx.fillStyle = "#00000";
var x = parseInt(k)
if (!noX) {
x = x + 1;
}
var sum = 0
for (var y in values) {
sum += values[y][x]
}
var t = titles[k] + (!options.nosum ? " (" + ((sums && sums[k]) ? sums[k] : sum.toFixed(0)) + ")" : "");
var w = ctx.measureText(t).width + 48;
if (w > lwidth && w > nlwidth) {
nlwidth = w
}
if (nlwidth > 0) {
rectwidth -= nlwidth - lwidth
lwidth = nlwidth
}
}
// Draw a border
ctx.lineWidth = 0.5;
ctx.strokeRect(35, 30, rectwidth, canvas.height - lheight - 40);
// Draw a title if set:
if (title != null) {
ctx.font="15px Arial";
ctx.fillStyle = "#00000";
ctx.textAlign = "center";
ctx.fillText(title,rectwidth/2, 15);
}
// Draw legend
ctx.textAlign = "left";
var posY = 50;
for (var k in titles) {
var x = parseInt(k)
if (!noX) {
x = x + 1;
}
var sum = 0
for (var y in values) {
sum += values[y][x]
}
var title = titles[k] + (!options.nosum ? " (" + ((sums && sums[k]) ? sums[k] : sum.toFixed(0)) + ")" : "");
ctx.fillStyle = colors[k % colors.length][0];
ctx.fillRect(40 + rectwidth + 35, posY-9, 10, 10);
// Add legend text
ctx.font="12px Arial";
ctx.fillStyle = "#00000";
ctx.fillText(title,canvas.width - lwidth + 40, posY);
posY += 15;
}
// Find max and min
var max = null;
var min = 0;
var stacked = null;
for (x in values) {
var s = 0;
for (y in values[x]) {
if (y > 0 || noX) {
s += values[x][y];
if (max == null || max < values[x][y]) {
max = values[x][y];
}
if (min == null || min > values[x][y]) {
min = values[x][y];
}
}
}
if (stacked == null || stacked < s) {
stacked = s;
}
}
if (stack) {
min = 0;
max = stacked;
}
// Set number of lines to draw and each step
var numLines = 5;
var step = (max-min) / (numLines+1);
// Prettify the max value so steps aren't ugly numbers
if (step %1 != 0) {
step = (Math.round(step+0.5));
max = step * (numLines+1);
}
// Draw horizontal lines
for (x = 0; x <= numLines; x++) {
var y = 30 + (((canvas.height-40-lheight) / (numLines+1)) * (x+1));
ctx.moveTo(35, y);
ctx.lineTo(35 + rectwidth, y);
ctx.lineWidth = 0.25;
ctx.stroke();
// Add values
ctx.font="10px Arial";
ctx.fillStyle = "#000000";
ctx.textAlign = "right";
ctx.fillText( Math.round( ((max-min) - (step*(x+1))) * 100 ) / 100,canvas.width - lwidth + 16, y-4);
ctx.fillText( Math.round( ((max-min) - (step*(x+1))) * 100 ) / 100,30, y-4);
}
// Draw vertical lines
var sx = 1
var numLines = values.length-1;
var step = (canvas.width - lwidth - 40) / values.length;
while (step < 24) {
step *= 2
sx *= 2
}
if (verts) {
ctx.beginPath();
for (var x = 1; x < values.length; x++) {
if (x % sx == 0) {
var y = 35 + (step * (x/sx));
ctx.moveTo(y, 30);
ctx.lineTo(y, canvas.height - 10 - lheight);
ctx.lineWidth = 0.25;
ctx.stroke();
}
}
}
// Some pre-calculations of steps
var step = (canvas.width - lwidth - 20) / (values.length+1);
var smallstep = (step / titles.length) - 2;
// Draw X values if noX isn't set:
if (noX != true) {
ctx.beginPath();
for (var i = 0; i < values.length; i++) {
smallstep = (step / (values[i].length-1)) - 2;
zz = 1
var x = 35 + ((step) * i);
var y = canvas.height - lheight + 5;
if (i % sx == 0) {
ctx.translate(x, y);
ctx.moveTo(0,0);
ctx.lineTo(0,-15);
ctx.stroke();
ctx.rotate(45*Math.PI/180);
ctx.textAlign = "left";
var val = values[i][0];
if (val.constructor.toString().match("Date()")) {
val = val.toDateString();
}
ctx.fillText(val.toString(), 0, 0);
ctx.rotate(-45*Math.PI/180);
ctx.translate(-x,-y);
}
}
}
// Draw each line
var stacks = [];
var pstacks = [];
for (k in values) { if (k > 0) { stacks[k] = 0; pstacks[k] = canvas.height - 40 - lheight; }}
for (k in titles) {
ctx.beginPath();
var color = colors[k % colors.length][0];
var f = parseInt(k) + 1;
if (noX) {
f = parseInt(k);
}
var value = values[0][f];
var step = rectwidth / numLines;
var x = 35;
var y = (canvas.height - 10 - lheight) - (((value-min) / (max-min)) * (canvas.height - 40 - lheight));
var py = y;
if (stack) {
stacks[0] = stacks[0] ? stacks[0] : 0
y -= stacks[0];
pstacks[0] = stacks[0];
stacks[0] += (((value-min) / (max-min)) * (canvas.height - 40 - lheight));
}
// Draw line
ctx.moveTo(x, y);
var pvalY = y;
var pvalX = x;
for (var i in values) {
if (i > 0) {
x = 35 + (step*i);
var f = parseInt(k) + 1;
if (noX == true) {
f = parseInt(k);
}
value = values[i][f];
y = (canvas.height - 10 - lheight) - (((value-min) / (max-min)) * (canvas.height - 40 - lheight));
if (stack) {
y -= stacks[i];
pstacks[i] = stacks[i];
stacks[i] += (((value-min) / (max-min)) * (canvas.height - 40- lheight));
}
// Draw curved lines??
/* We'll do: (x1,y1)-----(x1.5,y1)
* |
* (x1.5,y2)-----(x2,y2)
* with a quadratic beizer thingy
*/
if (curve) {
ctx.bezierCurveTo((pvalX + x) / 2, pvalY, (pvalX + x) / 2, y, x, y);
pvalX = x;
pvalY = y;
}
// Nope, just draw straight lines
else {
ctx.lineTo(x, y);
}
if (spots) {
ctx.fillStyle = color;
ctx.translate(x-2, y-2);
ctx.rotate(-45*Math.PI/180);
ctx.fillRect(-2,1,4,4);
ctx.rotate(45*Math.PI/180);
ctx.translate(-x+2, -y+2);
}
}
}
ctx.lineWidth = 2;
ctx.strokeStyle = color;
ctx.stroke();
// Draw stack area
if (stack) {
ctx.globalAlpha = 0.65;
var lastPoint = canvas.height - 40 - lheight;
for (i in values) {
if (i > 0) {
var f = parseInt(k) + 1;
if (noX == true) {
f = parseInt(k);
}
x = 35 + (step*i);
value = values[i][f];
y = (canvas.height - 10 - lheight) - (((value-min) / (max-min)) * (canvas.height - 40 - lheight));
y -= stacks[i];
lastPoint = pstacks[i];
}
}
var pvalY = y;
var pvalX = x;
for (i in values) {
var l = values.length - i - 1;
x = 35 + (step*l);
y = canvas.height - 10 - lheight - pstacks[l];
if (curve) {
ctx.bezierCurveTo((pvalX + x) / 2, pvalY, (pvalX + x) / 2, y, x, y);
pvalX = x;
pvalY = y;
}
else {
ctx.lineTo(x, y);
}
}
ctx.lineTo(35, py - pstacks[0]);
ctx.lineWidth = 0;
ctx.strokeStyle = colors[k % colors.length][0];
ctx.fillStyle = colors[k % colors.length][0];
ctx.fill();
ctx.fillStyle = "#000"
ctx.strokeStyle = "#000"
}
}
}