// 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.Rose = 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);
        //if (typeof conf.data === 'string') {
        //    conf.data = conf.data.split(/,|\|/);
        //}

        //for (var i=0; i<conf.data.length; ++i) {
        //    if (typeof conf.data[i] === 'string') {
        //        conf.data[i] = parseFloat(conf.data[i]);
        //    }
        //}
        
        




        this.type            = 'rose';
        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            = RGraph.SVG.arrayClone(conf.data, true);
        this.originalData    = RGraph.SVG.arrayClone(conf.data, true);
        this.angles          = [];
        this.angles2         = [];
        this.colorsParsed    = false;
        this.originalColors  = {};
        this.gradientCounter = 1;
        this.nodes           = [];
        this.shadowNodes     = [];
        this.max             = 0;
        this.redraw          = false;
        this.highlight_node  = null;
        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,
            amargin:       '3deg',
            
            backgroundGrid: true,
            backgroundGridColor:            '#ddd',
            backgroundGridRadialsCount:     null,
            backgroundGridRadialsAngleOffset: 0,
            backgroundGridConcentricsCount: 5,
            backgroundGridLinewidth:        1,

            colorsStroke: 'white',
            colors: [
                'red', 'black', 'orange', 'green', '#6ff', '#ccc',
                'pink', 'orange', 'cyan', 'maroon', 'olive', 'teal'
            ],
            colorsOpacity: 1,
            
            textColor:  'black',
            textFont:   'Arial, Verdana, sans-serif',
            textSize:   12,
            textBold:   false,
            textItalic: false,
            text:       null,

            labels:       [],
            labelsFont:   null,
            labelsSize:   null,
            labelsColor:  null,
            labelsBold:   null,
            labelsItalic: null,
            labelsRadialMargin: 10,
            labelsAngleOffset: 0,
            labelsFormattedDecimals:  0,
            labelsFormattedPoint:     '.',
            labelsFormattedThousand:  ',',
            labelsFormattedUnitsPre:  '',
            labelsFormattedUnitsPost: '',

            scaleVisible:     true,
            scaleUnitsPre:    '',
            scaleUnitsPost:   '',
            scaleMax:         null,
            scaleMin:         0,
            scalePoint:       '.',
            scaleThousand:    ',',
            scaleRound:       false,
            scaleDecimals:    0,
            scaleFormatter:   null,
            scaleBold:        null,
            scaleItalic:      null,
            scaleColor:       null,
            scaleSize:        null,
            scaleFont:        null,
            scaleLabelsCount: 5,

            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,
            
            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,0.25)',

            exploded: 0,


            key:            null,
            keyColors:      null,
            keyOffsetx:     0,
            keyOffsety:     0,
            keyLabelsOffsetx: 0,
            keyLabelsOffsety: -1,
            keyLabelsFont:    null,
            keyLabelsSize:    null,
            keyLabelsColor:   null,
            keyLabelsBold:    null,
            keyLabelsItalic:  null,
            
            segmentsAngleOffset: 0,
            variant: 'normal',
            
            effectWaveMultiplier:       1,// Do not delete this
            effectGrowMultiplier:       1,// Do not delete this
            effectRoundrobinMultiplier: 1, // Do not delete this
            
            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');



            // 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'));



            // Change this to maintain BC
            if (typeof properties.marginInner !== 'undefined') {
                properties.amargin = properties.marginInner;
            }



            // Reset the data back to the original values
            this.data = RGraph.SVG.arrayClone(this.originalData, true);



            // Reset the angles array to stop it growing
            this.angles  = [];
            
            // Create the arrays in the angles2 array based on
            // the data that we've been passed
            for (var i=0; i<this.data.length; ++i) {
                this.angles2[i] = [];
            }





            // 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);





            //
            // Convert the nargin from strings to a number
            //
            if (typeof properties.amargin === 'string' && properties.amargin.match(/([0-9.]+)deg/)) {
                properties.amargin = RegExp.$1 / (180 / Math.PI);
            }




            //
            // Add the data to the .originalData array and work out the max value
            // 
            // 2/5/14 Now also use this loop to ensure that the data pieces
            //        are numbers
            // 
            // **Is this necessary **
            //
            //if (RGraph.SVG.isArray(this.data) && (typeof this.data[0] === 'number' || typeof this.data[0] === 'string')) {
            //    this.data = [this.data];
            //}

            // Convert strings to numbers
            for (var i=0; i<this.data.length; ++i) {
                if (typeof this.data[i] === 'object') {
                    for (var j=0; j<this.data[i].length; ++j) {            
                        if (typeof this.data[i][j] === 'string') {
                            this.data[i][j] = RGraph.SVG.stringsToNumbers(this.data[i][j]);
                        }
                    }
                } else if (typeof this.data[i] === 'string') {
                    this.data[i] = RGraph.SVG.stringsToNumbers(this.data[i]);
                }
            }









            // Get the max value. This sets the maximum value on the
            // this.max variable
            this.getMaxValue();







            // Parse the colors for gradients
            RGraph.SVG.resetColorsToOriginalValues({object:this});
            this.parseColors();

            //
            // Get the scale
            //

            this.scale = RGraph.SVG.getScale({
                object:    this,
                numlabels: typeof properties.scaleLabelsCount === 'number' ? properties.scaleLabelsCount : properties.backgroundGridConcentricCount,
                unitsPre:  properties.scaleUnitsPre,
                unitsPost: properties.scaleUnitsPost,
                max:       typeof properties.scaleMax === 'number' ? properties.scaleMax : this.max,
                min:       properties.scaleMin,
                point:     properties.scalePoint,
                round:     properties.scaleRound,
                thousand:  properties.scaleThousand,
                decimals:  properties.scaleDecimals,
                strict:    typeof properties.scaleMax === 'number',
                formatter: properties.scaleFormatter
            });

            this.max = this.scale.max;

















            // 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 background 'grid'
            this.drawBackground();

            

            // Draw the chart
            this.drawRose();






            // Draw the labels
            this.drawLabels();



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






            // 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?');
            }

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



            // 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)
            {
                obj.hideHighlight(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);
        };








        //
        // Draw the background grid
        //
        this.drawBackground = function ()
        {
            if (properties.backgroundGrid) {
            
                // Create the background grid group tag
                var grid = RGraph.SVG.create({
                    svg: this.svg,
                    parent: this.svgAllGroup,
                    type: 'g',
                    attr: {
                        className: 'rgraph_radar_grid',
                        fill: 'rgba(0,0,0,0)',
                        stroke: properties.backgroundGridColor
                    },
                    style: {
                        pointerEvents: 'none'
                    }
                });
            
                // Draw the concentric "rings" grid lines that are
                // arranged around the centerx/centery along with
                // the radials that eminate from the center outwards

                var origin      = 0 - (RGraph.SVG.TRIG.PI / 2),
                    radials     = (typeof properties.backgroundGridRadialsCount === 'number' ? properties.backgroundGridRadialsCount :  this.data.length),
                    concentrics = properties.backgroundGridConcentricsCount,
                    step        = RGraph.SVG.TRIG.TWOPI / radials;





                // First draw the radial lines that emanate from the
                // center outwards
                if (radials > 0) {
                    // This draws the radials for the non-equi-angular ONLY
                    if (properties.variant === 'non-equi-angular') {














                            // Number of radials always matches the number of data pieces
                            var radials = this.data.length;
                            
                            // Work out the total of the second part of each data bit
                            for (var i=0,total=0; i<this.data.length; ++i) {
                                total += this.data[i][1];
                            }

                            for (var i=0,sum=0; i<this.data.length; ++i) {

                                var coords = RGraph.SVG.TRIG.toCartesian({
                                    cx: this.centerx,
                                    cy: this.centery,
                                    r: this.radius,
                                    angle: origin + ( (sum / total) * RGraph.SVG.TRIG.TWOPI) + properties.backgroundGridRadialsAngleOffset
                                });

                                var str = 'M {1} {2} L {3} {4}'.format(
                                    this.centerx,
                                    this.centery,
                                    coords.x,
                                    coords.y
                                );
            
                                RGraph.SVG.create({
                                    svg: this.svg,
                                    type: 'path',
                                    parent: grid,
                                    attr: {
                                        d: str,
                                        stroke: properties.backgroundGridColor,
                                        'stroke-width': properties.backgroundGridLinewidth
                                    }
                                });
                                
                                sum += this.data[i][1];
                            }












                    // This draws the radials for normal and STACKED Rose charts
                    } else {
                        for (var i=0,len=radials; i<len; ++i) {
        
                            var coords = RGraph.SVG.TRIG.toCartesian({
                                cx: this.centerx,
                                cy: this.centery,
                                r: this.radius,
                                angle: origin + (i * step) + properties.backgroundGridRadialsAngleOffset
                            });
        
                            var str = 'M {1} {2} L {3} {4}'.format(
                                this.centerx,
                                this.centery,
                                coords.x,
                                coords.y
                            );
        
                            RGraph.SVG.create({
                                svg: this.svg,
                                type: 'path',
                                parent: grid,
                                attr: {
                                    d: str,
                                    stroke: properties.backgroundGridColor,
                                    'stroke-width': properties.backgroundGridLinewidth
                                }
                            });
                        }
                    }
                }





                // Draw the concentrics
                if (concentrics > 0) {

                    for (var j=1; j<=concentrics; j++) {

                        // Add circle to the scene
                        RGraph.SVG.create({
                            svg: this.svg,
                            type: 'circle',
                            parent: grid,
                            attr: {
                                cx: this.centerx,
                                cy: this.centery,
                                r: this.radius * (j/concentrics),
                                fill: 'transparent',
                                stroke: properties.backgroundGridColor,
                                'stroke-width': properties.backgroundGridLinewidth
                            }
                        });
                    }
                }
            }
        };








        //
        // Draws the Rose chart
        //
        this.drawRose = function ()
        {
            var opt = arguments[0] || {};


            // Empty the this.coords array so that animations don't
            // continually add new segments on top of old ones.
            for (var i=0; i<this.angles.length; ++i) {
                this.angles[i].element.parentNode.removeChild(this.angles[i].element);
            }






            // Reset the angles array to stop it growing.
            //
            // This needs to be here so that the grow effect does cause the
            // angles arrays to grow and grow and grow...
            this.angles  = [];
            
            // Create the arrays in the angles2 array based on
            // the data that we've been passed
            for (var i=0; i<this.data.length; ++i) {
                this.angles2[i] = [];
            }




            // Jump to another function if we're drawing a non-equi-angular chart
            if (properties.variant === 'non-equi-angular') {
                return this.drawRoseNonEquiAngular(opt);
            }






            var radians = RGraph.SVG.TRIG.TWOPI / this.data.length;

            // Don't add this group twice
            if (!document.getElementById('rgraph_rose_segments_' + this.uid)) {
                var group = RGraph.SVG.create({
                    svg: this.svg,
                    type:'g',
                    parent: this.svgAllGroup,
                    attr: {
                        id: 'rgraph_rose_segments_' + this.uid
                    }
                });
            } else {
                var group = document.getElementById('rgraph_rose_segments_' + this.uid);
            }


            // Now loop thru the data
            for (var i=0,seq=0; i<this.data.length; ++i,++seq) {

                var radius = (this.data[i] / this.scale.max) * this.radius * properties.effectGrowMultiplier,
                    start  = (i / this.data.length) * RGraph.SVG.TRIG.TWOPI * properties.effectRoundrobinMultiplier,
                    end    = (((i / this.data.length) * RGraph.SVG.TRIG.TWOPI) + radians) * properties.effectRoundrobinMultiplier;

                // Get the exploded distance
                var explosion = this.getExploded({
                    index: i,
                    start: start - RGraph.SVG.TRIG.HALFPI,
                    end: end - RGraph.SVG.TRIG.HALFPI
                });



















                // Is the data piece an array or a number?
                if (typeof this.data[i] === 'object' && !RGraph.SVG.isNullish(this.data[i])) {
                
                    // Create a group for the parts of this segment
                    if (!document.getElementById('rose_' + this.uid + '_segment_group_' + i)) {
                        var segment_group = RGraph.SVG.create({
                            svg: this.svg,
                            type: 'g',
                            parent: group,
                            attr: {
                                id: 'rose_' + this.uid + '_segment_group_' + i
                            }
                        });
                    } else {
                        var segment_group = document.getElementById('rose_' + this.uid + '_segment_group_' + i)
                    }
                
                    for (var j=0,sum=0,accRadius=0; j<this.data[i].length; ++j,++seq) {
                    
                        //
                        // Must reset the prevradius variable
                        //
                        if (j === 0) {
                            prevRadius = 0;
                        }
                    
                        sum += this.data[i][j];
                        
                        var radius = (sum / this.scale.max) * this.radius * properties.effectGrowMultiplier,
                            cx     = this.centerx + (explosion[0] * properties.effectRoundrobinMultiplier),
                            cy     = this.centery + (explosion[1] * properties.effectRoundrobinMultiplier);
                        
                        // This (I think is the OUTER curve in the segment
                        var arcPath = RGraph.SVG.TRIG.getArcPath2({
                            cx: cx,
                            cy: cy,
                            r: radius,
                            start: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                            end: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                            anticlockwise: false
                        });
                        
                        // The inner most segment
                        if (j === 0) {
                            arcPath = '{1} z'.format(
                                arcPath
                            );
                        } else {
                        
                            var arcPath2 = RGraph.SVG.TRIG.getArcPath2({
                                cx: cx,
                                cy: cy,
                                r: prevRadius,
                                start: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                end: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                anticlockwise: true
                            });
                            arcPath = '{1} L {2} {3} {4}'.format(
                                arcPath,
                                cx,
                                cy,
                                arcPath2
                            );
                        }

                        var path = RGraph.SVG.create({
                            svg: this.svg,
                            type: 'path',
                            parent: segment_group,
                            attr: {
                                d: arcPath,
                                fill: properties.colorsSequential ? properties.colors[seq]  : properties.colors[j],
                                'fill-opacity': properties.colorsOpacity,
                                stroke: properties.colorsStroke,
                                'stroke-width': properties.linewidth,
                                
                                'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[seq] : '',
                                'data-index': i,
                                'data-centerx': cx,
                                'data-centery': cy,
                                'data-group': i,
                                'data-subindex': j,
                                'data-value': this.data[i][j],
                                'data-start-angle': start + properties.amargin + properties.segmentsAngleOffset,
                                'data-end-angle': end - properties.amargin + properties.segmentsAngleOffset,
                                'data-radius': radius,
                                'data-radius-inner': typeof prevRadius === 'number' ? prevRadius * properties.effectGrowMultiplier : 0,
                                'data-sequential-index': seq
                            }
                        });


                        // Install the tooltip listener
                        if (properties.tooltips && (properties.tooltips[seq] || typeof properties.tooltips === 'string') ) {
                        
                            // Make the tooltipsEvent default to click
                            if (properties.tooltipsEvent !== 'mousemove') {
                                properties.tooltipsEvent = 'click';
                            }
        
                            (function (index, group, seq, obj)
                            {
                                path.addEventListener(properties.tooltipsEvent, function (e)
                                {
                                    obj.removeHighlight();

                                    // Show the tooltip
                                    RGraph.SVG.tooltip({
                                        object: obj,
                                        group: group,
                                        index: index,
                                        sequentialIndex: seq,
                                        text: typeof properties.tooltips === 'string' ? properties.tooltips : properties.tooltips[seq],
                                        event: e
                                    });
                                    
                                    // Highlight the segment 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') {
                                    path.addEventListener('mousemove', function (e)
                                    {
                                        e.target.style.cursor = 'pointer';
                                    }, false);
                                }
                                
                            }(j, i, seq, this));
                        }

                        // Add the segment to the angles and angles2 array
                        this.angles.push({
                            object: this,
                            element: path
                        });

                        this.angles2[i].push({
                            object: this,
                            element: path
                        });
                    
                        var prevRadius = radius;
                    }










                    seq--;













                // A regular number
                } else {
                    
                    var cx = this.centerx + (explosion[0] * properties.effectRoundrobinMultiplier),
                        cy = this.centery + (explosion[1] * properties.effectRoundrobinMultiplier);

                    var arcPath = RGraph.SVG.TRIG.getArcPath2({
                        cx: cx,
                        cy: cy,
                        r: radius,
                        start: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                        end: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                        anticlockwise: false
                    });

                    var path = RGraph.SVG.create({
                        svg: this.svg,
                        type: 'path',
                        parent: group,
                        attr: {
                            d: '{1} z'.format(
                                arcPath
                            ),
                            fill: properties.colorsSequential ? properties.colors[i]  : properties.colors[0],
                            'fill-opacity': properties.colorsOpacity,
                            stroke: properties.colorsStroke,
                            'stroke-width': properties.linewidth,
                            
                            'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[i] : '',
                            'data-index': i,
                            'data-centerx': cx,
                            'data-centery': cy,
                            'data-value': this.data[i],
                            'data-start-angle': start + properties.amargin + properties.segmentsAngleOffset,
                            'data-end-angle': end - properties.amargin + properties.segmentsAngleOffset,
                            'data-radius': radius,
                            'data-sequential': seq
                        }
                    });

                    // Add the segment to the angles array
                    this.angles.push({
                        object: this,
                        element: path
                    });

                    this.angles2[i].push({
                        object: this,
                        element: path
                    });




                    if (properties.tooltips && (properties.tooltips[i] || typeof properties.tooltips === 'string') ) {
                    
                        // Make the tooltipsEvent default to click
                        if (properties.tooltipsEvent !== 'mousemove') {
                            properties.tooltipsEvent = 'click';
                        }
    
                        (function (index, obj)
                        {
                            path.addEventListener(properties.tooltipsEvent, function (e)
                            {
                                obj.removeHighlight();

                                // Show the tooltip
                                RGraph.SVG.tooltip({
                                    object: obj,
                                    index: index,
                                    group: 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') {
                                path.addEventListener('mousemove', function (e)
                                {
                                    e.target.style.cursor = 'pointer';
                                }, false);
                            }
                            
                        }(i, this));
                    }
                }
            }
        };








        //
        // Draws the radar, but only the non-equi-angular variant
        //
        this.drawRoseNonEquiAngular = function (opt)
        {
            if (!document.getElementById('rgraph_rose_segments_' + this.uid)) {
                var group = RGraph.SVG.create({
                    svg: this.svg,
                    type:'g',
                    parent: this.svgAllGroup,
                    attr: {
                        id: 'rgraph_rose_segments_' + this.uid
                    }
                });
            } else {
                var group = document.getElementById('rgraph_rose_segments_' + this.uid)
            }
            
            //Loop through the data summing the second data-pieces
            for (var i=0,total=0; i<this.data.length; ++i) {
                total += parseFloat(this.data[i][1]);
            }











            // The initial angles
            var start = 0;




            // Now loop thru the data
            for (var i=0,seq=0; i<this.data.length; ++i,++seq) {

                var radians = (this.data[i][1] / total) * RGraph.SVG.TRIG.TWOPI,
                    end     = start + radians;

                // Get the exploded distance
                var explosion = this.getExploded({
                    index: i,
                    start: start - RGraph.SVG.TRIG.HALFPI,
                    end: end - RGraph.SVG.TRIG.HALFPI
                });















                // A stacked non-equi-angular segment
                if (typeof this.data[i][0] === 'object' && !RGraph.SVG.isNullish(this.data[i][0])) {

                    if (!document.getElementById('rgraph_rose_' + this.uid + '_segment_group_' + i)) {
                        var segment_group = RGraph.SVG.create({
                            svg: this.svg,
                            type:'g',
                            parent: group,
                            attr: {
                                id: 'rgraph_rose_' + this.uid + '_segment_group_' + i
                            }
                        });
                    } else {
                        var segment_group = document.getElementById('rgraph_rose_' + this.uid + '_segment_group_' + i)
                    }

                    // Loop thru the set of values for this segment
                    for (var j=0,sum=0; j<this.data[i][0].length; ++j,++seq) {

                        sum += this.data[i][0][j];

                        // First segment in the stack or not?
                        if (j === 0) {
                            
                            var prevRadius = 0,
                                radius     = (sum / this.scale.max) * this.radius * properties.effectGrowMultiplier,
                                cx         = this.centerx + (explosion[0] * properties.effectRoundrobinMultiplier),
                                cy         = this.centery + (explosion[1] * properties.effectRoundrobinMultiplier);
                            
                            var arcPath = RGraph.SVG.TRIG.getArcPath2({
                                cx: cx,
                                cy: cy,
                                r: radius,
                                start: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                end: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                anticlockwise: false
                            });
                            
                            var arcPath2   = '';

                        } else {
                            
                            var prevRadius = radius, // The previous iterations radius
                                radius     = (sum / this.scale.max) * this.radius * properties.effectGrowMultiplier,
                                cx         = this.centerx + (explosion[0] * properties.effectRoundrobinMultiplier),
                                cy         = this.centery + (explosion[1] * properties.effectRoundrobinMultiplier);

                            var arcPath = RGraph.SVG.TRIG.getArcPath2({
                                cx: cx,
                                cy: cy,
                                r: radius,
                                start: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                end: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                anticlockwise: false
                            });

                            var arcPath2 = RGraph.SVG.TRIG.getArcPath2({
                                cx: cx,
                                cy: cy,
                                r: prevRadius,
                                start: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                end: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                                anticlockwise: true
                            });
                        }

                        var path = RGraph.SVG.create({
                            svg: this.svg,
                            type: 'path',
                            parent: segment_group,
                            attr: {
                                d: '{1} {2} z'.format(
                                    arcPath,
                                    arcPath2
                                ),
                                fill:                properties.colorsSequential ? properties.colors[seq]  : properties.colors[j],
                                'fill-opacity':      properties.colorsOpacity,
                                stroke:              properties.colorsStroke,
                                'stroke-width':      properties.linewidth,
                                'data-tooltip':      (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[i] : '',
                                'data-centerx':      cx,
                                'data-centery':      cy,
                                'data-index':        i,
                                'data-subindex':     j,
                                'data-value':        this.data[i][0][j],
                                'data-start-angle':  start + properties.amargin + properties.segmentsAngleOffset,
                                'data-end-angle':    end - properties.amargin + properties.segmentsAngleOffset,
                                'data-radius':       radius,
                                'data-radius-inner': prevRadius,
                                'data-sequential':   seq
                            }
                        });



                        // Add the segment to the angles array
                        this.angles.push({
                            object: this,
                            element: path
                        });
                       
                       this.angles2[i].push({
                            object: this,
                            element: path
                        });




                        // Install tooltips listeners
                        if (properties.tooltips && (properties.tooltips[seq] || typeof properties.tooltips === 'string') ) {

                            // Make the tooltipsEvent default to click
                            if (properties.tooltipsEvent !== 'mousemove') {
                                properties.tooltipsEvent = 'click';
                            }

                            (function (index,group,seq,obj)
                            {
                                path.addEventListener(properties.tooltipsEvent, function (e)
                                {
                                    obj.removeHighlight();

                                    // Show the tooltip
                                    RGraph.SVG.tooltip({
                                        object: obj,
                                        index: index,
                                        group: group,
                                        sequentialIndex: seq,
                                        text: typeof properties.tooltips === 'string' ? properties.tooltips : properties.tooltips[seq],
                                        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') {
                                    path.addEventListener('mousemove', function (e)
                                    {
                                        e.target.style.cursor = 'pointer';
                                    }, false);
                                }
                                
                            }(j, i, seq, this));
                        }
                        var prevRadius = radius;
                    }
                    seq--



                    
















                // A regular non-equi-angular segment
                } else {
                    var radius = (this.data[i][0] / this.scale.max) * this.radius * properties.effectGrowMultiplier,
                        cx     = this.centerx + (explosion[0] * properties.effectRoundrobinMultiplier),
                        cy     = this.centery + (explosion[1] * properties.effectRoundrobinMultiplier);

                    var arcPath = RGraph.SVG.TRIG.getArcPath2({
                        cx: cx,
                        cy: cy,
                        r: radius,
                        start: ((start + properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                        end: ((end - properties.amargin) * properties.effectRoundrobinMultiplier) + properties.segmentsAngleOffset,
                        anticlockwise: false
                    });

                    var path = RGraph.SVG.create({
                        svg: this.svg,
                        type: 'path',
                        parent: group,
                        attr: {
                            d: '{1} z'.format(
                                arcPath
                            ),
                            fill: properties.colorsSequential ? properties.colors[i]  : properties.colors[0],
                            'fill-opacity': properties.colorsOpacity,
                            stroke: properties.colorsStroke,
                            'stroke-width': properties.linewidth,
                            
                            'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[i] : '',
                            'data-centerx': cx,
                            'data-centery': cy,
                            'data-index': i,
                            'data-value': this.data[i][0],
                            'data-start-angle': start + properties.amargin + properties.segmentsAngleOffset,
                            'data-end-angle': end - properties.amargin + properties.segmentsAngleOffset,
                            'data-radius': radius,
                            'data-sequential': seq
                        }
                    });
    
                    // Add the segment to the angles array
                    this.angles.push({
                        object: this,
                        element: path
                    });
                   
                   this.angles2[i].push({
                        object: this,
                        element: path
                    });
    
    
    

                    if (properties.tooltips && (properties.tooltips[i] || typeof properties.tooltips === 'string') ) {
                    
                        // Make the tooltipsEvent default to click
                        if (properties.tooltipsEvent !== 'mousemove') {
                            properties.tooltipsEvent = 'click';
                        }
    
                        (function (index, group, seq, obj)
                        {
                            path.addEventListener(properties.tooltipsEvent, function (e)
                            {
                                obj.removeHighlight();
    
                                // Show the tooltip
                                RGraph.SVG.tooltip({
                                    object: obj,
                                    index: index,
                                    group: index,
                                    sequentialIndex: seq,
                                    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') {
                                path.addEventListener('mousemove', function (e)
                                {
                                    e.target.style.cursor = 'pointer';
                                }, false);
                            }
                            
                        }(i, i, seq, this));
                    }
                }
                
                
                // Increment the start angle for the next iteration of the loop
                start += radians;
            }
        };








        //
        // Redraws the chart if required
        //
        this.redrawRose = function ()
        {
        };








        //
        // Draw the labels
        //
        this.drawLabels = function ()
        {
            //
            // If the labels 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     || '.'
                    });
                }
            }





            // Draw the scale if required
            if (properties.scaleVisible) {




                // Don't add this group twice
                if (!document.getElementById('rgraph_rose_scale_labels_' + this.uid)) {
                    var group = RGraph.SVG.create({
                        svg: this.svg,
                        type:'g',
                        parent: this.svgAllGroup,
                        attr: {
                            id: 'rgraph_rose_scale_labels_' + this.uid
                        }
                    });
                } else {
                    var group = document.getElementById('rgraph_rose_scale_labels_' + this.uid);
                }
                
                // Get the text configuration
                var textConf = RGraph.SVG.getTextConf({
                    object: this,
                    prefix: 'scale'
                });



                for (var i=0; i<this.scale.labels.length; ++i) {
    
                    var x = this.centerx,
                        y = this.centery - (this.radius / this.scale.labels.length * (i+1) );



                    RGraph.SVG.text({
                        
                        object:     this,
                        svg:        this.svg,
                        parent:     group,

                        tag:        'labels.scale',
                        
                        text:       this.scale.labels[i],
                        
                        x:          x,
                        y:          y,
                        
                        halign:     'center',
                        valign:     'center',
                        
                        background: 'rgba(255,255,255,0.7)',
                        padding:    2,
                        
                        size:       textConf.size,
                        color:      textConf.color,
                        bold:       textConf.bold,
                        italic:     textConf.italic,
                        font:       textConf.font
                    });
                }
    
                // Draw the zero label
                var str = RGraph.SVG.numberFormat({
                    object:    this,
                    num:       this.scale.min.toFixed(properties.scaleDecimals),
                    prepend:   properties.scaleUnitsPre,
                    append:    properties.scaleUnitsPost,
                    point:     properties.scalePoint,
                    thousand:  properties.scaleThousand,
                    formatter: properties.scaleFormatter
                });
    
    
                RGraph.SVG.text({
                    
                    object:     this,
                    parent:     group,
                    tag:        'labels.scale',

                    text:       str,

                    x:          this.centerx,
                    y:          this.centery,

                    halign:     'center',
                    valign:     'center',

                    background: 'rgba(255,255,255,0.7)',
                    padding:    2,

                    size:       textConf.size,
                    color:      textConf.color,
                    bold:       textConf.bold,
                    italic:     textConf.italic,
                    font:       textConf.font
                });
            }







            // Used further down
            var halign;


            // Don't add this group twice
            if (!document.getElementById('rgraph_rose_circular_labels_' + this.uid)) {
                var group = RGraph.SVG.create({
                    svg: this.svg,
                    type:'g',
                    parent: this.svgAllGroup,
                    attr: {
                        id: 'rgraph_rose_circular_labels_' + this.uid
                    }
                });
            } else {
                var group = document.getElementById('rgraph_rose_circular_labels_' + this.uid);
            }

            // Set a default size for the labels
            if (typeof properties.labelsSize !== 'number') {
                properties.labelsSize = properties.textSize + 4;
            }



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

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

                if (properties.variant === 'non-equi-angular') {
                    var angle  = ((Number(this.angles2[i][0].element.getAttribute('data-end-angle')) - Number(this.angles2[i][0].element.getAttribute('data-start-angle'))) / 2) + Number(this.angles2[i][0].element.getAttribute('data-start-angle')) - RGraph.SVG.TRIG.HALFPI;
                } else {
                    var angle = (((RGraph.SVG.TRIG.TWOPI / properties.labels.length)) * i) - RGraph.SVG.TRIG.HALFPI + properties.labelsAngleOffset + (RGraph.SVG.TRIG.TWOPI / (2 * properties.labels.length));
                }

                var endpoint = RGraph.SVG.TRIG.getRadiusEndPoint({
                    r:     this.radius + properties.labelsRadialMargin,
                    angle: angle
                });

                // Accommodate the explosion for the label
                var explosion = this.getExploded({
                    index: i,
                    start: Number(this.angles2[i][0].element.getAttribute('data-start-angle')) - RGraph.SVG.TRIG.HALFPI,
                    end: Number(this.angles2[i][0].element.getAttribute('data-end-angle')) - RGraph.SVG.TRIG.HALFPI
                });


                endpoint[0] += this.centerx + explosion[0];
                endpoint[1] += this.centery + explosion[1];


                // Do the alignment based on which quadrant the label is in
                if (Math.round(endpoint[0]) > this.centerx) {
                    halign = 'left';
                } else if (Math.round(endpoint[0]) === this.centerx) {
                    halign = 'center';
                } else {
                    halign = 'right';
                }





                RGraph.SVG.text({

                    object: this,
                    svg:    this.svg,
                    parent: group,
                    tag:    'labels',

                    text:   typeof properties.labels[i] === 'string' ? properties.labels[i] : '',
                    
                    x:      endpoint[0],
                    y:      endpoint[1],
                    
                    halign: halign,
                    valign: 'center',
                    
                    background: 'rgba(255,255,255,0.7)',
                    padding:2,

                    size:   textConf.size,
                    color:  textConf.color,
                    bold:   textConf.bold,
                    italic: textConf.italic,
                    font:   textConf.font
                });
            }
        };








        //
        // This function can be used to highlight a segment on the chart
        // 
        // @param object circle The circle to highlight
        //
        this.highlight = function (path)
        {
            //
            // Get the details of the segment and then uswe the arcPath as
            // the d attribute to a path object.
            //
            var centerx     = path.getAttribute('data-centerx'),
                centery     = path.getAttribute('data-centery'),
                radius      = path.getAttribute('data-radius'),
                radiusInner = path.getAttribute('data-radius-inner'),
                start       = path.getAttribute('data-start-angle'),
                end         = path.getAttribute('data-end-angle');
            
            var arcPath = RGraph.SVG.TRIG.getArcPath3({
                cx:     centerx,
                cy:     centery,
                r:      radius,
                start:  start,
                end:    end,
                lineto: true
            });
            
            var arcPath_array = RGraph.SVG.TRIG.getArcPath3({
                cx:     centerx,
                cy:     centery,
                r:      radius,
                start:  start,
                end:    end,
                lineto: true,
                array: true
            });

            if (radiusInner) {
                var arcPath2 = RGraph.SVG.TRIG.getArcPath3({
                    cx:     centerx,
                    cy:     centery,
                    r:      radiusInner,
                    start:  end,
                    end:    start,
                    lineto: true,
                    anticlockwise: true
                });
            } else {
                arcPath2 = ' L {1} {2}'.format(centerx, centery);
            }

            var highlight = RGraph.SVG.create({
                svg: this.svg,
                parent: this.svgAllGroup,
                type: 'path',
                attr: {
                    d: 'M {1} {2} '.format(arcPath_array[1], arcPath_array[2]) + arcPath + ' ' + arcPath2 + ' z',
                    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
            });
        };








        //
        // Get the maximum value
        //
        this.getMaxValue = function ()
        {
            var max = 0;

            if (properties.variant === 'non-equi-angular') {
                for (var i=0; i<this.data.length; ++i) {
                    if (!RGraph.SVG.isNullish(this.data[i])) {
                        if (typeof this.data[i][0] === 'number') {
                            max = Math.max(max, this.data[i][0]);
                        } else if (typeof this.data[i][0] === 'object'){
                            max = Math.max(max, RGraph.SVG.arraySum(this.data[i][0]));
                        }
                    }
                }
            } else {
                for (var i=0; i<this.data.length; ++i) {
                    if (!RGraph.SVG.isNullish(this.data[i])) {
                        if (typeof this.data[i] === 'number') {
                            max = Math.max(max, this.data[i]);
                        } else if (typeof this.data[i] === 'object') {
                            max = Math.max(max, RGraph.SVG.arraySum(this.data[i]));
                        }
                    }
                }
            }
            
            this.max = max;
        };








        //
        // Gets the radius of a value
        //
        //@param number The value to get the radius for
        //
        this.getRadius = function (value)
        {
            return ( (value - properties.scaleMin) / (this.scale.max - properties.scaleMin) ) * this.radius;
        };








        //
        // 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 = function ()
        {
        };








        //
        // Rose chart Wave effect.
        // 
        // @param object OPTIONAL An object map of options. You specify 'frames'
        //                        here to give the number of frames in the effect
        //                        and also callback to specify a callback function
        //                        thats called at the end of the effect
        //
        this.wave = function ()
        {            
            // Reset the data to the original
            this.data = RGraph.SVG.arrayClone(this.originalData, true);

            // If there's only one segment call the grow function
            // instead
            if (this.data.length === 1) {
                return this.grow(arguments[0], arguments[1]);
            }

            var obj = this,
                opt = arguments[0] || {};

            opt.frames      =  opt.frames || 90;
            opt.startFrames = [];
            opt.counters    = [];

            var framesperbar = opt.frames / 3,
                frame        = -1,
                callback     = arguments[1] || function () {},
                original     = RGraph.SVG.arrayClone(this.originalData, true);


            for (var i=0,len=this.data.length; i<len; i+=1) {
                
                opt.startFrames[i] = ((opt.frames / 2) / (this.data.length - 1)) * i;

                if (typeof this.data[i] === 'object' && this.data[i]) {
                    opt.counters[i] = [];
                    for (var j=0; j<this.data[i].length; j++) {
                        opt.counters[i][j] = 0;
                    }
                } else {
                    opt.counters[i]    = 0;
                }
            }

            //
            // This stops the chart from jumping
            //
            obj.draw();
            obj.set('scaleMax', obj.scale.max);
            RGraph.SVG.clear(obj.svg);


            function iterator ()
            {
                ++frame;
                

                for (let i=0,len=obj.data.length; i<len; i+=1) {
                    if (frame > opt.startFrames[i]) {
                        
                        properties.effectWaveMultiplier = RGraph.SVG.FX.getEasingMultiplier(opt.frames, frame,0);
                        
                        if (typeof obj.data[i] === 'number') {

                            obj.originalData[i] = Math.min(
                                Math.abs(original[i]),
                                Math.abs(original[i] * ( (opt.counters[i]++) / framesperbar))
                            ) * properties.effectWaveMultiplier;

                        } else if (!RGraph.SVG.isNullish(obj.data[i])) {

                            for (let j=0,len2=obj.data[i].length; j<len2; j+=1) {
                                obj.originalData[i][j] = Math.min(
                                    Math.abs(original[i][j]),
                                    // ((frame - opt.startFrames[i]) / framesperbar) * originalHeight,
                                    Math.abs(original[i][j] * (frame - opt.startFrames[i]) / framesperbar)
                                ) * properties.effectWaveMultiplier;
                            }
                        }
                    } else {
                        obj.originalData[i] = typeof obj.originalData[i] === 'object' && obj.originalData[i] ? RGraph.SVG.arrayPad([], obj.originalData[i].length, 0) : (RGraph.SVG.isNullish(obj.originalData[i]) ? null : 0);
                    }
                }


                if (frame >= opt.frames) {
                    callback(obj);
                } else {

                    RGraph.SVG.redraw(obj.svg);
                    RGraph.SVG.FX.update(iterator);
                }
            }

            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;
        };








        //
        // Removes the tooltip highlight from the chart
        //
        this.removeHighlight =
        this.hideHighlight   = function ()
        {
            RGraph.SVG.removeHighlight();
        };








        //
        // Returns the exploded X/Y for a given explosion
        //
        //TODO Needs updating to current coding style, including converting
        //     arguments to an object
        //
        this.getExploded = function (opt)
        {
            var index    = opt.index,
                start    = opt.start,
                end      = opt.end,
                exploded = properties.exploded,
                explodedX,
                explodedY;

            //
            // Retrieve any exploded - the exploded can be an array of numbers or a single number
            // (which is applied to all segments)
            //
            if (typeof exploded === 'object' && typeof exploded[index] === 'number') {
                explodedX = Math.cos(((end - start) / 2) + start) * exploded[index];
                explodedY = (Math.sin(((end - start) / 2) + start) * exploded[index]);

            } else if (typeof exploded === 'number') {
                explodedX = Math.cos(((end - start) / 2) + start) * exploded;
                explodedY = Math.sin(((end - start) / 2) + start) * exploded;
    
            } else {
                explodedX = 0;
                explodedY = 0;
            }
            
            return [explodedX, explodedY];
        };








        //
        // The grow effect
        //
        this.grow = function (opt)
        {
            var obj      = this,
                opt      = arguments[0] || {},
                frame    = -1,
                frames   = opt.frames || 60,
                callback = opt.callback || function () {};

            properties.effectGrowMultiplier = 0.01;
            
            this.draw();
            
            function iterator ()
            {
                // Increase the frame counter
                ++frame;
                
                // Get the multiplier using easing
                var multiplier = RGraph.SVG.FX.getEasingMultiplier(frames, frame);

                // Set the multiplier that the radius of the segments is
                // multiplied with.
                properties.effectGrowMultiplier = multiplier;

                // Redraw the segments
                obj.drawRose();

                if (frame >= frames) {
                    callback(obj);
                } else {
                    RGraph.SVG.FX.update(iterator);
                }
            }
            
            iterator();
            
            return this;
        };








        //
        // The grow effect
        //
        this.roundRobin =
        this.roundrobin = function (opt)
        {
            var obj      = this,
                opt      = arguments[0] || {},
                frame    = -1,
                frames   = opt.frames || 60,
                callback = opt.callback || function () {};

            properties.effectRoundrobinMultiplier = 0.01;
            
            this.draw();
            
            function iterator ()
            {
                // Increase the frame counter
                ++frame;
                
                // Get the multiplier using easing
                var multiplier = RGraph.SVG.FX.getEasingMultiplier(frames, frame);

                // Set the multiplier that the radius of the segments is
                // multiplied with.
                properties.effectRoundrobinMultiplier = multiplier;

                // Redraw the segments
                obj.drawRose();

                if (frame >= frames) {
                    callback(obj);
                } else {
                    RGraph.SVG.FX.update(iterator);
                }
            }
            
            iterator();
            
            return this;
        };








        //
        // A worker function that handles Bar chart specific tooltip substitutions
        //
        this.tooltipSubstitutions = function (opt)
        {
            var indexes = RGraph.SVG.sequentialIndexToGrouped(opt.index, this.data);

            if (properties.variant === 'non-equi-angular') {

                // Stacked
                if (typeof this.data[0][0] === 'object') {
                
                    var tmp = [];
                
                    // Add all of the arrays to a temporary array
                    for (var i=0; i<this.data.length; ++i) {
                        tmp[i] = this.data[i][0];
                    }
                    
                    // Determine again the indexes
                    var indexes = RGraph.SVG.sequentialIndexToGrouped(opt.index, tmp);
                    var ret =  {
                          index: indexes[1],
                        dataset: indexes[0],
                sequentialIndex: opt.index,
                          value: this.data[indexes[0]][0][indexes[1]],
                         value2: this.data[indexes[0]][1],
                         values: this.data[indexes[0]][0]
                    };
                    
                    return ret;

                // Non-stacked
                } else {

                    return {
                          index: 0,
                        dataset: opt.index,
                sequentialIndex: opt.index,
                          value: (typeof this.data[opt.index] === 'object' && typeof this.data[opt.index][0] === 'number') ? this.data[opt.index][0] : mill,
                         value2: (typeof this.data[opt.index] === 'object' && typeof this.data[opt.index][1] === 'number') ? this.data[opt.index][1] : null,
                         values: typeof this.data[indexes[0]] === 'number' ? [this.data[indexes[0]]] : this.data[indexes[0]]
                    };
                }

            } else {

                return {
                      index: indexes[1],
                    dataset: indexes[0],
            sequentialIndex: opt.index,
                      value: typeof this.data[indexes[0]] === 'number' ? this.data[indexes[0]] : this.data[indexes[0]][indexes[1]],
                     value2: typeof value2 === 'number' ? value2 : null,
                     values: typeof this.data[indexes[0]] === 'number' ? [this.data[indexes[0]]] : this.data[indexes[0]]
                };
            }
        };








        //
        // 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, label, value, value2;

            color = properties.colors[index];

            // Different variations of the Rose chart

            // REGULAR CHART
            if (typeof this.data[specific.dataset] === 'number') {
                label = properties.tooltipsFormattedKeyLabels[specific.dataset] || '';
                color = !RGraph.SVG.isNullish(properties.tooltipsFormattedKeyColors) && properties.tooltipsFormattedKeyColors[specific.index]
                            ? properties.tooltipsFormattedKeyColors[specific.index]
                            : color;

            // NON-EQUI-ANGULAR CHART
            } else if (typeof this.data[specific.dataset] === 'object' && properties.variant === 'non-equi-angular') {

                // REGULAT NON-EQUI-ANGULAR
                if (RGraph.SVG.isNumber(this.data[specific.dataset][0])) {
                    
                    // Don't show the second value on a non-equi-angular chart
                    if (index > 0) {
                        return {continue: true};
                    }

                    color = colors[specific.index];
                    value = this.data[specific.dataset][0];
                   value2 = typeof this.data[specific.dataset][1] === 'number' ? this.data[specific.dataset][1] : null
                    label = properties.tooltipsFormattedKeyLabels[specific.dataset];//this.data[specific.dataset][0];

                // STACKED NON-EQUI-ANGULAR
                } else if (RGraph.SVG.isArray(this.data[specific.dataset][0])) {

                    color = colors[index];
                    value = this.data[specific.dataset][0][index];
                    value = typeof this.data[specific.dataset][0][index] === 'number' ? this.data[specific.dataset][0][index] : null;
                   value2 = typeof this.data[specific.dataset][1] === 'number' ? this.data[specific.dataset][1] : null;
                    label = properties.tooltipsFormattedKeyLabels[index];
                }
            // STACKED REGULAR CHART
            } else if (typeof this.data[specific.dataset] === 'object') {
                //label = properties.tooltipsFormattedKeyLabels[specific.dataset] || '';
                color = !RGraph.SVG.isNullish(properties.tooltipsFormattedKeyColors) && properties.tooltipsFormattedKeyColors[index]
                            ? properties.tooltipsFormattedKeyColors[index]
                            : color;
            }

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








        //
        // 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),
                angles     = this.angles[index];
                
                // Get the angles from the data attributes on the tag and
                // REMEMBER TO CONVERT THEM TO NUMBERS
                var startAngle  = parseFloat(angles.element.getAttribute('data-start-angle'));
                var endAngle    = parseFloat(angles.element.getAttribute('data-end-angle'));
                var radiusInner = parseFloat(angles.element.getAttribute('data-radius-inner'));
                var radiusOuter = parseFloat(angles.element.getAttribute('data-radius'));
                var angle       = ((endAngle - startAngle) / 2) + startAngle - RGraph.SVG.TRIG.HALFPI;
                
                if (isNaN(radiusInner)) {
                    radiusInner = 0;
                }

                var coords = RGraph.SVG.TRIG.toCartesian({
                    cx: this.centerx,
                    cy: this.centery,
                    r:  ((radiusOuter - radiusInner) / 2) + radiusInner,
                    angle: angle
                });


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

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








        //
        // This function handles clipping to scale values. Because
        // each chart handles scales differently, a worker function
        // is needed instead of it all being done centrally in the
        // main function.
        //
        // @param object clipPath The parent cipPath element
        //
        this.clipToScaleWorker = function (clipPath)
        {
            if (RegExp.$1 === 'min') from = this.min; else from = Number(RegExp.$1);
            if (RegExp.$2 === 'max') to   = this.max; else to   = Number(RegExp.$2);

            var r1 = this.getRadius(from),
                r2 = this.getRadius(to);

            // Change the radius if the number is "min"
            if (RegExp.$1 === 'min') {
                r1 = 0;
            }

            // Change the radius if the number is "max"
            if (RegExp.$2 === 'max') {
                r2 = Math.max(this.width, this.height);
            }

    
            var path1 = RGraph.SVG.TRIG.getArcPath3({
                cx:     this.centerx,
                cy:     this.centery,
                radius: r1,
                start:  0,
                end:    RGraph.SVG.TRIG.TWOPI,
                anticlockwise: false
            });

    
            var path2 = RGraph.SVG.TRIG.getArcPath3({
                cx:     this.centerx,
                cy:     this.centery,
                radius: r2,
                start:  RGraph.SVG.TRIG.TWOPI,
                end:    0,
                anticlockwise: true
            });
    

            RGraph.SVG.create({
                svg: this.svg,
                parent: clipPath,
                type: 'path',
                attr: {
                    d: 'M {1} {2} {3} {4} z'.format(
                        this.centerx,
                        this.centery,
                        path1,
                        path2
                    )
                }
            });
            
            // Now set the clip-path attribute on the
            // all-elements group
            this.svgAllGroup.setAttribute(
                'clip-path',
                'url(#' + clipPath.id + ')'
            );
        };








        //
        // 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);