code style/openspending
[contractdashboard.git] / lib / flot / jquery.flot.pie.js
blob:a/lib/flot/jquery.flot.pie.js -> blob:b/lib/flot/jquery.flot.pie.js
--- a/lib/flot/jquery.flot.pie.js
+++ b/lib/flot/jquery.flot.pie.js
@@ -1,751 +1,751 @@
-/*

-Flot plugin for rendering pie charts. The plugin assumes the data is 

-coming is as a single data value for each series, and each of those 

-values is a positive value or zero (negative numbers don't make 

-any sense and will cause strange effects). The data values do 

-NOT need to be passed in as percentage values because it 

-internally calculates the total and percentages.

-

-* Created by Brian Medendorp, June 2009

-* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars

-

-* Changes:

-	2009-10-22: lineJoin set to round

-	2009-10-23: IE full circle fix, donut

-	2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera

-	2009-11-17: Added IE hover capability submitted by Anthony Aragues

-	2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)

-		

-

-Available options are:

-series: {

-	pie: {

-		show: true/false

-		radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'

-		innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect

-		startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result

-		tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)

-		offset: {

-			top: integer value to move the pie up or down

-			left: integer value to move the pie left or right, or 'auto'

-		},

-		stroke: {

-			color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')

-			width: integer pixel width of the stroke

-		},

-		label: {

-			show: true/false, or 'auto'

-			formatter:  a user-defined function that modifies the text/style of the label text

-			radius: 0-1 for percentage of fullsize, or a specified pixel length

-			background: {

-				color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')

-				opacity: 0-1

-			},

-			threshold: 0-1 for the percentage value at which to hide labels (if they're too small)

-		},

-		combine: {

-			threshold: 0-1 for the percentage value at which to combine slices (if they're too small)

-			color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined

-			label: any text value of what the combined slice should be labeled

-		}

-		highlight: {

-			opacity: 0-1

-		}

-	}

-}

-

-More detail and specific examples can be found in the included HTML file.

-

-*/

-

-(function ($) 

-{

-	function init(plot) // this is the "body" of the plugin

-	{

-		var canvas = null;

-		var target = null;

-		var maxRadius = null;

-		var centerLeft = null;

-		var centerTop = null;

-		var total = 0;

-		var redraw = true;

-		var redrawAttempts = 10;

-		var shrink = 0.95;

-		var legendWidth = 0;

-		var processed = false;

-		var raw = false;

-		

-		// interactive variables	

-		var highlights = [];	

-	

-		// add hook to determine if pie plugin in enabled, and then perform necessary operations

-		plot.hooks.processOptions.push(checkPieEnabled);

-		plot.hooks.bindEvents.push(bindEvents);	

-

-		// check to see if the pie plugin is enabled

-		function checkPieEnabled(plot, options)

-		{

-			if (options.series.pie.show)

-			{

-				//disable grid

-				options.grid.show = false;

-				

-				// set labels.show

-				if (options.series.pie.label.show=='auto')

-					if (options.legend.show)

-						options.series.pie.label.show = false;

-					else

-						options.series.pie.label.show = true;

-				

-				// set radius

-				if (options.series.pie.radius=='auto')

-					if (options.series.pie.label.show)

-						options.series.pie.radius = 3/4;

-					else

-						options.series.pie.radius = 1;

-						

-				// ensure sane tilt

-				if (options.series.pie.tilt>1)

-					options.series.pie.tilt=1;

-				if (options.series.pie.tilt<0)

-					options.series.pie.tilt=0;

-			

-				// add processData hook to do transformations on the data

-				plot.hooks.processDatapoints.push(processDatapoints);

-				plot.hooks.drawOverlay.push(drawOverlay);	

-				

-				// add draw hook

-				plot.hooks.draw.push(draw);

-			}

-		}

-	

-		// bind hoverable events

-		function bindEvents(plot, eventHolder) 		

-		{		

-			var options = plot.getOptions();

-			

-			if (options.series.pie.show && options.grid.hoverable)

-				eventHolder.unbind('mousemove').mousemove(onMouseMove);

-				

-			if (options.series.pie.show && options.grid.clickable)

-				eventHolder.unbind('click').click(onClick);

-		}	

-		

-

-		// debugging function that prints out an object

-		function alertObject(obj)

-		{

-			var msg = '';

-			function traverse(obj, depth)

-			{

-				if (!depth)

-					depth = 0;

-				for (var i = 0; i < obj.length; ++i)

-				{

-					for (var j=0; j<depth; j++)

-						msg += '\t';

-				

-					if( typeof obj[i] == "object")

-					{	// its an object

-						msg += ''+i+':\n';

-						traverse(obj[i], depth+1);

-					}

-					else

-					{	// its a value

-						msg += ''+i+': '+obj[i]+'\n';

-					}

-				}

-			}

-			traverse(obj);

-			alert(msg);

-		}

-		

-		function calcTotal(data)

-		{

-			for (var i = 0; i < data.length; ++i)

-			{

-				var item = parseFloat(data[i].data[0][1]);

-				if (item)

-					total += item;

-			}

-		}	

-		

-		function processDatapoints(plot, series, data, datapoints) 

-		{	

-			if (!processed)

-			{

-				processed = true;

-			

-				canvas = plot.getCanvas();

-				target = $(canvas).parent();

-				options = plot.getOptions();

-			

-				plot.setData(combine(plot.getData()));

-			}

-		}

-		

-		function setupPie()

-		{

-			legendWidth = target.children().filter('.legend').children().width();

-		

-			// calculate maximum radius and center point

-			maxRadius =  Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;

-			centerTop = (canvas.height/2)+options.series.pie.offset.top;

-			centerLeft = (canvas.width/2);

-			

-			if (options.series.pie.offset.left=='auto')

-				if (options.legend.position.match('w'))

-					centerLeft += legendWidth/2;

-				else

-					centerLeft -= legendWidth/2;

-			else

-				centerLeft += options.series.pie.offset.left;

-					

-			if (centerLeft<maxRadius)

-				centerLeft = maxRadius;

-			else if (centerLeft>canvas.width-maxRadius)

-				centerLeft = canvas.width-maxRadius;

-		}

-		

-		function fixData(data)

-		{

-			for (var i = 0; i < data.length; ++i)

-			{

-				if (typeof(data[i].data)=='number')

-					data[i].data = [[1,data[i].data]];

-				else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')

-				{

-					if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')

-						data[i].label = data[i].data.label; // fix weirdness coming from flot

-					data[i].data = [[1,0]];

-					

-				}

-			}

-			return data;

-		}

-		

-		function combine(data)

-		{

-			data = fixData(data);

-			calcTotal(data);

-			var combined = 0;

-			var numCombined = 0;

-			var color = options.series.pie.combine.color;

-			

-			var newdata = [];

-			for (var i = 0; i < data.length; ++i)

-			{

-				// make sure its a number

-				data[i].data[0][1] = parseFloat(data[i].data[0][1]);

-				if (!data[i].data[0][1])

-					data[i].data[0][1] = 0;

-					

-				if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)

-				{

-					combined += data[i].data[0][1];

-					numCombined++;

-					if (!color)

-						color = data[i].color;

-				}				

-				else

-				{

-					newdata.push({

-						data: [[1,data[i].data[0][1]]], 

-						color: data[i].color, 

-						label: data[i].label,

-						angle: (data[i].data[0][1]*(Math.PI*2))/total,

-						percent: (data[i].data[0][1]/total*100)

-					});

-				}

-			}

-			if (numCombined>0)

-				newdata.push({

-					data: [[1,combined]], 

-					color: color, 

-					label: options.series.pie.combine.label,

-					angle: (combined*(Math.PI*2))/total,

-					percent: (combined/total*100)

-				});

-			return newdata;

-		}		

-		

-		function draw(plot, newCtx)

-		{

-			if (!target) return; // if no series were passed

-			ctx = newCtx;

-		

-			setupPie();

-			var slices = plot.getData();

-		

-			var attempts = 0;

-			while (redraw && attempts<redrawAttempts)

-			{

-				redraw = false;

-				if (attempts>0)

-					maxRadius *= shrink;

-				attempts += 1;

-				clear();

-				if (options.series.pie.tilt<=0.8)

-					drawShadow();

-				drawPie();

-			}

-			if (attempts >= redrawAttempts) {

-				clear();

-				target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>');

-			}

-			

-			if ( plot.setSeries && plot.insertLegend )

-			{

-				plot.setSeries(slices);

-				plot.insertLegend();

-			}

-			

-			// we're actually done at this point, just defining internal functions at this point

-			

-			function clear()

-			{

-				ctx.clearRect(0,0,canvas.width,canvas.height);

-				target.children().filter('.pieLabel, .pieLabelBackground').remove();

-			}

-			

-			function drawShadow()

-			{

-				var shadowLeft = 5;

-				var shadowTop = 15;

-				var edge = 10;

-				var alpha = 0.02;

-			

-				// set radius

-				if (options.series.pie.radius>1)

-					var radius = options.series.pie.radius;

-				else

-					var radius = maxRadius * options.series.pie.radius;

-					

-				if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)

-					return;	// shadow would be outside canvas, so don't draw it

-			

-				ctx.save();

-				ctx.translate(shadowLeft,shadowTop);

-				ctx.globalAlpha = alpha;

-				ctx.fillStyle = '#000';

-

-				// center and rotate to starting position

-				ctx.translate(centerLeft,centerTop);

-				ctx.scale(1, options.series.pie.tilt);

-				

-				//radius -= edge;

-				for (var i=1; i<=edge; i++)

-				{

-					ctx.beginPath();

-					ctx.arc(0,0,radius,0,Math.PI*2,false);

-					ctx.fill();

-					radius -= i;

-				}	

-				

-				ctx.restore();

-			}

-			

-			function drawPie()

-			{

-				startAngle = Math.PI*options.series.pie.startAngle;

-				

-				// set radius

-				if (options.series.pie.radius>1)

-					var radius = options.series.pie.radius;

-				else

-					var radius = maxRadius * options.series.pie.radius;

-				

-				// center and rotate to starting position

-				ctx.save();

-				ctx.translate(centerLeft,centerTop);

-				ctx.scale(1, options.series.pie.tilt);

-				//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera

-				

-				// draw slices

-				ctx.save();

-				var currentAngle = startAngle;

-				for (var i = 0; i < slices.length; ++i)

-				{

-					slices[i].startAngle = currentAngle;

-					drawSlice(slices[i].angle, slices[i].color, true);

-				}

-				ctx.restore();

-				

-				// draw slice outlines

-				ctx.save();

-				ctx.lineWidth = options.series.pie.stroke.width;

-				currentAngle = startAngle;

-				for (var i = 0; i < slices.length; ++i)

-					drawSlice(slices[i].angle, options.series.pie.stroke.color, false);

-				ctx.restore();

-					

-				// draw donut hole

-				drawDonutHole(ctx);

-				

-				// draw labels

-				if (options.series.pie.label.show)

-					drawLabels();

-				

-				// restore to original state

-				ctx.restore();

-				

-				function drawSlice(angle, color, fill)

-				{	

-					if (angle<=0)

-						return;

-				

-					if (fill)

-						ctx.fillStyle = color;

-					else

-					{

-						ctx.strokeStyle = color;

-						ctx.lineJoin = 'round';

-					}

-						

-					ctx.beginPath();

-					if (Math.abs(angle - Math.PI*2) > 0.000000001)

-						ctx.moveTo(0,0); // Center of the pie

-					else if ($.browser.msie)

-						angle -= 0.0001;

-					//ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera

-					ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);

-					ctx.closePath();

-					//ctx.rotate(angle); // This doesn't work properly in Opera

-					currentAngle += angle;

-					

-					if (fill)

-						ctx.fill();

-					else

-						ctx.stroke();

-				}

-				

-				function drawLabels()

-				{

-					var currentAngle = startAngle;

-					

-					// set radius

-					if (options.series.pie.label.radius>1)

-						var radius = options.series.pie.label.radius;

-					else

-						var radius = maxRadius * options.series.pie.label.radius;

-					

-					for (var i = 0; i < slices.length; ++i)

-					{

-						if (slices[i].percent >= options.series.pie.label.threshold*100)

-							drawLabel(slices[i], currentAngle, i);

-						currentAngle += slices[i].angle;

-					}

-					

-					function drawLabel(slice, startAngle, index)

-					{

-						if (slice.data[0][1]==0)

-							return;

-							

-						// format label text

-						var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;

-						if (lf)

-							text = lf(slice.label, slice);

-						else

-							text = slice.label;

-						if (plf)

-							text = plf(text, slice);

-							

-						var halfAngle = ((startAngle+slice.angle) + startAngle)/2;

-						var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);

-						var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;

-						

-						var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>";

-						target.append(html);

-						var label = target.children('#pieLabel'+index);

-						var labelTop = (y - label.height()/2);

-						var labelLeft = (x - label.width()/2);

-						label.css('top', labelTop);

-						label.css('left', labelLeft);

-						

-						// check to make sure that the label is not outside the canvas

-						if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)

-							redraw = true;

-						

-						if (options.series.pie.label.background.opacity != 0) {

-							// put in the transparent background separately to avoid blended labels and label boxes

-							var c = options.series.pie.label.background.color;

-							if (c == null) {

-								c = slice.color;

-							}

-							var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';

-							$('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);

-						}

-					} // end individual label function

-				} // end drawLabels function

-			} // end drawPie function

-		} // end draw function

-		

-		// Placed here because it needs to be accessed from multiple locations 

-		function drawDonutHole(layer)

-		{

-			// draw donut hole

-			if(options.series.pie.innerRadius > 0)

-			{

-				// subtract the center

-				layer.save();

-				innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;

-				layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color

-				layer.beginPath();

-				layer.fillStyle = options.series.pie.stroke.color;

-				layer.arc(0,0,innerRadius,0,Math.PI*2,false);

-				layer.fill();

-				layer.closePath();

-				layer.restore();

-				

-				// add inner stroke

-				layer.save();

-				layer.beginPath();

-				layer.strokeStyle = options.series.pie.stroke.color;

-				layer.arc(0,0,innerRadius,0,Math.PI*2,false);

-				layer.stroke();

-				layer.closePath();

-				layer.restore();

-				// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.

-			}

-		}

-		

-		//-- Additional Interactive related functions --

-		

-		function isPointInPoly(poly, pt)

-		{

-			for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)

-				((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))

-				&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])

-				&& (c = !c);

-			return c;

-		}

-		

-		function findNearbySlice(mouseX, mouseY)

-		{

-			var slices = plot.getData(),

-				options = plot.getOptions(),

-				radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;

-			

-			for (var i = 0; i < slices.length; ++i) 

-			{

-				var s = slices[i];	

-				

-				if(s.pie.show)

-				{

-					ctx.save();

-					ctx.beginPath();

-					ctx.moveTo(0,0); // Center of the pie

-					//ctx.scale(1, options.series.pie.tilt);	// this actually seems to break everything when here.

-					ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);

-					ctx.closePath();

-					x = mouseX-centerLeft;

-					y = mouseY-centerTop;

-					if(ctx.isPointInPath)

-					{

-						if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))

-						{

-							//alert('found slice!');

-							ctx.restore();

-							return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};

-						}

-					}

-					else

-					{

-						// excanvas for IE doesn;t support isPointInPath, this is a workaround. 

-						p1X = (radius * Math.cos(s.startAngle));

-						p1Y = (radius * Math.sin(s.startAngle));

-						p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));

-						p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));

-						p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));

-						p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));

-						p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));

-						p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));

-						p5X = (radius * Math.cos(s.startAngle+s.angle));

-						p5Y = (radius * Math.sin(s.startAngle+s.angle));

-						arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];

-						arrPoint = [x,y];

-						// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?

-						if(isPointInPoly(arrPoly, arrPoint))

-						{

-							ctx.restore();

-							return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};

-						}			

-					}

-					ctx.restore();

-				}

-			}

-			

-			return null;

-		}

-

-		function onMouseMove(e) 

-		{

-			triggerClickHoverEvent('plothover', e);

-		}

-		

-        function onClick(e) 

-		{

-			triggerClickHoverEvent('plotclick', e);

-        }

-

-		// trigger click or hover event (they send the same parameters so we share their code)

-		function triggerClickHoverEvent(eventname, e) 

-		{

-			var offset = plot.offset(),

-				canvasX = parseInt(e.pageX - offset.left),

-				canvasY =  parseInt(e.pageY - offset.top),

-				item = findNearbySlice(canvasX, canvasY);

-			

-			if (options.grid.autoHighlight) 

-			{

-				// clear auto-highlights

-				for (var i = 0; i < highlights.length; ++i) 

-				{

-					var h = highlights[i];

-					if (h.auto == eventname && !(item && h.series == item.series))

-						unhighlight(h.series);

-				}

-			}

-			

-			// highlight the slice

-			if (item) 

-			    highlight(item.series, eventname);

-				

-			// trigger any hover bind events

-			var pos = { pageX: e.pageX, pageY: e.pageY };

-			target.trigger(eventname, [ pos, item ]);	

-		}

-

-		function highlight(s, auto) 

-		{

-			if (typeof s == "number")

-				s = series[s];

-

-			var i = indexOfHighlight(s);

-			if (i == -1) 

-			{

-				highlights.push({ series: s, auto: auto });

-				plot.triggerRedrawOverlay();

-			}

-			else if (!auto)

-				highlights[i].auto = false;

-		}

-

-		function unhighlight(s) 

-		{

-			if (s == null) 

-			{

-				highlights = [];

-				plot.triggerRedrawOverlay();

-			}

-			

-			if (typeof s == "number")

-				s = series[s];

-

-			var i = indexOfHighlight(s);

-			if (i != -1) 

-			{

-				highlights.splice(i, 1);

-				plot.triggerRedrawOverlay();

-			}

-		}

-

-		function indexOfHighlight(s) 

-		{

-			for (var i = 0; i < highlights.length; ++i) 

-			{

-				var h = highlights[i];

-				if (h.series == s)

-					return i;

-			}

-			return -1;

-		}

-

-		function drawOverlay(plot, octx) 

-		{

-			//alert(options.series.pie.radius);

-			var options = plot.getOptions();

-			//alert(options.series.pie.radius);

-			

-			var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;

-

-			octx.save();

-			octx.translate(centerLeft, centerTop);

-			octx.scale(1, options.series.pie.tilt);

-			

-			for (i = 0; i < highlights.length; ++i) 

-				drawHighlight(highlights[i].series);

-			

-			drawDonutHole(octx);

-

-			octx.restore();

-

-			function drawHighlight(series) 

-			{

-				if (series.angle < 0) return;

-				

-				//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();

-				octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor

-				

-				octx.beginPath();

-				if (Math.abs(series.angle - Math.PI*2) > 0.000000001)

-					octx.moveTo(0,0); // Center of the pie

-				octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);

-				octx.closePath();

-				octx.fill();

-			}

-			

-		}	

-		

-	} // end init (plugin body)

-	

-	// define pie specific options and their default values

-	var options = {

-		series: {

-			pie: {

-				show: false,

-				radius: 'auto',	// actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)

-				innerRadius:0, /* for donut */

-				startAngle: 3/2,

-				tilt: 1,

-				offset: {

-					top: 0,

-					left: 'auto'

-				},

-				stroke: {

-					color: '#FFF',

-					width: 1

-				},

-				label: {

-					show: 'auto',

-					formatter: function(label, slice){

-						return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';

-					},	// formatter function

-					radius: 1,	// radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)

-					background: {

-						color: null,

-						opacity: 0

-					},

-					threshold: 0	// percentage at which to hide the label (i.e. the slice is too narrow)

-				},

-				combine: {

-					threshold: -1,	// percentage at which to combine little slices into one larger slice

-					color: null,	// color to give the new slice (auto-generated if null)

-					label: 'Other'	// label to give the new slice

-				},

-				highlight: {

-					//color: '#FFF',		// will add this functionality once parseColor is available

-					opacity: 0.5

-				}

-			}

-		}

-	};

-    

-	$.plot.plugins.push({

-		init: init,

-		options: options,

-		name: "pie",

-		version: "1.0"

-	});

-})(jQuery);

-
+/*
+Flot plugin for rendering pie charts. The plugin assumes the data is 
+coming is as a single data value for each series, and each of those 
+values is a positive value or zero (negative numbers don't make 
+any sense and will cause strange effects). The data values do 
+NOT need to be passed in as percentage values because it 
+internally calculates the total and percentages.
+
+* Created by Brian Medendorp, June 2009
+* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
+
+* Changes:
+	2009-10-22: lineJoin set to round
+	2009-10-23: IE full circle fix, donut
+	2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera
+	2009-11-17: Added IE hover capability submitted by Anthony Aragues
+	2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)
+		
+
+Available options are:
+series: {
+	pie: {
+		show: true/false
+		radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
+		innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
+		startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
+		tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
+		offset: {
+			top: integer value to move the pie up or down
+			left: integer value to move the pie left or right, or 'auto'
+		},
+		stroke: {
+			color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
+			width: integer pixel width of the stroke
+		},
+		label: {
+			show: true/false, or 'auto'
+			formatter:  a user-defined function that modifies the text/style of the label text
+			radius: 0-1 for percentage of fullsize, or a specified pixel length
+			background: {
+				color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
+				opacity: 0-1
+			},
+			threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
+		},
+		combine: {
+			threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
+			color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
+			label: any text value of what the combined slice should be labeled
+		}
+		highlight: {
+			opacity: 0-1
+		}
+	}
+}
+
+More detail and specific examples can be found in the included HTML file.
+
+*/
+
+(function ($) 
+{
+	function init(plot) // this is the "body" of the plugin
+	{
+		var canvas = null;
+		var target = null;
+		var maxRadius = null;
+		var centerLeft = null;
+		var centerTop = null;
+		var total = 0;
+		var redraw = true;
+		var redrawAttempts = 10;
+		var shrink = 0.95;
+		var legendWidth = 0;
+		var processed = false;
+		var raw = false;
+		
+		// interactive variables	
+		var highlights = [];	
+	
+		// add hook to determine if pie plugin in enabled, and then perform necessary operations
+		plot.hooks.processOptions.push(checkPieEnabled);
+		plot.hooks.bindEvents.push(bindEvents);	
+
+		// check to see if the pie plugin is enabled
+		function checkPieEnabled(plot, options)
+		{
+			if (options.series.pie.show)
+			{
+				//disable grid
+				options.grid.show = false;
+				
+				// set labels.show
+				if (options.series.pie.label.show=='auto')
+					if (options.legend.show)
+						options.series.pie.label.show = false;
+					else
+						options.series.pie.label.show = true;
+				
+				// set radius
+				if (options.series.pie.radius=='auto')
+					if (options.series.pie.label.show)
+						options.series.pie.radius = 3/4;
+					else
+						options.series.pie.radius = 1;
+						
+				// ensure sane tilt
+				if (options.series.pie.tilt>1)
+					options.series.pie.tilt=1;
+				if (options.series.pie.tilt<0)
+					options.series.pie.tilt=0;
+			
+				// add processData hook to do transformations on the data
+				plot.hooks.processDatapoints.push(processDatapoints);
+				plot.hooks.drawOverlay.push(drawOverlay);	
+				
+				// add draw hook
+				plot.hooks.draw.push(draw);
+			}
+		}
+	
+		// bind hoverable events
+		function bindEvents(plot, eventHolder) 		
+		{		
+			var options = plot.getOptions();
+			
+			if (options.series.pie.show && options.grid.hoverable)
+				eventHolder.unbind('mousemove').mousemove(onMouseMove);
+				
+			if (options.series.pie.show && options.grid.clickable)
+				eventHolder.unbind('click').click(onClick);
+		}	
+		
+
+		// debugging function that prints out an object
+		function alertObject(obj)
+		{
+			var msg = '';
+			function traverse(obj, depth)
+			{
+				if (!depth)
+					depth = 0;
+				for (var i = 0; i < obj.length; ++i)
+				{
+					for (var j=0; j<depth; j++)
+						msg += '\t';
+				
+					if( typeof obj[i] == "object")
+					{	// its an object
+						msg += ''+i+':\n';
+						traverse(obj[i], depth+1);
+					}
+					else
+					{	// its a value
+						msg += ''+i+': '+obj[i]+'\n';
+					}
+				}
+			}
+			traverse(obj);
+			alert(msg);
+		}
+		
+		function calcTotal(data)
+		{
+			for (var i = 0; i < data.length; ++i)
+			{
+				var item = par