Commit 81591ee4 authored by David Schnur's avatar David Schnur

Merge pull request #879 from dnschnur/pie-plugin-fixes

Fixed pie plugin 100% slice issues on IE7/8.
parents 2f7cfcb5 eac2d075
......@@ -28,6 +28,10 @@ $(function () {
{ label: "Series5", data: [[1,80]]},
{ label: "Series6", data: [[1,0]]}
];
var data = [
{ label: "Series A", data: 0.2063},
{ label: "Series B", data: 38888}
];
*/
// Randomly Generated Data
......
/*
Flot plugin for rendering pie charts. The plugin assumes the data is
coming is as a single data value for each series, and each of those
values is a positive value or zero (negative numbers don't make
any sense and will cause strange effects). The data values do
NOT need to be passed in as percentage values because it
internally calculates the total and percentages.
Flot plugin for rendering pie charts.
Copyright (c) 2007-2012 IOLA and Ole Laursen.
Licensed under the MIT license.
The plugin assumes that each series has a single data value, and that
each value is a positive integer or zero. Negative numbers don't make
sense for a pie chart, and have unpredictable results. The values do
NOT need to be passed in as percentages; the plugin will calculate the
total and per-slice percentages internally.
* Created by Brian Medendorp, June 2009
* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
......@@ -16,7 +20,6 @@ internally calculates the total and percentages.
2009-11-17: Added IE hover capability submitted by Anthony Aragues
2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)
Available options are:
series: {
pie: {
......@@ -58,10 +61,10 @@ More detail and specific examples can be found in the included HTML file.
*/
(function ($)
{
function init(plot) // this is the "body" of the plugin
{
(function($) {
function init(plot) {
var canvas = null;
var canvasWidth = 0;
var canvasHeight = 0;
......@@ -78,271 +81,286 @@ More detail and specific examples can be found in the included HTML file.
var raw = false;
// interactive variables
var highlights = [];
// add hook to determine if pie plugin in enabled, and then perform necessary operations
plot.hooks.processOptions.push(checkPieEnabled);
plot.hooks.bindEvents.push(bindEvents);
// check to see if the pie plugin is enabled
function checkPieEnabled(plot, options)
{
if (options.series.pie.show)
{
function checkPieEnabled(plot, options) {
if (options.series.pie.show) {
//disable grid
options.grid.show = false;
// set labels.show
if (options.series.pie.label.show=='auto')
if (options.legend.show)
if (options.series.pie.label.show == "auto") {
if (options.legend.show) {
options.series.pie.label.show = false;
else
} else {
options.series.pie.label.show = true;
}
}
// set radius
if (options.series.pie.radius=='auto')
if (options.series.pie.label.show)
if (options.series.pie.radius == "auto") {
if (options.series.pie.label.show) {
options.series.pie.radius = 3/4;
else
} else {
options.series.pie.radius = 1;
}
}
// ensure sane tilt
if (options.series.pie.tilt>1)
options.series.pie.tilt=1;
if (options.series.pie.tilt<0)
options.series.pie.tilt=0;
if (options.series.pie.tilt > 1) {
options.series.pie.tilt = 1;
} else if (options.series.pie.tilt < 0) {
options.series.pie.tilt = 0;
}
// add processData hook to do transformations on the data
plot.hooks.processDatapoints.push(processDatapoints);
plot.hooks.drawOverlay.push(drawOverlay);
// add draw hook
// draw hook
plot.hooks.draw.push(draw);
}
}
// bind hoverable events
function bindEvents(plot, eventHolder)
{
function bindEvents(plot, eventHolder) {
var options = plot.getOptions();
if (options.series.pie.show) {
if (options.grid.hoverable) {
eventHolder.unbind("mousemove").mousemove(onMouseMove);
}
if (options.grid.clickable) {
eventHolder.unbind("click").click(onClick);
}
}
}
if (options.series.pie.show && options.grid.hoverable)
eventHolder.unbind('mousemove').mousemove(onMouseMove);
// debugging function that prints out an object
if (options.series.pie.show && options.grid.clickable)
eventHolder.unbind('click').click(onClick);
}
function alertObject(obj) {
var msg = "";
// debugging function that prints out an object
function alertObject(obj)
{
var msg = '';
function traverse(obj, depth)
{
if (!depth)
function traverse(obj, depth) {
if (!depth) {
depth = 0;
for (var i = 0; i < obj.length; ++i)
{
for (var j=0; j<depth; j++)
msg += '\t';
}
if( typeof obj[i] == "object")
{ // its an object
msg += ''+i+':\n';
traverse(obj[i], depth+1);
for (var i = 0; i < obj.length; ++i) {
for (var j = 0; j < depth; j++) {
msg += "\t";
}
else
{ // its a value
msg += ''+i+': '+obj[i]+'\n';
if( typeof obj[i] == "object") {
msg += "" + i + ":\n";
traverse(obj[i], depth + 1);
} else {
msg += "" + i + ": " + obj[i] + "\n";
}
}
}
traverse(obj);
alert(msg);
}
function calcTotal(data)
{
for (var i = 0; i < data.length; ++i)
{
function calcTotal(data) {
for (var i = 0; i < data.length; ++i) {
var item = parseFloat(data[i].data[0][1]);
if (item)
if (item) {
total += item;
}
}
}
function processDatapoints(plot, series, data, datapoints)
{
if (!processed)
{
function processDatapoints(plot, series, data, datapoints) {
if (!processed) {
processed = true;
canvas = plot.getCanvas();
target = $(canvas).parent();
options = plot.getOptions();
canvasWidth = plot.getPlaceholder().width();
canvasHeight = plot.getPlaceholder().height();
plot.setData(combine(plot.getData()));
}
}
function setupPie()
{
legendWidth = target.children().filter('.legend').children().width() || 0;
function setupPie() {
legendWidth = target.children().filter(".legend").children().width() || 0;
// calculate maximum radius and center point
maxRadius = Math.min(canvasWidth,(canvasHeight/options.series.pie.tilt))/2;
centerTop = (canvasHeight/2)+options.series.pie.offset.top;
centerLeft = (canvasWidth/2);
if (options.series.pie.offset.left=='auto')
if (options.legend.position.match('w'))
centerLeft += legendWidth/2;
else
centerLeft -= legendWidth/2;
else
maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2;
centerTop = canvasHeight / 2 + options.series.pie.offset.top;
centerLeft = canvasWidth / 2;
if (options.series.pie.offset.left == "auto") {
if (options.legend.position.match("w")) {
centerLeft += legendWidth / 2;
} else {
centerLeft -= legendWidth / 2;
}
} else {
centerLeft += options.series.pie.offset.left;
}
if (centerLeft<maxRadius)
if (centerLeft < maxRadius) {
centerLeft = maxRadius;
else if (centerLeft>canvasWidth-maxRadius)
centerLeft = canvasWidth-maxRadius;
}
function fixData(data)
{
for (var i = 0; i < data.length; ++i)
{
if (typeof(data[i].data)=='number')
data[i].data = [[1,data[i].data]];
else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')
{
if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')
data[i].label = data[i].data.label; // fix weirdness coming from flot
data[i].data = [[1,0]];
} else if (centerLeft > canvasWidth - maxRadius) {
centerLeft = canvasWidth - maxRadius;
}
}
function fixData(data) {
for (var i = 0; i < data.length; ++i) {
if (typeof(data[i].data) == "number") {
data[i].data = [[1, data[i].data]];
} else if (typeof(data[i].data) == "undefined" || typeof(data[i].data[0]) == "undefined") {
if (typeof(data[i].data) != "undefined" && typeof(data[i].data.label) != "undefined") {
data[i].label = data[i].data.label; // fix weirdness coming from flot
}
data[i].data = [[1, 0]];
}
}
return data;
}
function combine(data)
{
function combine(data) {
data = fixData(data);
calcTotal(data);
var combined = 0;
var numCombined = 0;
var color = options.series.pie.combine.color;
var newdata = [];
for (var i = 0; i < data.length; ++i)
{
for (var i = 0; i < data.length; ++i) {
// make sure its a number
data[i].data[0][1] = parseFloat(data[i].data[0][1]);
if (!data[i].data[0][1])
if (!data[i].data[0][1]) {
data[i].data[0][1] = 0;
}
if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)
{
if (data[i].data[0][1] / total <= options.series.pie.combine.threshold) {
combined += data[i].data[0][1];
numCombined++;
if (!color)
if (!color) {
color = data[i].color;
}
else
{
} else {
newdata.push({
data: [[1,data[i].data[0][1]]],
data: [[1, data[i].data[0][1]]],
color: data[i].color,
label: data[i].label,
angle: (data[i].data[0][1]*(Math.PI*2))/total,
percent: (data[i].data[0][1]/total*100)
angle: data[i].data[0][1] * Math.PI * 2 / total,
percent: data[i].data[0][1] / total * 100
});
}
}
if (numCombined>0)
if (numCombined > 0) {
newdata.push({
data: [[1,combined]],
data: [[1, combined]],
color: color,
label: options.series.pie.combine.label,
angle: (combined*(Math.PI*2))/total,
percent: (combined/total*100)
angle: combined * Math.PI * 2 / total,
percent: combined / total * 100
});
}
return newdata;
}
function draw(plot, newCtx)
{
if (!target) return; // if no series were passed
ctx = newCtx;
function draw(plot, newCtx) {
if (!target) {
return; // if no series were passed
}
ctx = newCtx;
setupPie();
var slices = plot.getData();
var slices = plot.getData();
var attempts = 0;
while (redraw && attempts<redrawAttempts)
{
while (redraw && attempts<redrawAttempts) {
redraw = false;
if (attempts>0)
if (attempts > 0) {
maxRadius *= shrink;
}
attempts += 1;
clear();
if (options.series.pie.tilt<=0.8)
if (options.series.pie.tilt <= 0.8) {
drawShadow();
}
drawPie();
}
if (attempts >= redrawAttempts) {
clear();
target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>');
target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
}
if ( plot.setSeries && plot.insertLegend )
{
if (plot.setSeries && plot.insertLegend) {
plot.setSeries(slices);
plot.insertLegend();
}
// we're actually done at this point, just defining internal functions at this point
function clear()
{
ctx.clearRect(0,0,canvasWidth,canvasHeight);
target.children().filter('.pieLabel, .pieLabelBackground').remove();
function clear() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
target.children().filter(".pieLabel, .pieLabelBackground").remove();
}
function drawShadow()
{
function drawShadow() {
var shadowLeft = options.series.pie.shadow.left;
var shadowTop = options.series.pie.shadow.top;
var edge = 10;
var alpha = options.series.pie.shadow.alpha;
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
// set radius
if (options.series.pie.radius>1)
var radius = options.series.pie.radius;
else
var radius = maxRadius * options.series.pie.radius;
if (radius>=(canvasWidth/2)-shadowLeft || radius*options.series.pie.tilt>=(canvasHeight/2)-shadowTop || radius<=edge)
if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) {
return; // shadow would be outside canvas, so don't draw it
}
ctx.save();
ctx.translate(shadowLeft,shadowTop);
ctx.globalAlpha = alpha;
ctx.fillStyle = '#000';
ctx.fillStyle = "#000";
// center and rotate to starting position
ctx.translate(centerLeft,centerTop);
ctx.scale(1, options.series.pie.tilt);
//radius -= edge;
for (var i=1; i<=edge; i++)
{
for (var i = 1; i <= edge; i++) {
ctx.beginPath();
ctx.arc(0,0,radius,0,Math.PI*2,false);
ctx.arc(0, 0, radius, 0, Math.PI * 2, false);
ctx.fill();
radius -= i;
}
......@@ -350,27 +368,23 @@ More detail and specific examples can be found in the included HTML file.
ctx.restore();
}
function drawPie()
{
var startAngle = Math.PI * options.series.pie.startAngle;
var radius;
function drawPie() {
// set radius
if (options.series.pie.radius > 1)
radius = options.series.pie.radius;
else radius = maxRadius * options.series.pie.radius;
var startAngle = Math.PI * options.series.pie.startAngle;
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
// center and rotate to starting position
ctx.save();
ctx.translate(centerLeft,centerTop);
ctx.scale(1, options.series.pie.tilt);
//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
// draw slices
ctx.save();
var currentAngle = startAngle;
for (var i = 0; i < slices.length; ++i)
{
for (var i = 0; i < slices.length; ++i) {
slices[i].startAngle = currentAngle;
drawSlice(slices[i].angle, slices[i].color, true);
}
......@@ -382,106 +396,124 @@ More detail and specific examples can be found in the included HTML file.
ctx.save();
ctx.lineWidth = options.series.pie.stroke.width;
currentAngle = startAngle;
for (var i = 0; i < slices.length; ++i)
for (var i = 0; i < slices.length; ++i) {
drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
}
ctx.restore();
}
// draw donut hole
drawDonutHole(ctx);
// draw labels
if (options.series.pie.label.show)
if (options.series.pie.label.show) {
drawLabels();
}
// restore to original state
ctx.restore();
function drawSlice(angle, color, fill)
{
if (angle <= 0 || isNaN(angle))
function drawSlice(angle, color, fill) {
if (angle <= 0 || isNaN(angle)) {
return;
}
if (fill)
if (fill) {
ctx.fillStyle = color;
else
{
} else {
ctx.strokeStyle = color;
ctx.lineJoin = 'round';
ctx.lineJoin = "round";
}
ctx.beginPath();
if (Math.abs(angle - Math.PI*2) > 0.000000001)
ctx.moveTo(0,0); // Center of the pie
else if ($.browser.msie)
if (Math.abs(angle - Math.PI * 2) > 0.000000001) {
ctx.moveTo(0, 0); // Center of the pie
} else if ($.browser.msie) {
angle -= 0.0001;
//ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera
ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);
}
//ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false);
ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false);
ctx.closePath();
//ctx.rotate(angle); // This doesn't work properly in Opera
currentAngle += angle;
if (fill)
if (fill) {
ctx.fill();
else
} else {
ctx.stroke();
}
}
function drawLabels() {
function drawLabels()
{
var currentAngle = startAngle;
var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius;
// set radius
if (options.series.pie.label.radius>1)
var radius = options.series.pie.label.radius;
else
var radius = maxRadius * options.series.pie.label.radius;
for (var i = 0; i < slices.length; ++i)
{
if (slices[i].percent >= options.series.pie.label.threshold*100)
for (var i = 0; i < slices.length; ++i) {
if (slices[i].percent >= options.series.pie.label.threshold * 100) {
drawLabel(slices[i], currentAngle, i);
}
currentAngle += slices[i].angle;
}
function drawLabel(slice, startAngle, index)
{
if (slice.data[0][1]==0)
function drawLabel(slice, startAngle, index) {
if (slice.data[0][1] == 0) {
return;
}
// format label text
var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
if (lf)
if (lf) {
text = lf(slice.label, slice);
else
} else {
text = slice.label;
if (plf)
}
if (plf) {
text = plf(text, slice);
}
var halfAngle = ((startAngle+slice.angle) + startAngle)/2;
var halfAngle = ((startAngle + slice.angle) + startAngle) / 2;
var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>";
var html = "<span class='pieLabel' id='pieLabel" + index + "' style='position:absolute;top:" + y + "px;left:" + x + "px;'>" + text + "</span>";
target.append(html);
var label = target.children('#pieLabel'+index);
var labelTop = (y - label.height()/2);
var labelLeft = (x - label.width()/2);
label.css('top', labelTop);
label.css('left', labelLeft);
var label = target.children("#pieLabel" + index);
var labelTop = (y - label.height() / 2);
var labelLeft = (x - label.width() / 2);
label.css("top", labelTop);
label.css("left", labelLeft);
// check to make sure that the label is not outside the canvas
if (0-labelTop>0 || 0-labelLeft>0 || canvasHeight-(labelTop+label.height())<0 || canvasWidth-(labelLeft+label.width())<0)
if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) {
redraw = true;
}
if (options.series.pie.label.background.opacity != 0) {
// put in the transparent background separately to avoid blended labels and label boxes
var c = options.series.pie.label.background.color;
if (c == null) {
c = slice.color;
}
var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';
$('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);
var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;";
$("<div class='pieLabelBackground' style='position:absolute;width:" + label.width() + "px;height:" + label.height() + "px;" + pos + "background-color:" + c + ";'></div>")
.css("opacity", options.series.pie.label.background.opacity)
.insertBefore(label);
}
} // end individual label function
} // end drawLabels function
......@@ -489,38 +521,39 @@ More detail and specific examples can be found in the included HTML file.
} // end draw function
// Placed here because it needs to be accessed from multiple locations
function drawDonutHole(layer)
{
// draw donut hole
if(options.series.pie.innerRadius > 0)
{
function drawDonutHole(layer) {
if (options.series.pie.innerRadius > 0) {
// subtract the center
layer.save();
innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color
layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color
layer.beginPath();
layer.fillStyle = options.series.pie.stroke.color;
layer.arc(0,0,innerRadius,0,Math.PI*2,false);
layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
layer.fill();
layer.closePath();
layer.restore();
// add inner stroke
layer.save();
layer.beginPath();
layer.strokeStyle = options.series.pie.stroke.color;
layer.arc(0,0,innerRadius,0,Math.PI*2,false);
layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
layer.stroke();
layer.closePath();
layer.restore();
// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
}
}
//-- Additional Interactive related functions --
function isPointInPoly(poly, pt)
{
function isPointInPoly(poly, pt) {
for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
......@@ -528,57 +561,68 @@ More detail and specific examples can be found in the included HTML file.
return c;
}
function findNearbySlice(mouseX, mouseY)
{
var slices = plot.getData(),
options = plot.getOptions(),
radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
function findNearbySlice(mouseX, mouseY) {
var slices = plot.getData();
var options = plot.getOptions();
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
for (var i = 0; i < slices.length; ++i) {
for (var i = 0; i < slices.length; ++i)
{
var s = slices[i];
if(s.pie.show)
{
if (s.pie.show) {
ctx.save();
ctx.beginPath();
ctx.moveTo(0,0); // Center of the pie
ctx.moveTo(0, 0); // Center of the pie
//ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);
ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false);
ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false);
ctx.closePath();
x = mouseX-centerLeft;
y = mouseY-centerTop;
if(ctx.isPointInPath)
{
if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))
{
//alert('found slice!');
x = mouseX - centerLeft;
y = mouseY - centerTop;
if (ctx.isPointInPath) {
if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) {
ctx.restore();
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
}
return {
datapoint: [s.percent, s.data],
dataIndex: 0,
series: s,
seriesIndex: i
};
}
else
{
} else {
// excanvas for IE doesn;t support isPointInPath, this is a workaround.
p1X = (radius * Math.cos(s.startAngle));
p1Y = (radius * Math.sin(s.startAngle));
p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));
p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));
p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));
p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));
p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));
p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));
p5X = (radius * Math.cos(s.startAngle+s.angle));
p5Y = (radius * Math.sin(s.startAngle+s.angle));
arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];
arrPoint = [x,y];
p1X = radius * Math.cos(s.startAngle);
p1Y = radius * Math.sin(s.startAngle);
p2X = radius * Math.cos(s.startAngle + s.angle / 4);
p2Y = radius * Math.sin(s.startAngle + s.angle / 4);
p3X = radius * Math.cos(s.startAngle + s.angle / 2);
p3Y = radius * Math.sin(s.startAngle + s.angle / 2);
p4X = radius * Math.cos(s.startAngle + s.angle / 1.5);
p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5);
p5X = radius * Math.cos(s.startAngle + s.angle);
p5Y = radius * Math.sin(s.startAngle + s.angle);
arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]];
arrPoint = [x, y];
// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
if(isPointInPoly(arrPoly, arrPoint))
{
if (isPointInPoly(arrPoly, arrPoint)) {
ctx.restore();
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
return {
datapoint: [s.percent, s.data],
dataIndex: 0,
series: s,
seriesIndex: i
};
}
}
ctx.restore();
}
}
......@@ -586,82 +630,82 @@ More detail and specific examples can be found in the included HTML file.
return null;
}
function onMouseMove(e)
{
triggerClickHoverEvent('plothover', e);
function onMouseMove(e) {
triggerClickHoverEvent("plothover", e);
}
function onClick(e)
{
triggerClickHoverEvent('plotclick', e);
function onClick(e) {
triggerClickHoverEvent("plotclick", e);
}
// trigger click or hover event (they send the same parameters so we share their code)
function triggerClickHoverEvent(eventname, e)
{
var offset = plot.offset(),
canvasX = parseInt(e.pageX - offset.left),
canvasY = parseInt(e.pageY - offset.top),
item = findNearbySlice(canvasX, canvasY);
if (options.grid.autoHighlight)
{
function triggerClickHoverEvent(eventname, e) {
var offset = plot.offset();
var canvasX = parseInt(e.pageX - offset.left);
var canvasY = parseInt(e.pageY - offset.top);
var item = findNearbySlice(canvasX, canvasY);
if (options.grid.autoHighlight) {
// clear auto-highlights
for (var i = 0; i < highlights.length; ++i)
{
for (var i = 0; i < highlights.length; ++i) {
var h = highlights[i];
if (h.auto == eventname && !(item && h.series == item.series))
if (h.auto == eventname && !(item && h.series == item.series)) {
unhighlight(h.series);
}
}
}
// highlight the slice
if (item)
if (item) {
highlight(item.series, eventname);
}
// trigger any hover bind events
var pos = { pageX: e.pageX, pageY: e.pageY };
target.trigger(eventname, [ pos, item ]);
target.trigger(eventname, [pos, item]);
}
function highlight(s, auto)
{
if (typeof s == "number")
function highlight(s, auto) {
if (typeof s == "number") {
s = series[s];
}
var i = indexOfHighlight(s);
if (i == -1)
{
if (i == -1) {
highlights.push({ series: s, auto: auto });
plot.triggerRedrawOverlay();
}
else if (!auto)
} else if (!auto) {
highlights[i].auto = false;
}
}
function unhighlight(s)
{
if (s == null)
{
function unhighlight(s) {
if (s == null) {
highlights = [];
plot.triggerRedrawOverlay();
}
if (typeof s == "number")
if (typeof s == "number") {
s = series[s];
}
var i = indexOfHighlight(s);
if (i != -1)
{
if (i != -1) {
highlights.splice(i, 1);
plot.triggerRedrawOverlay();
}
}
function indexOfHighlight(s)
{
for (var i = 0; i < highlights.length; ++i)
{
function indexOfHighlight(s) {
for (var i = 0; i < highlights.length; ++i) {
var h = highlights[i];
if (h.series == s)
return i;
......@@ -669,11 +713,9 @@ More detail and specific examples can be found in the included HTML file.
return -1;
}
function drawOverlay(plot, octx)
{
//alert(options.series.pie.radius);
function drawOverlay(plot, octx) {
var options = plot.getOptions();
//alert(options.series.pie.radius);
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
......@@ -681,59 +723,61 @@ More detail and specific examples can be found in the included HTML file.
octx.translate(centerLeft, centerTop);
octx.scale(1, options.series.pie.tilt);
for (i = 0; i < highlights.length; ++i)
for (i = 0; i < highlights.length; ++i) {
drawHighlight(highlights[i].series);
}
drawDonutHole(octx);
octx.restore();
function drawHighlight(series)
{
if (series.angle <= 0 || isNaN(series.angle))
function drawHighlight(series) {
if (series.angle <= 0 || isNaN(series.angle)) {
return;
}
//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor
octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor
octx.beginPath();
if (Math.abs(series.angle - Math.PI*2) > 0.000000001)
octx.moveTo(0,0); // Center of the pie
octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);
if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) {
octx.moveTo(0, 0); // Center of the pie
}
octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false);
octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false);
octx.closePath();
octx.fill();
}
}
} // end init (plugin body)
// define pie specific options and their default values
var options = {
series: {
pie: {
show: false,
radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
innerRadius:0, /* for donut */
radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
innerRadius: 0, /* for donut */
startAngle: 3/2,
tilt: 1,
shadow: {
left: 5, // shadow left offset
top: 15, // shadow top offset
alpha: 0.02, // shadow alpha
alpha: 0.02 // shadow alpha
},
offset: {
top: 0,
left: 'auto'
left: "auto"
},
stroke: {
color: '#FFF',
color: "#fff",
width: 1
},
label: {
show: 'auto',
formatter: function(label, slice){
return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';
show: "auto",
formatter: function(label, slice) {
return "<div style='font-size:x-small;text-align:center;padding:2px;color:" + slice.color + ";'>" + label + "<br/>" + Math.round(slice.percent) + "%</div>";
}, // formatter function
radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
background: {
......@@ -745,10 +789,10 @@ More detail and specific examples can be found in the included HTML file.
combine: {
threshold: -1, // percentage at which to combine little slices into one larger slice
color: null, // color to give the new slice (auto-generated if null)
label: 'Other' // label to give the new slice
label: "Other" // label to give the new slice
},
highlight: {
//color: '#FFF', // will add this functionality once parseColor is available
//color: "#fff", // will add this functionality once parseColor is available
opacity: 0.5
}
}
......@@ -759,6 +803,7 @@ More detail and specific examples can be found in the included HTML file.
init: init,
options: options,
name: "pie",
version: "1.0"
version: "1.1"
});
})(jQuery);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment