text: /*
   * --------------------------------------------------------------------
   * 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; i<cols; i++){
  							dataGroups[i] = {};
  							dataGroups[i].points = [];
  							dataGroups[i].color = colors[i];
  							if(textColors[i]){ dataGroups[i].textColor = textColors[i]; }
  							self.find('tr:gt(0)').filter(o.rowFilter).each(function(){
  								dataGroups[i].points.push( $(this).find('td').filter(o.colFilter).eq(i).text()*1 );
  							});
  						};
  					}
  					return dataGroups;
  				},
  				allData: function(){
  					var allData = [];
  					$(this.dataGroups()).each(function(){
  						allData.push(this.points);
  					});
  					return allData;
  				},
  				dataSum: function(){
  					var dataSum = 0;
  					var allData = this.allData().join(',').split(',');
  					$(allData).each(function(){
  						dataSum += parseFloat(this);
  					});
  					return dataSum
  				},	
  				topValue: function(){
  						var topValue = 0;
  						var allData = this.allData().join(',').split(',');
  						$(allData).each(function(){
  							if(parseFloat(this,10)>topValue) topValue = parseFloat(this);
  						});
  						return topValue;
  				},
  				bottomValue: function(){
  						var bottomValue = 0;
  						var allData = this.allData().join(',').split(',');
  						$(allData).each(function(){
  							if(this<bottomValue) bottomValue = parseFloat(this);
  						});
  						return bottomValue;
  				},
  				memberTotals: function(){
  					var memberTotals = [];
  					var dataGroups = this.dataGroups();
  					$(dataGroups).each(function(l){
  						var count = 0;
  						$(dataGroups[l].points).each(function(m){
  							count +=dataGroups[l].points[m];
  						});
  						memberTotals.push(count);
  					});
  					return memberTotals;
  				},
  				yTotals: function(){
  					var yTotals = [];
  					var dataGroups = this.dataGroups();
  					var loopLength = this.xLabels().length;
  					for(var i = 0; i<loopLength; i++){
  						yTotals[i] =[];
  						var thisTotal = 0;
  						$(dataGroups).each(function(l){
  							yTotals[i].push(this.points[i]);
  						});
  						yTotals[i].join(',').split(',');
  						$(yTotals[i]).each(function(){
  							thisTotal += parseFloat(this);
  						});
  						yTotals[i] = thisTotal;
  						
  					}
  					return yTotals;
  				},
  				topYtotal: function(){
  					var topYtotal = 0;
  						var yTotals = this.yTotals().join(',').split(',');
  						$(yTotals).each(function(){
  							if(parseFloat(this,10)>topYtotal) 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 = $('<ul class="visualize-labels"></ul>')
  					.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 = $('<span class="visualize-label">' + labelval +'</span>')
  				        	.css(leftRight, 0)
  				        	.css(topBottom, 0);
  				        	if(labeltext)
  				        var label = $('<li class="visualize-label-pos"></li>')
  				       			.appendTo(labels)
  				        		.css({left: labelx, top: labely})
  				        		.append(labeltext);	
  				        labeltext
  				        	.css('font-size', radius / 8)		
  				        	.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 = $('<ul class="visualize-labels-x"></ul>')
  					.width(canvas.width())
  					.height(canvas.height())
  					.insertBefore(canvas);
  				$.each(xLabels, function(i){ 
  					var thisLi = $('<li><span>'+this+'</span></li>')
  						.prepend('<span class="line" />')
  						.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 = $('<ul class="visualize-labels-y"></ul>')
  					.width(canvas.width())
  					.height(canvas.height())
  					.insertBefore(canvas);
  					
  				$.each(yLabels, function(i){  
  					var thisLi = $('<li><span>'+this+'</span></li>')
  						.prepend('<span class="line"  />')
  						.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 = $('<ul class="visualize-labels-x"></ul>')
  					.width(canvas.width())
  					.height(canvas.height())
  					.insertBefore(canvas);
  				$.each(xLabels, function(i){ 
  					var thisLi = $('<li><span class="label">'+this+'</span></li>')
  						.prepend('<span class="line" />')
  						.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 = $('<ul class="visualize-labels-y"></ul>')
  					.width(canvas.width())
  					.height(canvas.height())
  					.insertBefore(canvas);
  				$.each(yLabels, function(i){  
  					var thisLi = $('<li><span>'+this+'</span></li>')
  						.prepend('<span class="line"  />')
  						.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<dataGroups.length; h++){
  					ctx.beginPath();
  					var linewidth = (xInterval-o.barGroupMargin*2) / dataGroups.length; //removed +1 
  					var strokeWidth = linewidth - (o.barMargin*2);
  					ctx.lineWidth = strokeWidth;
  					var points = dataGroups[h].points;
  					var integer = 0;
  					for(var i=0; i<points.length; i++){
  						var xVal = (integer-o.barGroupMargin)+(h*linewidth)+linewidth/2;
  						xVal += o.barGroupMargin*2;
  						
  						ctx.moveTo(xVal, 0);
  						ctx.lineTo(xVal, Math.round(-points[i]*yScale));
  						integer+=xInterval;
  					}
  					ctx.strokeStyle = dataGroups[h].color;
  					ctx.stroke();
  					ctx.closePath();
  				}
  			}
  		};
  	
  		//create new canvas, set w&h attrs (not inline styles)
  		var canvasNode = document.createElement("canvas"); 
  		canvasNode.setAttribute('height',o.height);
  		canvasNode.setAttribute('width',o.width);
  		var canvas = $(canvasNode);
  			
  		//get title for chart
  		var title = o.title || self.find('caption').text();
  		
  		//create canvas wrapper div, set inline w&h, append
  		var canvasContain = (container || $('<div class="visualize" role="img" aria-label="Chart representing data from the table: '+ title +'" />'))
  			.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 = $('<div class="visualize-info"></div>')
  				.appendTo(canvasContain);
  		}
  		
  		//append title
  		if(o.appendTitle){
  			$('<div class="visualize-title">'+ title +'</div>').appendTo(infoContain);
  		}
  		
  		
  		//append key
  		if(o.appendKey){
  			var newKey = $('<ul class="visualize-key"></ul>');
  			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){
  				$('<li><span class="visualize-key-color" style="background: '+dataGroups[i].color+'"></span><span class="visualize-key-label">'+ $(this).text() +'</span></li>')
  					.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);
  
  
  