PageSourceSearch

http://nexblue.co.kr/plugin/mongtree/admin/assets/vendor/flot/jquery.flot.pie.js

js nexblue.co.kr collected 2026-09-28 11:07:06 UTC 23,809 bytes, 820 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.