// 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.HBar = 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             = 'hbar';
        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.coords           = [];
        this.coords2          = [];
        this.coordsSpline     = [];
        this.stackedBackfaces = [];
        this.colorsParsed     = false;
        this.originalColors   = {};
        this.gradientCounter  = 1;
        this.isTrace          = false; // Used for the vertical line trace effect
        this.firstDraw        = true; // After the first draw this will be false











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

        this.properties =
        {
            marginLeft:      100,
            marginRight:     35,
            marginRightAuto: null,
            marginTop:       35,
            marginBottom:    35,
            marginLeftAuto:  true,

            backgroundColor:            null,
            backgroundImage:            null,
            backgroundImageAspect:      'none',
            backgroundImageStretch:     true,
            backgroundImageOpacity:     null,
            backgroundImageX:           null,
            backgroundImageY:           null,
            backgroundImageW:           null,
            backgroundImageH:           null,
            backgroundGrid:             true,
            backgroundGridColor:        '#ddd',
            backgroundGridLinewidth:    1,
            backgroundGridHlines:       true,
            backgroundGridHlinesCount:  null,
            backgroundGridVlines:       true,
            backgroundGridVlinesCount:  null,
            backgroundGridBorder:       true,
            backgroundGridDashed:       false,
            backgroundGridDotted:       false,
            backgroundGridDashArray:    null,
            
            // 20 colors. If you need more you need to set the colors property
            colors: [
                'red', '#0f0', '#00f', '#ff0', '#0ff', '#0f0','pink','orange','gray','black',
                'red', '#0f0', '#00f', '#ff0', '#0ff', '#0f0','pink','orange','gray','black'
            ],
            colorsSequential:     false,
            colorsStroke:          'rgba(0,0,0,0)',

            marginInner:              3,
            marginInnerGrouped:       2,
            marginInnerTop:           0,
            marginInnerBottom:        0,

            xaxis:                true,
            xaxisLinewidth:       1,
            xaxisTickmarks:       true,
            xaxisTickmarksLength: 5,
            xaxisColor:           'black',
            xaxisLabels:          [],
            xaxisLabelsOffsetx:   0,
            xaxisLabelsOffsety:   0,
            xaxisLabelsCount:     5,
            xaxisScale:           true,
            xaxisScaleUnitsPre:        '',
            xaxisScaleUnitsPost:       '',
            xaxisScaleStrict:          false,
            xaxisScaleDecimals:        0,
            xaxisScaleThousand:           '.',
            xaxisScaleThousand:        ',',
            xaxisScaleRound:           false,
            xaxisScaleMax:             null,
            xaxisScaleMin:             0,
            xaxisScaleFormatter:       null,
            xaxisLabelsPositionEdgeTickmarksCount: null,
            xaxisLabelsColor:       null,
            xaxisLabelsBold:        null,
            xaxisLabelsItalic:      null,
            xaxisLabelsFont:        null,
            xaxisLabelsSize:        null,
            xaxisTitle:           '',
            xaxisTitleBold:       null,
            xaxisTitleSize:       null,
            xaxisTitleFont:       null,
            xaxisTitleColor:      null,
            xaxisTitleItalic:     null,
            xaxisTitleOffsetx:    0,
            xaxisTitleOffsety:    0,
            xaxisTitleX:          null,
            xaxisTitleY:          null,
            xaxisTitleHalign:     null,
            xaxisTitleValign:     null,

            yaxis:                true,
            yaxisLinewidth:       1,
            yaxisTickmarks:       true,
            yaxisTickmarksLength: 3,
            yaxisTickmarksCount: 5,
            yaxisLabels:          [],
            yaxisLabelsPosition:  'section',
            yaxisLabelsOffsetx:   0,
            yaxisLabelsOffsety:   0,
            yaxisScale:           false,
            yaxisLabelsPositionSectionTickmarksCount: null,
            yaxisColor:           'black',
            yaxisLabelsFont:      null,
            yaxisLabelsSize:      null,
            yaxisLabelsColor:     null,
            yaxisLabelsBold:      null,
            yaxisLabelsItalic:    null,
            yaxisPosition:        'left',
            yaxisLabelsFormattedDecimals:  0,
            yaxisLabelsFormattedUnitsPre:  '',
            yaxisLabelsFormattedUnitsPost: '',
            yaxisLabelsFormattedThousand:  ',',
            yaxisLabelsFormattedPoint:     '.',
            yaxisTitle:           '',
            yaxisTitleBold:       null,
            yaxisTitleSize:       null,
            yaxisTitleFont:       null,
            yaxisTitleColor:      null,
            yaxisTitleItalic:     null,
            yaxisTitleOffsetx:    0,
            yaxisTitleOffsety:    0,
            yaxisTitleX:          null,
            yaxisTitleY:          null,
            yaxisTitleHalign:     null,
            yaxisTitleValign:     null,

            textColor:            'black',
            textFont:             'Arial, Verdana, sans-serif',
            textSize:             12,
            textBold:             false,
            textItalic:           false,
            text:                 null,
            
            labelsAbove:                  false,
            labelsAboveFont:              null,
            labelsAboveSize:              null,
            labelsAboveBold:              null,
            labelsAboveItalic:            null,
            labelsAboveColor:             null,
            labelsAboveBackground:        null,
            labelsAboveBackgroundPadding: 0,
            labelsAboveUnitsPre:          null,
            labelsAboveUnitsPost:         null,
            labelsAbovePoint:             null,
            labelsAboveThousand:          null,
            labelsAboveFormatter:         null,
            labelsAboveDecimals:          null,
            labelsAboveOffsetx:           0,
            labelsAboveOffsety:           0,
            labelsAboveHalign:            null,
            labelsAboveValign:            'center',
            labelsAboveSpecific:          null,
            
            labelsInbar:                  false,
            labelsInbarHalign:            'center',
            labelsInbarValign:            'center',
            labelsInbarFont:              null,
            labelsInbarSize:              null,
            labelsInbarBold:              null,
            labelsInbarItalic:            null,
            labelsInbarColor:             null,
            labelsInbarBackground:        null,
            labelsInbarBackgroundPadding: 0,
            labelsInbarUnitsPre:          null,
            labelsInbarUnitsPost:         null,
            labelsInbarPoint:             null,
            labelsInbarThousand:          null,
            labelsInbarFormatter:         null,
            labelsInbarDecimals:          null,
            labelsInbarOffsetx:           0,
            labelsInbarOffsety:           0,
            labelsInbarSpecific:          null,

            linewidth:            1,
            grouping:             'grouped',
            
            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,
            titleSubtitleColor:   '#aaa',
            titleSubtitleSize:    null,
            titleSubtitleFont:    null,
            titleSubtitleBold:    null,
            titleSubtitleItalic:  null,
            
            shadow:               false,
            shadowOffsetx:        2,
            shadowOffsety:        2,
            shadowBlur:           2,
            shadowColor:          'rgba(0,0,0,0.25)',



            key:              null,
            keyColors:        null,
            keyOffsetx:       0,
            keyOffsety:       0,
            keyLabelsOffsetx: 0,
            keyLabelsOffsety: -1,
            keyLabelsSize:    null,
            keyLabelsBold:    null,
            keyLabelsItalic:  null,
            keyLabelsColor:   null,
            keyLabelsFont:    null,

            line:                           false,
            lineLinejoin:                   'round',
            lineLinecap:                    'round',
            lineLinewidth:                  2,
            lineTickmarksLinewidth:         2,
            lineTickmarksStyle:             null,
            lineTickmarksSize:              5,
            lineColor:                      'black',
            lineShadow:                     true,
            lineShadowColor:               '#666',
            lineShadowBlur:                 2,
            lineShadowOffsetx:              2,
            lineShadowOffsety:              2,
            lineSpline:                     false,
            lineTickmarksDrawNonNull:       false,
            
            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');




            //
            // Do the yaxis label substitution. This has to be called
            // before the left (or right) margin size is calculated.
            //
            this.yaxisLabelSubstitution();




            // 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'));
            
            this.coords  = [];
            this.coords2 = [];



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


            //
            // Handle the marginLeft autosizing
            //
            if (properties.marginLeftAuto) {

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

                for (var i=0,len=properties.yaxisLabels.length,maxLength=0; i<len; ++i) {
                    var sizes = RGraph.SVG.measureText({
                        text:   properties.yaxisLabels[i],
                        bold:   textConf.bold,
                        size:   textConf.size,
                        font:   textConf.font,
                        italic: textConf.italic
                    });
                    
                    maxLength = Math.max(maxLength, sizes[0]);
                }
                
                //
                // If there's a Y axis title specified then account for that
                //
                if (properties.yaxisTitle) {
                    var textConf_title = RGraph.SVG.getTextConf({                    
                        object: this,
                        prefix: 'yaxisTitle'
                    });

                    var yaxisTitleSize = RGraph.SVG.measureText({
                        text: properties.yaxisTitle,
                        bold: textConf_title.bold,
                        size: textConf_title.size,
                        font: textConf_title.font,
                        font: textConf_title.italic
                    });
                    
                    maxLength = maxLength + yaxisTitleSize[1];
                }

                properties.marginLeft = maxLength + 15;

                // Minimum left margin of 15
                if (properties.marginLeft < 15) {
                    properties.marginLeft = 15;
                }
            }
            
            // Handle margin right autosizing for when the
            // yaxisPosition is set to ight
            if (properties.yaxisPosition === 'right' && properties.marginRightAuto !== false) {
                for (var i=0,len=properties.yaxisLabels.length,maxLength=0; i<len; ++i) {
                    var sizes = RGraph.SVG.measureText({
                        text: properties.yaxisLabels[i],
                        bold: properties.yaxisLabelsBold || properties.textBold,
                        size: properties.yaxisLabelsSize || properties.textSize,
                        font: properties.yaxisLabelsFont || properties.textFont
                    });
                    
                    maxLength = Math.max(maxLength, sizes[0]);
                }
                
                properties.marginRight = maxLength + 15;

                // Minimum right margin of 15
                if (properties.marginRight < 15) {
                    properties.marginRight = 15;
                }
            }




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



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



            // Go through the data and work out the maximum value
            var values = [];

            for (var i=0,max=0; i<this.data.length; ++i) {
                if (typeof this.data[i] === 'number') {
                    values.push(this.data[i]);
                
                } else if (RGraph.SVG.isArray(this.data[i]) && properties.grouping === 'grouped') {
                    values.push(RGraph.SVG.arrayMax(this.data[i]));

                } else if (RGraph.SVG.isArray(this.data[i]) && properties.grouping === 'stacked') {
                    values.push(RGraph.SVG.arraySum(this.data[i]));
                }
            }
            var max = RGraph.SVG.arrayMax(values);

            // A custom, user-specified maximum value
            if (typeof properties.xaxisScaleMax === 'number') {
                max = properties.xaxisScaleMax;
            }
            
            // Set the ymin to zero if it's set to mirror
            if (properties.xaxisScaleMin === 'mirror' || properties.xaxisScaleMin === 'middle' || properties.xaxisScaleMin === 'center') {
                this.mirrorScale   = true;
                properties.xaxisScaleMin = properties.xaxisScaleMax * -1;
            }


            //
            // Generate an appropiate scale
            //
            this.scale = RGraph.SVG.getScale({
                object:    this,
                numlabels: properties.xaxisLabelsCount,
                unitsPre:  properties.xaxisScaleUnitsPre,
                unitsPost: properties.xaxisScaleUnitsPost,
                max:       max,
                min:       properties.xaxisScaleMin,
                point:     properties.xaxisScalePoint,
                round:     properties.xaxisScaleRound,
                thousand:  properties.xaxisScaleThousand,
                decimals:  properties.xaxisScaleDecimals,
                strict:    typeof properties.xaxisScaleMax === 'number',
                formatter: properties.xaxisScaleFormatter
            });



            //
            // Get the scale a second time if the xmin should be mirored
            //
            // Set the xmin to zero if it's set mirror
            if (this.mirrorScale) {
                this.scale = RGraph.SVG.getScale({
                    object:    this,
                    numlabels: properties.xaxisLabelsCount,
                    unitsPre:  properties.xaxisScaleUnitsPre,
                    unitsPost: properties.xaxisScaleUnitsPost,
                    max:       this.scale.max,
                    min:       this.scale.max * -1,
                    point:     properties.xaxisScaleThousand,
                    round:     false,
                    thousand:  properties.xaxisScaleThousand,
                    decimals:  properties.xaxisScaleDecimals,
                    strict:    true,
                    formatter: properties.xaxisScaleFormatter
                });
            }

            // Now the scale has been generated adopt its max value
            this.max      = this.scale.max;
            properties.xaxisScaleMax = this.scale.max;

            this.min      = this.scale.min;
            properties.xaxisScaleMin = this.scale.min;












            // 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 first
            RGraph.SVG.drawBackground(this);

            // Draw the bars
            this.drawBars();


            // Draw the axes over the bars
            RGraph.SVG.drawXAxis(this);
            RGraph.SVG.drawYAxis(this);


            // Draw the labelsAbove
            this.drawLabelsAbove();


            // Draw the labelsInbar
            this.drawLabelsInbar();
            
            
            // Draw the verical line
            if (this.properties.line) {
                this.drawLine();
            }






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






            //
            // Add vertical lines
            //
            this.drawVerticalLines();





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








        //
        // Do the label substitution. This is called from the top of the
        // draw function
        //
        this.yaxisLabelSubstitution = function ()
        {
            if (properties.yaxisLabels && properties.yaxisLabels.length) {
                //
                // If the yaxisLabels option is a string then turn it
                // into an array.
                //
                if (typeof properties.yaxisLabels === 'string') {
                    properties.yaxisLabels = RGraph.SVG.arrayPad({
                        array:  [],
                        length: this.data.length,
                        value:  properties.yaxisLabels
                    });
                }

                //
                // Label substitution
                //
                for (var i=0; i<properties.yaxisLabels.length; ++i) {
                    properties.yaxisLabels[i] = RGraph.SVG.labelSubstitution({
                        object:    this,
                        text:      properties.yaxisLabels[i],
                        index:     i,
                        value:     this.data[i],
                        decimals:  properties.yaxisLabelsFormattedDecimals  || 0,
                        unitsPre:  properties.yaxisLabelsFormattedUnitsPre  || '',
                        unitsPost: properties.yaxisLabelsFormattedUnitsPost || '',
                        thousand:  properties.yaxisLabelsFormattedThousand  || ',',
                        point:     properties.yaxisLabelsFormattedPoint     || '.'
                    });
                }
            }
        };








        //
        // Draws the bars
        //
        this.drawBars = function ()
        {
            if (properties.shadow) {
                RGraph.SVG.setShadow({
                    object:  this,
                    offsetx: properties.shadowOffsetx,
                    offsety: properties.shadowOffsety,
                    blur:    properties.shadowBlur,
                    color:   properties.shadowColor,
                    id:      'dropShadow'
                });
            }

            // Go through the bars
            for (var i=0,sequentialIndex=0; i<this.data.length; ++i,++sequentialIndex) {

                // Put place holders in the coords arrays
                // for NULL VALUES
                if (RGraph.SVG.isNullish(this.data[i])) {
                    this.coords.push([]);
                    this.coords2.push([]);
                }

                //
                // NORMAL bars
                //
                if (typeof this.data[i] === 'number') {

                    var outerSegment = (this.graphHeight - properties.marginInnerTop - properties.marginInnerBottom) / this.data.length,
                        width        = this.getWidth(this.data[i]),
                        height       = ( (this.graphHeight - properties.marginInnerTop - properties.marginInnerBottom) / this.data.length) - properties.marginInner - properties.marginInner,
                        x            = this.getXCoord(
                                            (this.scale.min < 0 && this.scale.max < 0) || (this.scale.min > 0 && this.scale.max > 0) ? this.scale.min : 0
                                        ) - (this.data[i] <  0 ? width : 0),
                        y            = properties.marginTop + properties.marginInnerTop + properties.marginInner + (outerSegment * i);

                    // Allow for the Y axis to be positioned on the right hand side
                    if (properties.yaxisPosition === 'right' && this.scale.min >= 0) {
                        x = this.getXCoord(this.data[i]);
                    }
                    
                    if (properties.yaxisPosition === 'right' && this.scale.min < 0) {
                        x = this.getXCoord(0);
                    }

                    // If theres a min set but both the min and max are below
                    // zero the bars should be aligned to the right hand
                    // side
                    if (this.scale.min < 0 && this.scale.max < 0) {
                        x = this.width - properties.marginRight - width;
                    }
                    
                    // Adjust for a negative value
                    if (this.mirrorScale && properties.yaxisPosition === 'right') {
                        if (this.data[i] > 0) {
                            x = this.getXCoord(0) - width;
                        } else {
                            x = this.getXCoord(0);
                        }
                    }
                    
                    // If the X axis is right, move the bar left
                    if (this.data[i] > 0 && properties.yaxisPosition === 'right') {
                        x = this.getXCoord(0) - width;
                    }

                    var rect = RGraph.SVG.create({
                        svg: this.svg,
                        parent: this.svgAllGroup,
                        type: 'rect',
                        attr: {
                            stroke: properties.colorsStroke,
                            fill: properties.colorsSequential ? (properties.colors[sequentialIndex] ? properties.colors[sequentialIndex] : properties.colors[properties.colors.length - 1]) : properties.colors[0],
                            x: x,
                            y: y,
                            width: width,
                            height: height,
                            'stroke-width': properties.linewidth,
                            'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[i] : '',
                            'data-index': i,
                            'data-original-x': x,
                            'data-original-y': y,
                            'data-original-width': width,
                            'data-original-height': height,
                            'data-sequential-index': sequentialIndex,
                            'data-value': this.data[i],
                            filter: properties.shadow ? 'url(#dropShadow)' : ''
                        }
                    });

                    this.coords.push({
                        object:  this,
                        element: rect,
                        x:      parseFloat(rect.getAttribute('x')),
                        y:      parseFloat(rect.getAttribute('y')),
                        width:  parseFloat(rect.getAttribute('width')),
                        height: parseFloat(rect.getAttribute('height'))
                    });
                    
                    if (!this.coords2[i]) {
                        this.coords2[i] = [];
                    }

                    this.coords2[i].push({
                        object:  this,
                        element: rect,
                        x:      parseFloat(rect.getAttribute('x')),
                        y:      parseFloat(rect.getAttribute('y')),
                        width:  parseFloat(rect.getAttribute('width')),
                        height: parseFloat(rect.getAttribute('height'))
                    });





                    // Add toooltips if necessary
                    if (!RGraph.SVG.isNullish(properties.tooltips) && (properties.tooltips[sequentialIndex] || typeof properties.tooltips === 'string')) {

                        var obj = this;

                        //
                        // Add tooltip event listeners
                        //
                        (function (idx, seq)
                        {
                            rect.addEventListener(properties.tooltipsEvent.replace(/^on/, ''), function (e)
                            {
                                obj.removeHighlight();

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




                //
                // GROUPED charts
                //
                } else if (RGraph.SVG.isArray(this.data[i]) && properties.grouping === 'grouped') {

                    var outerSegment = ( (this.graphHeight - properties.marginInnerTop - properties.marginInnerBottom) / this.data.length),
                        innerSegment = outerSegment - (2 * properties.marginInner);

                    // Loop through the group
                    for (var j=0; j<this.data[i].length; ++j,++sequentialIndex) {

                        var width  = Math.abs((this.data[i][j] / (this.max - this.min)) * this.graphWidth),
                            height = ( (innerSegment - ((this.data[i].length - 1) * properties.marginInnerGrouped)) / this.data[i].length),
                            y      = properties.marginTop + properties.marginInner + properties.marginInnerTop + (outerSegment * i) + (j * height) + (j * properties.marginInnerGrouped),
                            x      = this.getXCoord(0) - (this.data[i][j] <  0 ? width : 0);

                        // Work out some coordinates for the width and X coords ///////////////////////
                        if (this.scale.max < 0 && this.scale.min < this.scale.max) {
                            var x1 = this.getXCoord(this.data[i][j]);
                            var x2 = this.getXCoord(this.scale.max);
                            x      = x1;
                            width  = x2 - x1;
                        
                        } else if (this.scale.min > 0 && this.scale.max > this.scale.min) {
                            var x1 = this.getXCoord(this.data[i][j]);
                            var x2 = this.getXCoord(this.scale.min);
                            x      = this.getXCoord(this.scale.min);
                            width  = x1 - x2;

                        }
                        //////////////////////////////////////////////////////////////////////////////

                        // Allow for the Y axis to be positioned on the right hand side
                        if (properties.yaxisPosition === 'right' && this.scale.min === 0) {
                            x = this.getXCoord(this.data[i][j]);
                        }

                        // Allow for the Y axis to be positioned on the right hand side
                        // with a scale of (for example) -5 -> 20
                        if (   properties.yaxisPosition === 'right'
                            && this.scale.min < 0
                            && this.scale.max >= 0) {

                            if (this.data[i][j] < 0) {
                                x = this.getXCoord(0);
                            } else {
                                x = this.getXCoord(this.data[i][j]);
                            }
                        }

                        // Fixes an odd bug
                        //if (this.mirrorScale && properties.yaxisPosition === 'right') {
                        //    if (this.data[i][j] > 0) {
                        //        x -= width;
                        //    } else {
                        //        x += width;
                        //    }
                        //}








                        //
                        // Determine the fill color
                        //
                        var fill;
                        
                        if (properties.colorsSequential) {
                            if (properties.colors[sequentialIndex]) {
                                fill = properties.colors[sequentialIndex];
                            }
                        } else {
                            if (properties.colors[j]) {
                                fill = properties.colors[j];
                            } else {
                                fill = properties.colors[properties.colors.length - 1];
                            }
                        }




                        var rect = RGraph.SVG.create({
                            svg: this.svg,
                            type: 'rect',
                            parent: this.svgAllGroup,
                            attr: {
                                stroke: properties['colorsStroke'],
                                fill: fill,
                                x: x,
                                y: y,
                                width: width,
                                height: height,
                                'stroke-width': properties.linewidth,
                                'data-index': i,
                                'data-original-x': x,
                                'data-original-y': y,
                                'data-original-width': width,
                                'data-original-height': height,
                                'data-sequential-index': sequentialIndex,
                                'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[sequentialIndex] : '',
                                'data-value': this.data[i][j],
                                filter: properties.shadow ? 'url(#dropShadow)' : ''
                            }
                        });
                    
                        this.coords.push({
                            object:  this,
                            element: rect,
                            x:      parseFloat(rect.getAttribute('x')),
                            y:      parseFloat(rect.getAttribute('y')),
                            width:  parseFloat(rect.getAttribute('width')),
                            height: parseFloat(rect.getAttribute('height'))
                        });

                        if (!this.coords2[i]) {
                            this.coords2[i] = [];
                        }
        
                        this.coords2[i].push({
                            object:  this,
                            element: rect,
                            x:      parseFloat(rect.getAttribute('x')),
                            y:      parseFloat(rect.getAttribute('y')),
                            width:  parseFloat(rect.getAttribute('width')),
                            height: parseFloat(rect.getAttribute('height'))
                        });


                        // Add the tooltip data- attribute
                        if (!RGraph.SVG.isNullish(properties.tooltips) && (properties.tooltips[sequentialIndex] || typeof properties.tooltips === 'string') ) {
                        
                            var obj = this;
    
                        
                            //
                            // Add tooltip event listeners
                            //
                            (function (idx, seq)
                            {
                                var indexes = RGraph.SVG.sequentialIndexToGrouped(seq, obj.data);

                                rect.addEventListener(properties.tooltipsEvent.replace(/^on/, ''), function (e)
                                {
                                    obj.removeHighlight();

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

                    --sequentialIndex;
                        


                //
                // STACKED CHARTS
                //
                } else if (RGraph.SVG.isArray(this.data[i]) && properties.grouping === 'stacked') {

                    // This is each bars "segment" of the chart
                    var section = ( (this.graphHeight - properties.marginInnerTop - properties.marginInnerBottom) / this.data.length);
                    
                    // Initialise the X coordinate
                    var x = this.getXCoord(0);

                    // Loop through the stack
                    for (var j=0; j<this.data[i].length; ++j,++sequentialIndex) {

                        var outerHeight = (this.graphHeight - properties.marginInnerTop - properties.marginInnerBottom) / this.data.length,
                            width       = Math.abs((this.data[i][j] / (this.max - this.min)) * this.graphWidth),
                            height      = outerHeight - (2 * properties.marginInner),
                            y           = properties.marginTop + properties.marginInner + properties.marginInnerTop + (outerHeight * i);

                        if (properties.yaxisPosition === 'right') {
                            x -= width;
                        }

                        // If this is the first iteration of the loop and a shadow
                        // is requested draw a rect here to create it.
                        if (j === 0 && properties.shadow) {

                            var fullWidth = Math.abs((RGraph.SVG.arraySum(this.data[i]) / (this.max - this.min)) * this.graphWidth);

                            var rect = RGraph.SVG.create({
                                svg: this.svg,
                                parent: this.svgAllGroup,
                                type: 'rect',
                                attr: {
                                    x: properties.yaxisPosition === 'right' ? this.getXCoord(0) - fullWidth : this.getXCoord(0),
                                    y: y,
                                    width: fullWidth,
                                    height: height,
                                    fill: 'white',
                                    'stroke-width': 0,
                                    'data-index': i,
                                    filter: 'url(#dropShadow)'
                                }
                            });
                            
                            this.stackedBackfaces[i] = rect;
                        }



                        // Create the visible bar
                        var rect = RGraph.SVG.create({
                            svg: this.svg,
                            type: 'rect',
                            parent: this.svgAllGroup,
                            attr: {
                                stroke: properties['colorsStroke'],
                                fill: properties.colorsSequential ? (properties.colors[sequentialIndex] ? properties.colors[sequentialIndex] : properties.colors[properties.colors.length - 1]) : (properties.colors[j] ? properties.colors[j] : properties.colors[properties.colors.length - 1]),
                                x: x,
                                y: y,
                                width: width,
                                height: height,
                                'stroke-width': properties.linewidth,
                                'data-original-width': width,
                                'data-original-height': height,
                                'data-original-x': x - width,
                                'data-original-y': y,
                                'data-index': i,
                                'data-sequential-index': sequentialIndex,
                                'data-tooltip': (!RGraph.SVG.isNullish(properties.tooltips) && properties.tooltips.length) ? properties.tooltips[sequentialIndex] : '',
                                'data-value': this.data[i][j]
                            }
                        });

                        this.coords.push({
                            object:  this,
                            element: rect,
                            x:      parseFloat(rect.getAttribute('x')),
                            y:      parseFloat(rect.getAttribute('y')),
                            width:  parseFloat(rect.getAttribute('width')),
                            height: parseFloat(rect.getAttribute('height'))
                        });

                        if (!this.coords2[i]) {
                            this.coords2[i] = [];
                        }
        
                        this.coords2[i].push({
                            object:  this,
                            element: rect,
                            x:      parseFloat(rect.getAttribute('x')),
                            y:      parseFloat(rect.getAttribute('y')),
                            width:  parseFloat(rect.getAttribute('width')),
                            height: parseFloat(rect.getAttribute('height'))
                        });



                        // Add the tooltips 
                        if (!RGraph.SVG.isNullish(properties.tooltips) && (properties.tooltips[sequentialIndex] || typeof properties.tooltips === 'string')) {
                        
                            var obj = this;
    
                        
                            //
                            // Add tooltip event listeners
                            //
                            (function (idx, seq)
                            {
                                rect.addEventListener(properties.tooltipsEvent.replace(/^on/, ''), function (e)
                                {
                                    obj.removeHighlight();

                                    var indexes = RGraph.SVG.sequentialIndexToGrouped(seq, obj.data);

                                    // Show the tooltip
                                    RGraph.SVG.tooltip({
                                        object: obj,
                                        index: indexes[1],
                                        group: idx,
                                        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);
                                }, false);
                                
                                rect.addEventListener('mousemove', function (e)
                                {
                                    e.target.style.cursor = 'pointer'
                                }, false);
                            })(i, sequentialIndex);
                        }
                        
                        
                        // Adjust the X coord
                        if (properties.yaxisPosition === 'right') {
                            //x -= width;
                        } else {
                            x += width;
                        }

                    }

                    --sequentialIndex;
                }
            }

        };









        //
        // This function can be used to retrieve the relevant X coordinate for a
        // particular value.
        // 
        // @param int value The value to get the X coordinate for
        //
        this.getXCoord = function (value)
        {
            var prop = this.properties;

            if (value > this.scale.max) {
                return null;
            }

            if (value < this.scale.min) {
                return null;
            }

            var x  = ((value - this.scale.min) / (this.scale.max - this.scale.min));
                x *= this.graphWidth;

            if (properties.yaxisPosition === 'right') {
                x  = this.width - properties.marginRight - x;
            } else {
                x += properties.marginLeft;
            }

            return x;
        };









        //
        // This function can be used to retrieve the relevant X coordinate for a
        // particular value.
        // 
        // @param int value The value to get the X coordinate for
        //
        this.getWidth = function (value)
        {
            if (this.scale.max <= 0 && this.scale.min < this.scale.max) {
                var x1 = this.getXCoord(this.scale.max);
                var x2 = this.getXCoord(value);
            
            } else if (this.scale.min > 0 && this.scale.max > this.scale.min) {
                var x1 = this.getXCoord(this.scale.min);
                var x2 = this.getXCoord(value);
            
            } else {
                var x1 = this.getXCoord(0);
                var x2 = this.getXCoord(value);
            }

            return Math.abs(x1 - x2);
        };
        
        //Math.abs(((this.data[i] - this.scale.min) / (this.max - this.scale.min)) * this.graphWidth)








        //
        // This function can be used to highlight a bar on the chart
        // 
        // @param object rect The rectangle to highlight
        //
        this.highlight = function (rect)
        {
            var x      = parseFloat(rect.getAttribute('x')) - 0.5,
                y      = parseFloat(rect.getAttribute('y')) - 0.5,
                width  = parseFloat(rect.getAttribute('width')) + 1,
                height = parseFloat(rect.getAttribute('height')) + 1;
            
            var highlight = RGraph.SVG.create({
                svg: this.svg,
                type: 'rect',
                parent: this.svgAllGroup,
                attr: {
                    stroke: properties.highlightStroke,
                    fill: properties.highlightFill,
                    x: x,
                    y: y,
                    width: width,
                    height: height,
                    '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);
            //}
            
            RGraph.SVG.drawYAxis(this, {
                labels: false,
                yaxis:  true,
                title:  false
            });


            // Store the highlight rect in the rebistry 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 cleared
            if (!Object.keys(this.originalColors).length) {
                this.originalColors = {
                    colors:              RGraph.SVG.arrayClone(properties.colors, true, true),
                    backgroundGridColor: RGraph.SVG.arrayClone(properties.backgroundGridColor, true),
                    highlightFill:       RGraph.SVG.arrayClone(properties.highlightFill, true),
                    backgroundColor:     RGraph.SVG.arrayClone(properties.backgroundColor, true),
                    lineColor:           RGraph.SVG.arrayClone(properties.lineColor, true),
                    lineFilledColor:     RGraph.SVG.arrayClone(properties.lineFilledColor, true)
                }
            }

            
            // colors
            var colors = properties.colors;

            if (colors) {
                for (var i=0; i<colors.length; ++i) {
                    colors[i] = RGraph.SVG.parseColorLinear({
                        object: this,
                        color: colors[i],
                        direction: 'horizontal',
                        start: properties.marginLeft,
                        end: this.width - properties.marginRight
                    });
                }
            }

            properties.backgroundGridColor = RGraph.SVG.parseColorLinear({object: this, color: properties.backgroundGridColor, direction: 'horizontal',start: properties.marginLeft,end: this.width - properties.marginRight});
            properties.highlightFill       = RGraph.SVG.parseColorLinear({object: this, color: properties.highlightFill, direction: 'horizontal',start: properties.marginLeft,end: this.width - properties.marginRight});
            properties.backgroundColor     = RGraph.SVG.parseColorLinear({object: this, color: properties.backgroundColor});
            properties.lineColor           = RGraph.SVG.parseColorLinear({object: this, color: properties.lineColor});
            properties.lineFilledColor     = RGraph.SVG.parseColorLinear({object: this, color: properties.lineFilledColor});
        };








        //
        // Draws the labelsAbove
        //
        this.drawLabelsAbove = function ()
        {
            // Go through the above labels
            if (properties.labelsAbove) {

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

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

                    var value = data[i].toFixed(typeof properties.labelsAboveDecimals === 'number' ? properties.labelsAboveDecimals : properties.xaxisScaleDecimals);
                    var indexes = RGraph.SVG.sequentialIndexToGrouped(i, this.data);



                    if (RGraph.SVG.isArray(this.data[indexes[0]]) && properties.grouping === 'stacked') {
                        if ((indexes[1] + 1) === this.data[indexes[0]].length) {
                            value = RGraph.SVG.arraySum(this.data[indexes[0]]);
                            value = value.toFixed(typeof properties.labelsAboveDecimals === 'number' ? properties.labelsAboveDecimals : properties.xaxisScaleDecimals);
                        } else {
                            continue;
                        }
                    }


                    var str = properties.labelsAboveSpecific ? properties.labelsAboveSpecific[i]?.toString() : RGraph.SVG.numberFormat({
                        object:    this,
                        num:       value,
                        prepend:   typeof properties.labelsAboveUnitsPre  === 'string'   ? properties.labelsAboveUnitsPre  : null,
                        append:    typeof properties.labelsAboveUnitsPost === 'string'   ? properties.labelsAboveUnitsPost : null,
                        point:     typeof properties.labelsAbovePoint     === 'string'   ? properties.labelsAbovePoint     : null,
                        thousand:  typeof properties.labelsAboveThousand  === 'string'   ? properties.labelsAboveThousand  : null,
                        formatter: typeof properties.labelsAboveFormatter === 'function' ? properties.labelsAboveFormatter : null
                    });

                    var halign = properties.labelsAboveHalign,
                        valign = properties.labelsAboveValign;


                    var dimensions = RGraph.SVG.measureText({
                        text: str,
                        bold: textConf.bold,
                        font: textConf.font,
                        size: textConf.size
                    });

                    var x      = (value >= 0)
                                   ? (parseFloat(this.coords[i].element.getAttribute('x')) + 7 + properties.labelsAboveOffsetx)
                                   : parseFloat(this.coords[i].element.getAttribute('x') - 7 - properties.labelsAboveOffsetx),
                        y      = parseFloat(this.coords[i].element.getAttribute('y')) + parseFloat(this.coords[i].element.getAttribute('height') / 2) + properties.labelsAboveOffsety,
                        width  = dimensions[0],
                        height = dimensions[1],
                        halign = (value >= 0) ? 'left': 'right';

                    // Corner case
                    if (properties.yaxisPosition === 'left' && properties.grouping === 'grouped') {
                        x = parseFloat(this.coords[i].element.getAttribute('x')) + parseFloat(this.coords[i].element.getAttribute('width')) + 7 + properties.labelsAboveOffsetx
                    }






                    // ADjust the values if the Y axis is on the RHS
                    if (properties.yaxisPosition === 'right') {
                        x = (value >= 0)
                            ? (parseFloat(this.coords[i].element.getAttribute('x')) - 7 - properties.labelsAboveOffsetx)
                            : parseFloat(this.coords[i].element.getAttribute('x') + 7 + properties.labelsAboveOffsetx),
                        halign = (value >= 0) ? 'right': 'left';
                    
                    // Special case for an oddity
                    } else if (RGraph.SVG.isArray(this.data[indexes[0]]) && properties.grouping === 'stacked' && properties.yaxisPosition === 'left') {
                        x += this.coords2[indexes[0]][indexes[1]].width;
                    }

                    // Another corner case
                    if (
                           properties.yaxisPosition === 'right'
                        && properties.grouping === 'grouped'
                        && properties.xaxisScaleMax > 0
                        && properties.xaxisScaleMin < 0
                       ) {
                        
                        var value = this.coords[i].element.getAttribute('data-value');
                       
                        if (value < 0) {
                            x = this.getXCoord(value) + 7;
                        } else {
                            x = this.getXCoord(value) - 7;
                        }
                    }

                    // Another corner case
                    if (
                           properties.yaxisPosition === 'left'
                        && properties.grouping === 'grouped'
                        && properties.xaxisScaleMax > 0
                        && properties.xaxisScaleMin < 0
                       ) {
                        
                        var value = this.coords[i].element.getAttribute('data-value');
                       
                        if (value < 0) {
                            x = this.getXCoord(value) - 7;
                        } else {
                            x = this.getXCoord(value) + 7;
                        }
                    }


                    // Account for the labels going off the edge of the SVG tag (whilst the Y axis
                    // is on the left)
                    if (properties.yaxisPosition === 'right') {
                        if (x - width < properties.marginLeft && value > 0) {
                            halign = 'left';
                            x      = properties.marginLeft + 5;
                            properties.labelsAboveBackground = properties.labelsAboveBackground || 'rgba(255,255,255,0.95)';
                        }
                    } else {
                        if (x + width > this.width && value > 0) {
                            halign = 'right';
                            x      = this.width - 5;
                            properties.labelsAboveBackground = properties.labelsAboveBackground || 'rgba(255,255,255,0.95)';
                        }
                    }
                    
                    // Another oddity - when there's regular data but the grouping
                    // is set to stacked and the Y axis is on the left
                    if (properties.grouping === 'stacked' && typeof this.data[indexes[0]] === 'number' && properties.yaxisPosition === 'left') {
                        x += parseInt(this.coords[i].element.getAttribute('width'));
                    }

                    // Horizontal alignment
                    if (typeof properties.labelsAboveHalign === 'string') {
                        halign = properties.labelsAboveHalign;
                    }

                    var text = RGraph.SVG.text({
                        
                        object:     this,
                        parent:     this.svgAllGroup,
                        tag:        'labels.above',
                        
                        text:       str,
                        
                        x:          x,
                        y:          y,
                        
                        halign:     halign,
                        valign:     valign,
                        
                        font:       textConf.font,
                        size:       textConf.size,
                        bold:       textConf.bold,
                        italic:     textConf.italic,
                        color:      textConf.color,
                        
                        background: properties.labelsAboveBackground        || null,
                        padding:    properties.labelsAboveBackgroundPadding || 0
                    });
                }
            }
        };








        //
        // Draws the labelsInbar
        //
        this.drawLabelsInbar = function ()
        {
            // Go through the above labels
            if (properties.labelsInbar) {
            
                // Default alignment
                var valign = properties.labelsInbarValign,
                    halign = properties.labelsInbarHalign;

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

                for (var i=0; i<this.coords.length; ++i) {
                    if (RGraph.SVG.isNumber(data[i]) && !isNaN(data[i]) ) {
                        // The rect element that represents the bar
                        var el      = this.coords[i].element;                    
                        var value   = data[i].toFixed(properties.labelsInbarDecimals);
                        var indexes = RGraph.SVG.sequentialIndexToGrouped(i, this.data);
    
    
                        var dimensions = RGraph.SVG.measureText({
                            text: str,
                            bold: textConf.bold,
                            font: textConf.font,
                            size: textConf.size
                        });
    
                        var x      = parseFloat(el.getAttribute('x')) + (parseFloat(el.getAttribute('width')) / 2) + properties.labelsInbarOffsetx,
                            y      = parseFloat(el.getAttribute('y')) + (parseFloat(el.getAttribute('height')) / 2) + properties.labelsInbarOffsety,
                            width  = dimensions[0],
                            height = dimensions[1];
                            
                        //
                        // Horizontal alignment
                        //
                        if (properties.labelsInbarHalign === 'left') {
                            halign = 'left';
                            x      = parseFloat(el.getAttribute('x')) + 5 + properties.labelsInbarOffsetx;
                        } else if (properties.labelsInbarHalign === 'right') {
                            halign = 'right';
                            x      = parseFloat(el.getAttribute('x')) + parseFloat(el.getAttribute('width')) - 5 + properties.labelsInbarOffsetx;
                        }

                        //
                        // Vertical alignment
                        //
                        if (properties.labelsInbarValign === 'bottom') {
                            valign = 'bottom';
                            y      = parseFloat(el.getAttribute('y')) - 5 + parseFloat(el.getAttribute('height')) + properties.labelsInbarOffsety;
                        } else if (properties.labelsInbarValign === 'top') {
                            valign = 'top';
                            y      = parseFloat(el.getAttribute('y')) + 5 + properties.labelsInbarOffsety;
                        }
    
                        var str = properties.labelsInbarSpecific ? properties.labelsInbarSpecific[i]?.toString() : RGraph.SVG.numberFormat({
                            object:    this,
                            num:       value,
                            prepend:   typeof properties.labelsInbarUnitsPre  === 'string'   ? properties.labelsInbarUnitsPre  : null,
                            append:    typeof properties.labelsInbarUnitsPost === 'string'   ? properties.labelsInbarUnitsPost : null,
                            point:     typeof properties.labelsInbarPoint     === 'string'   ? properties.labelsInbarPoint     : null,
                            thousand:  typeof properties.labelsInbarThousand  === 'string'   ? properties.labelsInbarThousand  : null,
                            formatter: typeof properties.labelsInbarFormatter === 'function' ? properties.labelsInbarFormatter : null
                        });
                        
                        // Specific label given
                        if (RGraph.SVG.isArray(properties.labelsInbarSpecific) && (RGraph.SVG.isString(properties.labelsInbarSpecific[i]) || RGraph.SVG.isNumber(properties.labelsInbarSpecific[i]))) {
                            str = properties.labelsInbarSpecific[i];
                        }
    
                        var text = RGraph.SVG.text({
                            object:     this,
                            parent:     this.svgAllGroup,
                            tag:        'labels.inbar',
                            text:       str,
                            x:          x,
                            y:          y,
                            halign:     halign,
                            valign:     valign,
                            font:       textConf.font,
                            size:       textConf.size,
                            bold:       textConf.bold,
                            italic:     textConf.italic,
                            color:      textConf.color,
                            background: properties.labelsInbarBackground        || null,
                            padding:    properties.labelsInbarBackgroundPadding || 0
                        });
                    }
                }
            }
        };








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








        //
        // The Bar chart grow effect
        //
        this.grow = function ()
        {
            var opt      = arguments[0] || {},
                frames   = opt.frames || 30,
                frame    = 0,
                obj      = this,
                data     = [],
                height   = null,
                seq      = 0;

            //
            // Copy the data
            //
            data = RGraph.SVG.arrayClone(this.data, true);

            this.draw();

            var iterate = function ()
            {
                for (var i=0,seq=0,len=obj.coords.length; i<len; ++i, ++seq) {

                    var   multiplier = (frame / frames)
                        // RGraph.SVG.FX.getEasingMultiplier(frames, frame)
                        // RGraph.SVG.FX.getEasingMultiplier(frames, frame);
                
                
                
                
                    // TODO Go through the data and update the value according to
                    // the frame number
                    if (typeof data[i] === 'number') {

                        width       = Math.abs(obj.getXCoord(data[i]) - obj.getXCoord(0));
                        obj.data[i] = data[i] * multiplier;
                        width       = multiplier * width;
                        
                        // Set the new width on the rect
                        obj.coords[seq].element.setAttribute(
                            'width',
                            width
                        );

                        // Set the correct Y coord on the object
                        obj.coords[seq].element.setAttribute(
                            'x',
                            data[i] > 0
                                ? obj.getXCoord(0) - (properties.yaxisPosition === 'right' ? width : 0)
                                : (properties.xaxisScaleMin < 0 && properties.xaxisScaleMax > 0 ? (properties.yaxisPosition === 'right' ? obj.getXCoord(0) : obj.getXCoord(0) - width) : obj.getXCoord(0))
                        );

                    } else if (typeof data[i] === 'object') {

                        var accumulativeWidth = 0;

                        for (var j=0,len2=data[i].length; j<len2; ++j, ++seq) {

                            width          = Math.abs(obj.getXCoord(data[i][j]) - obj.getXCoord(0));
                            width          = multiplier * width;
                            obj.data[i][j] = data[i][j] * multiplier;

                            accumulativeWidth += width;

                            obj.coords[seq].element.setAttribute(
                                'width',
                                width
                            );

                            if (properties.yaxisPosition === 'right') {
                                if (properties.grouping === 'stacked') {
                                    obj.coords[seq].element.setAttribute(
                                        'x',
                                        obj.getXCoord(0) - accumulativeWidth
                                    );
                                } else {
                                    obj.coords[seq].element.setAttribute(
                                        'x',
                                        obj.getXCoord(0) - (obj.coords[seq].element.getAttribute('data-value') < 0 ? 0 : width)
                                    );
                                }
                            
                            } else {

                                obj.coords[seq].element.setAttribute(
                                    'x',
                                    properties.grouping === 'stacked'
                                        ? obj.getXCoord(0) + (accumulativeWidth - width)
                                        : properties.grouping === 'grouped' && obj.coords[seq].element.getAttribute('data-value') < 0 ? obj.getXCoord(0) - width : obj.getXCoord(0)
                                );
                            }
                        }

                        //
                        // Set the height and Y cooord of the backfaces if necessary
                        //
                        if (obj.stackedBackfaces[i]) {
                            obj.stackedBackfaces[i].setAttribute(
                                'width',
                                accumulativeWidth
                            );

                            obj.stackedBackfaces[i].setAttribute(
                                'x',
                                properties.yaxisPosition === 'right' ? obj.getXCoord(0) - accumulativeWidth : obj.getXCoord(0)
                            );
                        }

                        // Decrease seq by one so that it's not incremented twice
                        --seq;
                    }
                }

                if (frame++ < frames) {
                    //setTimeout(iterate, frame > 1 ? opt.delay : 200);
                    RGraph.SVG.FX.update(iterate);
                } else if (opt.callback) {
                    RGraph.SVG.redraw();
                    (opt.callback)(obj);
                }
            };

            iterate();
            
            return this;
        };








        //
        // HBar 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
        //
        // **************************************************************
        // *** In order to deal with stacked charts, this function is ***
        // *** complicated - probably significantly more so than it   ***
        // *** needs to be. As such it most definitely needs          ***
        // *** refactoring                                            ***
        // **************************************************************
        //
        this.wave = function ()
        {
            var stackedAccumulativeWidth = 0;

            // First draw the chart
            this.draw();


            var obj = this,
                opt = arguments[0] || {};
            
            opt.frames      = opt.frames || 60;
            opt.startFrames = [];
            opt.counters    = [];

            var framesperbar = opt.frames / 3,
                frame        = -1,
                callback     = opt.callback || function () {},
                width;

            for (var i=0,len=this.coords.length; i<len; i+=1) {
                opt.startFrames[i] = ((opt.frames / 2) / (obj.coords.length - 1)) * i;
                opt.counters[i]    = 0;
                
                // Now zero the width of the bar
                this.coords[i].element.setAttribute('width', 0);
                
                // Use this loop to set the stackedBackfaces to 0 width
                if (properties.grouping === 'stacked' && obj.stackedBackfaces[i]) {
                    obj.stackedBackfaces[i].setAttribute('width', 0);
                }
            }
            
            // Edge-case
            if (properties.grouping === 'stacked' && properties.yaxisPosition === 'right') {                
                previousX = obj.width - properties.marginRight;
                previousW = 0;
            }


            function iterator ()
            {
                ++frame;
                var group = 0;

                for (var i=0,len=obj.coords.length; i<len; i+=1) {
                    if (frame > opt.startFrames[i]) {
                        
                        var originalWidth = obj.coords[i].element.getAttribute('data-original-width'),
                            value         = parseFloat(obj.coords[i].element.getAttribute('data-value')),
                            seq           = i;
                            indexes       = RGraph.SVG.sequentialIndexToGrouped(i, obj.data);
                            
                            if (indexes[0] !== group) {
                                group = indexes[0];
                            }

                        obj.coords[i].element.setAttribute(
                            'width',
                            width = Math.min(
                                ((frame - opt.startFrames[i]) / framesperbar) * originalWidth,
                                originalWidth
                            )
                        );

                        stackedAccumulativeWidth += width;

                        if (properties.yaxisPosition === 'right') {
                            if (properties.grouping === 'stacked') {

                                if (indexes[1] === 0) {
                                    obj.coords[i].element.setAttribute('x',obj.width - properties.marginRight - width);
                                    
                                    var previousX = obj.coords[i].element.getAttribute('x');
                                } else {
                                    obj.coords[i].element.setAttribute(
                                        'x',
                                        previousX - width
                                    );
                                }
                                
                            
                            } else {
                                obj.coords[i].element.setAttribute(
                                    'x',
                                    value >=0
                                        ? obj.getXCoord(0) - width
                                        : obj.getXCoord(0)
                                );
                            }
                        } else {
                            obj.coords[i].element.setAttribute(
                                'x',
                                value >=0 ? obj.getXCoord(0) : obj.getXCoord(0) - width
                            );
                        }
                        
                        
                        if (properties.grouping === 'stacked' && RGraph.SVG.isArray(obj.data[indexes[0]])) {

                            // Are these two needed any more? //
                            //var seq = obj.coords[i].element.getAttribute('data-sequential-index');
                            //var indexes = RGraph.SVG.sequentialIndexToGrouped(seq, obj.data);
                            ////////////////////////////////////

                            if (properties.yaxisPosition === 'left' && indexes[1] > 0) {
                                obj.coords[i].element.setAttribute(
                                    'x',
                                    parseInt(obj.coords[i - 1].element.getAttribute('x')) + parseInt(obj.coords[i - 1].element.getAttribute('width'))
                                );
                            }

                            // Not really related to the code above, reuse this if()
                            // condition to set the width of the backface
                            //obj.stackedBackfaces[indexes[0]].setAttribute('width', width);
                            for (var j=0,cumulativeWidth=0; j<obj.coords2[indexes[0]].length; ++j) {
                                cumulativeWidth += parseInt(obj.coords2[indexes[0]][j].element.getAttribute('width'))
                            }
                            
                            if (properties.yaxisPosition === 'right') {
                                obj.stackedBackfaces[indexes[0]].setAttribute('width', cumulativeWidth);
                                obj.stackedBackfaces[indexes[0]].setAttribute('x', obj.width - properties.marginRight - cumulativeWidth);
                            } else {
                            
                                obj.stackedBackfaces[indexes[0]].setAttribute('x', obj.getXCoord(0));

                                obj.stackedBackfaces[indexes[0]].setAttribute(
                                    'width',
                                    cumulativeWidth
                                );
                            }
                            
                            previousX = obj.coords[i].element.getAttribute('x');
                            previousW = obj.coords[i].element.getAttribute('width');
                        }
                    }
                }


                if (frame >= opt.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);

            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]],
                 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)
        {
            if (typeof this.data[0] === 'object') {
                var label = (!RGraph.SVG.isNullish(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[index])
                                ? properties.tooltipsFormattedKeyLabels[index]
                                : '';

            } else {
                var label = (!RGraph.SVG.isNullish(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[specific.index])
                                ? properties.tooltipsFormattedKeyLabels[specific.index]
                                : '';
            }


            return {
                label: label
            };
        };








        //
        // 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),
                coords   = this.coords[args.index];

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

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








        //
        // Draw a line on the chart - just each of the X points
        // (ie the top of the bars) connected by a line. This
        // does mean you can set the colors property to transparent
        // and you have a vertical line.
        //
        this.drawLine = function ()
        {
            if (this.properties.lineShadow) {
                RGraph.SVG.setShadow({
                    object:     this,
                    id:         'lineDropShadow_' + this.uid,
                    offsetx:    this.properties.lineShadowOffsetx,
                    offsety:    this.properties.lineShadowOffsety,
                    blur:       this.properties.lineShadowBlur,
                    color:      this.properties.lineShadowColor
                });
            }

            if (this.properties.lineSpline) {
                
                // Set this so that we can refer to the object
                var obj = this;
                var c   = RGraph.SVG.arrayClone(this.coords, true);

                // Get the coordinates for the spline line
                // coordinates
                var coordinates = Spline(c, {return: true});
                
                // Draw a line back to the X axis (whichever side
                // that may be)
                if (this.properties.lineFilled) {
                
                    // Copy the coordinates array so that the coordinates of
                    // the axis can bw added to it
                    var fillAreaCoordinates = RGraph.SVG.arrayClone(coordinates, true);
                
                    // Add the coordinates that take the fill back
                    // to the Y axis
                    if (this.properties.yaxisPosition === 'right') {
                        fillAreaCoordinates.push([this.width - this.properties.marginRight, coordinates[coordinates.length - 1][1]]);
                        fillAreaCoordinates.push([this.width - this.properties.marginRight, coordinates[0][1]]);
                    } else {
                        fillAreaCoordinates.push([this.properties.marginLeft, coordinates[coordinates.length - 1][1]]);
                        fillAreaCoordinates.push([this.properties.marginLeft, coordinates[0][1]]);
                    }
                
                    RGraph.SVG.create({
                        svg: obj.svg,
                        parent: obj.svgAllGroup,
                        type: 'path',
                        attr: {
                            fill:              obj.properties.lineFilledColor,
                            stroke:            'transparent',
                            d:                 RGraph.SVG.create.pathString(fillAreaCoordinates),
                            'clip-path':       obj.isTrace ? 'url(#trace-effect-clip)' : ''
                        }
                    });
                }
                
                RGraph.SVG.create({
                    svg: obj.svg,
                    parent: obj.svgAllGroup,
                    type: 'path',
                    attr: {
                        fill:              "transparent",
                        stroke:            obj.properties.lineColor,
                        'stroke-width':    obj.properties.lineLinewidth,
                        'stroke-linejoin': 'round',
                        'stroke-linecap':  'round',
                        d:                 RGraph.SVG.create.pathString(coordinates),
                        filter:            obj.properties.lineShadow ? 'url(#lineDropShadow_' + obj.uid + ')' : '',
                        'clip-path':       obj.isTrace ? 'url(#trace-effect-clip)' : ''
                    }
                });

            } else {
            
                // TODO Need to skip null values at the start
                //      of the array here
            
                var d = '';

                // Move to the first coordinate
                if (RGraph.SVG.isNumber(this.data[0])) {
                    if (this.properties.yaxisPosition === 'right') {
                        d = 'M {1} {2} '.format(
                            this.coords[0].x,
                            this.coords[0].y + (this.coords[0].height / 2)
                        );
                    } else {

                        var v = Number(this.coords[0].element.getAttribute('data-value'));

                        d = 'M {1} {2} '.format(
                            this.coords[0].x + (v < 0 ? 0 : this.coords[0].width),
                            this.coords[0].y + (this.coords[0].height / 2)
                        );
                    }
                }

                // Draw a line to subsequent points unless
                // that point is null, in which case move
                // to it instead
                for (let i=0; i<this.coords.length; ++i) {

                    if (RGraph.SVG.isNullish(this.data[i])) {
                        var action = 'M';
                        var x      = 0;
                        var y      = 0;
                    } else if (RGraph.SVG.isNullish(this.data[i - 1])) {
                        var action  = 'M';
                        var x       = this.coords[i].x + (this.properties.yaxisPosition === 'right' ? 0 : this.coords[i].width);
                        var y       = this.coords[i].y + (this.coords[i].height / 2);
                    } else {
                        var action  = 'L';
                        var x       = this.coords[i].x + (this.properties.yaxisPosition === 'right' ? (this.data[i] < 0 ? this.coords[i].width : 0) : (this.data[i] < 0 ? 0 : this.coords[i].width) );
                        var y       = this.coords[i].y + (this.coords[i].height / 2);
                    }

                    d += 
                        '{1} {2} {3} '.format(
                        action,x, y
                    );
                }






                if (properties.lineFilled) {
                
                    RGraph.SVG.create({
                        svg:    this.svg,
                        type:   'path',
                        parent: this.svgAllGroup,
                        attr: {
                            d: d + ' L ' + (properties.yaxisPosition === 'right' ? [this.width - this.properties.marginRight, y].join(' ') : [this.properties.marginLeft, y].join(' '))
                                 + ' L ' + (properties.yaxisPosition === 'right' ? [this.width - this.properties.marginRight, this.coords[0].y + (this.coords[0].height / 2)] : [this.properties.marginLeft, (this.coords[0].y + (this.coords[0].height / 2))]).join(' ')
                                 + ' z',
                            stroke: 'transparent',
                            fill:   !RGraph.SVG.isNullish(this.properties.lineFilledColor) ? this.properties.lineFilledColor : this.properties.lineColor,
                            'stroke-width': 0.001,
                            'stroke-linecap': this.properties.lineLinecap,
                            'stroke-linejoin': this.properties.lineLinejoin,
                            //filter: this.properties.lineShadow ? 'url(#lineDropShadow_' + this.uid + ')' : '',
                            'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
                        }
                    });
                }





                var path = RGraph.SVG.create({
                    svg:    this.svg,
                    type:   'path',
                    parent: this.svgAllGroup,
                    attr: {
                        d:      d,
                        stroke: this.properties.lineColor,
                        fill:   'transparent',
                        'stroke-width': this.properties.lineLinewidth,
                        'stroke-linecap': this.properties.lineLinecap,
                        'stroke-linejoin': this.properties.lineLinejoin,
                        filter: this.properties.lineShadow ? 'url(#lineDropShadow_' + this.uid + ')' : '',
                        'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
                    }
                });

            }











            //
            // TODO Add more styles of tickmarks
            //
            var obj = this;
            this.coords.forEach(function (v, k, arr)
            {
                if (typeof obj.properties.lineTickmarksStyle === 'string') {
                    
                    var isEndTick  = (k === 0 || k === (arr.length - 1));
                    var isNull     = RGraph.SVG.isNullish(obj.data[k]);
                    var prevIsNull = RGraph.SVG.isNullish(obj.data[k - 1]);
                    var nextIsNull = RGraph.SVG.isNullish(obj.data[k + 1]);
                    var isLast     = k === (arr.length - 1);

                    //
                    // Does this tickmark need to be drawn?
                    //
                    if (isNull && !obj.properties.lineTickmarksDrawNull) return;
                    if (!isNull && prevIsNull && nextIsNull && !obj.properties.lineTickmarksDrawNonNull) return;

                    //
                    // Draw the various styles of tickmark
                    //
                    switch (obj.properties.lineTickmarksStyle) {
                        case 'circle':
                        case 'filledcircle':
                        case 'filledendcircle':
                        case 'endcircle':
                            if (
                                   (obj.properties.lineTickmarksStyle.indexOf('end') >= 0 && isEndTick)
                                || obj.properties.lineTickmarksStyle.indexOf('end') === -1
                               ) {



                                    var x = v.x;
                                    var y = v.y + (v.height / 2);

                                    // Adjust the tickmark if the
                                    // Y-axis position is on the left
                                    // and the value is positive
                                    if (   obj.properties.yaxisPosition === 'left'
                                        && Number(v.element.getAttribute('data-value')) > 0
                                       ) {
                                        x += v.width;
                                    }

                                    // Adjust the tickmark if the
                                    // Y-axis position is on the right
                                    // and the value is negative
                                    if (   obj.properties.yaxisPosition === 'right'
                                        && Number(v.element.getAttribute('data-value')) < 0
                                       ) {
                                        x += v.width;
                                    }


                                RGraph.SVG.create({
                                    svg:  obj.svg,
                                    type: 'circle',
                                    parent: obj.svgAllGroup,
                                    attr: {
                                        cx:          x,
                                        cy:          y,
                                        r:           obj.properties.lineTickmarksSize,
                                        fill:        obj.properties.lineColor,
                                        filter:      obj.properties.lineShadow ? 'url(#lineDropShadow_' + obj.uid + ')' : '',
                                        'clip-path': obj.isTrace ? 'url(#trace-effect-clip)' : ''
                                    }
                                });

                                // Draw the center part of the circle tickmark
                                if (obj.properties.lineTickmarksStyle.indexOf('filled') < 0) {

                                    RGraph.SVG.create({
                                        svg:    obj.svg,
                                        type:   'circle',
                                        parent: obj.svgAllGroup,
                                        attr: {
                                            cx:         x,
                                            cy:         y,
                                            r:          RGraph.SVG.isNumber(obj.properties.lineTickmarksLinewidth)
                                                          ? (obj.properties.lineTickmarksSize - obj.properties.lineTickmarksLinewidth)
                                                          : (obj.properties.lineTickmarksSize - 3),
                                            fill:       'white',
                                            'clip-path':obj.isTrace ? 'url(#trace-effect-clip)' : ''
                                        }
                                    });
                                }
                            }
                            break;

                        case 'square':
                        case 'rect':
                        case 'filledsquare':
                        case 'filledrect':
                        case 'filledendsquare':
                        case 'filledendrect':
                        case 'endsquare':
                        case 'endrect':
                            if (
                                   (obj.properties.lineTickmarksStyle.indexOf('end') >= 0 && isEndTick)
                                || obj.properties.lineTickmarksStyle.indexOf('end') === -1
                               ) {

                                    var x = v.x - obj.properties.lineTickmarksSize + (RGraph.SVG.isNullish(obj.properties.lineTickmarksLinewidth) ? 3 : obj.properties.lineTickmarksLinewidth);
                                    var y = v.y + (v.height / 2) - obj.properties.lineTickmarksSize;

                                    // Adjust the tickmark if the
                                    // Y-axis position is on the left
                                    // and the value is positive
                                    if (   obj.properties.yaxisPosition === 'left'
                                        && Number(v.element.getAttribute('data-value')) > 0
                                       ) {
                                        x += v.width;
                                    }

                                    // Adjust the tickmark if the
                                    // Y-axis position is on the right
                                    // and the value is negative
                                    if (   obj.properties.yaxisPosition === 'right'
                                        && Number(v.element.getAttribute('data-value')) < 0
                                       ) {
                                        x += v.width;
                                    }

                                    RGraph.SVG.create({
                                        svg:    obj.svg,
                                        type:   'rect',
                                        parent: obj.svgAllGroup,
                                        attr: {
                                            x:           x,
                                            y:           y,
                                            width:       obj.properties.lineTickmarksSize * 2,
                                            height:      obj.properties.lineTickmarksSize * 2,
                                            fill:        obj.properties.lineColor,
                                            filter:      obj.properties.lineShadow ? 'url(#lineDropShadow_' + obj.uid + ')' : '',
                                            'clip-path': obj.isTrace ? 'url(#trace-effect-clip)' : ''
                                        }
                                    });

                                // Draw the center part of the rect tickmark
                                if (obj.properties.lineTickmarksStyle.indexOf('filled') < 0) {
                                
                                
                                    RGraph.SVG.create({
                                        svg:    obj.svg,
                                        type:   'rect',
                                        parent: obj.svgAllGroup,
                                        attr: {
                                            x:           x + (obj.properties.lineTickmarksLinewidth),
                                            y:           y + (obj.properties.lineTickmarksLinewidth),
                                            width:       RGraph.SVG.isNumber(obj.properties.lineTickmarksLinewidth)
                                                            ? (obj.properties.lineTickmarksSize * 2) - (2 * obj.properties.lineTickmarksLinewidth)
                                                            : (obj.properties.lineTickmarksSize * 2) - 3 - 3,
                                            height:      RGraph.SVG.isNumber(obj.properties.lineTickmarksLinewidth)
                                                            ? (obj.properties.lineTickmarksSize * 2) - (2 * obj.properties.lineTickmarksLinewidth)
                                                            : (obj.properties.lineTickmarksSize * 2) - 3 - 3,
                                            fill:        'white',
                                            'clip-path': obj.isTrace ? 'url(#trace-effect-clip)' : ''
                                        }
                                    });
                                }
                            }
                            break;    
                    }
                }
            });






            //
            // This function draws a spline using the HBar coords
            // 
            // @param array  coords  The coordinates
            //
            function Spline (coords, opt = {})
            {
                var coordsSpline = [[]];

                var yCoords     = [],
                    interval    = (obj.height - obj.properties.marginTop - obj.properties.marginBottom) / coords.length;
                    path        = [];

                //
                // The drawSpline function needs an array of JUST
                // the X values - so put the coords into the correct
                // format
                //

                for (var i=0; i<coords.length;++i) {
                    coords[i] = Number(coords[i].x) +
                                    (obj.properties.yaxisPosition === 'right'
                                        ? (obj.data[i] < 0 ? Number(coords[i].width) : 0)
                                        : (obj.data[i] < 0 ? 0 : Number(coords[i].width))
                                    );
                }

    

    
                //
                // Get the Points array in the format we want -
                // first value should be null along with the lst
                // value
                //
                var P = [coords[0]];
                for (var i=0; i<coords.length; ++i) {
                    P.push(coords[i]);
                }
                P.push(
                    coords[coords.length - 1] + (coords[coords.length - 1] - coords[coords.length - 2])
                );

                // This is is necessary to center the points
                // within each bar/section
                var halfsection = ((obj.height - obj.properties.marginTop - obj.properties.marginBottom) / obj.data.length) / 2

                for (var j=1; j<P.length-2; ++j) {
                    for (var t=0; t<10; ++t) {
                        
                        var xCoord = Spline( t/10, P[j-1], P[j], P[j+1], P[j+2] );

                        yCoords.push(
                            ((j-1) * interval) + (t * (interval / 10)) + obj.properties.marginTop + halfsection
                        );

                        path.push([
                            xCoord,
                            yCoords[yCoords.length - 1]
                        ]);
    
                        
                        if (typeof index == 'number') {
                            coordsSpline[0].push([
                                xCoords[xCoords.length - 1],
                                yCoords[yCoords.length - 1]
                            ]);
                        }
                    }
                }

    
                // Draw the last section
                var last = [
                    obj.coords[obj.coords.length - 1].x + (obj.properties.yaxisPosition === 'right'  ? obj.coords[obj.coords.length - 1].width : 0),
                    obj.coords[obj.coords.length - 1].y + (obj.coords[obj.coords.length - 1].height / 2)
                ];
                
                // Adjust the position of the last point
                if (obj.properties.yaxisPosition === 'left' && obj.coords[j - 1].element.getAttribute('data-value') > 0) {
                    last[0] += obj.coords[obj.coords.length - 1].width;
                }
                
                // Adjust the position of the last point
                if (obj.properties.yaxisPosition === 'right' && obj.coords[j - 1].element.getAttribute('data-value') > 0) {
                    last[0] -= obj.coords[obj.coords.length - 1].width;
                }

                path.push([
                    last[0],
                    last[1]
                ]);

                if (typeof index === 'number') {
                    coordsSpline[0].push([
                        last[0],
                        last[1]
                    ]);
                }

                var str = '';
                path.forEach(function (v,k,arr)
                {
                    var command = (k === 0 ? 'M' : 'L');
                    str += ' {1} {2} {3}'.format(command, v[0], v[1]);
                });



                //
                // Create the path which depicts the spline
                //
                if (opt.return) {

                    // The 'path' variable appears to contain all
                    // of the coordinates.
                    return path;

                } else {

                    RGraph.SVG.create({
                        svg: obj.svg,
                        parent: obj.svgAllGroup,
                        type: 'path',
                        attr: {
                            fill:              "transparent",
                            stroke:            obj.properties.lineColor,
                            'stroke-width':    obj.properties.lineLinewidth,
                            'stroke-linejoin': 'round',
                            'stroke-linecap':  'round',
                            d:                 str,
                            filter:            obj.properties.lineShadow ? 'url(#lineDropShadow_' + obj.uid + ')' : '',
                            'clip-path':       obj.isTrace ? 'url(#trace-effect-clip)' : ''
                        }
                    });
                }

    
        
                function Spline (t, P0, P1, P2, P3)
                {
                    return 0.5 * ((2 * P1) +
                                 ((0-P0) + P2) * t +
                                 ((2*P0 - (5*P1) + (4*P2) - P3) * (t*t) +
                                 ((0-P0) + (3*P1)- (3*P2) + P3) * (t*t*t)));
                }
            }
        };








        //
        // A trace effect for the line
        //
        // @param object    Options to give to the effect
        // @param  function A function to call when the effect has completed
        //
        this.trace = function ()
        {
            var opt      = arguments[0] || {},
                frame    = 1,
                frames   = opt.frames || 60,
                obj      = this;
            
            this.isTrace = true;

            this.draw();
                


            // Create the clip area

            var clippath = RGraph.SVG.create({
                svg:  this.svg,
                type: 'clipPath',
                parent: this.svg.defs,
                attr: {
                    id: 'trace-effect-clip'
                }
            });

            var clippathrect = RGraph.SVG.create({
                svg: this.svg,
                type: 'rect',
                parent: clippath,
                attr: {
                    x: 0,
                    y: 0,
                    width: this.width,
                    height: 0
                }
            });

            var iterator = function ()
            {
                var height = (frame++) / frames * obj.height;

                clippathrect.setAttribute("height", height);

                if (frame <= frames) {
                    RGraph.SVG.FX.update(iterator);
                } else {
                    
                    // Remove the clippath
                    clippath.parentNode.removeChild(clippath);
                    
                    if (opt.callback) {
                        (opt.callback)(obj);
                    }
                }
            };
            
            iterator();
            
            return this;
        };








        //
        // 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.
        //
        // @param object clipPath The <clipPath> node
        //
        this.clipToScaleWorker = function (clipPath)
        {
            // The Regular expression is actually done by the
            // calling RGraph.clipTo.start() function  in the core
            // library
            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 height  = this.height,
                x1     = this.getXCoord(from),
                x2     = this.getXCoord(to),
                width  = Math.abs(x2 - x1),
                y      = 0,
                x      = Math.min(x1, x2);


            // Increase the width if the maximum value is "max"
            if (RegExp.$2 === 'max') {
                width += this.properties.marginRight;
            }
        
            // Increase the height if the minimum value is "min"
            if (RegExp.$1 === 'min') {
                x = 0;
                width += this.properties.marginLeft;
            }


            RGraph.SVG.create({
                svg:    this.svg,
                type:   'rect',
                parent: clipPath,
                attr: {
                    x:      x,
                    y:      y,
                    width:  width,
                    height: height
                }
            });
            
            // Now set the clip-path attribute on the first
            // Line charts all-elements group
            this.svgAllGroup.setAttribute(
                'clip-path',
                'url(#' + clipPath.id + ')'
            );
        };








    //
    // This function allows the drawing of custom lines
    //
    this.drawVerticalLines = function ()
    {
        var lines = this.properties.verticalLines,
            avg,x,y,label,halign,valign,hmargin = 5,vmargin = 10,
            position,textFont,textSize,textColor,textBold,textItalic,
            data,linewidth,data_linear;

        if (lines) {
        
            data_linear = RGraph.SVG.arrayLinearize(this.data);

            //
            // Set some defaults for the configuration of
            // each line
            //
            var defaults = {
                dotted:             false,
                dashed:             true,
                color:              '#666', // Same as labelColor property below
                linewidth:          1,
                label:              'Average (%{value})',
                labelPosition:      'top',
                labelColor:         '#666', // Same as color property above
                labelOffsetx:       0,
                labelOffsety:       0
            };

        
            // Loop through each line to be drawn
            for (var i=0; i<this.properties.verticalLines.length; ++i) {

                var conf       = lines[i],
                    textFont   = conf.labelFont  || this.properties.textFont,
                    textColor  = conf.labelColor || defaults.labelColor,
                    textSize   = conf.labelSize  || this.properties.textSize,
                    textBold   = conf.labelBold   ? conf.labelBold   : this.properties.textBold,
                    textItalic = conf.labelItalic ? conf.labelItalic : this.properties.textItalic;








                    if (typeof conf.value === 'number') {
                        x = this.getXCoord(conf.value);
                    
                    } else {
                        conf.value = 'average';
                        avg = RGraph.SVG.arraySum(data_linear) /  data_linear.length;
                        x   = this.getXCoord(avg);
                    }










                //
                // Dotted or dashed lines
                //
                linedash = '';

                if (conf.dotted === true) {
                    linedash = '1 3';
                }
                
                if (conf.dashed === true || (RGraph.SVG.isUndefined(conf.dashed) && RGraph.SVG.isUndefined(conf.dotted)) ) {
                    linedash = '6 6';
                }










                //
                // Draw the line
                //
                RGraph.SVG.create({
                    svg:    this.svg,
                    type:   'line',
                    parent: this.svgAllGroup,
                    attr: {
                        x1:                 x,
                        y1:                 this.properties.marginTop,
                        x2:                 x,
                        y2:                 this.height - this.properties.marginBottom,
                        'stroke-width':     typeof conf.linewidth === 'number' ? conf.linewidth : defaults.linewidth,
                        'stroke-dasharray': linedash,
                        stroke:             conf.color || defaults.color
                    }
                });





                //
                // Draw the label
                //




                

                // Default position for the label
                conf.labelPosition = conf.labelPosition || defaults.labelPosition;

                labelPosition = conf.labelPosition.trim();



                // Account for linewidth
                linewidth = typeof conf.linewidth === 'number' ? conf.linewidth : defaults.linewidth;

                //
                // Determine the value to show
                //
                if (RGraph.SVG.isString(conf.value) && conf.value === 'average') {
                    num = avg;
                    num = num.toFixed(conf.labelValueDecimals);
                    
                } else {
                    var num = Number(conf.value).toFixed(conf.labelValueDecimals);
                }

                num = RGraph.SVG.numberFormat({
                   object: this,
                      num: num,
                  prepend: conf.labelValueUnitsPre,
                   append: conf.labelValueUnitsPost,
                 thousand: conf.labelValueThousand,
                    point: conf.labelValuePoint,
                formatter: conf.labelValueFormatter,
                 decimals: conf.labelValueDecimals
                });



                //
                // Is the label positioned at the top or the bottom
                //
                
                // Default labelPosition is the top
                if (!conf.labelPosition) {
                    conf.labelPosition = 'top';
                }
                
                // Determine the position of the label
                if (conf.labelPosition === 'bottom') {
                    var y = this.height - this.properties.marginBottom + (conf.labelOffsety || 0) + 7;
                } else {
                    var y = (this.properties.marginTop + (conf.labelOffsety || 0) - 7);
                }

                //
                // Draw the label
                //
                RGraph.SVG.text({
                    object: this,
                      text: (RGraph.SVG.isString(conf.label) ? conf.label : defaults.label).replace('%{value}', num),
                         x: RGraph.SVG.isNumber(conf.labelX) ? conf.labelX : (x + (conf.labelOffsetx || 0)),
                         y: RGraph.SVG.isNumber(conf.labelY) ? conf.labelY : y,
                    valign: RGraph.SVG.isString(conf.labelValign) ? conf.labelValign : (conf.labelPosition === 'bottom' ? 'top' : 'bottom'),
                    halign: RGraph.SVG.isString(conf.labelHalign) ? conf.labelHalign : 'center',
                      size: textSize,
                      font: textFont,
                     color: textColor,
                    italic: textItalic,
                      bold: textBold,
                background: conf.label ? '#fff9' : null
                });
            }
        }
    };








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