PluginProbe
Property Hive / trunk
Property Hive vtrunk
2.4.0 2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 All 262 releases
propertyhive / assets / js / jquery-flot / jquery.flot.pie.js

jquery.flot.pie.js in Property Hive trunk, at assets/js/jquery-flot/jquery.flot.pie.js

817 lines 23.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Flot plugin for rendering pie charts.
2
3 Copyright (c) 2007-2013 IOLA and Ole Laursen.
4 Licensed under the MIT license.
5
6 The plugin assumes that each series has a single data value, and that each
7 value is a positive integer or zero. Negative numbers don't make sense for a
8 pie chart, and have unpredictable results. The values do NOT need to be
9 passed in as percentages; the plugin will calculate the total and per-slice
10 percentages internally.
11
12 * Created by Brian Medendorp
13
14 * Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars
15
16 The plugin supports these options:
17
18 series: {
19 pie: {
20 show: true/false
21 radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
22 innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
23 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
24 tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
25 offset: {
26 top: integer value to move the pie up or down
27 left: integer value to move the pie left or right, or 'auto'
28 },
29 stroke: {
30 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
31 width: integer pixel width of the stroke
32 },
33 label: {
34 show: true/false, or 'auto'
35 formatter: a user-defined function that modifies the text/style of the label text
36 radius: 0-1 for percentage of fullsize, or a specified pixel length
37 background: {
38 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
39 opacity: 0-1
40 },
41 threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
42 },
43 combine: {
44 threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
45 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
46 label: any text value of what the combined slice should be labeled
47 }
48 highlight: {
49 opacity: 0-1
50 }
51 }
52 }
53
54 More detail and specific examples can be found in the included HTML file.
55
56 */
57
58 (function($) {
59
60 // Maximum redraw attempts when fitting labels within the plot
61
62 var REDRAW_ATTEMPTS = 10;
63
64 // Factor by which to shrink the pie when fitting labels within the plot
65
66 var REDRAW_SHRINK = 0.95;
67
68 function init(plot) {
69
70 var canvas = null,
71 target = null,
72 maxRadius = null,
73 centerLeft = null,
74 centerTop = null,
75 processed = false,
76 ctx = null;
77
78 // interactive variables
79
80 var highlights = [];
81
82 // add hook to determine if pie plugin in enabled, and then perform necessary operations
83
84 plot.hooks.processOptions.push(function(plot, options) {
85 if (options.series.pie.show) {
86
87 options.grid.show = false;
88
89 // set labels.show
90
91 if (options.series.pie.label.show == "auto") {
92 if (options.legend.show) {
93 options.series.pie.label.show = false;
94 } else {
95 options.series.pie.label.show = true;
96 }
97 }
98
99 // set radius
100
101 if (options.series.pie.radius == "auto") {
102 if (options.series.pie.label.show) {
103 options.series.pie.radius = 3/4;
104 } else {
105 options.series.pie.radius = 1;
106 }
107 }
108
109 // ensure sane tilt
110
111 if (options.series.pie.tilt > 1) {
112 options.series.pie.tilt = 1;
113 } else if (options.series.pie.tilt < 0) {
114 options.series.pie.tilt = 0;
115 }
116 }
117 });
118
119 plot.hooks.bindEvents.push(function(plot, eventHolder) {
120 var options = plot.getOptions();
121 if (options.series.pie.show) {
122 if (options.grid.hoverable) {
123 eventHolder.unbind("mousemove").mousemove(onMouseMove);
124 }
125 if (options.grid.clickable) {
126 eventHolder.unbind("click").click(onClick);
127 }
128 }
129 });
130
131 plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) {
132 var options = plot.getOptions();
133 if (options.series.pie.show) {
134 processDatapoints(plot, series, data, datapoints);
135 }
136 });
137
138 plot.hooks.drawOverlay.push(function(plot, octx) {
139 var options = plot.getOptions();
140 if (options.series.pie.show) {
141 drawOverlay(plot, octx);
142 }
143 });
144
145 plot.hooks.draw.push(function(plot, newCtx) {
146 var options = plot.getOptions();
147 if (options.series.pie.show) {
148 draw(plot, newCtx);
149 }
150 });
151
152 function processDatapoints(plot, series, datapoints) {
153 if (!processed) {
154 processed = true;
155 canvas = plot.getCanvas();
156 target = $(canvas).parent();
157 options = plot.getOptions();
158 plot.setData(combine(plot.getData()));
159 }
160 }
161
162 function combine(data) {
163
164 var total = 0,
165 combined = 0,
166 numCombined = 0,
167 color = options.series.pie.combine.color,
168 newdata = [];
169
170 // Fix up the raw data from Flot, ensuring the data is numeric
171
172 for (var i = 0; i < data.length; ++i) {
173
174 var value = data[i].data;
175
176 // If the data is an array, we'll assume that it's a standard
177 // Flot x-y pair, and are concerned only with the second value.
178
179 // Note how we use the original array, rather than creating a
180 // new one; this is more efficient and preserves any extra data
181 // that the user may have stored in higher indexes.
182
183 if ($.isArray(value) && value.length == 1) {
184 value = value[0];
185 }
186
187 if ($.isArray(value)) {
188 // Equivalent to $.isNumeric() but compatible with jQuery < 1.7
189 if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) {
190 value[1] = +value[1];
191 } else {
192 value[1] = 0;
193 }
194 } else if (!isNaN(parseFloat(value)) && isFinite(value)) {
195 value = [1, +value];
196 } else {
197 value = [1, 0];
198 }
199
200 data[i].data = [value];
201 }
202
203 // Sum up all the slices, so we can calculate percentages for each
204
205 for (var i = 0; i < data.length; ++i) {
206 total += data[i].data[0][1];
207 }
208
209 // Count the number of slices with percentages below the combine
210 // threshold; if it turns out to be just one, we won't combine.
211
212 for (var i = 0; i < data.length; ++i) {
213 var value = data[i].data[0][1];
214 if (value / total <= options.series.pie.combine.threshold) {
215 combined += value;
216 numCombined++;
217 if (!color) {
218 color = data[i].color;
219 }
220 }
221 }
222
223 for (var i = 0; i < data.length; ++i) {
224 var value = data[i].data[0][1];
225 if (numCombined < 2 || value / total > options.series.pie.combine.threshold) {
226 newdata.push({
227 data: [[1, value]],
228 color: data[i].color,
229 label: data[i].label,
230 angle: value * Math.PI * 2 / total,
231 percent: value / (total / 100)
232 });
233 }
234 }
235
236 if (numCombined > 1) {
237 newdata.push({
238 data: [[1, combined]],
239 color: color,
240 label: options.series.pie.combine.label,
241 angle: combined * Math.PI * 2 / total,
242 percent: combined / (total / 100)
243 });
244 }
245
246 return newdata;
247 }
248
249 function draw(plot, newCtx) {
250
251 if (!target) {
252 return; // if no series were passed
253 }
254
255 var canvasWidth = plot.getPlaceholder().width(),
256 canvasHeight = plot.getPlaceholder().height(),
257 legendWidth = target.children().filter(".legend").children().width() || 0;
258
259 ctx = newCtx;
260
261 // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE!
262
263 // When combining smaller slices into an 'other' slice, we need to
264 // add a new series. Since Flot gives plugins no way to modify the
265 // list of series, the pie plugin uses a hack where the first call
266 // to processDatapoints results in a call to setData with the new
267 // list of series, then subsequent processDatapoints do nothing.
268
269 // The plugin-global 'processed' flag is used to control this hack;
270 // it starts out false, and is set to true after the first call to
271 // processDatapoints.
272
273 // Unfortunately this turns future setData calls into no-ops; they
274 // call processDatapoints, the flag is true, and nothing happens.
275
276 // To fix this we'll set the flag back to false here in draw, when
277 // all series have been processed, so the next sequence of calls to
278 // processDatapoints once again starts out with a slice-combine.
279 // This is really a hack; in 0.9 we need to give plugins a proper
280 // way to modify series before any processing begins.
281
282 processed = false;
283
284 // calculate maximum radius and center point
285
286 maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2;
287 centerTop = canvasHeight / 2 + options.series.pie.offset.top;
288 centerLeft = canvasWidth / 2;
289
290 if (options.series.pie.offset.left == "auto") {
291 if (options.legend.position.match("w")) {
292 centerLeft += legendWidth / 2;
293 } else {
294 centerLeft -= legendWidth / 2;
295 }
296 } else {
297 centerLeft += options.series.pie.offset.left;
298 }
299
300 if (centerLeft < maxRadius) {
301 centerLeft = maxRadius;
302 } else if (centerLeft > canvasWidth - maxRadius) {
303 centerLeft = canvasWidth - maxRadius;
304 }
305
306 var slices = plot.getData(),
307 attempts = 0;
308
309 // Keep shrinking the pie's radius until drawPie returns true,
310 // indicating that all the labels fit, or we try too many times.
311
312 do {
313 if (attempts > 0) {
314 maxRadius *= REDRAW_SHRINK;
315 }
316 attempts += 1;
317 clear();
318 if (options.series.pie.tilt <= 0.8) {
319 drawShadow();
320 }
321 } while (!drawPie() && attempts < REDRAW_ATTEMPTS)
322
323 if (attempts >= REDRAW_ATTEMPTS) {
324 clear();
325 target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
326 }
327
328 if (plot.setSeries && plot.insertLegend) {
329 plot.setSeries(slices);
330 plot.insertLegend();
331 }
332
333 // we're actually done at this point, just defining internal functions at this point
334
335 function clear() {
336 ctx.clearRect(0, 0, canvasWidth, canvasHeight);
337 target.children().filter(".pieLabel, .pieLabelBackground").remove();
338 }
339
340 function drawShadow() {
341
342 var shadowLeft = options.series.pie.shadow.left;
343 var shadowTop = options.series.pie.shadow.top;
344 var edge = 10;
345 var alpha = options.series.pie.shadow.alpha;
346 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
347
348 if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) {
349 return; // shadow would be outside canvas, so don't draw it
350 }
351
352 ctx.save();
353 ctx.translate(shadowLeft,shadowTop);
354 ctx.globalAlpha = alpha;
355 ctx.fillStyle = "#000";
356
357 // center and rotate to starting position
358
359 ctx.translate(centerLeft,centerTop);
360 ctx.scale(1, options.series.pie.tilt);
361
362 //radius -= edge;
363
364 for (var i = 1; i <= edge; i++) {
365 ctx.beginPath();
366 ctx.arc(0, 0, radius, 0, Math.PI * 2, false);
367 ctx.fill();
368 radius -= i;
369 }
370
371 ctx.restore();
372 }
373
374 function drawPie() {
375
376 var startAngle = Math.PI * options.series.pie.startAngle;
377 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
378
379 // center and rotate to starting position
380
381 ctx.save();
382 ctx.translate(centerLeft,centerTop);
383 ctx.scale(1, options.series.pie.tilt);
384 //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
385
386 // draw slices
387
388 ctx.save();
389 var currentAngle = startAngle;
390 for (var i = 0; i < slices.length; ++i) {
391 slices[i].startAngle = currentAngle;
392 drawSlice(slices[i].angle, slices[i].color, true);
393 }
394 ctx.restore();
395
396 // draw slice outlines
397
398 if (options.series.pie.stroke.width > 0) {
399 ctx.save();
400 ctx.lineWidth = options.series.pie.stroke.width;
401 currentAngle = startAngle;
402 for (var i = 0; i < slices.length; ++i) {
403 drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
404 }
405 ctx.restore();
406 }
407
408 // draw donut hole
409
410 drawDonutHole(ctx);
411
412 ctx.restore();
413
414 // Draw the labels, returning true if they fit within the plot
415
416 if (options.series.pie.label.show) {
417 return drawLabels();
418 } else return true;
419
420 function drawSlice(angle, color, fill) {
421
422 if (angle <= 0 || isNaN(angle)) {
423 return;
424 }
425
426 if (fill) {
427 ctx.fillStyle = color;
428 } else {
429 ctx.strokeStyle = color;
430 ctx.lineJoin = "round";
431 }
432
433 ctx.beginPath();
434 if (Math.abs(angle - Math.PI * 2) > 0.000000001) {
435 ctx.moveTo(0, 0); // Center of the pie
436 }
437
438 //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
439 ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false);
440 ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false);
441 ctx.closePath();
442 //ctx.rotate(angle); // This doesn't work properly in Opera
443 currentAngle += angle;
444
445 if (fill) {
446 ctx.fill();
447 } else {
448 ctx.stroke();
449 }
450 }
451
452 function drawLabels() {
453
454 var currentAngle = startAngle;
455 var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius;
456
457 for (var i = 0; i < slices.length; ++i) {
458 if (slices[i].percent >= options.series.pie.label.threshold * 100) {
459 if (!drawLabel(slices[i], currentAngle, i)) {
460 return false;
461 }
462 }
463 currentAngle += slices[i].angle;
464 }
465
466 return true;
467
468 function drawLabel(slice, startAngle, index) {
469
470 if (slice.data[0][1] == 0) {
471 return true;
472 }
473
474 // format label text
475
476 var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
477
478 if (lf) {
479 text = lf(slice.label, slice);
480 } else {
481 text = slice.label;
482 }
483
484 if (plf) {
485 text = plf(text, slice);
486 }
487
488 var halfAngle = ((startAngle + slice.angle) + startAngle) / 2;
489 var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
490 var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
491
492 var html = "<span class='pieLabel' id='pieLabel" + index + "' style='position:absolute;top:" + y + "px;left:" + x + "px;'>" + text + "</span>";
493 target.append(html);
494
495 var label = target.children("#pieLabel" + index);
496 var labelTop = (y - label.height() / 2);
497 var labelLeft = (x - label.width() / 2);
498
499 label.css("top", labelTop);
500 label.css("left", labelLeft);
501
502 // check to make sure that the label is not outside the canvas
503
504 if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) {
505 return false;
506 }
507
508 if (options.series.pie.label.background.opacity != 0) {
509
510 // put in the transparent background separately to avoid blended labels and label boxes
511
512 var c = options.series.pie.label.background.color;
513
514 if (c == null) {
515 c = slice.color;
516 }
517
518 var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;";
519 $("<div class='pieLabelBackground' style='position:absolute;width:" + label.width() + "px;height:" + label.height() + "px;" + pos + "background-color:" + c + ";'></div>")
520 .css("opacity", options.series.pie.label.background.opacity)
521 .insertBefore(label);
522 }
523
524 return true;
525 } // end individual label function
526 } // end drawLabels function
527 } // end drawPie function
528 } // end draw function
529
530 // Placed here because it needs to be accessed from multiple locations
531
532 function drawDonutHole(layer) {
533 if (options.series.pie.innerRadius > 0) {
534
535 // subtract the center
536
537 layer.save();
538 var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
539 layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color
540 layer.beginPath();
541 layer.fillStyle = options.series.pie.stroke.color;
542 layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
543 layer.fill();
544 layer.closePath();
545 layer.restore();
546
547 // add inner stroke
548
549 layer.save();
550 layer.beginPath();
551 layer.strokeStyle = options.series.pie.stroke.color;
552 layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
553 layer.stroke();
554 layer.closePath();
555 layer.restore();
556
557 // TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
558 }
559 }
560
561 //-- Additional Interactive related functions --
562
563 function isPointInPoly(poly, pt) {
564 for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
565 ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
566 && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
567 && (c = !c);
568 return c;
569 }
570
571 function findNearbySlice(mouseX, mouseY) {
572
573 var slices = plot.getData(),
574 options = plot.getOptions(),
575 radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius,
576 x, y;
577
578 for (var i = 0; i < slices.length; ++i) {
579
580 var s = slices[i];
581
582 if (s.pie.show) {
583
584 ctx.save();
585 ctx.beginPath();
586 ctx.moveTo(0, 0); // Center of the pie
587 //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
588 ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false);
589 ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false);
590 ctx.closePath();
591 x = mouseX - centerLeft;
592 y = mouseY - centerTop;
593
594 if (ctx.isPointInPath) {
595 if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) {
596 ctx.restore();
597 return {
598 datapoint: [s.percent, s.data],
599 dataIndex: 0,
600 series: s,
601 seriesIndex: i
602 };
603 }
604 } else {
605
606 // excanvas for IE doesn;t support isPointInPath, this is a workaround.
607
608 var p1X = radius * Math.cos(s.startAngle),
609 p1Y = radius * Math.sin(s.startAngle),
610 p2X = radius * Math.cos(s.startAngle + s.angle / 4),
611 p2Y = radius * Math.sin(s.startAngle + s.angle / 4),
612 p3X = radius * Math.cos(s.startAngle + s.angle / 2),
613 p3Y = radius * Math.sin(s.startAngle + s.angle / 2),
614 p4X = radius * Math.cos(s.startAngle + s.angle / 1.5),
615 p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5),
616 p5X = radius * Math.cos(s.startAngle + s.angle),
617 p5Y = radius * Math.sin(s.startAngle + s.angle),
618 arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]],
619 arrPoint = [x, y];
620
621 // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
622
623 if (isPointInPoly(arrPoly, arrPoint)) {
624 ctx.restore();
625 return {
626 datapoint: [s.percent, s.data],
627 dataIndex: 0,
628 series: s,
629 seriesIndex: i
630 };
631 }
632 }
633
634 ctx.restore();
635 }
636 }
637
638 return null;
639 }
640
641 function onMouseMove(e) {
642 triggerClickHoverEvent("plothover", e);
643 }
644
645 function onClick(e) {
646 triggerClickHoverEvent("plotclick", e);
647 }
648
649 // trigger click or hover event (they send the same parameters so we share their code)
650
651 function triggerClickHoverEvent(eventname, e) {
652
653 var offset = plot.offset();
654 var canvasX = parseInt(e.pageX - offset.left);
655 var canvasY = parseInt(e.pageY - offset.top);
656 var item = findNearbySlice(canvasX, canvasY);
657
658 if (options.grid.autoHighlight) {
659
660 // clear auto-highlights
661
662 for (var i = 0; i < highlights.length; ++i) {
663 var h = highlights[i];
664 if (h.auto == eventname && !(item && h.series == item.series)) {
665 unhighlight(h.series);
666 }
667 }
668 }
669
670 // highlight the slice
671
672 if (item) {
673 highlight(item.series, eventname);
674 }
675
676 // trigger any hover bind events
677
678 var pos = { pageX: e.pageX, pageY: e.pageY };
679 target.trigger(eventname, [pos, item]);
680 }
681
682 function highlight(s, auto) {
683 //if (typeof s == "number") {
684 // s = series[s];
685 //}
686
687 var i = indexOfHighlight(s);
688
689 if (i == -1) {
690 highlights.push({ series: s, auto: auto });
691 plot.triggerRedrawOverlay();
692 } else if (!auto) {
693 highlights[i].auto = false;
694 }
695 }
696
697 function unhighlight(s) {
698 if (s == null) {
699 highlights = [];
700 plot.triggerRedrawOverlay();
701 }
702
703 //if (typeof s == "number") {
704 // s = series[s];
705 //}
706
707 var i = indexOfHighlight(s);
708
709 if (i != -1) {
710 highlights.splice(i, 1);
711 plot.triggerRedrawOverlay();
712 }
713 }
714
715 function indexOfHighlight(s) {
716 for (var i = 0; i < highlights.length; ++i) {
717 var h = highlights[i];
718 if (h.series == s)
719 return i;
720 }
721 return -1;
722 }
723
724 function drawOverlay(plot, octx) {
725
726 var options = plot.getOptions();
727
728 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
729
730 octx.save();
731 octx.translate(centerLeft, centerTop);
732 octx.scale(1, options.series.pie.tilt);
733
734 for (var i = 0; i < highlights.length; ++i) {
735 drawHighlight(highlights[i].series);
736 }
737
738 drawDonutHole(octx);
739
740 octx.restore();
741
742 function drawHighlight(series) {
743
744 if (series.angle <= 0 || isNaN(series.angle)) {
745 return;
746 }
747
748 //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
749 octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor
750 octx.beginPath();
751 if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) {
752 octx.moveTo(0, 0); // Center of the pie
753 }
754 octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false);
755 octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false);
756 octx.closePath();
757 octx.fill();
758 }
759 }
760 } // end init (plugin body)
761
762 // define pie specific options and their default values
763
764 var options = {
765 series: {
766 pie: {
767 show: false,
768 radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
769 innerRadius: 0, /* for donut */
770 startAngle: 3/2,
771 tilt: 1,
772 shadow: {
773 left: 5, // shadow left offset
774 top: 15, // shadow top offset
775 alpha: 0.02 // shadow alpha
776 },
777 offset: {
778 top: 0,
779 left: "auto"
780 },
781 stroke: {
782 color: "#fff",
783 width: 1
784 },
785 label: {
786 show: "auto",
787 formatter: function(label, slice) {
788 return "<div style='font-size:x-small;text-align:center;padding:2px;color:" + slice.color + ";'>" + label + "<br/>" + Math.round(slice.percent) + "%</div>";
789 }, // formatter function
790 radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
791 background: {
792 color: null,
793 opacity: 0
794 },
795 threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow)
796 },
797 combine: {
798 threshold: -1, // percentage at which to combine little slices into one larger slice
799 color: null, // color to give the new slice (auto-generated if null)
800 label: "Other" // label to give the new slice
801 },
802 highlight: {
803 //color: "#fff", // will add this functionality once parseColor is available
804 opacity: 0.5
805 }
806 }
807 }
808 };
809
810 $.plot.plugins.push({
811 init: init,
812 options: options,
813 name: "pie",
814 version: "1.1"
815 });
816
817 })(jQuery);