    /**
    * o------------------------------------------------------------------------------o
    * | This file is part of the RGraph package - you can learn more at:             |
    * |                                                                              |
    * |                          http://www.rgraph.org                               |
    * |                                                                              |
    * | This package is licensed under the RGraph license. A quick summary is        |
    * | that the code is free to use for non-commercial purposes. For commercial     |
    * | purposes there is a small license fee to pay. You can read more at:          |
    * |                                                                              |
    * |                      http://www.rgraph.org/LICENSE.txt                       |
    * o------------------------------------------------------------------------------o
    *
    * © Copyright 2008,2009 Richard Heyes
    */

    /**
    * The bar chart constructor
    * 
    * @param object canvas The canvas object
    * @param array  data   The chart data
    */
    function Bar(id, data)
    {
        // Get the canvas and context objects
        this.id                = id;
        this.canvas            = $(id);
        this.context           = this.canvas.getContext ? this.canvas.getContext("2d") : null;
        this.canvas.__object__ = this;
        this.type              = 'bar';

        this.max               = 0;
        this.stackedOrGrouped  = false;

        // Various config type stuff
        this.properties                   = [];
        this.properties['barcolor1']      = '#dce5fe';
        this.properties['barcolor2']      = '#eee';
        this.properties['backgroundgrid'] = true;
        this.properties['gridcolor']      = '#ddd';
        this.properties['gridwidth']      = 0.5;
        this.properties['ytickgap']       = 20;
        this.properties['smallyticks']    = 3;
        this.properties['largeyticks']    = 5;
        this.properties['hmargin']         = 5;
        this.properties['strokecolor']    = '#666';
        this.properties['gutter']         = 15;
        this.properties['labels']         = null;
        this.properties['xaxispos']       = 'bottom';
        this.properties['textstyle']      = '#000';
        this.properties['textheight']     = 10;
        this.properties['ymax']           = null;
        this.properties['title']          = '';
        this.properties['colors']         = ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'];
        this.properties['grouping']       = 'grouped';
        this.properties['shadow']         = false;
        this.properties['shadowcolor']    = 'rgba(80,80,80,0.5)';
        this.properties['coords']         = [];
        this.properties['tooltips']       = null;
        this.properties['horgridsize']    = 40;
        this.properties['vergridsize']    = 18;
        this.properties['key']            = [];
        this.properties['keybackground']  = '#fff';
        this.properties['keyposition']    = 'graph';
        this.properties['contextmenu']    = null;
        this.properties['textangle']      = 0;
        this.properties['line']           = null;
        this.properties['units.pre']      = '';
        this.properties['units.post']     = '';
        this.properties['hbars']          = null;

        // Check for support
        if (!this.canvas) {
            alert('[BAR] No canvas support');
            return;
        }
        
        // Check the canvasText library has been included
        if (typeof(RGraph) == 'undefined') {
            alert('[BAR] Fatal error: The common library does not appear to have been included');
        }

        /**
        * Determine whether the chart will contain stacked or grouped bars
        */
        for (i in this.data) {
            if (typeof(this.data[i]) == 'object') {
                this.stackedOrGrouped = true;
            }
        }

        // Store the data
        this.data = data;
        
        // Used to store the coords of the bars
        this.coords = [];
    }


    /**
    * A setter
    * 
    * @param name  string The name of the property to set
    * @param value mixed  The value of the property
    */
    Bar.prototype.Set = function (name, value)
    {
        this.properties[name.toLowerCase()] = value;
    }


    /**
    * A getter
    * 
    * @param name  string The name of the property to get
    */
    Bar.prototype.Get = function (name)
    {
        return this.properties[name.toLowerCase()];
    }


    /**
    * The function you call to draw the bar chart
    */
    Bar.prototype.Draw = function ()
    {
        /**
        * Check for tooltips AND context menus. You can't have both at once
        */
        if (this.Get('tooltips') && this.Get('tooltips').length && this.Get('contextmenu')) {
            alert('[BAR] (' + this.id + ') You cannot have both context menus and tooltips at the same time. Turning them BOTH off');
            this.Set('tooltips', null);
            this.Set('contextmenu', null);
        }

        /**
        * Stop the coords array from growin uncontrollably
        */
        this.coords = [];

        /**
        * Work out a few things. They need to be here because they depend on things you can change before you
        * call Draw() but after you instantiate the object
        */
        this.grapharea      = this.canvas.height - ( (2 * this.Get('gutter')));
        this.halfgrapharea  = this.grapharea / 2;
        this.halfTextHeight = this.Get('textheight') / 2;

        // Progressively Draw the chart
        RGraph.background.Draw(this);

        this.Drawbars();
        this.DrawAxes();
        this.DrawLabels();
        
        // Draw the key if necessary
        if (this.Get('key').length) {
            RGraph.DrawKey(this, this.Get('key'), this.Get('colors'));
        }
        
        
        /**
        * Setup the context menu if required
        */
        RGraph.ShowContext(this);


        /**
        * Is a line is defined, draw it
        */
        if (this.Get('line')) {
            
            // Check the length of the data(s)
            if (this.Get('line').original_data[0].length != this.data.length) {
                alert("[BAR] You're adding a line with a differing amount of data points to the bar chart - this is not permitted");
            }
            
            // Check the X axis positions
            if (this.Get('xaxispos') != this.Get('line').Get('xaxispos')) {
                alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised");
            }

            this.Get('line').Set('gutter', this.Get('gutter'));
            this.Get('line').Set('noaxes', true);
            this.Get('line').Set('barcolor1', 'rgba(0,0,0,0)');
            this.Get('line').Set('barcolor2', 'rgba(0,0,0,0)');
            this.Get('line').Set('backgroundgrid', false);
            this.Get('line').Set('ylabels', false);
            this.Get('line').Set('hmargin', (this.canvas.width - (2 * this.Get('gutter'))) / (this.Get('line').original_data[0].length * 2));
            
            // If a custom yMax is set, use that
            if (this.Get('ymax')) {
                this.Get('line').Set('ymax', this.Get('ymax'));
            }

            this.Get('line').Draw();
        }


        /**
        * Now draw some text off canvas to prevent a bug in the text library becoming apparent
        * It might actually not be a bug in the text libaray, but ho-hum
        */
        RGraph.Text(this.context, 'sans', 2, this.canvas.width + 50, this.canvas.height + 50, '.');
    }

    
    /**
    * Draws the charts axes
    */
    Bar.prototype.DrawAxes = function ()
    {
        this.context.beginPath();
        this.context.strokeStyle = '#000';

        // Draw the Y axis
        this.context.moveTo(this.Get('gutter'), this.Get('gutter'));
        this.context.lineTo(this.Get('gutter'), this.canvas.height - this.Get('gutter'));
        
        // Draw the X axis
        this.context.moveTo(this.Get('gutter'), (this.Get('xaxispos') == 'center' ? this.canvas.height / 2 : this.canvas.height - this.Get('gutter')));
        this.context.lineTo(this.canvas.width - this.Get('gutter'), this.Get('xaxispos') == 'center' ? this.canvas.height / 2 : this.canvas.height - this.Get('gutter'));

        // Draw the Y tickmarks
        var yTickGap = (this.canvas.height - (2 * this.Get('gutter'))) / 10;

        for (y=this.Get('gutter');
             this.Get('xaxispos') == 'center' ? y <= (this.canvas.height - this.Get('gutter')) : y < (this.canvas.height - this.Get('gutter'));
             y += yTickGap) {

            if (this.Get('xaxispos') == 'center' && y == (this.canvas.height / 2)) continue;
            
            this.context.moveTo(this.Get('gutter'), y);
            this.context.lineTo(this.Get('gutter')  - 3, y);
        }

        // Draw the X tickmarks
        xTickGap = (this.canvas.width - (2 * this.Get('gutter')) ) / this.data.length;
        yStart   = this.canvas.height - this.Get('gutter');
        yEnd     = (this.canvas.height - this.Get('gutter')) + 3;
        
        //////////////// X TICKS ////////////////

        // Now move the Y start end positions down if the axis is set to center
        if (this.Get('xaxispos') == 'center') {
            yStart = (this.canvas.height / 2) + 3;
            yEnd   = (this.canvas.height / 2) - 3;
        }

        for (x=this.Get('gutter') + xTickGap; x<=this.canvas.width - this.Get('gutter'); x+=xTickGap) {
            this.context.moveTo(x, yStart);
            this.context.lineTo(x, yEnd);
        }

        //////////////// X TICKS ////////////////

        this.context.stroke();
    }
    
    /**
    * Draws the bars
    */
    Bar.prototype.Drawbars = function ()
    {
        this.context.lineWidth   = 1;
        this.context.strokeStyle = this.Get('strokecolor');
        this.context.fillStyle   = this.Get('colors')[0];
        var prevX                = 0;
        var prevY                = 0;

        /**
        * Work out the max value
        */
        if (this.Get('yMax')) {
            this.scale = RGraph.getScale(this.Get('ymax'));
            this.max   = this.scale[4];
        } else {
            for (i in this.data) {
                if (typeof(this.data[i]) == 'object') {
                    var value = this.Get('grouping') == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;

                } else {
                    var value = Number(this.data[i]);
                }

                this.max = Math.max(Math.abs(this.max), Math.abs(value));
            }

            this.scale = RGraph.getScale(this.max);
            this.max   = this.scale[4];

        }
        
        /**
        * Draw horizontal bars here
        */
        if (this.Get('hbars') && this.Get('hbars').length > 0) {
            RGraph.DrawBars(this);
        }

        for (i in this.data) {

            // Work out the width and height
            var width  = (this.canvas.width - (2 * this.Get('gutter')) ) / this.data.length;;
            var height = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - (2 * this.Get('gutter')) );

            var orig_height = height;

            // Half the height if the Y axis is at the center
            if (this.Get('xaxispos') == 'center') {
                height /= 2;
            }

            var x      = (i * width) + this.Get('gutter');
            var y      = this.Get('xaxispos') == 'center' ? (this.canvas.height / 2) - height : this.canvas.height - height - this.Get('gutter');
            var hmargin = this.Get('hmargin');
            var gutter = this.Get('gutter');

            // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
            if (height < 0) {
                y += height;
                height = Math.abs(height);
            }

            /**
            * Draw the bar
            */
            this.context.beginPath();
                if (typeof(this.data[i]) == 'number') {
                    
                    var barWidth = width - (2 * hmargin);
                    
                    // Set the fill color
                    this.context.strokeStyle = '#666';
                    this.context.fillStyle = this.Get('colors')[0];

                    // Regular bar
                    this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height);
                    this.context.strokeRect(x + hmargin, y   , barWidth, height);

                    // Create a simple SHADOW effect
                    if (this.Get('shadow')) {
                        var offset = 2;
                        this.context.fillStyle = this.Get('shadowcolor');
                        this.context.fillRect(x + hmargin + offset, y - offset, barWidth, offset);
                        this.context.fillRect(x + hmargin + barWidth, y, offset, Math.max(0, height - offset) );
                    }

                    this.coords[i] = [x, y, width, height];


                /**
                * Stacked bar
                */
                } else if (typeof(this.data[i]) == 'object' && this.Get('grouping') == 'stacked') {
                    
                    var barWidth    = width - (2 * hmargin);

                    for (j in this.data[i]) {
                    
                        // Stacked bar chart and X axis pos in the middle - poitless since negative values are not permitted
                        if (this.Get('xaxispos') == 'center') {
                            alert("[BAR] It's pointless having the X axis position at the center on a stacked bar chart. Put it at the bottom.");
                            return;
                        }

                        // Negative values not permitted for the stacked chart
                        if (this.data[i][j] < 0) {
                            alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');
                            return;
                        }

                        // Set the fill color
                        this.context.fillStyle = this.Get('colors')[j]

                        var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.Get('gutter')) );

                        // If the X axis pos is in the center, we need to half the  height
                        if (this.Get('xaxispos') == 'center') {
                            height /= 2;
                        }

                        var totalHeight = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - this.Get('hmargin') - (2 * this.Get('gutter')));

                        this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height);
                        this.context.strokeRect(x + hmargin, y, width - (2 * hmargin), height);

                        /**
                        * Store the coords for tooltips
                        */
                        this.coords.push([x, y, width, height]);

                        y += height;
                        
                    }

                    // Create a simple SHADOW effect
                    if (this.Get('shadow')) {    
                        var offset = 2;
                        this.context.fillStyle = this.Get('shadowcolor');
                        this.context.fillRect(x + hmargin + offset, this.canvas.height - this.Get('gutter') - totalHeight - offset, barWidth, offset);
                        this.context.fillRect(x + hmargin + barWidth, this.canvas.height - this.Get('gutter') - totalHeight, offset, totalHeight - offset);
                    }


                /**
                * Grouped bar
                */
                } else if (typeof(this.data[i]) == 'object' && this.Get('grouping') == 'grouped') {
                    for (j in this.data[i]) {
                        // Set the fill color
                        this.context.fillStyle = this.Get('colors')[j]

                        var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length;
                        var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.Get('gutter')) );

                        // If the X axis pos is in the center, we need to half the  height
                        if (this.Get('xaxispos') == 'center') {
                            height /= 2;
                        }

                        var startX = x + hmargin + (j * individualBarWidth);
                        var startY = (this.Get('xaxispos') == 'bottom' ? this.canvas.height : (this.canvas.height / 2) + this.Get('gutter')) - this.Get('gutter') - height;

                        // Account for a bug in chrome that doesn't allow negative heights
                        if (height < 0) {
                            startY += height;
                            height = Math.abs(height);
                        }

                        this.context.fillRect(startX, startY, individualBarWidth, height);
                        this.context.strokeRect(startX, startY, individualBarWidth, height);
                        y += height;
                        
                        this.coords.push([startX - this.Get('hmargin'), startY, individualBarWidth + (2 * this.Get('hmargin')), height]);

                        // Create a simple SHADOW effect
                        if (this.Get('shadow')) {
                            var offset = 2;
                            this.context.fillStyle = this.Get('shadowcolor');
                            this.context.fillRect(startX + offset, startY - offset, individualBarWidth, offset);
                            this.context.fillRect(startX + individualBarWidth, startY, offset, Math.max(0, height - offset));
                        }
                    }
                }

            this.context.closePath();
        }


        /**
        * Install the onclick event handler
        */
        if (this.Get('tooltips')) {
        
            // Need to register this object for redrawing
            RGraph.Register(this);

            /**
            * Install the window onclick handler
            */
            window.onclick = function ()
            {
                RGraph.Redraw();
            }

            /**
            * Install the onclick event handler for the tooltips
            */
            this.canvas.onclick = function (e)
            {
                var canvas = $(this.id)
                var obj = canvas.__object__;

                /**
                * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
                * This "deselects" any already selected bar
                */
                RGraph.Clear(canvas);
                obj.Draw();
    
                /**
                * Get the mouse X/Y coordinates
                */
                var mouseCoords = RGraph.getMouseXY(e);

                /**
                * Loop through the bars determining if the mouse is over a bar
                */
                for (var i=0; i<obj.coords.length; i++) {

                    var mouseX = mouseCoords[0];  // In relation to the canvas
                    var mouseY = mouseCoords[1];  // In relation to the canvas
                    var left   = obj.coords[i][0];
                    var top    = obj.coords[i][1];
                    var width  = obj.coords[i][2];
                    var height = obj.coords[i][3];

                    if (mouseX >= (left + 5 /* 5 is the hmargin */ ) && mouseX <= (left + width - 5) && mouseY >= top && mouseY <= (top + height) ) {
                        
                        obj.context.beginPath();
                        obj.context.strokeStyle = '#000';
                        obj.context.fillStyle   = 'rgba(255,255,255,0.5)';
                        obj.context.strokeRect(left + obj.Get('hmargin'), top, width - (2 * obj.Get('hmargin')), height);
                        obj.context.fillRect(left + obj.Get('hmargin'), top, width - (2 * obj.Get('hmargin')), height);
    
                        obj.context.stroke();
                        obj.context.fill();
    
                        /**
                        * Show a tooltip if it's defined
                        */
                        if (obj.Get('tooltips')[i]) {

                            RGraph.Tooltip(canvas, obj.Get('tooltips')[i], e.pageX, e.pageY);
                        }
                    }
                }

                /**
                * Stop the event bubbling
                */
                e.cancelBubble = true;
            }

            // This resets the bar graph
            if (RGraph.Registry.Get('tooltip')) {
                RGraph.Registry.Get('tooltip').style.display = 'none';
                RGraph.Registry.Set('tooltip', null)
            }
        }
    }
    
    /**
    * Draws the labels for the graph
    */
    Bar.prototype.DrawLabels = function ()
    {
        // Draw the Y axis labels:
        this.Drawlabels_center();
        this.Drawlabels_bottom();

        /**
        * The X axis labels
        */
        if (this.Get('labels') && this.data.length == this.Get('labels').length) {
        
            var yOffset = 13;

            /**
            * Text angle
            */
            var angle  = 0;
            var halign = 'center';

            if (this.Get('textangle') == 45 || this.Get('textangle') == 90) {
                angle  = -1 * this.Get('textangle');
                halign   = 'right';
                yOffset -= 5;
            }

            // Draw the X axis labels
            this.context.strokeStyle = this.Get('textstyle');
            
            // How wide is each bar
            var barWidth = (this.canvas.width - this.Get('gutter') - this.Get('gutter')) / this.data.length;

            // Draw the X tickmarks
            var i=0;
            for (x=this.Get('gutter') + (xTickGap / 2); x<=this.canvas.width - this.Get('gutter'); x+=xTickGap) {
                RGraph.Text(this.context,'sans',
                                      this.Get('textheight'),
                                      x,
                                      (this.canvas.height - this.Get('gutter')) + yOffset,
                                      String(this.Get('labels')[i++]),
                                      null,
                                      halign,
                                      null,
                                      angle);
            }

        /**
        * Only show the lower sides labels if the X axis is in the centre
        */
        } else if (this.Get('labels')) {
                alert('[BAR] (' + this.id + ') Number of labels does not match the number of data points');
        }
    }

    /**
    * Draws the X axis in the middle
    */
    Bar.prototype.Drawlabels_center = function ()
    {
        if (this.Get('xaxispos') == 'center') {
        
            ///////////////////////////////////////////////////////////////////////////////////

            /**
            * Draw the top labels
            */
            var interval = (this.grapharea * (1/10) );

            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5,                this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[4], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (1*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[3], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (2*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[2], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (3*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[1], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (4*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[0], this.Get('units.pre'), this.Get('units.post')), null, 'right');

            ///////////////////////////////////////////////////////////////////////////////////

            /**
            * Draw the bottom (X axis) labels
            */
            var interval = (this.grapharea) / 10;

            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (this.grapharea + this.Get('gutter') + this.halfTextHeight) - (4 * interval), '-' + RGraph.number_format(this.scale[0], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (this.grapharea + this.Get('gutter') + this.halfTextHeight) - (3 * interval), '-' + RGraph.number_format(this.scale[1], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (this.grapharea + this.Get('gutter') + this.halfTextHeight) - (2 * interval), '-' + RGraph.number_format(this.scale[2], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (this.grapharea + this.Get('gutter') + this.halfTextHeight) - interval, '-' + RGraph.number_format(this.scale[3], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5,  this.grapharea + this.Get('gutter') + this.halfTextHeight, '-' + RGraph.number_format(this.scale[4], this.Get('units.pre'), this.Get('units.post')), null, 'right');

            ///////////////////////////////////////////////////////////////////////////////////

        }
    }

    /**
    * Draws the X axdis at the bottom (the default)
    */
    Bar.prototype.Drawlabels_bottom = function ()
    {
        if (this.Get('xaxispos') != 'center') {
            var interval = (this.grapharea * (1/5) );

            //RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, this.Get('gutter') + this.halfTextHeight, String(Math.round(this.max)), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[4], this.Get('units.pre'), this.Get('units.post')), null, 'right');

            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (1*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[3], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (2*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[2], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (3*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[1], this.Get('units.pre'), this.Get('units.post')), null, 'right');
            RGraph.Text(this.context, 'sans', this.Get('textheight'), this.Get('gutter') - 5, (4*interval) + this.Get('gutter') + this.halfTextHeight, RGraph.number_format(this.scale[0], this.Get('units.pre'), this.Get('units.post')), null, 'right');
        }
    }
