Add new theme plus new icons
[flowspy] / static / b3theme / js / plugins / flot / jquery.flot.pie.js
diff --git a/static/b3theme/js/plugins/flot/jquery.flot.pie.js b/static/b3theme/js/plugins/flot/jquery.flot.pie.js
new file mode 100644 (file)
index 0000000..70941dd
--- /dev/null
@@ -0,0 +1,750 @@
+/*\r
+Flot plugin for rendering pie charts. The plugin assumes the data is \r
+coming is as a single data value for each series, and each of those \r
+values is a positive value or zero (negative numbers don't make \r
+any sense and will cause strange effects). The data values do \r
+NOT need to be passed in as percentage values because it \r
+internally calculates the total and percentages.\r
+\r
+* Created by Brian Medendorp, June 2009\r
+* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars\r
+\r
+* Changes:\r
+       2009-10-22: lineJoin set to round\r
+       2009-10-23: IE full circle fix, donut\r
+       2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera\r
+       2009-11-17: Added IE hover capability submitted by Anthony Aragues\r
+       2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)\r
+               \r
+\r
+Available options are:\r
+series: {\r
+       pie: {\r
+               show: true/false\r
+               radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'\r
+               innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect\r
+               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\r
+               tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)\r
+               offset: {\r
+                       top: integer value to move the pie up or down\r
+                       left: integer value to move the pie left or right, or 'auto'\r
+               },\r
+               stroke: {\r
+                       color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')\r
+                       width: integer pixel width of the stroke\r
+               },\r
+               label: {\r
+                       show: true/false, or 'auto'\r
+                       formatter:  a user-defined function that modifies the text/style of the label text\r
+                       radius: 0-1 for percentage of fullsize, or a specified pixel length\r
+                       background: {\r
+                               color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')\r
+                               opacity: 0-1\r
+                       },\r
+                       threshold: 0-1 for the percentage value at which to hide labels (if they're too small)\r
+               },\r
+               combine: {\r
+                       threshold: 0-1 for the percentage value at which to combine slices (if they're too small)\r
+                       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\r
+                       label: any text value of what the combined slice should be labeled\r
+               }\r
+               highlight: {\r
+                       opacity: 0-1\r
+               }\r
+       }\r
+}\r
+\r
+More detail and specific examples can be found in the included HTML file.\r
+\r
+*/\r
+\r
+(function ($) \r
+{\r
+       function init(plot) // this is the "body" of the plugin\r
+       {\r
+               var canvas = null;\r
+               var target = null;\r
+               var maxRadius = null;\r
+               var centerLeft = null;\r
+               var centerTop = null;\r
+               var total = 0;\r
+               var redraw = true;\r
+               var redrawAttempts = 10;\r
+               var shrink = 0.95;\r
+               var legendWidth = 0;\r
+               var processed = false;\r
+               var raw = false;\r
+               \r
+               // interactive variables        \r
+               var highlights = [];    \r
+       \r
+               // add hook to determine if pie plugin in enabled, and then perform necessary operations\r
+               plot.hooks.processOptions.push(checkPieEnabled);\r
+               plot.hooks.bindEvents.push(bindEvents); \r
+\r
+               // check to see if the pie plugin is enabled\r
+               function checkPieEnabled(plot, options)\r
+               {\r
+                       if (options.series.pie.show)\r
+                       {\r
+                               //disable grid\r
+                               options.grid.show = false;\r
+                               \r
+                               // set labels.show\r
+                               if (options.series.pie.label.show=='auto')\r
+                                       if (options.legend.show)\r
+                                               options.series.pie.label.show = false;\r
+                                       else\r
+                                               options.series.pie.label.show = true;\r
+                               \r
+                               // set radius\r
+                               if (options.series.pie.radius=='auto')\r
+                                       if (options.series.pie.label.show)\r
+                                               options.series.pie.radius = 3/4;\r
+                                       else\r
+                                               options.series.pie.radius = 1;\r
+                                               \r
+                               // ensure sane tilt\r
+                               if (options.series.pie.tilt>1)\r
+                                       options.series.pie.tilt=1;\r
+                               if (options.series.pie.tilt<0)\r
+                                       options.series.pie.tilt=0;\r
+                       \r
+                               // add processData hook to do transformations on the data\r
+                               plot.hooks.processDatapoints.push(processDatapoints);\r
+                               plot.hooks.drawOverlay.push(drawOverlay);       \r
+                               \r
+                               // add draw hook\r
+                               plot.hooks.draw.push(draw);\r
+                       }\r
+               }\r
+       \r
+               // bind hoverable events\r
+               function bindEvents(plot, eventHolder)          \r
+               {               \r
+                       var options = plot.getOptions();\r
+                       \r
+                       if (options.series.pie.show && options.grid.hoverable)\r
+                               eventHolder.unbind('mousemove').mousemove(onMouseMove);\r
+                               \r
+                       if (options.series.pie.show && options.grid.clickable)\r
+                               eventHolder.unbind('click').click(onClick);\r
+               }       \r
+               \r
+\r
+               // debugging function that prints out an object\r
+               function alertObject(obj)\r
+               {\r
+                       var msg = '';\r
+                       function traverse(obj, depth)\r
+                       {\r
+                               if (!depth)\r
+                                       depth = 0;\r
+                               for (var i = 0; i < obj.length; ++i)\r
+                               {\r
+                                       for (var j=0; j<depth; j++)\r
+                                               msg += '\t';\r
+                               \r
+                                       if( typeof obj[i] == "object")\r
+                                       {       // its an object\r
+                                               msg += ''+i+':\n';\r
+                                               traverse(obj[i], depth+1);\r
+                                       }\r
+                                       else\r
+                                       {       // its a value\r
+                                               msg += ''+i+': '+obj[i]+'\n';\r
+                                       }\r
+                               }\r
+                       }\r
+                       traverse(obj);\r
+                       alert(msg);\r
+               }\r
+               \r
+               function calcTotal(data)\r
+               {\r
+                       for (var i = 0; i < data.length; ++i)\r
+                       {\r
+                               var item = parseFloat(data[i].data[0][1]);\r
+                               if (item)\r
+                                       total += item;\r
+                       }\r
+               }       \r
+               \r
+               function processDatapoints(plot, series, data, datapoints) \r
+               {       \r
+                       if (!processed)\r
+                       {\r
+                               processed = true;\r
+                       \r
+                               canvas = plot.getCanvas();\r
+                               target = $(canvas).parent();\r
+                               options = plot.getOptions();\r
+                       \r
+                               plot.setData(combine(plot.getData()));\r
+                       }\r
+               }\r
+               \r
+               function setupPie()\r
+               {\r
+                       legendWidth = target.children().filter('.legend').children().width();\r
+               \r
+                       // calculate maximum radius and center point\r
+                       maxRadius =  Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;\r
+                       centerTop = (canvas.height/2)+options.series.pie.offset.top;\r
+                       centerLeft = (canvas.width/2);\r
+                       \r
+                       if (options.series.pie.offset.left=='auto')\r
+                               if (options.legend.position.match('w'))\r
+                                       centerLeft += legendWidth/2;\r
+                               else\r
+                                       centerLeft -= legendWidth/2;\r
+                       else\r
+                               centerLeft += options.series.pie.offset.left;\r
+                                       \r
+                       if (centerLeft<maxRadius)\r
+                               centerLeft = maxRadius;\r
+                       else if (centerLeft>canvas.width-maxRadius)\r
+                               centerLeft = canvas.width-maxRadius;\r
+               }\r
+               \r
+               function fixData(data)\r
+               {\r
+                       for (var i = 0; i < data.length; ++i)\r
+                       {\r
+                               if (typeof(data[i].data)=='number')\r
+                                       data[i].data = [[1,data[i].data]];\r
+                               else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')\r
+                               {\r
+                                       if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')\r
+                                               data[i].label = data[i].data.label; // fix weirdness coming from flot\r
+                                       data[i].data = [[1,0]];\r
+                                       \r
+                               }\r
+                       }\r
+                       return data;\r
+               }\r
+               \r
+               function combine(data)\r
+               {\r
+                       data = fixData(data);\r
+                       calcTotal(data);\r
+                       var combined = 0;\r
+                       var numCombined = 0;\r
+                       var color = options.series.pie.combine.color;\r
+                       \r
+                       var newdata = [];\r
+                       for (var i = 0; i < data.length; ++i)\r
+                       {\r
+                               // make sure its a number\r
+                               data[i].data[0][1] = parseFloat(data[i].data[0][1]);\r
+                               if (!data[i].data[0][1])\r
+                                       data[i].data[0][1] = 0;\r
+                                       \r
+                               if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)\r
+                               {\r
+                                       combined += data[i].data[0][1];\r
+                                       numCombined++;\r
+                                       if (!color)\r
+                                               color = data[i].color;\r
+                               }                               \r
+                               else\r
+                               {\r
+                                       newdata.push({\r
+                                               data: [[1,data[i].data[0][1]]], \r
+                                               color: data[i].color, \r
+                                               label: data[i].label,\r
+                                               angle: (data[i].data[0][1]*(Math.PI*2))/total,\r
+                                               percent: (data[i].data[0][1]/total*100)\r
+                                       });\r
+                               }\r
+                       }\r
+                       if (numCombined>0)\r
+                               newdata.push({\r
+                                       data: [[1,combined]], \r
+                                       color: color, \r
+                                       label: options.series.pie.combine.label,\r
+                                       angle: (combined*(Math.PI*2))/total,\r
+                                       percent: (combined/total*100)\r
+                               });\r
+                       return newdata;\r
+               }               \r
+               \r
+               function draw(plot, newCtx)\r
+               {\r
+                       if (!target) return; // if no series were passed\r
+                       ctx = newCtx;\r
+               \r
+                       setupPie();\r
+                       var slices = plot.getData();\r
+               \r
+                       var attempts = 0;\r
+                       while (redraw && attempts<redrawAttempts)\r
+                       {\r
+                               redraw = false;\r
+                               if (attempts>0)\r
+                                       maxRadius *= shrink;\r
+                               attempts += 1;\r
+                               clear();\r
+                               if (options.series.pie.tilt<=0.8)\r
+                                       drawShadow();\r
+                               drawPie();\r
+                       }\r
+                       if (attempts >= redrawAttempts) {\r
+                               clear();\r
+                               target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>');\r
+                       }\r
+                       \r
+                       if ( plot.setSeries && plot.insertLegend )\r
+                       {\r
+                               plot.setSeries(slices);\r
+                               plot.insertLegend();\r
+                       }\r
+                       \r
+                       // we're actually done at this point, just defining internal functions at this point\r
+                       \r
+                       function clear()\r
+                       {\r
+                               ctx.clearRect(0,0,canvas.width,canvas.height);\r
+                               target.children().filter('.pieLabel, .pieLabelBackground').remove();\r
+                       }\r
+                       \r
+                       function drawShadow()\r
+                       {\r
+                               var shadowLeft = 5;\r
+                               var shadowTop = 15;\r
+                               var edge = 10;\r
+                               var alpha = 0.02;\r
+                       \r
+                               // set radius\r
+                               if (options.series.pie.radius>1)\r
+                                       var radius = options.series.pie.radius;\r
+                               else\r
+                                       var radius = maxRadius * options.series.pie.radius;\r
+                                       \r
+                               if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)\r
+                                       return; // shadow would be outside canvas, so don't draw it\r
+                       \r
+                               ctx.save();\r
+                               ctx.translate(shadowLeft,shadowTop);\r
+                               ctx.globalAlpha = alpha;\r
+                               ctx.fillStyle = '#000';\r
+\r
+                               // center and rotate to starting position\r
+                               ctx.translate(centerLeft,centerTop);\r
+                               ctx.scale(1, options.series.pie.tilt);\r
+                               \r
+                               //radius -= edge;\r
+                               for (var i=1; i<=edge; i++)\r
+                               {\r
+                                       ctx.beginPath();\r
+                                       ctx.arc(0,0,radius,0,Math.PI*2,false);\r
+                                       ctx.fill();\r
+                                       radius -= i;\r
+                               }       \r
+                               \r
+                               ctx.restore();\r
+                       }\r
+                       \r
+                       function drawPie()\r
+                       {\r
+                               startAngle = Math.PI*options.series.pie.startAngle;\r
+                               \r
+                               // set radius\r
+                               if (options.series.pie.radius>1)\r
+                                       var radius = options.series.pie.radius;\r
+                               else\r
+                                       var radius = maxRadius * options.series.pie.radius;\r
+                               \r
+                               // center and rotate to starting position\r
+                               ctx.save();\r
+                               ctx.translate(centerLeft,centerTop);\r
+                               ctx.scale(1, options.series.pie.tilt);\r
+                               //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera\r
+                               \r
+                               // draw slices\r
+                               ctx.save();\r
+                               var currentAngle = startAngle;\r
+                               for (var i = 0; i < slices.length; ++i)\r
+                               {\r
+                                       slices[i].startAngle = currentAngle;\r
+                                       drawSlice(slices[i].angle, slices[i].color, true);\r
+                               }\r
+                               ctx.restore();\r
+                               \r
+                               // draw slice outlines\r
+                               ctx.save();\r
+                               ctx.lineWidth = options.series.pie.stroke.width;\r
+                               currentAngle = startAngle;\r
+                               for (var i = 0; i < slices.length; ++i)\r
+                                       drawSlice(slices[i].angle, options.series.pie.stroke.color, false);\r
+                               ctx.restore();\r
+                                       \r
+                               // draw donut hole\r
+                               drawDonutHole(ctx);\r
+                               \r
+                               // draw labels\r
+                               if (options.series.pie.label.show)\r
+                                       drawLabels();\r
+                               \r
+                               // restore to original state\r
+                               ctx.restore();\r
+                               \r
+                               function drawSlice(angle, color, fill)\r
+                               {       \r
+                                       if (angle<=0)\r
+                                               return;\r
+                               \r
+                                       if (fill)\r
+                                               ctx.fillStyle = color;\r
+                                       else\r
+                                       {\r
+                                               ctx.strokeStyle = color;\r
+                                               ctx.lineJoin = 'round';\r
+                                       }\r
+                                               \r
+                                       ctx.beginPath();\r
+                                       if (Math.abs(angle - Math.PI*2) > 0.000000001)\r
+                                               ctx.moveTo(0,0); // Center of the pie\r
+                                       else if ($.browser.msie)\r
+                                               angle -= 0.0001;\r
+                                       //ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera\r
+                                       ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);\r
+                                       ctx.closePath();\r
+                                       //ctx.rotate(angle); // This doesn't work properly in Opera\r
+                                       currentAngle += angle;\r
+                                       \r
+                                       if (fill)\r
+                                               ctx.fill();\r
+                                       else\r
+                                               ctx.stroke();\r
+                               }\r
+                               \r
+                               function drawLabels()\r
+                               {\r
+                                       var currentAngle = startAngle;\r
+                                       \r
+                                       // set radius\r
+                                       if (options.series.pie.label.radius>1)\r
+                                               var radius = options.series.pie.label.radius;\r
+                                       else\r
+                                               var radius = maxRadius * options.series.pie.label.radius;\r
+                                       \r
+                                       for (var i = 0; i < slices.length; ++i)\r
+                                       {\r
+                                               if (slices[i].percent >= options.series.pie.label.threshold*100)\r
+                                                       drawLabel(slices[i], currentAngle, i);\r
+                                               currentAngle += slices[i].angle;\r
+                                       }\r
+                                       \r
+                                       function drawLabel(slice, startAngle, index)\r
+                                       {\r
+                                               if (slice.data[0][1]==0)\r
+                                                       return;\r
+                                                       \r
+                                               // format label text\r
+                                               var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;\r
+                                               if (lf)\r
+                                                       text = lf(slice.label, slice);\r
+                                               else\r
+                                                       text = slice.label;\r
+                                               if (plf)\r
+                                                       text = plf(text, slice);\r
+                                                       \r
+                                               var halfAngle = ((startAngle+slice.angle) + startAngle)/2;\r
+                                               var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);\r
+                                               var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;\r
+                                               \r
+                                               var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>";\r
+                                               target.append(html);\r
+                                               var label = target.children('#pieLabel'+index);\r
+                                               var labelTop = (y - label.height()/2);\r
+                                               var labelLeft = (x - label.width()/2);\r
+                                               label.css('top', labelTop);\r
+                                               label.css('left', labelLeft);\r
+                                               \r
+                                               // check to make sure that the label is not outside the canvas\r
+                                               if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)\r
+                                                       redraw = true;\r
+                                               \r
+                                               if (options.series.pie.label.background.opacity != 0) {\r
+                                                       // put in the transparent background separately to avoid blended labels and label boxes\r
+                                                       var c = options.series.pie.label.background.color;\r
+                                                       if (c == null) {\r
+                                                               c = slice.color;\r
+                                                       }\r
+                                                       var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';\r
+                                                       $('<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);\r
+                                               }\r
+                                       } // end individual label function\r
+                               } // end drawLabels function\r
+                       } // end drawPie function\r
+               } // end draw function\r
+               \r
+               // Placed here because it needs to be accessed from multiple locations \r
+               function drawDonutHole(layer)\r
+               {\r
+                       // draw donut hole\r
+                       if(options.series.pie.innerRadius > 0)\r
+                       {\r
+                               // subtract the center\r
+                               layer.save();\r
+                               innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;\r
+                               layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color\r
+                               layer.beginPath();\r
+                               layer.fillStyle = options.series.pie.stroke.color;\r
+                               layer.arc(0,0,innerRadius,0,Math.PI*2,false);\r
+                               layer.fill();\r
+                               layer.closePath();\r
+                               layer.restore();\r
+                               \r
+                               // add inner stroke\r
+                               layer.save();\r
+                               layer.beginPath();\r
+                               layer.strokeStyle = options.series.pie.stroke.color;\r
+                               layer.arc(0,0,innerRadius,0,Math.PI*2,false);\r
+                               layer.stroke();\r
+                               layer.closePath();\r
+                               layer.restore();\r
+                               // TODO: add extra shadow inside hole (with a mask) if the pie is tilted.\r
+                       }\r
+               }\r
+               \r
+               //-- Additional Interactive related functions --\r
+               \r
+               function isPointInPoly(poly, pt)\r
+               {\r
+                       for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)\r
+                               ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))\r
+                               && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])\r
+                               && (c = !c);\r
+                       return c;\r
+               }\r
+               \r
+               function findNearbySlice(mouseX, mouseY)\r
+               {\r
+                       var slices = plot.getData(),\r
+                               options = plot.getOptions(),\r
+                               radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;\r
+                       \r
+                       for (var i = 0; i < slices.length; ++i) \r
+                       {\r
+                               var s = slices[i];      \r
+                               \r
+                               if(s.pie.show)\r
+                               {\r
+                                       ctx.save();\r
+                                       ctx.beginPath();\r
+                                       ctx.moveTo(0,0); // Center of the pie\r
+                                       //ctx.scale(1, options.series.pie.tilt);        // this actually seems to break everything when here.\r
+                                       ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);\r
+                                       ctx.closePath();\r
+                                       x = mouseX-centerLeft;\r
+                                       y = mouseY-centerTop;\r
+                                       if(ctx.isPointInPath)\r
+                                       {\r
+                                               if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))\r
+                                               {\r
+                                                       //alert('found slice!');\r
+                                                       ctx.restore();\r
+                                                       return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               // excanvas for IE doesn;t support isPointInPath, this is a workaround. \r
+                                               p1X = (radius * Math.cos(s.startAngle));\r
+                                               p1Y = (radius * Math.sin(s.startAngle));\r
+                                               p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));\r
+                                               p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));\r
+                                               p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));\r
+                                               p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));\r
+                                               p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));\r
+                                               p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));\r
+                                               p5X = (radius * Math.cos(s.startAngle+s.angle));\r
+                                               p5Y = (radius * Math.sin(s.startAngle+s.angle));\r
+                                               arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];\r
+                                               arrPoint = [x,y];\r
+                                               // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?\r
+                                               if(isPointInPoly(arrPoly, arrPoint))\r
+                                               {\r
+                                                       ctx.restore();\r
+                                                       return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};\r
+                                               }                       \r
+                                       }\r
+                                       ctx.restore();\r
+                               }\r
+                       }\r
+                       \r
+                       return null;\r
+               }\r
+\r
+               function onMouseMove(e) \r
+               {\r
+                       triggerClickHoverEvent('plothover', e);\r
+               }\r
+               \r
+        function onClick(e) \r
+               {\r
+                       triggerClickHoverEvent('plotclick', e);\r
+        }\r
+\r
+               // trigger click or hover event (they send the same parameters so we share their code)\r
+               function triggerClickHoverEvent(eventname, e) \r
+               {\r
+                       var offset = plot.offset(),\r
+                               canvasX = parseInt(e.pageX - offset.left),\r
+                               canvasY =  parseInt(e.pageY - offset.top),\r
+                               item = findNearbySlice(canvasX, canvasY);\r
+                       \r
+                       if (options.grid.autoHighlight) \r
+                       {\r
+                               // clear auto-highlights\r
+                               for (var i = 0; i < highlights.length; ++i) \r
+                               {\r
+                                       var h = highlights[i];\r
+                                       if (h.auto == eventname && !(item && h.series == item.series))\r
+                                               unhighlight(h.series);\r
+                               }\r
+                       }\r
+                       \r
+                       // highlight the slice\r
+                       if (item) \r
+                           highlight(item.series, eventname);\r
+                               \r
+                       // trigger any hover bind events\r
+                       var pos = { pageX: e.pageX, pageY: e.pageY };\r
+                       target.trigger(eventname, [ pos, item ]);       \r
+               }\r
+\r
+               function highlight(s, auto) \r
+               {\r
+                       if (typeof s == "number")\r
+                               s = series[s];\r
+\r
+                       var i = indexOfHighlight(s);\r
+                       if (i == -1) \r
+                       {\r
+                               highlights.push({ series: s, auto: auto });\r
+                               plot.triggerRedrawOverlay();\r
+                       }\r
+                       else if (!auto)\r
+                               highlights[i].auto = false;\r
+               }\r
+\r
+               function unhighlight(s) \r
+               {\r
+                       if (s == null) \r
+                       {\r
+                               highlights = [];\r
+                               plot.triggerRedrawOverlay();\r
+                       }\r
+                       \r
+                       if (typeof s == "number")\r
+                               s = series[s];\r
+\r
+                       var i = indexOfHighlight(s);\r
+                       if (i != -1) \r
+                       {\r
+                               highlights.splice(i, 1);\r
+                               plot.triggerRedrawOverlay();\r
+                       }\r
+               }\r
+\r
+               function indexOfHighlight(s) \r
+               {\r
+                       for (var i = 0; i < highlights.length; ++i) \r
+                       {\r
+                               var h = highlights[i];\r
+                               if (h.series == s)\r
+                                       return i;\r
+                       }\r
+                       return -1;\r
+               }\r
+\r
+               function drawOverlay(plot, octx) \r
+               {\r
+                       //alert(options.series.pie.radius);\r
+                       var options = plot.getOptions();\r
+                       //alert(options.series.pie.radius);\r
+                       \r
+                       var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;\r
+\r
+                       octx.save();\r
+                       octx.translate(centerLeft, centerTop);\r
+                       octx.scale(1, options.series.pie.tilt);\r
+                       \r
+                       for (i = 0; i < highlights.length; ++i) \r
+                               drawHighlight(highlights[i].series);\r
+                       \r
+                       drawDonutHole(octx);\r
+\r
+                       octx.restore();\r
+\r
+                       function drawHighlight(series) \r
+                       {\r
+                               if (series.angle < 0) return;\r
+                               \r
+                               //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();\r
+                               octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor\r
+                               \r
+                               octx.beginPath();\r
+                               if (Math.abs(series.angle - Math.PI*2) > 0.000000001)\r
+                                       octx.moveTo(0,0); // Center of the pie\r
+                               octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);\r
+                               octx.closePath();\r
+                               octx.fill();\r
+                       }\r
+                       \r
+               }       \r
+               \r
+       } // end init (plugin body)\r
+       \r
+       // define pie specific options and their default values\r
+       var options = {\r
+               series: {\r
+                       pie: {\r
+                               show: false,\r
+                               radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)\r
+                               innerRadius:0, /* for donut */\r
+                               startAngle: 3/2,\r
+                               tilt: 1,\r
+                               offset: {\r
+                                       top: 0,\r
+                                       left: 'auto'\r
+                               },\r
+                               stroke: {\r
+                                       color: '#FFF',\r
+                                       width: 1\r
+                               },\r
+                               label: {\r
+                                       show: 'auto',\r
+                                       formatter: function(label, slice){\r
+                                               return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';\r
+                                       },      // formatter function\r
+                                       radius: 1,      // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)\r
+                                       background: {\r
+                                               color: null,\r
+                                               opacity: 0\r
+                                       },\r
+                                       threshold: 0    // percentage at which to hide the label (i.e. the slice is too narrow)\r
+                               },\r
+                               combine: {\r
+                                       threshold: -1,  // percentage at which to combine little slices into one larger slice\r
+                                       color: null,    // color to give the new slice (auto-generated if null)\r
+                                       label: 'Other'  // label to give the new slice\r
+                               },\r
+                               highlight: {\r
+                                       //color: '#FFF',                // will add this functionality once parseColor is available\r
+                                       opacity: 0.5\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+    \r
+       $.plot.plugins.push({\r
+               init: init,\r
+               options: options,\r
+               name: "pie",\r
+               version: "1.0"\r
+       });\r
+})(jQuery);\r