function quokkaLines()

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"
        }

    }
}