3 * Pure JavaScript plotting plugin using jQuery
8 * Copyright (c) 2009-2012 Chris Leonello
9 * jqPlot is currently available for use in all personal or commercial projects
10 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12 * choose the license that best suits your project and use it accordingly.
14 * Although not required, the author would appreciate an email letting him
15 * know of any substantial use of jqPlot. You can reach the author at:
16 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
18 * If you are feeling kind and generous, consider supporting the project by
19 * making a donation at: http://www.jqplot.com/donate.php .
21 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
25 * http://hexmen.com/blog/2007/03/printf-sprintf/
26 * http://hexmen.com/js/sprintf.js
27 * The author (Ash Searle) has placed this code in the public domain:
28 * "This code is unrestricted: you are free to use it however you like."
33 * Class: $.jqplot.PieRenderer
34 * Plugin renderer to draw a pie chart.
35 * x values, if present, will be used as slice labels.
36 * y values give slice size.
38 * To use this renderer, you need to include the
39 * pie renderer plugin, for example:
41 * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script>
43 * Properties described here are passed into the $.jqplot function
44 * as options on the series renderer. For example:
46 * > plot2 = $.jqplot('chart2', [s1, s2], {
48 * > renderer:$.jqplot.PieRenderer,
56 * A pie plot will trigger events on the plot target
57 * according to user interaction. All events return the event object,
58 * the series index, the point (slice) index, and the point data for
59 * the appropriate slice.
61 * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
62 * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
63 * if highlighting is enabled.
64 * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
65 * a highlighted slice.
66 * 'jqplotDataClick' - triggered when the user clicks on a slice.
67 * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
68 * the "captureRightClick" option is set to true on the plot.
70 $.jqplot.PieRenderer = function(){
71 $.jqplot.LineRenderer.call(this);
74 $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
75 $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
77 // called with scope of a series
78 $.jqplot.PieRenderer.prototype.init = function(options, plot) {
82 // Outer diameter of the pie, auto computed by default
85 // padding between the pie and plot edges, legend, etc.
88 // angular spacing between pie slices in degrees.
91 // true or false, wether to fil the slices.
94 // offset of the shadow from the slice and offset of
95 // each succesive stroke of the shadow from the last.
96 this.shadowOffset = 2;
98 // transparency of the shadow (0 = transparent, 1 = opaque)
99 this.shadowAlpha = 0.07;
101 // number of strokes to apply to the shadow,
102 // each stroke offset shadowOffset from the last.
103 this.shadowDepth = 5;
104 // prop: highlightMouseOver
105 // True to highlight slice when moused over.
106 // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
107 this.highlightMouseOver = true;
108 // prop: highlightMouseDown
109 // True to highlight when a mouse button is pressed over a slice.
110 // This will be disabled if highlightMouseOver is true.
111 this.highlightMouseDown = false;
112 // prop: highlightColors
113 // an array of colors to use when highlighting a slice.
114 this.highlightColors = [];
116 // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
117 // Defaults to percentage of each pie slice.
118 this.dataLabels = 'percent';
119 // prop: showDataLabels
120 // true to show data labels on slices.
121 this.showDataLabels = false;
122 // prop: dataLabelFormatString
123 // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
124 this.dataLabelFormatString = null;
125 // prop: dataLabelThreshold
126 // Threshhold in percentage (0-100) of pie area, below which no label will be displayed.
127 // This applies to all label types, not just to percentage labels.
128 this.dataLabelThreshold = 3;
129 // prop: dataLabelPositionFactor
130 // A Multiplier (0-1) of the pie radius which controls position of label on slice.
131 // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
132 this.dataLabelPositionFactor = 0.52;
133 // prop: dataLabelNudge
134 // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
135 this.dataLabelNudge = 2;
136 // prop: dataLabelCenterOn
137 // True to center the data label at its position.
138 // False to set the inside facing edge of the label at its position.
139 this.dataLabelCenterOn = true;
141 // Angle to start drawing pie in degrees.
142 // According to orientation of canvas coordinate system:
143 // 0 = on the positive x axis
144 // -90 = on the positive y axis.
145 // 90 = on the negaive y axis.
146 // 180 or - 180 = on the negative x axis.
148 this.tickRenderer = $.jqplot.PieTickRenderer;
149 // Used as check for conditions where pie shouldn't be drawn.
150 this._drawData = true;
153 // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
154 if (options.highlightMouseDown && options.highlightMouseOver == null) {
155 options.highlightMouseOver = false;
158 $.extend(true, this, options);
160 if (this.sliceMargin < 0) {
161 this.sliceMargin = 0;
164 this._diameter = null;
166 // array of [start,end] angles arrays, one for each slice. In radians.
167 this._sliceAngles = [];
168 // index of the currenty highlighted point, if any
169 this._highlightedPoint = null;
171 // set highlight colors if none provided
172 if (this.highlightColors.length == 0) {
173 for (var i=0; i<this.seriesColors.length; i++){
174 var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
175 var newrgb = [rgba[0], rgba[1], rgba[2]];
176 var sum = newrgb[0] + newrgb[1] + newrgb[2];
177 for (var j=0; j<3; j++) {
178 // when darkening, lowest color component can be is 60.
179 newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
180 newrgb[j] = parseInt(newrgb[j], 10);
182 this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
186 this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
188 plot.postParseOptionsHooks.addOnce(postParseOptions);
189 plot.postInitHooks.addOnce(postInit);
190 plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
191 plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
192 plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
193 plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
194 plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
195 plot.postDrawHooks.addOnce(postPlotDraw);
198 $.jqplot.PieRenderer.prototype.setGridData = function(plot) {
199 // set gridData property. This will hold angle in radians of each data point.
202 var sa = this.startAngle/180*Math.PI;
204 // don't know if we have any valid data yet, so set plot to not draw.
205 this._drawData = false;
206 for (var i=0; i<this.data.length; i++){
207 if (this.data[i][1] != 0) {
208 // we have data, O.K. to draw.
209 this._drawData = true;
211 stack.push(this.data[i][1]);
212 td.push([this.data[i][0]]);
214 stack[i] += stack[i-1];
216 tot += this.data[i][1];
218 var fact = Math.PI*2/stack[stack.length - 1];
220 for (var i=0; i<stack.length; i++) {
221 td[i][1] = stack[i] * fact;
222 td[i][2] = this.data[i][1]/tot;
227 $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) {
231 var sa = this.startAngle/180*Math.PI;
232 // don't know if we have any valid data yet, so set plot to not draw.
233 this._drawData = false;
234 for (var i=0; i<data.length; i++){
235 if (this.data[i][1] != 0) {
236 // we have data, O.K. to draw.
237 this._drawData = true;
239 stack.push(data[i][1]);
240 td.push([data[i][0]]);
242 stack[i] += stack[i-1];
246 var fact = Math.PI*2/stack[stack.length - 1];
248 for (var i=0; i<stack.length; i++) {
249 td[i][1] = stack[i] * fact;
250 td[i][2] = data[i][1]/tot;
255 function calcRadiusAdjustment(ang) {
256 return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0);
259 function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) {
261 var ang = ang2 - ang1;
262 var absang = Math.abs(ang);
263 var sm = sliceMargin;
268 if (sm > 0 && absang > 0.01 && absang < 6.282) {
269 rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang);
275 $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
276 if (this._drawData) {
277 var r = this._radius;
278 var fill = this.fill;
279 var lineWidth = this.lineWidth;
280 var sm = this.sliceMargin;
281 if (this.fill == false) {
282 sm += this.lineWidth;
285 ctx.translate(this._center[0], this._center[1]);
287 var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
289 var transx = rprime * Math.cos((ang1 + ang2) / 2.0);
290 var transy = rprime * Math.sin((ang1 + ang2) / 2.0);
292 if ((ang2 - ang1) <= Math.PI) {
299 ctx.translate(transx, transy);
302 for (var i=0, l=this.shadowDepth; i<l; i++) {
304 ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
307 for (var i=0, l=this.shadowDepth; i<l; i++) {
318 function doDraw (rad) {
319 // Fix for IE and Chrome that can't seem to draw circles correctly.
320 // ang2 should always be <= 2 pi since that is the way the data is converted.
321 // 2Pi = 6.2831853, Pi = 3.1415927
322 if (ang2 > 6.282 + this.startAngle) {
323 ang2 = 6.282 + this.startAngle;
325 ang1 = 6.281 + this.startAngle;
328 // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids
329 // ugly line on unfilled pies.
335 ctx.fillStyle = color;
336 ctx.strokeStyle = color;
337 ctx.lineWidth = lineWidth;
338 ctx.arc(0, 0, rad, ang1, ang2, false);
351 // called with scope of series
352 $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) {
354 var opts = (options != undefined) ? options : {};
355 // offset and direction of offset due to legend placement
359 var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
360 if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
361 var li = options.legendInfo;
362 switch (li.location) {
364 offx = li.width + li.xoffset;
367 offx = li.width + li.xoffset;
370 offx = li.width + li.xoffset;
373 offx = li.width + li.xoffset;
377 offx = li.width + li.xoffset;
381 offx = li.width + li.xoffset;
385 offy = li.height + li.yoffset;
388 offy = li.height + li.yoffset;
396 var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
397 var fill = (opts.fill != undefined) ? opts.fill : this.fill;
398 var cw = ctx.canvas.width;
399 var ch = ctx.canvas.height;
400 var w = cw - offx - 2 * this.padding;
401 var h = ch - offy - 2 * this.padding;
402 var mindim = Math.min(w,h);
405 // Fixes issue #272. Thanks hugwijst!
406 // reset slice angles array.
407 this._sliceAngles = [];
409 var sm = this.sliceMargin;
410 if (this.fill == false) {
411 sm += this.lineWidth;
417 var ang, ang1, ang2, shadowColor;
418 var sa = this.startAngle / 180 * Math.PI;
420 // have to pre-draw shadows, so loop throgh here and calculate some values also.
421 for (var i=0, l=gd.length; i<l; i++) {
422 ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
423 ang2 = gd[i][1] + sa;
425 this._sliceAngles.push([ang1, ang2]);
427 rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
429 if (Math.abs(ang2-ang1) > Math.PI) {
430 maxrprime = Math.max(rprime, maxrprime);
434 if (this.diameter != null && this.diameter > 0) {
435 this._diameter = this.diameter - 2*maxrprime;
438 this._diameter = d - 2*maxrprime;
441 // Need to check for undersized pie. This can happen if
442 // plot area too small and legend is too big.
443 if (this._diameter < 6) {
444 $.jqplot.log('Diameter of pie too small, not rendering.');
448 var r = this._radius = this._diameter/2;
450 this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)];
453 for (var i=0, l=gd.length; i<l; i++) {
454 shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
455 this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], shadowColor, true);
459 for (var i=0; i<gd.length; i++) {
461 this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], colorGenerator.next(), false);
463 if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
464 var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label;
466 if (this.dataLabels == 'label') {
467 fstr = this.dataLabelFormatString || '%s';
468 label = $.jqplot.sprintf(fstr, gd[i][0]);
470 else if (this.dataLabels == 'value') {
471 fstr = this.dataLabelFormatString || '%d';
472 label = $.jqplot.sprintf(fstr, this.data[i][1]);
474 else if (this.dataLabels == 'percent') {
475 fstr = this.dataLabelFormatString || '%d%%';
476 label = $.jqplot.sprintf(fstr, gd[i][2]*100);
478 else if (this.dataLabels.constructor == Array) {
479 fstr = this.dataLabelFormatString || '%s';
480 label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
483 var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
485 var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
486 var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
488 var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem);
489 if (this.dataLabelCenterOn) {
490 x -= labelelem.width()/2;
491 y -= labelelem.height()/2;
494 x -= labelelem.width() * Math.sin(avgang/2);
495 y -= labelelem.height()/2;
499 labelelem.css({left: x, top: y});
504 $.jqplot.PieAxisRenderer = function() {
505 $.jqplot.LinearAxisRenderer.call(this);
508 $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
509 $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
512 // There are no traditional axes on a pie chart. We just need to provide
513 // dummy objects with properties so the plot will render.
514 // called with scope of axis object.
515 $.jqplot.PieAxisRenderer.prototype.init = function(options){
517 this.tickRenderer = $.jqplot.PieTickRenderer;
518 $.extend(true, this, options);
519 // I don't think I'm going to need _dataBounds here.
520 // have to go Axis scaling in a way to fit chart onto plot area
521 // and provide u2p and p2u functionality for mouse cursor, etc.
522 // for convienence set _dataBounds to 0 and 100 and
523 // set min/max to 0 and 100.
524 this._dataBounds = {min:0, max:100};
527 this.showTicks = false;
529 this.showMark = false;
536 $.jqplot.PieLegendRenderer = function(){
537 $.jqplot.TableLegendRenderer.call(this);
540 $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
541 $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer;
544 * Class: $.jqplot.PieLegendRenderer
545 * Legend Renderer specific to pie plots. Set by default
546 * when user creates a pie plot.
548 $.jqplot.PieLegendRenderer.prototype.init = function(options) {
552 // Maximum number of rows in the legend. 0 or null for unlimited.
553 this.numberRows = null;
554 // prop: numberColumns
555 // Maximum number of columns in the legend. 0 or null for unlimited.
556 this.numberColumns = null;
557 $.extend(true, this, options);
560 // called with context of legend
561 $.jqplot.PieLegendRenderer.prototype.draw = function() {
564 var series = this._series;
567 this._elem = $(document.createElement('table'));
568 this._elem.addClass('jqplot-table-legend');
570 var ss = {position:'absolute'};
571 if (this.background) {
572 ss['background'] = this.background;
575 ss['border'] = this.border;
578 ss['fontSize'] = this.fontSize;
580 if (this.fontFamily) {
581 ss['fontFamily'] = this.fontFamily;
583 if (this.textColor) {
584 ss['textColor'] = this.textColor;
586 if (this.marginTop != null) {
587 ss['marginTop'] = this.marginTop;
589 if (this.marginBottom != null) {
590 ss['marginBottom'] = this.marginBottom;
592 if (this.marginLeft != null) {
593 ss['marginLeft'] = this.marginLeft;
595 if (this.marginRight != null) {
596 ss['marginRight'] = this.marginRight;
601 // Pie charts legends don't go by number of series, but by number of data points
602 // in the series. Refactor things here for that.
609 var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
613 if (this.numberRows) {
614 nr = this.numberRows;
615 if (!this.numberColumns){
616 nc = Math.ceil(pd.length/nr);
619 nc = this.numberColumns;
622 else if (this.numberColumns) {
623 nc = this.numberColumns;
624 nr = Math.ceil(pd.length/this.numberColumns);
637 for (i=0; i<nr; i++) {
638 tr = $(document.createElement('tr'));
639 tr.addClass('jqplot-table-legend');
642 tr.prependTo(this._elem);
646 tr.appendTo(this._elem);
649 for (j=0; j<nc; j++) {
650 if (idx < pd.length){
651 lt = this.labels[idx] || pd[idx][0].toString();
652 color = colorGenerator.next();
669 rs = (pad) ? this.rowSpacing : '0';
673 td1 = $(document.createElement('td'));
674 td1.addClass('jqplot-table-legend jqplot-table-legend-swatch');
675 td1.css({textAlign: 'center', paddingTop: rs});
677 div0 = $(document.createElement('div'));
678 div0.addClass('jqplot-table-legend-swatch-outline');
679 div1 = $(document.createElement('div'));
680 div1.addClass('jqplot-table-legend-swatch');
681 div1.css({backgroundColor: color, borderColor: color});
682 td1.append(div0.append(div1));
684 td2 = $(document.createElement('td'));
685 td2.addClass('jqplot-table-legend jqplot-table-legend-label');
686 td2.css('paddingTop', rs);
688 if (this.escapeHtml){
712 $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) {
714 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
715 plot.target.trigger('jqplotDataMouseOver', ins);
716 if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
717 plot.target.trigger('jqplotDataHighlight', ins);
718 highlight (plot, ins[0], ins[1]);
721 else if (neighbor == null) {
727 // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
729 // setup default renderers for axes and legend so user doesn't have to
730 // called with scope of plot
731 function preInit(target, data, options) {
732 options = options || {};
733 options.axesDefaults = options.axesDefaults || {};
734 options.legend = options.legend || {};
735 options.seriesDefaults = options.seriesDefaults || {};
736 // only set these if there is a pie series
738 if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
741 else if (options.series) {
742 for (var i=0; i < options.series.length; i++) {
743 if (options.series[i].renderer == $.jqplot.PieRenderer) {
750 options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
751 options.legend.renderer = $.jqplot.PieLegendRenderer;
752 options.legend.preDraw = true;
753 options.seriesDefaults.pointLabels = {show: false};
757 function postInit(target, data, options) {
758 for (var i=0; i<this.series.length; i++) {
759 if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) {
760 // don't allow mouseover and mousedown at same time.
761 if (this.series[i].highlightMouseOver) {
762 this.series[i].highlightMouseDown = false;
768 // called with scope of plot
769 function postParseOptions(options) {
770 for (var i=0; i<this.series.length; i++) {
771 this.series[i].seriesColors = this.seriesColors;
772 this.series[i].colorGenerator = $.jqplot.colorGenerator;
776 function highlight (plot, sidx, pidx) {
777 var s = plot.series[sidx];
778 var canvas = plot.plugins.pieRenderer.highlightCanvas;
779 canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
780 s._highlightedPoint = pidx;
781 plot.plugins.pieRenderer.highlightedSeriesIndex = sidx;
782 s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false);
785 function unhighlight (plot) {
786 var canvas = plot.plugins.pieRenderer.highlightCanvas;
787 canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
788 for (var i=0; i<plot.series.length; i++) {
789 plot.series[i]._highlightedPoint = null;
791 plot.plugins.pieRenderer.highlightedSeriesIndex = null;
792 plot.target.trigger('jqplotDataUnhighlight');
795 function handleMove(ev, gridpos, datapos, neighbor, plot) {
797 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
798 var evt1 = jQuery.Event('jqplotDataMouseOver');
799 evt1.pageX = ev.pageX;
800 evt1.pageY = ev.pageY;
801 plot.target.trigger(evt1, ins);
802 if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
803 var evt = jQuery.Event('jqplotDataHighlight');
804 evt.which = ev.which;
805 evt.pageX = ev.pageX;
806 evt.pageY = ev.pageY;
807 plot.target.trigger(evt, ins);
808 highlight (plot, ins[0], ins[1]);
811 else if (neighbor == null) {
816 function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
818 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
819 if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
820 var evt = jQuery.Event('jqplotDataHighlight');
821 evt.which = ev.which;
822 evt.pageX = ev.pageX;
823 evt.pageY = ev.pageY;
824 plot.target.trigger(evt, ins);
825 highlight (plot, ins[0], ins[1]);
828 else if (neighbor == null) {
833 function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
834 var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
835 if (idx != null && plot.series[idx].highlightMouseDown) {
840 function handleClick(ev, gridpos, datapos, neighbor, plot) {
842 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
843 var evt = jQuery.Event('jqplotDataClick');
844 evt.which = ev.which;
845 evt.pageX = ev.pageX;
846 evt.pageY = ev.pageY;
847 plot.target.trigger(evt, ins);
851 function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
853 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
854 var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
855 if (idx != null && plot.series[idx].highlightMouseDown) {
858 var evt = jQuery.Event('jqplotDataRightClick');
859 evt.which = ev.which;
860 evt.pageX = ev.pageX;
861 evt.pageY = ev.pageY;
862 plot.target.trigger(evt, ins);
866 // called within context of plot
867 // create a canvas which we can draw on.
868 // insert it before the eventCanvas, so eventCanvas will still capture events.
869 function postPlotDraw() {
870 // Memory Leaks patch
871 if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) {
872 this.plugins.pieRenderer.highlightCanvas.resetCanvas();
873 this.plugins.pieRenderer.highlightCanvas = null;
876 this.plugins.pieRenderer = {highlightedSeriesIndex:null};
877 this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
879 // do we have any data labels? if so, put highlight canvas before those
880 var labels = $(this.targetId+' .jqplot-data-label');
882 $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
884 // else put highlight canvas before event canvas.
886 this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
889 var hctx = this.plugins.pieRenderer.highlightCanvas.setContext();
890 this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
893 $.jqplot.preInitHooks.push(preInit);
895 $.jqplot.PieTickRenderer = function() {
896 $.jqplot.AxisTickRenderer.call(this);
899 $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
900 $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;