Вход Регистрация
Файл: admin/skins/lib/flot/jquery.flot.pie.js
Строк: 735
<?php
/*
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.

* Created by Brian Medendorp, June 2009
* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars

* Changes:
    2009-10-22: lineJoin set to round
    2009-10-23: IE full circle fix, donut
    2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera
    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: {
        show: true/false
        radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
        innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
        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
        tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
        offset: {
            top: integer value to move the pie up or down
            left: integer value to move the pie left or right, or 'auto'
        },
        stroke: {
            color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
            width: integer pixel width of the stroke
        },
        label: {
            show: true/false, or 'auto'
            formatter:  a user-defined function that modifies the text/style of the label text
            radius: 0-1 for percentage of fullsize, or a specified pixel length
            background: {
                color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
                opacity: 0-1
            },
            threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
        },
        combine: {
            threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
            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
            label: any text value of what the combined slice should be labeled
        }
        highlight: {
            opacity: 0-1
        }
    }
}

More detail and specific examples can be found in the included HTML file.

*/

(function ($) 
{
    function 
init(plot// this is the "body" of the plugin
    
{
        var 
canvas null;
        var 
target null;
        var 
maxRadius null;
        var 
centerLeft null;
        var 
centerTop null;
        var 
total 0;
        var 
redraw true;
        var 
redrawAttempts 10;
        var 
shrink 0.95;
        var 
legendWidth 0;
        var 
processed false;
        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(plotoptions)
        {
            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)
                        
options.series.pie.label.show false;
                    else
                        
options.series.pie.label.show true;
                
                
// set radius
                
if (options.series.pie.radius=='auto')
                    if (
options.series.pie.label.show)
                        
options.series.pie.radius 3/4;
                    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;
            
                
// add processData hook to do transformations on the data
                
plot.hooks.processDatapoints.push(processDatapoints);
                
plot.hooks.drawOverlay.push(drawOverlay);    
                
                
// add draw hook
                
plot.hooks.draw.push(draw);
            }
        }
    
        
// bind hoverable events
        
function bindEvents(ploteventHolder)         
        {        
            var 
options plot.getOptions();
            
            if (
options.series.pie.show && options.grid.hoverable)
                
eventHolder.unbind('mousemove').mousemove(onMouseMove);
                
            if (
options.series.pie.show && options.grid.clickable)
                
eventHolder.unbind('click').click(onClick);
        }    
        

        
// debugging function that prints out an object
        
function alertObject(obj)
        {
            var 
msg '';
            function 
traverse(objdepth)
            {
                if (!
depth)
                    
depth 0;
                for (var 
0obj.length; ++i)
                {
                    for (var 
j=0j<depthj++)
                        
msg += 't';
                
                    if( 
typeof obj[i] == "object")
                    {    
// its an object
                        
msg += ''+i+':n';
                        
traverse(obj[i], depth+1);
                    }
                    else
                    {    
// its a value
                        
msg += ''+i+': '+obj[i]+'n';
                    }
                }
            }
            
traverse(obj);
            
alert(msg);
        }
        
        function 
calcTotal(data)
        {
            for (var 
0data.length; ++i)
            {
                var 
item parseFloat(data[i].data[0][1]);
                if (
item)
                    
total += item;
            }
        }    
        
        function 
processDatapoints(plotseriesdatadatapoints
        {    
            if (!
processed)
            {
                
processed true;
            
                
canvas plot.getCanvas();
                
target = $(canvas).parent();
                
options plot.getOptions();
            
                
plot.setData(combine(plot.getData()));
            }
        }
        
        function 
setupPie()
        {
            
legendWidth target.children().filter('.legend').children().width();
        
            
// calculate maximum radius and center point
            
maxRadius =  Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;
            
centerTop = (canvas.height/2)+options.series.pie.offset.top;
            
centerLeft = (canvas.width/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)
                
centerLeft maxRadius;
            else if (
centerLeft>canvas.width-maxRadius)
                
centerLeft canvas.width-maxRadius;
        }
        
        function 
fixData(data)
        {
            for (var 
0data.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)
        {
            
data fixData(data);
            
calcTotal(data);
            var 
combined 0;
            var 
numCombined 0;
            var 
color options.series.pie.combine.color;
            
            var 
newdata = [];
            for (var 
0data.length; ++i)
            {
                
// make sure its a number
                
data[i].data[0][1] = parseFloat(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)
                {
                    
combined += data[i].data[0][1];
                    
numCombined++;
                    if (!
color)
                        
color data[i].color;
                }                
                else
                {
                    
newdata.push({
                        
data: [[1,data[i].data[0][1]]], 
                        
colordata[i].color
                        
labeldata[i].label,
                        
angle: (data[i].data[0][1]*(Math.PI*2))/total,
                        
percent: (data[i].data[0][1]/total*100)
                    });
                }
            }
            if (
numCombined>0)
                
newdata.push({
                    
data: [[1,combined]], 
                    
colorcolor
                    
labeloptions.series.pie.combine.label,
                    
angle: (combined*(Math.PI*2))/total,
                    
percent: (combined/total*100)
                });
            return 
newdata;
        }        
        
        function 
draw(plotnewCtx)
        {
            if (!
target) return; // if no series were passed
            
ctx newCtx;
        
            
setupPie();
            var 
slices plot.getData();
        
            var 
attempts 0;
            while (
redraw && attempts<redrawAttempts)
            {
                
redraw false;
                if (
attempts>0)
                    
maxRadius *= shrink;
                
attempts += 1;
                
clear();
                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>');
            }
            
            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,canvas.width,canvas.height);
                
target.children().filter('.pieLabel, .pieLabelBackground').remove();
            }
            
            function 
drawShadow()
            {
                var 
shadowLeft 5;
                var 
shadowTop 15;
                var 
edge 10;
                var 
alpha 0.02;
            
                
// set radius
                
if (options.series.pie.radius>1)
                    var 
radius options.series.pie.radius;
                else
                    var 
radius maxRadius options.series.pie.radius;
                    
                if (
radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/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';

                
// center and rotate to starting position
                
ctx.translate(centerLeft,centerTop);
                
ctx.scale(1options.series.pie.tilt);
                
                
//radius -= edge;
                
for (var i=1i<=edgei++)
                {
                    
ctx.beginPath();
                    
ctx.arc(0,0,radius,0,Math.PI*2,false);
                    
ctx.fill();
                    
radius -= i;
                }    
                
                
ctx.restore();
            }
            
            function 
drawPie()
            {
                
startAngle Math.PI*options.series.pie.startAngle;
                
                
// set radius
                
if (options.series.pie.radius>1)
                    var 
radius options.series.pie.radius;
                else
                    var 
radius maxRadius options.series.pie.radius;
                
                
// center and rotate to starting position
                
ctx.save();
                
ctx.translate(centerLeft,centerTop);
                
ctx.scale(1options.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 
0slices.length; ++i)
                {
                    
slices[i].startAngle currentAngle;
                    
drawSlice(slices[i].angleslices[i].colortrue);
                }
                
ctx.restore();
                
                
// draw slice outlines
                
ctx.save();
                
ctx.lineWidth options.series.pie.stroke.width;
                
currentAngle startAngle;
                for (var 
0slices.length; ++i)
                    
drawSlice(slices[i].angleoptions.series.pie.stroke.colorfalse);
                
ctx.restore();
                    
                
// draw donut hole
                
drawDonutHole(ctx);
                
                
// draw labels
                
if (options.series.pie.label.show)
                    
drawLabels();
                
                
// restore to original state
                
ctx.restore();
                
                function 
drawSlice(anglecolorfill)
                {    
                    if (
angle<=0)
                        return;
                
                    if (
fill)
                        
ctx.fillStyle color;
                    else
                    {
                        
ctx.strokeStyle color;
                        
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)
                        
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.closePath();
                    
//ctx.rotate(angle); // This doesn't work properly in Opera
                    
currentAngle += angle;
                    
                    if (
fill)
                        
ctx.fill();
                    else
                        
ctx.stroke();
                }
                
                function 
drawLabels()
                {
                    var 
currentAngle startAngle;
                    
                    
// 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 
0slices.length; ++i)
                    {
                        if (
slices[i].percent >= options.series.pie.label.threshold*100)
                            
drawLabel(slices[i], currentAnglei);
                        
currentAngle += slices[i].angle;
                    }
                    
                    function 
drawLabel(slicestartAngleindex)
                    {
                        if (
slice.data[0][1]==0)
                            return;
                            
                        
// format label text
                        
var lf options.legend.labelFormattertextplf options.series.pie.label.formatter;
                        if (
lf)
                            
text lf(slice.labelslice);
                        else
                            
text slice.label;
                        if (
plf)
                            
text plf(textslice);
                            
                        var 
halfAngle = ((startAngle+slice.angle) + startAngle)/2;
                        var 
centerLeft Math.round(Math.cos(halfAngle) * radius);
                        var 
centerTop Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
                        
                        var 
html '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' 'px;left:' 'px;">' text "</span>";
                        
target.append(html);
                        var 
label target.children('#pieLabel'+index);
                        var 
labelTop = (label.height()/2);
                        var 
labelLeft = (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-labelLeft>|| canvas.height-(labelTop+label.height())<|| canvas.width-(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 options.series.pie.label.background.color;
                            if (
== null) {
                                
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:' ';"> </div>').insertBefore(label).css('opacity'options.series.pie.label.background.opacity);
                        }
                    } 
// end individual label function
                
// end drawLabels function
            
// end drawPie function
        
// 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)
            {
                
// subtract the center
                
layer.save();
                
innerRadius options.series.pie.innerRadius 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.beginPath();
                
layer.fillStyle options.series.pie.stroke.color;
                
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.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(polypt)
        {
            for(var 
false= -1poly.length1; ++li)
                ((
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])
                && (
= !c);
            return 
c;
        }
        
        function 
findNearbySlice(mouseXmouseY)
        {
            var 
slices plot.getData(),
                
options plot.getOptions(),
                
radius options.series.pie.radius options.series.pie.radius maxRadius options.series.pie.radius;
            
            for (var 
0slices.length; ++i
            {
                var 
slices[i];    
                
                if(
s.pie.show)
                {
                    
ctx.save();
                    
ctx.beginPath();
                    
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.closePath();
                    
mouseX-centerLeft;
                    
mouseY-centerTop;
                    if(
ctx.isPointInPath)
                    {
                        if (
ctx.isPointInPath(mouseX-centerLeftmouseY-centerTop))
                        {
                            
//alert('found slice!');
                            
ctx.restore();
                            return {
datapoint: [s.percents.data], dataIndex0seriessseriesIndexi};
                        }
                    }
                    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];
                        
// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
                        
if(isPointInPoly(arrPolyarrPoint))
                        {
                            
ctx.restore();
                            return {
datapoint: [s.percents.data], dataIndex0seriessseriesIndexi};
                        }            
                    }
                    
ctx.restore();
                }
            }
            
            return 
null;
        }

        function 
onMouseMove(e
        {
            
triggerClickHoverEvent('plothover'e);
        }
        
        function 
onClick(e
        {
            
triggerClickHoverEvent('plotclick'e);
        }

        
// trigger click or hover event (they send the same parameters so we share their code)
        
function triggerClickHoverEvent(eventnamee
        {
            var 
offset plot.offset(),
                
canvasX parseInt(e.pageX offset.left),
                
canvasY =  parseInt(e.pageY offset.top),
                
item findNearbySlice(canvasXcanvasY);
            
            if (
options.grid.autoHighlight
            {
                
// clear auto-highlights
                
for (var 0highlights.length; ++i
                {
                    var 
highlights[i];
                    if (
h.auto == eventname && !(item && h.series == item.series))
                        
unhighlight(h.series);
                }
            }
            
            
// highlight the slice
            
if (item
                
highlight(item.serieseventname);
                
            
// trigger any hover bind events
            
var pos = { pageXe.pageXpageYe.pageY };
            
target.trigger(eventname, [ positem ]);    
        }

        function 
highlight(sauto
        {
            if (
typeof s == "number")
                
series[s];

            var 
indexOfHighlight(s);
            if (
== -1
            {
                
highlights.push({ seriessautoauto });
                
plot.triggerRedrawOverlay();
            }
            else if (!
auto)
                
highlights[i].auto false;
        }

        function 
unhighlight(s
        {
            if (
== null
            {
                
highlights = [];
                
plot.triggerRedrawOverlay();
            }
            
            if (
typeof s == "number")
                
series[s];

            var 
indexOfHighlight(s);
            if (
!= -1
            {
                
highlights.splice(i1);
                
plot.triggerRedrawOverlay();
            }
        }

        function 
indexOfHighlight(s
        {
            for (var 
0highlights.length; ++i
            {
                var 
highlights[i];
                if (
h.series == s)
                    return 
i;
            }
            return -
1;
        }

        function 
drawOverlay(plotoctx
        {
            
//alert(options.series.pie.radius);
            
var options plot.getOptions();
            
//alert(options.series.pie.radius);
            
            
var radius options.series.pie.radius options.series.pie.radius maxRadius options.series.pie.radius;

            
octx.save();
            
octx.translate(centerLeftcenterTop);
            
octx.scale(1options.series.pie.tilt);
            
            for (
0highlights.length; ++i
                
drawHighlight(highlights[i].series);
            
            
drawDonutHole(octx);

            
octx.restore();

            function 
drawHighlight(series
            {
                if (
series.angle 0) 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.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);
                
octx.closePath();
                
octx.fill();
            }
            
        }    
        
    } 
// end init (plugin body)
    
    // define pie specific options and their default values
    
var options = {
        
series: {
            
pie: {
                
showfalse,
                
radius'auto',    // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
                
innerRadius:0/* for donut */
                
startAngle3/2,
                
tilt1,
                
offset: {
                    
top0,
                    
left'auto'
                
},
                
stroke: {
                    
color'#FFF',
                    
width1
                
},
                
label: {
                    
show'auto',
                    
formatter: function(labelslice){
                        return 
'<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';
                    },    
// formatter function
                    
radius1,    // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
                    
background: {
                        
colornull,
                        
opacity0
                    
},
                    
threshold0    // percentage at which to hide the label (i.e. the slice is too narrow)
                
},
                
combine: {
                    
threshold: -1,    // percentage at which to combine little slices into one larger slice
                    
colornull,    // color to give the new slice (auto-generated if null)
                    
label'Other'    // label to give the new slice
                
},
                
highlight: {
                    
//color: '#FFF',        // will add this functionality once parseColor is available
                    
opacity0.5
                
}
            }
        }
    };
    
    $.
plot.plugins.push({
        
initinit,
        
optionsoptions,
        
name"pie",
        
version"1.0"
    
});
})(
jQuery);
?>
Онлайн: 0
Реклама