/* * -------------------------------------------------------------------- * jQuery inputToButton plugin * Author: Scott Jehl, scott@filamentgroup.com * Copyright (c) 2009 Filament Group * licensed under MIT (filamentgroup.com/examples/mit-license.txt) * -------------------------------------------------------------------- */ (function($) { $.fn.visualize = function(options, container){ return $(this).each(function(){ //configuration var o = $.extend({ type: 'bar', //also available: area, pie, line width: $(this).width(), //height of canvas - defaults to table height height: $(this).height(), //height of canvas - defaults to table height appendTitle: true, //table caption text is added to chart title: null, //grabs from table caption if null appendKey: true, //color key is added to chart rowFilter: ' ', colFilter: ' ', colors: ['#be1e2d','#666699','#92d5ea','#ee8310','#8d10ee','#5a3b16','#26a4ed','#f45a90','#e9e744'], textColors: [], //corresponds with colors array. null/undefined items will fall back to CSS parseDirection: 'x', //which direction to parse the table data pieMargin: 20, //pie charts only - spacing around pie pieLabelsAsPercent: true, pieLabelPos: 'inside', lineWeight: 4, //for line and area - stroke weight barGroupMargin: 10, barMargin: 1, //space around bars in bar chart (added to both sides of bar) yLabelInterval: 30 //distance between y labels },options); //reset width, height to numbers o.width = parseFloat(o.width); o.height = parseFloat(o.height); var self = $(this); //function to scrape data from html table function scrapeTable(){ var colors = o.colors; var textColors = o.textColors; var tableData = { dataGroups: function(){ var dataGroups = []; if(o.parseDirection == 'x'){ self.find('tr:gt(0)').filter(o.rowFilter).each(function(i){ dataGroups[i] = {}; dataGroups[i].points = []; dataGroups[i].color = colors[i]; if(textColors[i]){ dataGroups[i].textColor = textColors[i]; } $(this).find('td').filter(o.colFilter).each(function(){ dataGroups[i].points.push( parseFloat($(this).text()) ); }); }); } else { var cols = self.find('tr:eq(1) td').filter(o.colFilter).size(); for(var i=0; itopValue) topValue = parseFloat(this); }); return topValue; }, bottomValue: function(){ var bottomValue = 0; var allData = this.allData().join(',').split(','); $(allData).each(function(){ if(thistopYtotal) topYtotal = parseFloat(this); }); return topYtotal; }, totalYRange: function(){ return this.topValue() - this.bottomValue(); }, xLabels: function(){ var xLabels = []; if(o.parseDirection == 'x'){ self.find('tr:eq(0) th').filter(o.colFilter).each(function(){ xLabels.push($(this).html()); }); } else { self.find('tr:gt(0) th').filter(o.rowFilter).each(function(){ xLabels.push($(this).html()); }); } return xLabels; }, yLabels: function(){ var yLabels = []; yLabels.push(bottomValue); var numLabels = Math.round(o.height / o.yLabelInterval); var loopInterval = Math.ceil(totalYRange / numLabels) || 1; while( yLabels[yLabels.length-1] < topValue - loopInterval){ yLabels.push(yLabels[yLabels.length-1] + loopInterval); } yLabels.push(topValue); return yLabels; } }; return tableData; }; //function to create a chart var createChart = { pie: function(){ canvasContain.addClass('visualize-pie'); if(o.pieLabelPos == 'outside'){ canvasContain.addClass('visualize-pie-outside'); } var centerx = Math.round(canvas.width()/2); var centery = Math.round(canvas.height()/2); var radius = centery - o.pieMargin; var counter = 0.0; var toRad = function(integer){ return (Math.PI/180)*integer; }; var labels = $('') .insertAfter(canvas); //draw the pie pieces $.each(memberTotals, function(i){ var fraction = (this <= 0 || isNaN(this))? 0 : this / dataSum; ctx.beginPath(); ctx.moveTo(centerx, centery); ctx.arc(centerx, centery, radius, counter * Math.PI * 2 - Math.PI * 0.5, (counter + fraction) * Math.PI * 2 - Math.PI * 0.5, false); ctx.lineTo(centerx, centery); ctx.closePath(); ctx.fillStyle = dataGroups[i].color; ctx.fill(); // draw labels var sliceMiddle = (counter + fraction/2); var distance = o.pieLabelPos == 'inside' ? radius/1.5 : radius + radius / 5; var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance)); var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance)); var leftRight = (labelx > centerx) ? 'right' : 'left'; var topBottom = (labely > centery) ? 'bottom' : 'top'; var percentage = parseFloat((fraction*100).toFixed(2)); if(percentage){ var labelval = (o.pieLabelsAsPercent) ? percentage + '%' : this; var labeltext = $('' + labelval +'') .css(leftRight, 0) .css(topBottom, 0); if(labeltext) var label = $('
  • ') .appendTo(labels) .css({left: labelx, top: labely}) .append(labeltext); labeltext .css('font-size', radius / 16) .css('margin-'+leftRight, -labeltext.width()/2) .css('margin-'+topBottom, -labeltext.outerHeight()/2); if(dataGroups[i].textColor){ labeltext.css('color', dataGroups[i].textColor); } } counter+=fraction; }); }, line: function(area){ if(area){ canvasContain.addClass('visualize-area'); } else{ canvasContain.addClass('visualize-line'); } //write X labels var xInterval = canvas.width() / (xLabels.length -1); var xlabelsUL = $('') .width(canvas.width()) .height(canvas.height()) .insertBefore(canvas); $.each(xLabels, function(i){ var thisLi = $('
  • '+this+'
  • ') .prepend('') .css('left', xInterval * i) .appendTo(xlabelsUL); var label = thisLi.find('span:not(.line)'); var leftOffset = label.width()/-2; if(i == 0){ leftOffset = 0; } else if(i== xLabels.length-1){ leftOffset = -label.width(); } label .css('margin-left', leftOffset) .addClass('label'); }); //write Y labels var yScale = canvas.height() / totalYRange; var liBottom = canvas.height() / (yLabels.length-1); var ylabelsUL = $('
      ') .width(canvas.width()) .height(canvas.height()) .insertBefore(canvas); $.each(yLabels, function(i){ var thisLi = $('
    • '+this+'
    • ') .prepend('') .css('bottom',liBottom*i) .prependTo(ylabelsUL); var label = thisLi.find('span:not(.line)'); var topOffset = label.height()/-2; if(i == 0){ topOffset = -label.height(); } else if(i== yLabels.length-1){ topOffset = 0; } label .css('margin-top', topOffset) .addClass('label'); }); //start from the bottom left ctx.translate(0,zeroLoc); //iterate and draw $.each(dataGroups,function(h){ ctx.beginPath(); ctx.lineWidth = o.lineWeight; ctx.lineJoin = 'round'; var points = this.points; var integer = 0; ctx.moveTo(0,-(points[0]*yScale)); $.each(points, function(){ ctx.lineTo(integer,-(this*yScale)); integer+=xInterval; }); ctx.strokeStyle = this.color; ctx.stroke(); if(area){ ctx.lineTo(integer,0); ctx.lineTo(0,0); ctx.closePath(); ctx.fillStyle = this.color; ctx.globalAlpha = .3; ctx.fill(); ctx.globalAlpha = 1.0; } else {ctx.closePath();} }); }, area: function(){ createChart.line(true); }, bar: function(){ canvasContain.addClass('visualize-bar'); //write X labels var xInterval = canvas.width() / (xLabels.length); var xlabelsUL = $('
        ') .width(canvas.width()) .height(canvas.height()) .insertBefore(canvas); $.each(xLabels, function(i){ var thisLi = $('
      • '+this+'
      • ') .prepend('') .css('left', xInterval * i) .width(xInterval) .appendTo(xlabelsUL); var label = thisLi.find('span.label'); label.addClass('label'); }); //write Y labels var yScale = canvas.height() / totalYRange; var liBottom = canvas.height() / (yLabels.length-1); var ylabelsUL = $('
          ') .width(canvas.width()) .height(canvas.height()) .insertBefore(canvas); $.each(yLabels, function(i){ var thisLi = $('
        • '+this+'
        • ') .prepend('') .css('bottom',liBottom*i) .prependTo(ylabelsUL); var label = thisLi.find('span:not(.line)'); var topOffset = label.height()/-2; if(i == 0){ topOffset = -label.height(); } else if(i== yLabels.length-1){ topOffset = 0; } label .css('margin-top', topOffset) .addClass('label'); }); //start from the bottom left ctx.translate(0,zeroLoc); //iterate and draw for(var h=0; h')) .height(o.height) .width(o.width) .append(canvas); //scrape table (this should be cleaned up into an obj) var tableData = scrapeTable(); var dataGroups = tableData.dataGroups(); var allData = tableData.allData(); var dataSum = tableData.dataSum(); var topValue = tableData.topValue(); var bottomValue = tableData.bottomValue(); var memberTotals = tableData.memberTotals(); var totalYRange = tableData.totalYRange(); var zeroLoc = o.height * (topValue/totalYRange); var xLabels = tableData.xLabels(); var yLabels = tableData.yLabels(); //title/key container if(o.appendTitle || o.appendKey){ var infoContain = $('
          ') .appendTo(canvasContain); } //append title if(o.appendTitle){ $('
          '+ title +'
          ').appendTo(infoContain); } //append key if(o.appendKey){ var newKey = $('
            '); var selector; if(o.parseDirection == 'x'){ selector = self.find('tr:gt(0) th').filter(o.rowFilter); } else{ selector = self.find('tr:eq(0) th').filter(o.colFilter); } selector.each(function(i){ $('
          • '+ $(this).text() +'
          • ') .appendTo(newKey); }); newKey.appendTo(infoContain); }; //append new canvas to page if(!container){canvasContain.insertAfter(this); } if( typeof(G_vmlCanvasManager) != 'undefined' ){ G_vmlCanvasManager.init(); G_vmlCanvasManager.initElement(canvas[0]); } //set up the drawing board var ctx = canvas[0].getContext('2d'); //create chart createChart[o.type](); //clean up some doubled lines that sit on top of canvas borders (done via JS due to IE) $('.visualize-line li:first-child span.line, .visualize-line li:last-child span.line, .visualize-area li:first-child span.line, .visualize-area li:last-child span.line, .visualize-bar li:first-child span.line,.visualize-bar .visualize-labels-y li:last-child span.line').css('border','none'); if(!container){ //add event for updating canvasContain.bind('visualizeRefresh', function(){ self.visualize(o, $(this).empty()); }); } }).next(); //returns canvas(es) }; })(jQuery);