Вход Регистрация
Файл: public_html/admin/vendors/flot/jquery.flot.pie.js
Строк: 815
<?php
/* Flot plugin for rendering pie charts.

Copyright (c) 2007-2013 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

* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars

The plugin supports these options:

    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($) {

    
// Maximum redraw attempts when fitting labels within the plot

    
var REDRAW_ATTEMPTS 10;

    
// Factor by which to shrink the pie when fitting labels within the plot

    
var REDRAW_SHRINK 0.95;

    function 
init(plot) {

        var 
canvas null,
            
target null,
            
maxRadius null,
            
centerLeft null,
            
centerTop null,
            
processed false,
            
ctx null;

        
// interactive variables

        
var highlights = [];

        
// add hook to determine if pie plugin in enabled, and then perform necessary operations

        
plot.hooks.processOptions.push(function(plotoptions) {
            if (
options.series.pie.show) {

                
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;
                } else if (
options.series.pie.tilt 0) {
                    
options.series.pie.tilt 0;
                }
            }
        });

        
plot.hooks.bindEvents.push(function(ploteventHolder) {
            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);
                }
            }
        });

        
plot.hooks.processDatapoints.push(function(plotseriesdatadatapoints) {
            var 
options plot.getOptions();
            if (
options.series.pie.show) {
                
processDatapoints(plotseriesdatadatapoints);
            }
        });

        
plot.hooks.drawOverlay.push(function(plotoctx) {
            var 
options plot.getOptions();
            if (
options.series.pie.show) {
                
drawOverlay(plotoctx);
            }
        });

        
plot.hooks.draw.push(function(plotnewCtx) {
            var 
options plot.getOptions();
            if (
options.series.pie.show) {
                
draw(plotnewCtx);
            }
        });

        function 
processDatapoints(plotseriesdatapoints) {
            if (!
processed)    {
                
processed true;
                
canvas plot.getCanvas();
                
target = $(canvas).parent();
                
options plot.getOptions();
                
plot.setData(combine(plot.getData()));
            }
        }

        function 
combine(data) {

            var 
total 0,
                
combined 0,
                
numCombined 0,
                
color options.series.pie.combine.color,
                
newdata = [];

            
// Fix up the raw data from Flot, ensuring the data is numeric

            
for (var 0data.length; ++i) {

                var 
value data[i].data;

                
// If the data is an array, we'll assume that it's a standard
                // Flot x-y pair, and are concerned only with the second value.

                // Note how we use the original array, rather than creating a
                // new one; this is more efficient and preserves any extra data
                // that the user may have stored in higher indexes.

                
if ($.isArray(value)) {
                    if ($.
isNumeric(value[1])) {
                        
value[1] = +value[1];
                    } else {
                        
value[1] = 0;
                    }
                } else if ($.
isNumeric(value)) {
                    
value = [1, +value];
                } else {
                    
value = [10];
                }

                
data[i].data = [value];
            }

            
// Sum up all the slices, so we can calculate percentages for each

            
for (var 0data.length; ++i) {
                
total += data[i].data[0][1];
            }

            
// Count the number of slices with percentages below the combine
            // threshold; if it turns out to be just one, we won't combine.

            
for (var 0data.length; ++i) {
                var 
value data[i].data[0][1];
                if (
value total <= options.series.pie.combine.threshold) {
                    
combined += value;
                    
numCombined++;
                    if (!
color) {
                        
color data[i].color;
                    }
                }
            }

            for (var 
0data.length; ++i) {
                var 
value data[i].data[0][1];
                if (
numCombined || value total options.series.pie.combine.threshold) {
                    
newdata.push({
                        
data: [[1value]],
                        
colordata[i].color,
                        
labeldata[i].label,
                        
anglevalue Math.PI total,
                        
percentvalue / (total 100)
                    });
                }
            }

            if (
numCombined 1) {
                
newdata.push({
                    
data: [[1combined]],
                    
colorcolor,
                    
labeloptions.series.pie.combine.label,
                    
anglecombined Math.PI total,
                    
percentcombined / (total 100)
                });
            }

            return 
newdata;
        }

        function 
draw(plotnewCtx) {

            if (!
target) {
                return; 
// if no series were passed
            
}

            var 
canvasWidth plot.getPlaceholder().width(),
                
canvasHeight plot.getPlaceholder().height(),
                
legendWidth target.children().filter(".legend").children().width() || 0;

            
ctx newCtx;

            
// WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE!

            // When combining smaller slices into an 'other' slice, we need to
            // add a new series.  Since Flot gives plugins no way to modify the
            // list of series, the pie plugin uses a hack where the first call
            // to processDatapoints results in a call to setData with the new
            // list of series, then subsequent processDatapoints do nothing.

            // The plugin-global 'processed' flag is used to control this hack;
            // it starts out false, and is set to true after the first call to
            // processDatapoints.

            // Unfortunately this turns future setData calls into no-ops; they
            // call processDatapoints, the flag is true, and nothing happens.

            // To fix this we'll set the flag back to false here in draw, when
            // all series have been processed, so the next sequence of calls to
            // processDatapoints once again starts out with a slice-combine.
            // This is really a hack; in 0.9 we need to give plugins a proper
            // way to modify series before any processing begins.

            
processed false;

            
// calculate maximum radius and center point

            
maxRadius =  Math.min(canvasWidthcanvasHeight options.series.pie.tilt) / 2;
            
centerTop canvasHeight 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) {
                
centerLeft maxRadius;
            } else if (
centerLeft canvasWidth maxRadius) {
                
centerLeft canvasWidth maxRadius;
            }

            var 
slices plot.getData(),
                
attempts 0;

            
// Keep shrinking the pie's radius until drawPie returns true,
            // indicating that all the labels fit, or we try too many times.

            
do {
                if (
attempts 0) {
                    
maxRadius *= REDRAW_SHRINK;
                }
                
attempts += 1;
                
clear();
                if (
options.series.pie.tilt <= 0.8) {
                    
drawShadow();
                }
            } while (!
drawPie() && attempts REDRAW_ATTEMPTS)

            if (
attempts >= REDRAW_ATTEMPTS) {
                
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(00canvasWidthcanvasHeight);
                
target.children().filter(".pieLabel, .pieLabelBackground").remove();
            }

            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 options.series.pie.radius maxRadius options.series.pie.radius;

                if (
radius >= canvasWidth shadowLeft || radius options.series.pie.tilt >= canvasHeight 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 1<= edgei++) {
                    
ctx.beginPath();
                    
ctx.arc(00radius0Math.PI 2false);
                    
ctx.fill();
                    
radius -= i;
                }

                
ctx.restore();
            }

            function 
drawPie() {

                var 
startAngle Math.PI options.series.pie.startAngle;
                var 
radius options.series.pie.radius options.series.pie.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

                
if (options.series.pie.stroke.width 0) {
                    
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);

                
ctx.restore();

                
// Draw the labels, returning true if they fit within the plot

                
if (options.series.pie.label.show) {
                    return 
drawLabels();
                } else return 
true;

                function 
drawSlice(anglecolorfill) {

                    if (
angle <= || isNaN(angle)) {
                        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(00); // Center of the pie
                    
}

                    
//ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
                    
ctx.arc(00radius,currentAnglecurrentAngle angle 2false);
                    
ctx.arc(00radius,currentAngle angle 2currentAngle anglefalse);
                    
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;
                    var 
radius options.series.pie.label.radius options.series.pie.label.radius maxRadius options.series.pie.label.radius;

                    for (var 
0slices.length; ++i) {
                        if (
slices[i].percent >= options.series.pie.label.threshold 100) {
                            if (!
drawLabel(slices[i], currentAnglei)) {
                                return 
false;
                            }
                        }
                        
currentAngle += slices[i].angle;
                    }

                    return 
true;

                    function 
drawLabel(slicestartAngleindex) {

                        if (
slice.data[0][1] == 0) {
                            return 
true;
                        }

                        
// 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 (labelTop || labelLeft || canvasHeight - (labelTop label.height()) < || canvasWidth - (labelLeft label.width()) < 0) {
                            return 
false;
                        }

                        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>")
                                .
css("opacity"options.series.pie.label.background.opacity)
                                .
insertBefore(label);
                        }

                        return 
true;
                    } 
// 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) {
            if (
options.series.pie.innerRadius 0) {

                
// subtract the center

                
layer.save();
                var 
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(00innerRadius0Math.PI 2false);
                
layer.fill();
                
layer.closePath();
                
layer.restore();

                
// add inner stroke

                
layer.save();
                
layer.beginPath();
                
layer.strokeStyle options.series.pie.stroke.color;
                
layer.arc(00innerRadius0Math.PI 2false);
                
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,
                
xy;

            for (var 
0slices.length; ++i) {

                var 
slices[i];

                if (
s.pie.show) {

                    
ctx.save();
                    
ctx.beginPath();
                    
ctx.moveTo(00); // Center of the pie
                    //ctx.scale(1, options.series.pie.tilt);    // this actually seems to break everything when here.
                    
ctx.arc(00radiuss.startAngles.startAngle s.angle 2false);
                    
ctx.arc(00radiuss.startAngle s.angle 2s.startAngle s.anglefalse);
                    
ctx.closePath();
                    
mouseX centerLeft;
                    
mouseY centerTop;

                    if (
ctx.isPointInPath) {
                        if (
ctx.isPointInPath(mouseX centerLeftmouseY centerTop)) {
                            
ctx.restore();
                            return {
                                
datapoint: [s.percents.data],
                                
dataIndex0,
                                
seriess,
                                
seriesIndexi
                            
};
                        }
                    } else {

                        
// excanvas for IE doesn;t support isPointInPath, this is a workaround.

                        
var 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 = [[00], [p1Xp1Y], [p2Xp2Y], [p3Xp3Y], [p4Xp4Y], [p5Xp5Y]],
                            
arrPoint = [xy];

                        
// 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],
                                
dataIndex0,
                                
seriess,
                                
seriesIndexi
                            
};
                        }
                    }

                    
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();
            var 
canvasX parseInt(e.pageX offset.left);
            var 
canvasY =  parseInt(e.pageY offset.top);
            var 
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") {
            //    s = 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") {
            //    s = 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) {

            var 
options plot.getOptions();

            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 (var 
0highlights.length; ++i) {
                
drawHighlight(highlights[i].series);
            }

            
drawDonutHole(octx);

            
octx.restore();

            function 
drawHighlight(series) {

                if (
series.angle <= || 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.beginPath();
                if (
Math.abs(series.angle Math.PI 2) > 0.000000001) {
                    
octx.moveTo(00); // Center of the pie
                
}
                
octx.arc(00radiusseries.startAngleseries.startAngle series.angle 2false);
                
octx.arc(00radiusseries.startAngle series.angle 2series.startAngle series.anglefalse);
                
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)
                
innerRadius0/* for donut */
                
startAngle3/2,
                
tilt1,
                
shadow: {
                    
left5,    // shadow left offset
                    
top15,    // shadow top offset
                    
alpha0.02    // shadow alpha
                
},
                
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.1"
    
});

})(
jQuery);
?>
Онлайн: 1
Реклама