PluginProbe
HAL / 2.3
HAL v2.3
trunk 1.0 1.1 1.2 1.3 1.4 1.4.2 1.4.3 1.4.4 2.0 2.0.1 2.0.10 2.0.2 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.2 2.3 2.4 2.4.1 2.4.2 2.5 All 30 releases
hal / js / jqplot.pieRenderer.js

jqplot.pieRenderer.js in HAL 2.3, at js/jqplot.pieRenderer.js

904 lines 34.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * jqPlot
3 * Pure JavaScript plotting plugin using jQuery
4 *
5 * Version: 1.0.8
6 * Revision: 1250
7 *
8 * Copyright (c) 2009-2013 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.
13 *
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 .
17 *
18 * If you are feeling kind and generous, consider supporting the project by
19 * making a donation at: http://www.jqplot.com/donate.php .
20 *
21 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22 *
23 * version 2007.04.27
24 * author 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."
29 *
30 */
31 (function($) {
32 /**
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.
37 *
38 * To use this renderer, you need to include the
39 * pie renderer plugin, for example:
40 *
41 * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script>
42 *
43 * Properties described here are passed into the $.jqplot function
44 * as options on the series renderer. For example:
45 *
46 * > plot2 = $.jqplot('chart2', [s1, s2], {
47 * > seriesDefaults: {
48 * > renderer:$.jqplot.PieRenderer,
49 * > rendererOptions:{
50 * > sliceMargin: 2,
51 * > startAngle: -90
52 * > }
53 * > }
54 * > });
55 *
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.
60 *
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.
69 */
70 $.jqplot.PieRenderer = function(){
71 $.jqplot.LineRenderer.call(this);
72 };
73
74 $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
75 $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
76
77 // called with scope of a series
78 $.jqplot.PieRenderer.prototype.init = function(options, plot) {
79 // Group: Properties
80 //
81 // prop: diameter
82 // Outer diameter of the pie, auto computed by default
83 this.diameter = null;
84 // prop: padding
85 // padding between the pie and plot edges, legend, etc.
86 this.padding = 20;
87 // prop: sliceMargin
88 // angular spacing between pie slices in degrees.
89 this.sliceMargin = 0;
90 // prop: fill
91 // true or false, whether to fil the slices.
92 this.fill = true;
93 // prop: shadowOffset
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;
97 // prop: shadowAlpha
98 // transparency of the shadow (0 = transparent, 1 = opaque)
99 this.shadowAlpha = 0.07;
100 // prop: shadowDepth
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 = [];
115 // prop: dataLabels
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;
140 // prop: startAngle
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.
147 this.startAngle = 0;
148 this.tickRenderer = $.jqplot.PieTickRenderer;
149 // Used as check for conditions where pie shouldn't be drawn.
150 this._drawData = true;
151 this._type = 'pie';
152
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;
156 }
157
158 $.extend(true, this, options);
159
160 if (this.sliceMargin < 0) {
161 this.sliceMargin = 0;
162 }
163
164 this._diameter = null;
165 this._radius = 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;
170
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);
181 }
182 this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
183 }
184 }
185
186 this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
187
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);
196 };
197
198 $.jqplot.PieRenderer.prototype.setGridData = function(plot) {
199 // set gridData property. This will hold angle in radians of each data point.
200 var stack = [];
201 var td = [];
202 var sa = this.startAngle/180*Math.PI;
203 var tot = 0;
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;
210 }
211 stack.push(this.data[i][1]);
212 td.push([this.data[i][0]]);
213 if (i>0) {
214 stack[i] += stack[i-1];
215 }
216 tot += this.data[i][1];
217 }
218 var fact = Math.PI*2/stack[stack.length - 1];
219
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;
223 }
224 this.gridData = td;
225 };
226
227 $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) {
228 var stack = [];
229 var td = [];
230 var tot = 0;
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;
238 }
239 stack.push(data[i][1]);
240 td.push([data[i][0]]);
241 if (i>0) {
242 stack[i] += stack[i-1];
243 }
244 tot += data[i][1];
245 }
246 var fact = Math.PI*2/stack[stack.length - 1];
247
248 for (var i=0; i<stack.length; i++) {
249 td[i][1] = stack[i] * fact;
250 td[i][2] = data[i][1]/tot;
251 }
252 return td;
253 };
254
255 function calcRadiusAdjustment(ang) {
256 return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0);
257 }
258
259 function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) {
260 var rprime = 0;
261 var ang = ang2 - ang1;
262 var absang = Math.abs(ang);
263 var sm = sliceMargin;
264 if (fill == false) {
265 sm += lineWidth;
266 }
267
268 if (sm > 0 && absang > 0.01 && absang < 6.282) {
269 rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang);
270 }
271
272 return rprime;
273 }
274
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;
283 }
284 ctx.save();
285 ctx.translate(this._center[0], this._center[1]);
286
287 var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
288
289 var transx = rprime * Math.cos((ang1 + ang2) / 2.0);
290 var transy = rprime * Math.sin((ang1 + ang2) / 2.0);
291
292 if ((ang2 - ang1) <= Math.PI) {
293 r -= rprime;
294 }
295 else {
296 r += rprime;
297 }
298
299 ctx.translate(transx, transy);
300
301 if (isShadow) {
302 for (var i=0, l=this.shadowDepth; i<l; i++) {
303 ctx.save();
304 ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
305 doDraw(r);
306 }
307 for (var i=0, l=this.shadowDepth; i<l; i++) {
308 ctx.restore();
309 }
310 }
311
312 else {
313 doDraw(r);
314 }
315 ctx.restore();
316 }
317
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;
324 if (ang1 > ang2) {
325 ang1 = 6.281 + this.startAngle;
326 }
327 }
328 // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids
329 // ugly line on unfilled pies.
330 if (ang1 >= ang2) {
331 return;
332 }
333
334 ctx.beginPath();
335 ctx.fillStyle = color;
336 ctx.strokeStyle = color;
337 ctx.lineWidth = lineWidth;
338 ctx.arc(0, 0, rad, ang1, ang2, false);
339 ctx.lineTo(0,0);
340 ctx.closePath();
341
342 if (fill) {
343 ctx.fill();
344 }
345 else {
346 ctx.stroke();
347 }
348 }
349 };
350
351 // called with scope of series
352 $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) {
353 var i;
354 var opts = (options != undefined) ? options : {};
355 // offset and direction of offset due to legend placement
356 var offx = 0;
357 var offy = 0;
358 var trans = 1;
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) {
363 case 'nw':
364 offx = li.width + li.xoffset;
365 break;
366 case 'w':
367 offx = li.width + li.xoffset;
368 break;
369 case 'sw':
370 offx = li.width + li.xoffset;
371 break;
372 case 'ne':
373 offx = li.width + li.xoffset;
374 trans = -1;
375 break;
376 case 'e':
377 offx = li.width + li.xoffset;
378 trans = -1;
379 break;
380 case 'se':
381 offx = li.width + li.xoffset;
382 trans = -1;
383 break;
384 case 'n':
385 offy = li.height + li.yoffset;
386 break;
387 case 's':
388 offy = li.height + li.yoffset;
389 trans = -1;
390 break;
391 default:
392 break;
393 }
394 }
395
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);
403 var d = mindim;
404
405 // Fixes issue #272. Thanks hugwijst!
406 // reset slice angles array.
407 this._sliceAngles = [];
408
409 var sm = this.sliceMargin;
410 if (this.fill == false) {
411 sm += this.lineWidth;
412 }
413
414 var rprime;
415 var maxrprime = 0;
416
417 var ang, ang1, ang2, shadowColor;
418 var sa = this.startAngle / 180 * Math.PI;
419
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;
424
425 this._sliceAngles.push([ang1, ang2]);
426
427 rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
428
429 if (Math.abs(ang2-ang1) > Math.PI) {
430 maxrprime = Math.max(rprime, maxrprime);
431 }
432 }
433
434 if (this.diameter != null && this.diameter > 0) {
435 this._diameter = this.diameter - 2*maxrprime;
436 }
437 else {
438 this._diameter = d - 2*maxrprime;
439 }
440
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.');
445 return;
446 }
447
448 var r = this._radius = this._diameter/2;
449
450 this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)];
451
452 if (this.shadow) {
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);
456 }
457 }
458
459 for (var i=0; i<gd.length; i++) {
460
461 this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], colorGenerator.next(), false);
462
463 if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
464 var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label;
465
466 if (this.dataLabels == 'label') {
467 fstr = this.dataLabelFormatString || '%s';
468 label = $.jqplot.sprintf(fstr, gd[i][0]);
469 }
470 else if (this.dataLabels == 'value') {
471 fstr = this.dataLabelFormatString || '%d';
472 label = $.jqplot.sprintf(fstr, this.data[i][1]);
473 }
474 else if (this.dataLabels == 'percent') {
475 fstr = this.dataLabelFormatString || '%d%%';
476 label = $.jqplot.sprintf(fstr, gd[i][2]*100);
477 }
478 else if (this.dataLabels.constructor == Array) {
479 fstr = this.dataLabelFormatString || '%s';
480 label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
481 }
482
483 var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
484
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;
487
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;
492 }
493 else {
494 x -= labelelem.width() * Math.sin(avgang/2);
495 y -= labelelem.height()/2;
496 }
497 x = Math.round(x);
498 y = Math.round(y);
499 labelelem.css({left: x, top: y});
500 }
501 }
502 };
503
504 $.jqplot.PieAxisRenderer = function() {
505 $.jqplot.LinearAxisRenderer.call(this);
506 };
507
508 $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
509 $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
510
511
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){
516 //
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};
525 this.min = 0;
526 this.max = 100;
527 this.showTicks = false;
528 this.ticks = [];
529 this.showMark = false;
530 this.show = false;
531 };
532
533
534
535
536 $.jqplot.PieLegendRenderer = function(){
537 $.jqplot.TableLegendRenderer.call(this);
538 };
539
540 $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
541 $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer;
542
543 /**
544 * Class: $.jqplot.PieLegendRenderer
545 * Legend Renderer specific to pie plots. Set by default
546 * when user creates a pie plot.
547 */
548 $.jqplot.PieLegendRenderer.prototype.init = function(options) {
549 // Group: Properties
550 //
551 // prop: numberRows
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);
558 };
559
560 // called with context of legend
561 $.jqplot.PieLegendRenderer.prototype.draw = function() {
562 var legend = this;
563 if (this.show) {
564 var series = this._series;
565
566
567 this._elem = $(document.createElement('table'));
568 this._elem.addClass('jqplot-table-legend');
569
570 var ss = {position:'absolute'};
571 if (this.background) {
572 ss['background'] = this.background;
573 }
574 if (this.border) {
575 ss['border'] = this.border;
576 }
577 if (this.fontSize) {
578 ss['fontSize'] = this.fontSize;
579 }
580 if (this.fontFamily) {
581 ss['fontFamily'] = this.fontFamily;
582 }
583 if (this.textColor) {
584 ss['textColor'] = this.textColor;
585 }
586 if (this.marginTop != null) {
587 ss['marginTop'] = this.marginTop;
588 }
589 if (this.marginBottom != null) {
590 ss['marginBottom'] = this.marginBottom;
591 }
592 if (this.marginLeft != null) {
593 ss['marginLeft'] = this.marginLeft;
594 }
595 if (this.marginRight != null) {
596 ss['marginRight'] = this.marginRight;
597 }
598
599 this._elem.css(ss);
600
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.
603
604 var pad = false,
605 reverse = false,
606 nr,
607 nc;
608 var s = series[0];
609 var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
610
611 if (s.show) {
612 var pd = s.data;
613 if (this.numberRows) {
614 nr = this.numberRows;
615 if (!this.numberColumns){
616 nc = Math.ceil(pd.length/nr);
617 }
618 else{
619 nc = this.numberColumns;
620 }
621 }
622 else if (this.numberColumns) {
623 nc = this.numberColumns;
624 nr = Math.ceil(pd.length/this.numberColumns);
625 }
626 else {
627 nr = pd.length;
628 nc = 1;
629 }
630
631 var i, j;
632 var tr, td1, td2;
633 var lt, rs, color;
634 var idx = 0;
635 var div0, div1;
636
637 for (i=0; i<nr; i++) {
638 tr = $(document.createElement('tr'));
639 tr.addClass('jqplot-table-legend');
640
641 if (reverse){
642 tr.prependTo(this._elem);
643 }
644
645 else{
646 tr.appendTo(this._elem);
647 }
648
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();
653 if (!reverse){
654 if (i>0){
655 pad = true;
656 }
657 else{
658 pad = false;
659 }
660 }
661 else{
662 if (i == nr -1){
663 pad = false;
664 }
665 else{
666 pad = true;
667 }
668 }
669 rs = (pad) ? this.rowSpacing : '0';
670
671
672
673 td1 = $(document.createElement('td'));
674 td1.addClass('jqplot-table-legend jqplot-table-legend-swatch');
675 td1.css({textAlign: 'center', paddingTop: rs});
676
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));
683
684 td2 = $(document.createElement('td'));
685 td2.addClass('jqplot-table-legend jqplot-table-legend-label');
686 td2.css('paddingTop', rs);
687
688 if (this.escapeHtml){
689 td2.text(lt);
690 }
691 else {
692 td2.html(lt);
693 }
694 if (reverse) {
695 td2.prependTo(tr);
696 td1.prependTo(tr);
697 }
698 else {
699 td1.appendTo(tr);
700 td2.appendTo(tr);
701 }
702 pad = true;
703 }
704 idx++;
705 }
706 }
707 }
708 }
709 return this._elem;
710 };
711
712 $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) {
713 if (neighbor) {
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]);
719 }
720 }
721 else if (neighbor == null) {
722 unhighlight (plot);
723 }
724 };
725
726
727 // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
728
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
737 var setopts = false;
738 if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
739 setopts = true;
740 }
741 else if (options.series) {
742 for (var i=0; i < options.series.length; i++) {
743 if (options.series[i].renderer == $.jqplot.PieRenderer) {
744 setopts = true;
745 }
746 }
747 }
748
749 if (setopts) {
750 options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
751 options.legend.renderer = $.jqplot.PieLegendRenderer;
752 options.legend.preDraw = true;
753 options.seriesDefaults.pointLabels = {show: false};
754 }
755 }
756
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;
763 }
764 }
765 }
766 }
767
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;
773 }
774 }
775
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);
783 }
784
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;
790 }
791 plot.plugins.pieRenderer.highlightedSeriesIndex = null;
792 plot.target.trigger('jqplotDataUnhighlight');
793 }
794
795 function handleMove(ev, gridpos, datapos, neighbor, plot) {
796 if (neighbor) {
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]);
809 }
810 }
811 else if (neighbor == null) {
812 unhighlight (plot);
813 }
814 }
815
816 function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
817 if (neighbor) {
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]);
826 }
827 }
828 else if (neighbor == null) {
829 unhighlight (plot);
830 }
831 }
832
833 function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
834 var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
835 if (idx != null && plot.series[idx].highlightMouseDown) {
836 unhighlight(plot);
837 }
838 }
839
840 function handleClick(ev, gridpos, datapos, neighbor, plot) {
841 if (neighbor) {
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);
848 }
849 }
850
851 function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
852 if (neighbor) {
853 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
854 var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
855 if (idx != null && plot.series[idx].highlightMouseDown) {
856 unhighlight(plot);
857 }
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);
863 }
864 }
865
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;
874 }
875
876 this.plugins.pieRenderer = {highlightedSeriesIndex:null};
877 this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
878
879 // do we have any data labels? if so, put highlight canvas before those
880 var labels = $(this.targetId+' .jqplot-data-label');
881 if (labels.length) {
882 $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
883 }
884 // else put highlight canvas before event canvas.
885 else {
886 this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
887 }
888
889 var hctx = this.plugins.pieRenderer.highlightCanvas.setContext();
890 this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
891 }
892
893 $.jqplot.preInitHooks.push(preInit);
894
895 $.jqplot.PieTickRenderer = function() {
896 $.jqplot.AxisTickRenderer.call(this);
897 };
898
899 $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
900 $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;
901
902 })(jQuery);
903
904