// o---------------------------------------------------------------------------------o
    // | This file is part of the RGraph package - you can learn more at:                |
    // |                                                                                 |
    // |                       https://www.rgraph.net/license.html                       |
    // |                                                                                 |
    // | RGraph is dual-licensed under the Open Source GPL license. That means that it's |
    // | free to use and there are no restrictions on what you can use RGraph for!       |
    // | If the GPL license does not suit you however, then there's an inexpensive       |
    // | commercial license option available. See the URL above for more details.        |
    // o---------------------------------------------------------------------------------o

    RGraph     = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
    RGraph.SVG = RGraph.SVG || {};

// Module pattern
(function (win, doc, undefined)
{
    RGraph.SVG.Pie = function (conf)
    {
        //
        // A setter that the constructor uses (at the end)
        // to set all of the properties
        //
        // @param string name  The name of the property to set
        // @param string value The value to set the property to
        //
        this.set = function (name, value)
        {
            if (arguments.length === 1 && typeof name === 'object') {
                for (i in arguments[0]) {
                    if (typeof i === 'string') {
                        this.set(i, arguments[0][i]);
                    }
                }
            } else {


                // Go through all of the properties and make sure
                // that they're using the correct capitalisation
                name = this.properties_lowercase_map[name.toLowerCase()] || name;

                var ret = RGraph.SVG.commonSetter({
                    object: this,
                    name:   name,
                    value:  value
                });
                
                name  = ret.name;
                value = ret.value;

                this.properties[name] = value;

                // If setting the colors, update the originalColors
                // property too
                if (name === 'colors') {
                    this.originalColors = RGraph.SVG.arrayClone(value, true);
                    this.colorsParsed = false;
                }
            }

            return this;
        };








        //
        // A getter.
        // 
        // @param name  string The name of the property to get
        //
        this.get = function (name)
        {
            // Go through all of the properties and make sure
            // that they're using the correct capitalisation
            name = this.properties_lowercase_map[name.toLowerCase()] || name;

            return this.properties[name];
        };








        // Convert strings to numbers
        conf.data = RGraph.SVG.stringsToNumbers(conf.data);




        this.type            = 'pie';
        this.id              = conf.id;
        this.uid             = RGraph.SVG.createUID();
        this.container       = document.getElementById(this.id);
        this.layers          = {}; // MUST be before the SVG tag is created!
        this.svg             = RGraph.SVG.createSVG({object: this,container: this.container});
        this.svgAllGroup     = RGraph.SVG.createAllGroup(this);
        this.clipid          = null; // Used to clip the canvas
        this.isRGraph        = true;
        this.isrgraph        = true;
        this.rgraph          = true;
        this.width           = Number(this.svg.getAttribute('width'));
        this.height          = Number(this.svg.getAttribute('height'));
        this.data            = conf.data;
        this.angles          = [];
        this.colorsParsed    = false;
        this.originalColors  = {};
        this.gradientCounter = 1;
        this.nodes           = [];
        this.shadowNodes     = [];
        this.firstDraw        = true; // After the first draw this will be false











        // Add this object to the ObjectRegistry
        RGraph.SVG.OR.add(this);
        
        // Set the DIV container to be inline-block
        this.container.style.display = 'inline-block';

        this.properties =
        {
            centerx: null,
            centery: null,
            radius:  null,
            
            marginLeft:    35,
            marginRight:   35,
            marginTop:     35,
            marginBottom:  35,
            
            colors: [
                '#f66', '#6f6', '#66f', '#ff6', '#6ff', '#ccc',
                'pink', 'orange', 'cyan', 'maroon', 'olive', 'teal'
            ],
            colorsStroke:      'rgba(0,0,0,0)',
            
            textColor: 'black',
            textFont: 'Arial, Verdana, sans-serif',
            textSize: 12,
            textBold: false,
            textItalic: false,
            text:       null,
            
            labels: [],
            labelsSticks: true,
            labelsSticksOffset: 50,
            labelsFormattedDecimals:  0,
            labelsFormattedPoint:     '.',
            labelsFormattedThousand:  ',',
            labelsFormattedUnitsPre:  '',
            labelsFormattedUnitsPost: '',

            linewidth: 1,
            
            tooltips:                        null,
            tooltipsOverride:                null,
            tooltipsEffect:                  'fade',
            tooltipsCssClass:                'RGraph_tooltip',
            tooltipsCss:                     null,
            tooltipsEvent:                   'click',
            tooltipsPersistent:              false,
            tooltipsFormattedThousand:       ',',
            tooltipsFormattedPoint:          '.',
            tooltipsFormattedDecimals:       0,
            tooltipsFormattedUnitsPre:       '',
            tooltipsFormattedUnitsPost:      '',
            tooltipsFormattedKeyColors:      null,
            tooltipsFormattedKeyColorsShape: 'square',
            tooltipsFormattedKeyLabels:      [],
            tooltipsFormattedTableHeaders:   null,
            tooltipsFormattedTableData:      null,
            tooltipsPointer:                 true,
            tooltipsPointerOffsetx:          0,
            tooltipsPointerOffsety:          0,
            tooltipsPositionStatic:          true,

            highlightStroke: 'rgba(0,0,0,0)',
            highlightFill: 'rgba(255,255,255,0.7)',
            highlightLinewidth: 1,
            highlightStyle: 'normal',
            highlightStyleOutlineWidth: 7,
            
            title:       '',
            titleX:      null,
            titleY:      null,
            titleHalign: 'center',
            titleValign: null,
            titleSize:   null,
            titleColor:  null,
            titleFont:   null,
            titleBold:   null,
            titleItalic: null,
            
            titleSubtitle:       null,
            titleSubtitleSize:   null,
            titleSubtitleColor:  '#aaa',
            titleSubtitleFont:   null,
            titleSubtitleBold:   null,
            titleSubtitleItalic: null,
            
            shadow: false,
            shadowOffsetx: 2,
            shadowOffsety: 2,
            shadowBlur: 2,
            shadowColor: 'rgba(0,0,0,.25)',
            
            exploded: 0,
            roundRobinMultiplier: 1,
            
            donut:              false,
            donutWidth:         75,

            key:            null,
            keyColors:      null,
            keyOffsetx:     0,
            keyOffsety:     0,
            keyLabelsOffsetx: 0,
            keyLabelsOffsety: -1,
            keyLabelsColor:   null,
            keyLabelsFont:    null,
            keyLabelsSize:    null,
            keyLabelsBold:    null,
            keyLabelsItalic:  null,
            
            clip: null,
            
            zoom: false
        };

        //
        // Add the reverse look-up table  for property names
        // so that property names can be specified in any case.
        //
        this.properties_lowercase_map = [];
        for (var i in this.properties) {
            if (typeof i === 'string') {
                this.properties_lowercase_map[i.toLowerCase()] = i;
            }
        }


        //
        // Copy the global object properties to this instance
        //
        RGraph.SVG.getGlobals(this);





        //
        // "Decorate" the object with the generic effects if the effects library has been included
        //
        if (RGraph.SVG.FX && typeof RGraph.SVG.FX.decorate === 'function') {
            RGraph.SVG.FX.decorate(this);
        }





        // Add the responsive function to the object
        this.responsive = RGraph.SVG.responsive;





        var properties = this.properties;








        //
        // The draw method draws the Bar chart
        //
        this.draw = function ()
        {
            // Fire the beforedraw event
            RGraph.SVG.fireCustomEvent(this, 'onbeforedraw');













            //(Re)set this so it doesn't grow endlessly
            this.angles = [];











            // Should be the first(ish) thing that's done in the
            // .draw() function except for the onbeforedraw event
            // and the installation of clipping.
            this.width  = Number(this.svg.getAttribute('width'));
            this.height = Number(this.svg.getAttribute('height'));















            // Create the defs tag if necessary
            RGraph.SVG.createDefs(this);


            this.graphWidth  = this.width - properties.marginLeft - properties.marginRight;
            this.graphHeight = this.height - properties.marginTop - properties.marginBottom;



            // Work out the center point
            this.centerx = (this.graphWidth / 2) + properties.marginLeft;
            this.centery = (this.graphHeight / 2) + properties.marginTop;
            this.radius  = Math.min(this.graphWidth, this.graphHeight) / 2;



            // Allow the user to override the calculated centerx/y/radius
            this.centerx = typeof properties.centerx === 'number' ? properties.centerx : this.centerx;
            this.centery = typeof properties.centery === 'number' ? properties.centery : this.centery;
            this.radius  = typeof properties.radius  === 'number' ? properties.radius  : this.radius;
            
            //
            // Allow the centerx/centery/radius to be a plus/minus
            //
            if (typeof properties.radius === 'string' && properties.radius.match(/^\+|-\d+$/) )   this.radius  += parseFloat(properties.radius);
            if (typeof properties.centerx === 'string' && properties.centerx.match(/^\+|-\d+$/) ) this.centerx += parseFloat(properties.centerx);
            if (typeof properties.centery === 'string' && properties.centery.match(/^\+|-\d+$/) ) this.centery += parseFloat(properties.centery);


            // Parse the colors for gradients
            // Must be after the cx/cy/r calculations
            RGraph.SVG.resetColorsToOriginalValues({object:this});
            this.parseColors();


            // Go through the data and work out the maximum value
            this.max   = RGraph.SVG.arrayMax(this.data);
            this.total = RGraph.SVG.arraySum(this.data);

            // Set the explosion to be an array if it's a number
            if (typeof properties.exploded === 'number' && properties.exploded > 0) {
                var val = properties.exploded;
    
                properties.exploded = [];
    
                for (var i=0; i<this.data.length; ++i) {
                    properties.exploded[i] = val;
                }
            }












            // Install clipping if requested
            if (this.properties.clip) {

                this.clipid = RGraph.SVG.installClipping(this);

                // Add the clip ID to the all group
                this.svgAllGroup.setAttribute(
                    'clip-path',
                    'url(#{1})'.format(this.clipid)
                );
            } else {
                // No clipping - so ensure that there's no clip-path
                // attribute
                this.clipid = null;
                this.svgAllGroup.removeAttribute('clip-path');
            }






            // Draw the segments
            this.drawSegments({shadow: true});



            // Draw the title and subtitle
            RGraph.SVG.drawTitle(this);



            // Draw the labels

            //
            // If the xaxisLabels option is a string then turn it
            // into an array.
            //
            if (properties.labels && properties.labels.length) {
                if (typeof properties.labels === 'string') {
                    properties.labels = RGraph.SVG.arrayPad({
                        array:  [],
                        length: this.data.length,
                        value:  properties.labels
                    });
                }

                // Label substitution
                //
                for (var i=0; i<properties.labels.length; ++i) {
                    properties.labels[i] = RGraph.SVG.labelSubstitution({
                        object:    this,
                        text:      properties.labels[i],
                        index:     i,
                        value:     this.data[i],
                        decimals:  properties.labelsFormattedDecimals  || 0,
                        unitsPre:  properties.labelsFormattedUnitsPre  || '',
                        unitsPost: properties.labelsFormattedUnitsPost || '',
                        thousand:  properties.labelsFormattedThousand  || ',',
                        point:     properties.labelsFormattedPoint     || '.'
                    });
                }
            }

            if (properties.labelsSticks) {
                this.drawLabelsSticks();
            } else {
                this.drawLabels();
            }
            
            
            //
            // Draw the ingraph labels if required
            //
            this.drawIngraphLabels();




            // Draw the key
            if (typeof properties.key !== null && RGraph.SVG.drawKey) {
                RGraph.SVG.drawKey(this);
            } else if (!RGraph.SVG.isNullish(properties.key)) {
                alert('The drawKey() function does not exist - have you forgotten to include the key library?');
            }

            



            // Add the event listener that clears the highlight if
            // there is any. Must be MOUSEDOWN (ie before the click event)
            var obj = this;
            document.body.addEventListener('mousedown', function (e)
            {
                RGraph.SVG.removeHighlight(obj);
            }, false);








            //
            // Allow the addition of custom text via the
            // text: property.
            //
            RGraph.SVG.addCustomText(this);





            // Lastly - install the zoom event listeners if
            // requested
            if (this.properties.zoom) {
                RGraph.SVG.addZoom(this);
            }












            //
            // Fire the onfirstdraw event
            //
            if (this.firstDraw) {
                this.firstDraw = false;
                RGraph.SVG.fireCustomEvent(this, 'onfirstdraw');
            }




            // Fire the draw event
            RGraph.SVG.fireCustomEvent(this, 'ondraw');







            //
            // Install any inline responsive configuration. This
            // should be last in the draw function - even after
            // the draw events.
            //
            RGraph.SVG.installInlineResponsive(this);











            return this;
        };








        //
        // New create() shortcut function
        // For example:
        //    this.create('rect,x:0,y:0,width:100,height:100'[,parent]);
        //
        // @param str string The tag definition to parse and create
        // @param     object The (optional) parent element
        // @return    object The new tag
        //
        this.create = function (str)
        {
            var def = RGraph.SVG.create.parseStr(this, str);
            def.svg = this.svg;
            
            // By default the parent is the SVG tag - but if
            // requested then change it to the tag that has
            // been given
            if (arguments[1]) {
                def.parent = arguments[1];
            }

            return RGraph.SVG.create(def);
        };








        //
        // Draws the segments
        //
        // @param bool     Whether or not this is a redraw. If this is a redraw
        //                 shadows are omitted
        //
        this.drawSegments = function (opt)
        {
            var start   = 0,
                end     = 0,
                angle   = 0,
                sum     = RGraph.SVG.arraySum(this.data),
                segment = 0;




            // Work out the start and end angles for the data
            for (var i=0,len=this.data.length; i<len; ++i) {
            
                var value = this.data[i] * properties.roundRobinMultiplier;

                start   = angle;
                segment = ((value / sum) * RGraph.SVG.TRIG.TWOPI);
                end     = start + segment;

                var explosion = RGraph.SVG.TRIG.getRadiusEndPoint({
                    angle: start + (segment / 2),
                    r: properties.exploded[i]
                });

                var explosionX = explosion[1],
                    explosionY = explosion[0];


                this.angles[i] = {
                    start:   start,
                    end:     end,
                    angle:   end - start,
                    halfway: ((end - start) / 2) + start,
                    cx:      this.centerx + (parseFloat(explosionX) || 0),
                    cy:      this.centery - (parseFloat(explosionY) || 0),
                    radius:  this.radius,
                    object: this
                };

                // Increase the angle at which we start drawing the next segment at
                angle += (end - start);
            }



            if (opt.shadow) {
                RGraph.SVG.setShadow({
                    object:  this,
                    offsetx: properties.shadowOffsetx,
                    offsety: properties.shadowOffsety,
                    blur:    properties.shadowBlur,
                    color:   properties.shadowColor,
                    id:      'dropShadow'
                });
            }


            //
            // This loop goes thru the angles that were
            // generated above and adds them to the
            // scene
            //
            for (var i=0; i<this.angles.length; ++i) {

                var path = RGraph.SVG.TRIG.getArcPath({
                    cx:    this.angles[i].cx,
                    cy:    this.angles[i].cy,
                    r:     this.radius,
                    start: this.angles[i].start,
                    end:   this.angles[i].end
                });





                // Donut
                if (properties.donut) {
                
                    var donutWidth = properties.donutWidth;
                
                    var donut_path = RGraph.SVG.TRIG.getArcPath3({
                        cx:     this.angles[i].cx,
                        cy:     this.angles[i].cy,
                        r:      this.radius - donutWidth,
                        start:  this.angles[i].end,
                        end:    this.angles[i].start,
                        moveto: false,
                        anticlockwise: true
                    });

                    var xy = RGraph.SVG.TRIG.getRadiusEndPoint({
                        angle: this.angles[i].end - RGraph.SVG.TRIG.HALFPI,
                        r:     this.radius - donutWidth
                    });
                    
                
                
                
                    path =   path
                           + " L {1} {2} ".format(xy[0] + this.angles[i].cx, xy[1] + this.angles[i].cy)
                           + donut_path
                           + " Z";
                
                
                } else {
                
                    path = path + " L {1} {2} ".format(
                        this.angles[i].cx,
                        this.angles[i].cy
                    ) + " Z"
                }



                var arc = RGraph.SVG.create({
                    svg: this.svg,
                    parent: this.svgAllGroup,
                    type: 'path',
                    attr: {
                        d: path,
                        fill: properties.colors[i],
                        stroke: properties.colorsStroke,
                        'stroke-width': properties.linewidth,
                        'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[i] : '',
                        'data-index': i,
                        'data-value': value,
                        'data-start-angle': this.angles[i].start,
                        'data-end-angle': this.angles[i].end,
                        'data-radius': this.radius,
                        filter: (properties.shadow && opt.shadow) ? 'url(#dropShadow)' : ''
                    }
                });

                // Store the path with the relevant entry in the obj.angles array
                this.angles[i].element = arc;
                

                // Store a reference to the node
                if (properties.shadow && opt.shadow) {
                    this.shadowNodes[i] = arc;
                } else {
                    this.nodes[i] = arc;
                }

                if (properties.tooltips && (properties.tooltips[i] || typeof properties.tooltips === 'string') && (!opt.shadow || !properties.shadow)) {
                
                    // Make the tooltipsEvent default to click
                    if (properties.tooltipsEvent !== 'mousemove') {
                        properties.tooltipsEvent = 'click';
                    }

                    (function (index, obj)
                    {
                        arc.addEventListener(properties.tooltipsEvent, function (e)
                        {
                            // If the event for tooltips is mousemove and the
                            // tooltip is already visible then do nothing
                            var tooltip = RGraph.SVG.REG.get('tooltip');
                            if (tooltip && properties.tooltipsEvent === 'mousemove' && index === tooltip.__index__) {
                                return;
                            }





                            obj.removeHighlight();

                            // Show the tooltip
                            RGraph.SVG.tooltip({
                                object:          obj,
                                index:           index,
                                sequentialIndex: index,
                                text:            typeof properties.tooltips === 'string' ? properties.tooltips : properties.tooltips[index],
                                event:           e
                            });
                            
                            // Highlight the rect that has been clicked on
                            obj.highlight(e.target);
                            
                            var highlight = RGraph.SVG.REG.get('highlight');
                            
                            if (properties.tooltipsEvent === 'mousemove') {
                                highlight.style.cursor = 'pointer';
                            }
                            
                        }, false);

                        // Install the event listener that changes the
                        // cursor if necessary
                        if (properties.tooltipsEvent === 'click') {
                            arc.addEventListener('mousemove', function (e)
                            {
                                e.target.style.cursor = 'pointer';
                            }, false);
                        }
                        
                    }(i, this));
                }
            }

            //
            // Redraw the segments if necessary so that they're on
            // top of any shadow
            //
            if (properties.shadow && opt.shadow) {
                this.redrawSegments();
            }
        };








        //
        // Redraw the Bars o that the bars appear above any shadow
        //
        this.redrawSegments = function ()
        {
            this.drawSegments({shadow: false});
        };








        //
        // Draw the labels
        //
        this.drawLabels = function ()
        {
            var angles   = this.angles,
                labels   = properties.labels,
                textConf = RGraph.SVG.getTextConf({
                    object: this,
                    prefix: 'labels'
                });

            for (var i=0; i<angles.length; ++i) {
                
                var endpoint = RGraph.SVG.TRIG.getRadiusEndPoint({
                    angle: angles[i].halfway - RGraph.SVG.TRIG.HALFPI,
                    r: angles[i].radius + 15
                });
                
                var x = endpoint[0] + angles[i].cx,
                    y = endpoint[1] + angles[i].cy,
                    valign,
                    halign;

                // Figure out the valign and halign based on the quadrant
                // the center of the sgement is in.
                if (angles[i].halfway > 0 && angles[i].halfway < RGraph.SVG.TRIG.HALFPI) {
                    halign = 'left';
                    valign = 'bottom';
                } else if (angles[i].halfway > RGraph.SVG.TRIG.HALFPI && angles[i].halfway < RGraph.SVG.TRIG.PI) {
                    halign = 'left';
                    valign = 'top';
                } else if (angles[i].halfway > RGraph.SVG.TRIG.PI && angles[i].halfway < (RGraph.SVG.TRIG.HALFPI + RGraph.SVG.TRIG.PI)) {
                    halign = 'right';
                    valign = 'top';
                } else if (angles[i].halfway > (RGraph.SVG.TRIG.HALFPI + RGraph.SVG.TRIG.PI) && angles[i].halfway < RGraph.SVG.TRIG.TWOPI) {
                    halign = 'right';
                    valign = 'top';
                }

                RGraph.SVG.text({
                    object: this,
                    parent: this.svgAllGroup,
                    tag:    'labels',
                    
                    text:   typeof labels[i] === 'string' ? labels[i] : '',
                    
                    x:      x,
                    y:      y,
                    
                    valign: valign,
                    halign: halign,
                    
                    font:   textConf.font,
                    size:   textConf.size,
                    bold:   textConf.bold,
                    italic: textConf.italic,
                    color:  textConf.color
                });
            }
        };








        //
        // Draws the ingraph labels
        //
        this.drawIngraphLabels = function ()
        {
            if (properties.labelsIngraph) {

                var textConf = RGraph.SVG.getTextConf({
                    object: this,
                    prefix: 'labelsIngraph'
                });

                for (var i=0; i<this.angles.length; ++i) {
    
                    // Some defaults
                    var halign   = properties.labelsIngraphHalign || 'center',
                        valign   = properties.labelsIngraphValign || 'center',

                        bgcolor  = properties.labelsIngraphBackground  || 'transparent',
                        decimals = properties.labelsIngraphDecimals    || 0,
                        padding  = typeof properties.labelsIngraphBackground === 'string' ? 3 : 0;

                    // Work out the coordinates
                    var xy = RGraph.SVG.TRIG.getRadiusEndPoint({
                        angle: this.angles[i].halfway - RGraph.SVG.TRIG.HALFPI,
                            r: this.angles[i].radius * (typeof properties.labelsIngraphRadiusPos === 'number' ? properties.labelsIngraphRadiusPos : 0.5)
                    });
                    
                    if (typeof properties.labelsIngraphSpecific === 'object' && properties.labelsIngraphSpecific) {
                        if (typeof properties.labelsIngraphSpecific[i] === 'string') {
                            var str = properties.labelsIngraphSpecific[i];
                        } else {
                            var str = '';
                        }
                    } else {
                        if (typeof properties.labelsIngraphFormatter === 'function') {
                            var str = properties.labelsIngraphFormatter({
                                object: this,
                                number: this.data[i].toFixed(decimals)
                            })
                        } else {

                            var str = RGraph.SVG.numberFormat({
                                prepend:  properties.labelsIngraphUnitsPre,
                                append:   properties.labelsIngraphUnitsPost,
                                point:    properties.labelsIngraphPoint,
                                thousand: properties.labelsIngraphThousand,
                                num:      this.data[i].toFixed(decimals),
                                object: this
                            });
                        }
                    }
    
                    // Draw the text
                    RGraph.SVG.text({
                        object:     this,
                        parent:     this.svgAllGroup,
                        tag:        'labels.ingraph',
                        x:          this.angles[i].cx + xy[0],
                        y:          this.angles[i].cy + xy[1],
                        text:       str,
                        halign:     halign,
                        valign:     valign,
                        
                        font:   textConf.font,
                        size:   textConf.size,
                        bold:   textConf.bold,
                        italic: textConf.italic,
                        color:  textConf.color,

                        background: bgcolor,
                        padding:    padding
                    });
                }
            }
        };








        //
        // This function draws the labels in a list format
        //
        this.drawLabelsSticks = function ()
        {
            var labels_right  = [],
                labels_left   = [],
                labels_coords = [];

            for (var i=0; i<this.angles.length; ++i) {

                var angle          = (this.angles[i].start + ((this.angles[i].end - this.angles[i].start) / 2)) - RGraph.SVG.TRIG.HALFPI, // Midpoint
                    
                    endpoint_inner = RGraph.SVG.TRIG.getRadiusEndPoint({angle: angle, r: this.radius + 5}),
                    endpoint_outer = RGraph.SVG.TRIG.getRadiusEndPoint({angle: angle, r: this.radius + 30}),
                    
                    explosion = [
                        (typeof properties.exploded === 'number' ? properties.exploded : properties.exploded[i]),
                        Math.cos(angle) * (typeof properties.exploded === 'number' ? properties.exploded : properties.exploded[i]),
                        Math.sin(angle) * (typeof properties.exploded === 'number' ? properties.exploded : properties.exploded[i])
                    ];
                
                // Initialise this array
                labels_coords[i] = [];
                
                // Initialise this
                var labels = {};





                // Push the label into the correct array
                if (angle > RGraph.SVG.TRIG.HALFPI) {
                
                    var index = labels_left.length;

                    labels_left[index]        = [];
                    labels_left[index].text   = properties.labels[i];
                    labels_left[index].halign = 'right';
                    labels                    = labels_left;

                    labels_coords[i].halign = 'right';
                } else {
                    
                    var index = labels_right.length; 

                    labels_right[index]        = [];
                    labels_right[index].text   = properties.labels[i];
                    labels_right[index].halign = 'right';
                    labels                     = labels_right;

                    labels_coords[i].halign = 'left';
                }







                endpoint_inner[0] += (explosion[1] || 0);
                endpoint_inner[1] += (explosion[2] || 0);
                
                endpoint_outer[0] += (explosion[1] || 0);
                endpoint_outer[1] += (explosion[2] || 0);
            
                var x,y;

                if (labels[index].text) {
                    var stick = RGraph.SVG.create({
                        svg: this.svg,
                        parent: this.svgAllGroup,
                        type: 'path',
                        attr: {
                            d: 'M {1} {2} L {3} {4}'.format(
                                this.centerx + endpoint_inner[0],
                                this.centery + endpoint_inner[1],
                                this.centerx + endpoint_outer[0],
                                this.centery + endpoint_outer[1]
                            ),
                            stroke: '#999',
                            fill: 'rgba(0,0,0,0)'
                        }
                    });
                }
                
                // The path is altered later so this needs saving
                if (stick) {
                    labels[index].stick = stick;
                }
                
                x = (this.centerx + endpoint_outer[0] + (angle > 1.57 ? -50 : 50));
                y = (this.centery + endpoint_outer[1]);


                labels_coords[i].x      = x ;
                labels_coords[i].y      = y;
                labels_coords[i].text = properties.labels[i];
            }

            // Calculate the spacing for each side
            var vspace_right = (this.height - properties.marginTop - properties.marginBottom) / labels_right.length;
            var vspace_left  = (this.height - properties.marginTop - properties.marginBottom) / labels_left.length;

            // Reset these
            x = y = 0;



            // Get the text configuration
            var textConf = RGraph.SVG.getTextConf({
                object: this,
                prefix: 'labels'
            });

            // Loop through the RHS labels
            for (var i=0; i<labels_right.length; ++i) {
                if (labels_right[i] && labels_right[i].text) {

                    x = this.centerx + this.radius + properties.labelsSticksOffset;
                    y = properties.marginTop + (vspace_right * i) + (vspace_right / 2);


                    // Add the label to the scene
                    RGraph.SVG.text({
                        
                        object: this,
                        parent: this.svgAllGroup,
                        tag:    'labels.sticks',
                        
                        text:   typeof labels_right[i].text === 'string' ? labels_right[i].text : '',
                        
                        x:      x,
                        y:      y,
                        
                        valign: 'center',
                        halign: labels_right[i].text,
                        
                        font:   textConf.font,
                        size:   textConf.size,
                        bold:   textConf.bold,
                        italic: textConf.italic,
                        color:  textConf.color
                    });
                    
                    // Now update the path of the stick
                    var str = labels_right[i].stick.getAttribute('d').replace(/ L /, ' Q ') + ' {1} {2}';

                    labels_right[i].stick.setAttribute(
                        'd',
                        str.format(
                            x - 5,
                            y
                        )
                    );
                }
            }





            // Loop through the LHS labels
            for (var i=0; i<labels_left.length; ++i) {
                if (labels_left[i] && labels_left[i].text) {

                    x = this.centerx - this.radius - properties.labelsSticksOffset;
                    y = this.height - (properties.marginTop + (vspace_left * i) + (vspace_left / 2));

                
                    // Add the label to the scene
                    RGraph.SVG.text({
                        
                        object: this,
                        parent: this.svgAllGroup,
                        tag:    'labels.sticks',
                        
                        text:   typeof labels_left[i].text === 'string' ? labels_left[i].text : '',
                        
                        x:      x - 7,
                        y:      y,
                        
                        valign: 'center',
                        halign: labels_left[i].halign,
                        
                        font:   textConf.font,
                        size:   textConf.size,
                        bold:   textConf.bold,
                        italic: textConf.italic,
                        color:  textConf.color
                    });
                    
                    // Now update the path of the stick
                    var str = labels_left[i].stick.getAttribute('d').replace(/ L /, ' Q ') + ' {1} {2}';

                    labels_left[i].stick.setAttribute(
                        'd',
                        str.format(
                            x - 5,
                            y
                        )
                    );
                }
            }
        };








        //
        // This function can be used to highlight a segment on the chart
        // 
        // @param object segment The segment to highlight
        //
        this.highlight = function (segment)
        {
            // Outline style highlighting
            if (properties.highlightStyle === 'outline') {
                
                var index = segment.getAttribute('data-index');
            
                var path = RGraph.SVG.TRIG.getArcPath3({
                    start:          this.angles[index].start,
                    end:            this.angles[index].end,
                    cx:             this.angles[index].cx,
                    cy:             this.angles[index].cy,
                    r:              this.angles[index].radius + 2,
                    anticlockwise:  false,
                    lineto:         false
                });
            
                // Add the reverse arc
                path += RGraph.SVG.TRIG.getArcPath3({
                    start:          this.angles[index].end,
                    end:            this.angles[index].start,
                    cx:             this.angles[index].cx,
                    cy:             this.angles[index].cy,
                    r:              this.angles[index].radius + 2 + properties.highlightStyleOutlineWidth,
                    anticlockwise:  true
                });
            
                var highlight = RGraph.SVG.create({
                    svg: this.svg,
                    parent: this.svgAllGroup,
                    type: 'path',
                    attr: {
                        d: path,
                        fill: properties.colors[index],
                        stroke: 'transparent'
                    },
                    style: {
                        pointerEvents: 'none'
                    }
                });
            
            // Regular highlighting
            } else {
            
                var highlight = RGraph.SVG.create({
                    svg: this.svg,
                    parent: this.svgAllGroup,
                    type: 'path',
                    attr: {
                        d: segment.getAttribute('d'),
                        fill: properties.highlightFill,
                        stroke: properties.highlightStroke,
                        'stroke-width': properties.highlightLinewidth
                    },
                    style: {
                        pointerEvents: 'none'
                    }
                });
            }

            if (properties.tooltipsEvent === 'mousemove') {
                highlight.addEventListener('mouseout', function (e)
                {
                    highlight.parentNode.removeChild(highlight);
                    RGraph.SVG.hideTooltip();

                    RGraph.SVG.REG.set('highlight', null);
                }, false);
            }


            // Store the highlight rect in the registry so
            // it can be cleared later
            RGraph.SVG.REG.set('highlight', highlight);
        };








        //
        // This allows for easy specification of gradients
        //
        this.parseColors = function () 
        {
            // Save the original colors so that they can be restored when the canvas is reset
            if (!Object.keys(this.originalColors).length) {
                this.originalColors = {
                    colors:        RGraph.SVG.arrayClone(properties.colors, true),
                    highlightFill: RGraph.SVG.arrayClone(properties.highlightFill, true)
                }
            }
            
            
            // colors
            var colors = properties.colors;

            if (colors) {
                for (var i=0; i<colors.length; ++i) {
                    colors[i] = RGraph.SVG.parseColorRadial({
                        object: this,
                        color: colors[i]
                    });
                }
            }
            
            // Highlight fill
            properties.highlightFill = RGraph.SVG.parseColorRadial({
                object: this,
                color: properties.highlightFill
            });
        };








        //
        // A roundRobin effect for the Pie chart
        //
        // @param object    Options for the effect
        // @param function  An optional callback function to call when
        //                  the effect is complete
        //
        this.roundrobin =
        this.roundRobin = function ()
        {
            var obj          = this,
                opt          = arguments[0] || {},
                data         = RGraph.SVG.arrayClone(this.data, true),
                frame        = 1,
                frames       = opt.frames || 30,
                callback     = typeof opt.callback === 'function' ? opt.callback : function () {},
                dataSum      = RGraph.SVG.arraySum(this.data),
                textColor    = properties.textColor,
                ingraph      = properties.labelsIngraph,
                multiplier   = 0;

            
            // Set the text colors to transparent
            properties.textColor     = 'rgba(0,0,0,0)';
            properties.labelsIngraph = false;


            // Draw the chart first
            obj.draw();
            
            // Now get the resulting angles
            var angles = RGraph.SVG.arrayClone(this.angles, true);

            function iterator ()
            {
                multiplier =  (1 / frames) * frame++;

                for (var i=0; i<angles.length; ++i) {

                    var value = obj.data[i];

                    obj.angles[i].start = angles[i].start * multiplier;
                    obj.angles[i].end   = angles[i].end   * multiplier;

                    //var segment = (((value * properties.roundRobinMultiplier) / dataSum) * RGraph.SVG.TRIG.TWOPI);
                    var segment = ((obj.angles[i].end - obj.angles[i].start) / 2),
                        explodedX = Math.cos(obj.angles[i].start + segment - RGraph.SVG.TRIG.HALFPI) * (properties.exploded[i] || 0),
                        explodedY = Math.sin(obj.angles[i].start + segment - RGraph.SVG.TRIG.HALFPI) * (properties.exploded[i] || 0);



                    var path = RGraph.SVG.TRIG.getArcPath({
                        cx:    obj.centerx + explodedX,
                        cy:    obj.centery + explodedY,
                        r:     obj.radius,
                        start: obj.angles[i].start,
                        end:   obj.angles[i].end
                    });





                    // Donut
                    if (properties.donut) {
                    
                        var donutWidth = properties.donutWidth;
                    
                        var donut_path = RGraph.SVG.TRIG.getArcPath3({ 
                            cx:     obj.angles[i].cx,
                            cy:     obj.angles[i].cy,
                            r:      obj.radius - donutWidth,
                            start:  obj.angles[i].end,
                            end:    obj.angles[i].start,
                            moveto: false,
                            anticlockwise: true
                        });
                        
                        var xy = RGraph.SVG.TRIG.getRadiusEndPoint({
                            angle: obj.angles[i].end - RGraph.SVG.TRIG.HALFPI,
                            r:     obj.radius - donutWidth
                        });
                    
                        path =   path
                               + " L {1} {2} ".format(xy[0] + obj.angles[i].cx, xy[1] + obj.angles[i].cy)
                               + donut_path
                               + " Z";
                    
                    } else {
                    
                        path = path + " L {1} {2} ".format(
                            obj.angles[i].cx,
                            obj.angles[i].cy
                        ) + " Z"
                    }









                    path = path + " L {1} {2} Z".format(
                        obj.centerx + explodedX,
                        obj.centery + explodedY
                    );

                    if (obj.shadowNodes && obj.shadowNodes[i]) {
                        obj.shadowNodes[i].setAttribute('d', path);
                    }
                    obj.nodes[i].setAttribute('d', path);
                }


                if (frame <= frames) {
                    RGraph.SVG.FX.update(iterator);
                } else {
                    properties.textColor     = textColor;
                    properties.labelsIngraph = ingraph;

                    RGraph.SVG.redraw(obj.svg);

                    callback(obj);
                }
            }
            
            iterator();

            return this;
        };








        //
        // Using a function to add events makes it easier to facilitate method
        // chaining
        // 
        // @param string   type The type of even to add
        // @param function func 
        //
        this.on = function (type, func)
        {
            if (type.substr(0,2) !== 'on') {
                type = 'on' + type;
            }
            
            RGraph.SVG.addCustomEventListener(this, type, func);
    
            return this;
        };








        //
        // Used in chaining. Runs a function there and then - not waiting for
        // the events to fire (eg the onbeforedraw event)
        // 
        // @param function func The function to execute
        //
        this.exec = function (func)
        {
            func(this);
            
            return this;
        };








        //
        // Remove highlight from the chart (tooltips)
        //
        this.removeHighlight = function ()
        {
            //var highlight = RGraph.SVG.REG.get('highlight');
            //if (highlight && highlight.parentNode) {
            //    highlight.parentNode.removeChild(highlight);
            //}
            
            //RGraph.SVG.REG.set('highlight', null);
            
            RGraph.SVG.removeHighlight();
        };








        //
        // A worker function that handles Bar chart specific tooltip substitutions
        //
        this.tooltipSubstitutions = function (opt)
        {
            return {
                  index: opt.index,
                dataset: 0,
        sequentialIndex: opt.index,
                  value: this.data[opt.index],
                 values: [this.data[opt.index]]
            };
        };








        //
        // A worker function that returns the correct color/label/value
        //
        // @param object specific The indexes that are applicable
        // @param number index    The appropriate index
        //
        this.tooltipsFormattedCustom = function (specific, index, colors)
        {
            var color = colors[specific.index];
            var label = ( (typeof properties.tooltipsFormattedKeyLabels === 'object' && typeof properties.tooltipsFormattedKeyLabels[specific.index] === 'string') ? properties.tooltipsFormattedKeyLabels[specific.index] : '');

            return {
                label: label,
                color: color
            };
        };








        //
        // This allows for static tooltip positioning
        //
        this.positionTooltipStatic = function (args)
        {
            var obj        = args.object,
                e          = args.event,
                tooltip    = args.tooltip,
                index      = args.index,
                svgXY      = RGraph.SVG.getSVGXY(obj.svg),
                segment    = this.angles[args.index],
                angle      = segment.halfway,
                multiplier = 0.5;

//
            // Determine the correct radius to use when calculating the
            // coordinates of the tooltip
            //
            if (properties.donut) {
                // Determine the radius
                var radius = (this.radius - properties.donutWidth) + (properties.donutWidth / 2);
            
            } else {
              var radius = this.radius * multiplier;
            }
            
            // Account for any explosion
            if (properties.exploded[index]) {
                radius += properties.exploded[index];
            }

            var endpoint = RGraph.SVG.TRIG.getRadiusEndPoint({
                angle: angle - RGraph.SVG.TRIG.HALFPI,
                    r: radius
            });

            // Position the tooltip in the X direction
            args.tooltip.style.left = (
                  svgXY[0]                  // The X coordinate of the canvas
                  + this.centerx            // The center X coord
                  + endpoint[0]             // The endpoint X coordinate
                - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width
            ) + 'px';

            args.tooltip.style.top  = (
                  svgXY[1]             // The Y coordinate of the canvas
                  + this.centery       // The center Y coord
                  + endpoint[1]        // The endpoint Y coordinate
                - tooltip.offsetHeight // The height of the tooltip
                - 10                   // An arbitrary amount
            ) + 'px';
        };








        //
        // Set the options that the user has provided
        //
        for (i in conf.options) {
            if (typeof i === 'string') {
                this.set(i, conf.options[i]);
            }
        }
    };








    return this;








// End module pattern
})(window, document);