
/* PieChart
 * --------
 * Custom jQuery 'object' that handles the actual plotting
 * and updating of the pie chart visual using Raphael
 *
 */


var PieChart = function(init) {
    
    // create the Raphael canvas to draw on
    this.r = Raphael(init.container);
    
    this.data = init.data;
    
    // determine the circle size and position that should be used
    this.radius = Math.min(this.r.width, this.r.height) * 0.5  / 2;
    this.center = [this.r.width / 2, this.r.height / 2];
    
    // custom Raphael attribute to set up a pie slice based on start/end
    // degree measurements
    this.r.customAttributes.segment = function (x, y, r, a1, a2) {
        var flag = (a2 - a1) > 180;
        var clr = (a2 - a1) / 360;
        a1 = (a1 % 360) * Math.PI / 180;
        a2 = (a2 % 360) * Math.PI / 180;
        return {
            'path': [["M", x, y], ["l", r * Math.cos(a1), r * Math.sin(a1)], ["A", r, r, 0, +flag, 1, x + r * Math.cos(a2), y + r * Math.sin(a2)], ["z"]]
        };
    };
    
    // start drawing slices from the top of the circle (zero would be the right side)
    this.orientation = -90;
        
    // store each pie slice element in an array so we can manipulate them later
    this.paths = [];
    
    // iterate over each value passed in and set up the pie slices
    // each slice will be 0 degrees wide initially, essentially invisible
    // and collapsed so we can animate the pie chart expanding when ready
    var i = 0;
    for (slice in this.data) {
        var path = this.r.path();
        path.attr({
            'segment': [this.center[0], this.center[1], this.radius, this.orientation, this.orientation],
            'stroke': init.backgroundColor,
            'stroke-width': 1,
            'title': this.data[slice].label,
            'fill': init.colors[i]
        });
        this.data[slice].slice = path;
        this.$container = $(init.container).css('position', 'relative');
        this.data[slice].color = init.colors[i++];
    }
    
    // now that we're set up, animate the pie chart opening by passing in the real values
    this.update(this.data);
};

PieChart.prototype = {
    
    // change the values of the pie chart
    'update': function(new_data, speed) {
        
        // get rid of any existing labels
        $('.label').remove();
        
        // speed at which the animation will happen - default: 2 seconds
        speed = speed || 2000;
        
        // update data dictionary to use new values passed in
        var s;
        for (s in new_data) this.data[s].value = new_data[s].value;
        
        // calculate the new total of all values that we're working with
        var total = 0;
        for (s in this.data) total += this.data[s].value;
        
        // this is going to keep track of where we are as we go around the circle drawing pie slices
        var start = this.orientation;
        
        // iterate over each pie slice
        for (s in this.data) {
            var slice = this.data[s];
            
            // calculate the size of the slice in degrees, based on a total of 360 for the circle
            var size = 360 / total * slice.value;
            
            // animate the size of the pie slice
            slice.slice.animate({'segment': [this.center[0], this.center[1], this.radius, start, start + size]}, speed, '<>');

            // make the label element for the slice
            var percent = Math.round(size / 360 * 100, 10);
            slice.$label = $('<div/>', {'class': 'label'})
                .css({'position': 'absolute', 'fontFamily': 'Helvetica, Arial, sans-serif'})
                .append(
                    $('<div/>', {'text': slice.label}).css({
                        'fontSize': '12px',
                        'color': '#888'
                    })
                )
                .append(
                    $('<div/>', {'text': percent + '%'}).css({
                        'color': slice.color,
                        'fontSize': '20px',
                        'fontWeight': 'bold'
                    })
                )
                .hide()
                .appendTo(this.$container);
                
            // figure out where to put the label
            var centerAngle = ((start + (size/2)) % 360) * Math.PI / 180;
            var x = this.center[0] + Math.cos(centerAngle) * (this.radius * 1.1);
            var y = this.center[1] + Math.sin(centerAngle) * (this.radius * 1.1);
            
            // what quadrant is this label in, numbered clockwise from top-right to top-left
            var quadrant;
            if (x - this.center[0] > 0) {
                if (y - this.center[1] < 0) {
                    quadrant = 1;
                } else {
                    quadrant = 2;
                }
            } else {
                if (y - this.center[1] > 0) {
                    quadrant = 3;
                } else {
                    quadrant = 4;
                }
            }
            
            // position and align label based on quadrant it is in
            if (quadrant == 1 || quadrant == 4) {
                slice.$label.css({'top': y - slice.$label.outerHeight()});
            } else {
                slice.$label.css({'top': y});
            }
            if (quadrant == 1 || quadrant == 3) {
                slice.$label.css({'textAlign': 'right'});
            }
            if (quadrant <= 2) {
                slice.$label.css({'left': x});
            } else {
                slice.$label.css({'right': this.r.width - x});
            }
            
            // now make the label visible
            slice.$label.show();
            
            // move the start cursor up to just after the slice we just drew
            start += size;
        }
    }
};


(function($) {
        
    var methods = {
        
        'init': function(options) {

            // bail out if we didn't get a content dictionary (the only required value to initialize)
            if (options == null || options.data == null) $.error('Pie plugin requires a data set to initialize');

            // default settings
            var settings = {
                'data': null,
                'backgroundColor': '#fff',
                'colors': ['#008259', '#006081', '#c91010', '#db6e00', '#dcb812', '#a30099']
            };

            return this.each(function() {        
 
                // if options exist, merge them with our default settings
                if (options) $.extend(settings, options);

                // animate in the pie chart
                this.p = new PieChart({
                    'container': this,
                    'data': settings.data,
                    'colors': settings.colors,
                    'backgroundColor': settings.backgroundColor
                });
                
            });

        },

        'update': function(options) {
            return this.each(function() {
                this.p.update(options.data, 1000);
            });
        }

    };
    
    
    /*
     * Connect up the calls to the appropriate method
     */
     
    $.fn.pie = function(method, options) {
    
        if (methods[method]) {
            return methods[method].apply(this, [options]);
        } else if ( typeof method === 'object' || !method ) {
            return methods.init.apply(this, [method]);
        } else {
            $.error('Method ' +  method + ' does not exist in pie plugin');
        }
        return this;
    };
    
})(jQuery);
