initial commit
[home-automation.git] / libraries / RGraph.hbar.js
1     /**
2     * o------------------------------------------------------------------------------o
3     * | This file is part of the RGraph package - you can learn more at:             |
4     * |                                                                              |
5     * |                          http://www.rgraph.net                               |
6     * |                                                                              |
7     * | This package is licensed under the RGraph license. For all kinds of business |
8     * | purposes there is a small one-time licensing fee to pay and for non          |
9     * | commercial  purposes it is free to use. You can read the full license here:  |
10     * |                                                                              |
11     * |                      http://www.rgraph.net/LICENSE.txt                       |
12     * o------------------------------------------------------------------------------o
13     */
14     
15     if (typeof(RGraph) == 'undefined') RGraph = {};
16
17     /**
18     * The horizontal bar chart constructor. The horizontal bar is a minor variant
19     * on the bar chart. If you have big labels, this may be useful as there is usually
20     * more space available for them.
21     * 
22     * @param object canvas The canvas object
23     * @param array  data   The chart data
24     */
25     RGraph.HBar = function (id, data)
26     {
27         // Get the canvas and context objects
28         this.id                = id;
29         this.canvas            = document.getElementById(id);
30         this.context           = this.canvas.getContext ? this.canvas.getContext("2d") : null;
31         this.canvas.__object__ = this;
32         this.data              = data;
33         this.type              = 'hbar';
34         this.coords            = [];
35         this.isRGraph          = true;
36
37
38         /**
39         * Compatibility with older browsers
40         */
41         RGraph.OldBrowserCompat(this.context);
42
43         
44         this.max = 0;
45         this.stackedOrGrouped  = false;
46
47         // Default properties
48         this.properties = {
49             'chart.gutter':                 25,
50             'chart.background.grid':        true,
51             'chart.background.grid.color':  '#ddd',
52             'chart.background.grid.width':  1,
53             'chart.background.grid.hsize':  25,
54             'chart.background.grid.vsize':  25,
55             'chart.background.barcolor1':   'white',
56             'chart.background.barcolor2':   'white',
57             'chart.background.grid.hlines': true,
58             'chart.background.grid.vlines': true,
59             'chart.background.grid.border': true,
60             'chart.background.grid.autofit':false,
61             'chart.background.grid.autofit.numhlines': 14,
62             'chart.background.grid.autofit.numvlines': 20,
63             'chart.title':                  '',
64             'chart.title.background':       null,
65             'chart.title.xaxis':            '',
66             'chart.title.yaxis':            '',
67             'chart.title.xaxis.pos':        0.25,
68             'chart.title.yaxis.pos':        0.5,
69             'chart.title.hpos':             null,
70             'chart.title.vpos':             null,
71             'chart.text.size':              10,
72             'chart.text.color':             'black',
73             'chart.text.font':              'Verdana',
74             'chart.colors':                 ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'],
75             'chart.labels':                 [],
76             'chart.labels.above':           false,
77             'chart.labels.above.decimals':  0,
78             'chart.contextmenu':            null,
79             'chart.key':                    [],
80             'chart.key.background':         'white',
81             'chart.key.position':           'graph',
82             'chart.key.shadow':             false,
83             'chart.key.shadow.color':       '#666',
84             'chart.key.shadow.blur':        3,
85             'chart.key.shadow.offsetx':     2,
86             'chart.key.shadow.offsety':     2,
87             'chart.key.position.gutter.boxed': true,
88             'chart.key.position.x':         null,
89             'chart.key.position.y':         null,
90             'chart.key.color.shape':        'square',
91             'chart.key.rounded':            true,
92             'chart.units.pre':              '',
93             'chart.units.post':             '',
94             'chart.units.ingraph':          false,
95             'chart.strokestyle':            'black',
96             'chart.xmax':                   0,
97             'chart.axis.color':             'black',
98             'chart.shadow':                 false,
99             'chart.shadow.color':           '#666',
100             'chart.shadow.blur':            3,
101             'chart.shadow.offsetx':         3,
102             'chart.shadow.offsety':         3,
103             'chart.vmargin':                3,
104             'chart.grouping':               'grouped',
105             'chart.tooltips':               null,
106             'chart.tooltips.effect':         'fade',
107             'chart.tooltips.css.class':      'RGraph_tooltip',
108             'chart.tooltips.highlight':     true,
109             'chart.annotatable':            false,
110             'chart.annotate.color':         'black',
111             'chart.zoom.factor':            1.5,
112             'chart.zoom.fade.in':           true,
113             'chart.zoom.fade.out':          true,
114             'chart.zoom.hdir':              'right',
115             'chart.zoom.vdir':              'down',
116             'chart.zoom.frames':            10,
117             'chart.zoom.delay':             50,
118             'chart.zoom.shadow':            true,
119             'chart.zoom.mode':              'canvas',
120             'chart.zoom.thumbnail.width':   75,
121             'chart.zoom.thumbnail.height':  75,
122             'chart.zoom.background':        true,
123             'chart.zoom.action':            'zoom',
124             'chart.resizable':              false,
125             'chart.scale.point':            '.',
126             'chart.scale.thousand':         ',',
127             'chart.scale.decimals':         null
128         }
129
130         // Check for support
131         if (!this.canvas) {
132             alert('[HBAR] No canvas support');
133             return;
134         }
135         
136         // Check the canvasText library has been included
137         if (typeof(RGraph) == 'undefined') {
138             alert('[HBAR] Fatal error: The common library does not appear to have been included');
139         }
140
141         for (i=0; i<this.data.length; ++i) {
142             if (typeof(this.data[i]) == 'object') {
143                 this.stackedOrGrouped = true;
144             }
145         }
146     }
147
148
149     /**
150     * A setter
151     * 
152     * @param name  string The name of the property to set
153     * @param value mixed  The value of the property
154     */
155     RGraph.HBar.prototype.Set = function (name, value)
156     {
157         if (name == 'chart.labels.abovebar') {
158             name = 'chart.labels.above';
159         }
160
161         this.properties[name.toLowerCase()] = value;
162     }
163
164
165     /**
166     * A getter
167     * 
168     * @param name  string The name of the property to get
169     */
170     RGraph.HBar.prototype.Get = function (name)
171     {
172         if (name == 'chart.labels.abovebar') {
173             name = 'chart.labels.above';
174         }
175
176         return this.properties[name];
177     }
178
179
180     /**
181     * The function you call to draw the bar chart
182     */
183     RGraph.HBar.prototype.Draw = function ()
184     {
185         /**
186         * Fire the onbeforedraw event
187         */
188         RGraph.FireCustomEvent(this, 'onbeforedraw');
189
190
191         /**
192         * Clear all of this canvases event handlers (the ones installed by RGraph)
193         */
194         RGraph.ClearEventListeners(this.id);
195
196
197         var gutter = this.Get('chart.gutter');
198
199         /**
200         * Stop the coords array from growing uncontrollably
201         */
202         this.coords = [];
203
204         /**
205         * Work out a few things. They need to be here because they depend on things you can change before you
206         * call Draw() but after you instantiate the object
207         */
208         this.graphwidth     = this.canvas.width - ( (4 * gutter));
209         this.graphheight    = this.canvas.height - (2 * gutter);
210         this.halfgrapharea  = this.grapharea / 2;
211         this.halfTextHeight = this.Get('chart.text.size') / 2;
212         this.lgutter = 3 * gutter;
213
214         // Progressively Draw the chart
215         this.DrawBackground();
216
217         this.Drawbars();
218         this.DrawAxes();
219         this.DrawLabels();
220
221
222         // Draw the key if necessary
223         if (this.Get('chart.key').length) {
224             RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
225         }
226
227         /**
228         * Install the event handlers for tooltips
229         */
230         if (this.Get('chart.tooltips')) {
231
232             // Need to register this object for redrawing
233             RGraph.Register(this);
234
235             /**
236             * Install the window onclick handler
237             */
238             window.onclick = function ()
239             {
240                 RGraph.Redraw();
241             }
242
243
244
245             /**
246             * If the cursor is over a hotspot, change the cursor to a hand
247             */
248             //this.canvas.onmousemove = function (e)
249             var canvas_onmousemove_func = function (e)
250             {
251                 e = RGraph.FixEventObject(e);
252
253                 var canvas = document.getElementById(this.id);
254                 var obj = canvas.__object__;
255
256                 /**
257                 * Get the mouse X/Y coordinates
258                 */
259                 var mouseCoords = RGraph.getMouseXY(e);
260
261                 /**
262                 * Loop through the bars determining if the mouse is over a bar
263                 */
264                 for (var i=0,len=obj.coords.length; i<len; i++) {
265
266                     var mouseX = mouseCoords[0];  // In relation to the canvas
267                     var mouseY = mouseCoords[1];  // In relation to the canvas
268                     var left   = obj.coords[i][0];
269                     var top    = obj.coords[i][1];
270                     var width  = obj.coords[i][2];
271                     var height = obj.coords[i][3];
272
273                     if (   mouseX >= (left)
274                         && mouseX <= (left + width)
275                         && mouseY >= top
276                         && mouseY <= (top + height)
277                         && (   typeof(obj.Get('chart.tooltips')) == 'function'
278                             || obj.Get('chart.tooltips')[i]
279                            ) ) {
280
281                         canvas.style.cursor = 'pointer';
282                         return;
283                     }
284
285                     canvas.style.cursor = 'default';
286                 }
287             }
288             this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
289             RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
290
291
292             /**
293             * Install the onclick event handler for the tooltips
294             */
295             //this.canvas.onclick = function (e)
296             var canvas_onclick_func = function (e)
297             {
298                 e = RGraph.FixEventObject(e);
299
300                 //var canvas = document.getElementById(this.id);
301                 var canvas = e.target;
302                 var obj = canvas.__object__;
303
304                 /**
305                 * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
306                 * This "deselects" any already selected bar
307                 */
308                 RGraph.Redraw();
309
310                 /**
311                 * Get the mouse X/Y coordinates
312                 */
313                 var mouseCoords = RGraph.getMouseXY(e);
314
315                 /**
316                 * Loop through the bars determining if the mouse is over a bar
317                 */
318                 for (var i=0,len=obj.coords.length; i<len; i++) {
319
320                     var mouseX = mouseCoords[0];  // In relation to the canvas
321                     var mouseY = mouseCoords[1];  // In relation to the canvas
322                     var left   = obj.coords[i][0];
323                     var top    = obj.coords[i][1];
324                     var width  = obj.coords[i][2];
325                     var height = obj.coords[i][3];
326                     var idx    = i;
327
328                     if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
329
330                         /**
331                         * Get the tooltip text
332                         */
333                         if (typeof(obj.Get('chart.tooltips')) == 'function') {
334                             var text = obj.Get('chart.tooltips')(idx);
335                         
336                         } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') {
337                             var text = obj.Get('chart.tooltips')[idx](idx);
338                         
339                         } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
340                             var text = obj.Get('chart.tooltips')[idx];
341
342                         } else {
343                             var text = null;
344                         }
345
346                         /**
347                         * Show a tooltip if it's defined
348                         */
349                         if (String(text).length && text != null) {
350
351                             obj.context.beginPath();
352                             obj.context.strokeStyle = 'black';
353                             obj.context.fillStyle   = 'rgba(255,255,255,0.5)';
354                             obj.context.strokeRect(left, top, width, height);
355                             obj.context.fillRect(left, top, width, height);
356         
357                             obj.context.stroke();
358                             obj.context.fill();
359
360                             RGraph.Tooltip(canvas, text, e.pageX, e.pageY, i);
361                         }
362                     }
363                 }
364
365                 /**
366                 * Stop the event bubbling
367                 */
368                 e.stopPropagation();
369             }
370             this.canvas.addEventListener('click', canvas_onclick_func, false);
371             RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
372
373             // This resets the bar graph
374             if (RGraph.Registry.Get('chart.tooltip')) {
375                 RGraph.Registry.Get('chart.tooltip').style.display = 'none';
376                 RGraph.Registry.Set('chart.tooltip', null)
377             }
378         }
379
380         /**
381         * Setup the context menu if required
382         */
383         if (this.Get('chart.contextmenu')) {
384             RGraph.ShowContext(this);
385         }
386
387
388         /**
389         * Draw "in graph" labels
390         */
391         RGraph.DrawInGraphLabels(this);
392         
393         /**
394         * If the canvas is annotatable, do install the event handlers
395         */
396         if (this.Get('chart.annotatable')) {
397             RGraph.Annotate(this);
398         }
399         
400         /**
401         * This bit shows the mini zoom window if requested
402         */
403         if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
404             RGraph.ShowZoomWindow(this);
405         }
406
407         
408         /**
409         * This function enables resizing
410         */
411         if (this.Get('chart.resizable')) {
412             RGraph.AllowResizing(this);
413         }
414         
415         /**
416         * Fire the RGraph ondraw event
417         */
418         RGraph.FireCustomEvent(this, 'ondraw');
419     }
420     
421     /**
422     * This draws the axes
423     */
424     RGraph.HBar.prototype.DrawAxes = function ()
425     {
426         var gutter  = this.Get('chart.gutter');
427         var halfway = ((this.canvas.width - (4 * gutter)) / 2) + (3 * gutter);
428
429         this.context.beginPath();
430         this.context.lineWidth   = 1;
431         this.context.strokeStyle = this.Get('chart.axis.color');
432
433         // Draw the Y axis
434         if (this.Get('chart.yaxispos') == 'center') {
435             this.context.moveTo(halfway, gutter);
436             this.context.lineTo(halfway, this.canvas.height - gutter);
437         } else {
438             this.context.moveTo(gutter * 3, gutter);
439             this.context.lineTo(gutter * 3, this.canvas.height - gutter);
440         }
441
442         // Draw the X axis
443         this.context.moveTo(gutter * 3, this.canvas.height - gutter);
444         this.context.lineTo(this.canvas.width - gutter, this.canvas.height - gutter);
445
446         // Draw the Y tickmarks
447         var yTickGap = (this.canvas.height - (2 * gutter)) / this.data.length;
448
449         for (y=gutter; y<(this.canvas.height - gutter); y+=yTickGap) {
450             if (this.Get('chart.yaxispos') == 'center') {
451                 this.context.moveTo(halfway + 3, y);
452                 this.context.lineTo(halfway  - 3, y);
453             } else {
454                 this.context.moveTo(gutter * 3, y);
455                 this.context.lineTo( (gutter * 3)  - 3, y);
456             }
457         }
458
459
460         // Draw the X tickmarks
461         xTickGap = (this.canvas.width - (4 * gutter) ) / 10;
462         yStart   = this.canvas.height - gutter;
463         yEnd     = (this.canvas.height - gutter) + 3;
464
465         for (x=(this.canvas.width - gutter), i=0; this.Get('chart.yaxispos') == 'center' ? x>=(3 * gutter) : x>(3*gutter); x-=xTickGap) {
466
467             if (this.Get('chart.yaxispos') != 'center' || i != 5) {
468                 this.context.moveTo(x, yStart);
469                 this.context.lineTo(x, yEnd);
470             }
471             i++;
472         }
473
474         this.context.stroke();
475     }
476
477
478     /**
479     * This function draws the background. The common function isn't used because the left gutter is
480     * three times as big.
481     * 
482     * @param  object obj The graph object
483     */
484     RGraph.HBar.prototype.DrawBackground = function ()
485     {
486         var gutter  = this.Get('chart.gutter');
487         var size    = this.Get('chart.text.size');
488         var font    = this.Get('chart.text.font');
489         var canvas  = this.canvas;
490         var context = this.context;
491
492         this.context.beginPath();
493
494         // Draw the horizontal bars
495         this.context.fillStyle = this.Get('chart.background.barcolor1');
496         for (var i=gutter; i < (canvas.height - gutter); i+=80) {
497             context.fillRect (gutter * 3, i, canvas.width - (gutter * 4), Math.min(40, canvas.height - gutter - i) );
498         }
499
500         this.context.fillStyle = this.Get('chart.background.barcolor2');
501         for (var i= (40 + gutter); i < (canvas.height - gutter); i+=80) {
502             context.fillRect (gutter * 3, i, canvas.width - (gutter * 4), i + 40 > (canvas.height - gutter) ? canvas.height - (gutter + i) : 40);
503         }
504         
505         this.context.stroke();
506
507         // Draw the background grid
508         if (this.Get('chart.background.grid')) {
509         
510             // If autofit is specified, use the .numhlines and .numvlines along with the width to work
511             // out the hsize and vsize
512             if (this.Get('chart.background.grid.autofit')) {
513                 var vsize = (canvas.width - (4 * gutter)) / this.Get('chart.background.grid.autofit.numvlines');
514                 var hsize = (canvas.height - (4 * gutter)) / this.Get('chart.background.grid.autofit.numhlines');
515                 
516                 this.Set('chart.background.grid.vsize', vsize);
517                 this.Set('chart.background.grid.hsize', hsize);
518             }
519
520             context.beginPath();
521             context.lineWidth   = this.Get('chart.background.grid.width');
522             context.strokeStyle = this.Get('chart.background.grid.color');
523
524             // Draw the horizontal lines
525             if (this.Get('chart.background.grid.hlines')) {
526                 for (y=gutter; y < (canvas.height - gutter); y+=this.Get('chart.background.grid.hsize')) {
527                     context.moveTo(gutter * 3, y);
528                     context.lineTo(canvas.width - gutter, y);
529                 }
530             }
531
532             // Draw the vertical lines
533             if (this.Get('chart.background.grid.vlines')) {
534                 for (x=gutter * 3; x <= (canvas.width - gutter); x+=this.Get('chart.background.grid.vsize')) {
535                     context.moveTo(x, gutter);
536                     context.lineTo(x, canvas.height - gutter);
537                 }
538             }
539
540             if (this.Get('chart.background.grid.border')) {
541                 // Make sure a rectangle, the same colour as the grid goes around the graph
542                 context.strokeStyle = this.Get('chart.background.grid.color');
543                 context.strokeRect(gutter * 3, gutter, canvas.width - (4 * gutter), canvas.height - (2 * gutter));
544             }
545         }
546         
547         context.stroke();
548
549
550         // Draw the title if one is set
551         if ( typeof(this.Get('chart.title')) == 'string') {
552             
553             RGraph.DrawTitle(canvas,
554                              this.Get('chart.title'),
555                              gutter,
556                              (3 * gutter) + ((canvas.width - (4 * gutter)) / 2),
557                              size + 2);
558         }
559
560         context.stroke();
561
562
563         // X axis title
564         if (typeof(this.Get('chart.title.xaxis')) == 'string' && this.Get('chart.title.xaxis').length) {        
565             context.beginPath();
566             RGraph.Text(context, font, size + 2, canvas.width / 2, canvas.height - (gutter * this.Get('chart.title.xaxis.pos')), this.Get('chart.title.xaxis'), 'center', 'center', false, false, false, true);
567             context.fill();
568         }
569
570
571         // Y axis title
572         if (typeof(this.Get('chart.title.yaxis')) == 'string' && this.Get('chart.title.yaxis').length) {
573             context.beginPath();
574             RGraph.Text(context, font, size + 2, gutter * this.Get('chart.title.yaxis.pos'), canvas.height / 2, this.Get('chart.title.yaxis'), 'center', 'center', false, 270, false, true);
575             context.fill();
576         }
577     }
578
579
580     /**
581     * This draws the labels for the graph
582     */
583     RGraph.HBar.prototype.DrawLabels = function ()
584     {
585         var gutter     = this.Get('chart.gutter');
586         var context    = this.context;
587         var canvas     = this.canvas;
588         var units_pre  = this.Get('chart.units.pre');
589         var units_post = this.Get('chart.units.post');
590         var text_size  = this.Get('chart.text.size');
591         var font       = this.Get('chart.text.font');
592
593
594         /**
595         * Set the units to blank if they're to be used for ingraph labels only
596         */
597         if (this.Get('chart.units.ingraph')) {
598             units_pre  = '';
599             units_post = '';
600         }
601
602
603         /**
604         * Draw the X axis labels
605         */
606         this.context.beginPath();
607         this.context.fillStyle = this.Get('chart.text.color');
608
609         //var interval = (this.canvas.width - (4 * gutter)) / (t ? 10 : 5);
610
611         if (this.Get('chart.yaxispos') == 'center') {
612             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (10/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', 'center');
613             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (9/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', 'center');
614             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (8/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', 'center');
615             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (7/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', 'center');
616             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (6/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', 'center');
617             
618             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (4/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, -1 * this.scale[0], units_pre, units_post), 'center', 'center');
619             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (3/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, -1 * this.scale[1], units_pre, units_post), 'center', 'center');
620             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (2/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, -1 * this.scale[2], units_pre, units_post), 'center', 'center');
621             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (1/10)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, -1 * this.scale[3], units_pre, units_post), 'center', 'center');
622             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (0)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, -1 * this.scale[4], units_pre, units_post), 'center', 'center');
623
624         } else {
625
626             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (5/5)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', 'center');
627             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (4/5)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', 'center');
628             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (3/5)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', 'center');
629             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (2/5)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', 'center');
630             RGraph.Text(context, font, text_size, (gutter * 3) + (this.graphwidth * (1/5)), gutter + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', 'center');
631         }
632         
633         this.context.fill();
634         this.context.stroke();
635
636         /**
637         * The Y axis labels
638         */
639         if (typeof(this.Get('chart.labels')) == 'object') {
640         
641             var xOffset = 5;
642             var font    = this.Get('chart.text.font');
643
644             // Draw the X axis labels
645             this.context.fillStyle = this.Get('chart.text.color');
646             
647             // How wide is each bar
648             var barHeight = (this.canvas.height - (2 * gutter) ) / this.Get('chart.labels').length;
649             
650             // Reset the xTickGap
651             yTickGap = (this.canvas.height - (2 * gutter)) / this.Get('chart.labels').length
652
653             // Draw the X tickmarks
654             var i=0;
655             for (y=gutter + (yTickGap / 2); y<=this.canvas.height - gutter; y+=yTickGap) {
656                 RGraph.Text(this.context, font,
657                                       this.Get('chart.text.size'),
658                                       (gutter * 3) - xOffset,
659                                       y,
660                                       String(this.Get('chart.labels')[i++]),
661                                       'center',
662                                       'right');
663             }
664         }
665     }
666     
667     
668     /**
669     * This function draws the actual bars
670     */
671     RGraph.HBar.prototype.Drawbars = function ()
672     {
673         this.context.lineWidth   = 1;
674         this.context.strokeStyle = this.Get('chart.strokestyle');
675         this.context.fillStyle   = this.Get('chart.colors')[0];
676         var prevX                = 0;
677         var prevY                = 0;
678
679         /**
680         * Work out the max value
681         */
682         if (this.Get('chart.xmax')) {
683             this.scale = [
684                           (this.Get('chart.xmax') * 0.2).toFixed(this.Get('chart.scale.decimals')),
685                           (this.Get('chart.xmax') * 0.4).toFixed(this.Get('chart.scale.decimals')),
686                           (this.Get('chart.xmax') * 0.6).toFixed(this.Get('chart.scale.decimals')),
687                           (this.Get('chart.xmax') * 0.8).toFixed(this.Get('chart.scale.decimals')),
688                           (this.Get('chart.xmax')).toFixed(this.Get('chart.scale.decimals'))
689                          ];
690             this.max = this.scale[4];
691         } else {
692             var grouping = this.Get('chart.grouping');
693
694             for (i=0; i<this.data.length; ++i) {
695                 if (typeof(this.data[i]) == 'object') {
696                     var value = grouping == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;
697                 } else {
698                     var value = Number(this.data[i]);
699                 }
700
701                 this.max = Math.max(Math.abs(this.max), Math.abs(value));
702             }
703
704             this.scale = RGraph.getScale(this.max, this);
705             this.max   = this.scale[4];
706         }
707
708
709         /**
710         * The bars are drawn HERE
711         */
712         var gutter     = this.Get('chart.gutter');
713         var graphwidth = (this.canvas.width - (4 * gutter));
714         var halfwidth  = graphwidth / 2;
715
716         for (i=0; i<this.data.length; ++i) {
717
718             // Work out the width and height
719             var width  = (this.data[i] / this.max) *  graphwidth;
720             var height = this.graphheight / this.data.length;
721
722             var orig_height = height;
723
724             var x       = 3 * gutter;
725             var y       = gutter + (i * height);
726             var vmargin = this.Get('chart.vmargin');
727             var gutter  = gutter;
728
729             // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
730             if (width < 0) {
731                 x -= width;
732                 width = Math.abs(width);
733             }
734
735             /**
736             * Turn on the shadow if need be
737             */
738             if (this.Get('chart.shadow')) {
739                 this.context.shadowColor   = this.Get('chart.shadow.color');
740                 this.context.shadowBlur    = this.Get('chart.shadow.blur');
741                 this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
742                 this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
743             }
744
745             /**
746             * Draw the bar
747             */
748             this.context.beginPath();
749                 if (typeof(this.data[i]) == 'number') {
750                     
751                     var barHeight = height - (2 * vmargin);
752                     var barWidth  = (this.data[i] / this.max) * this.graphwidth;
753                     var barX      = 3 * gutter;
754
755                     // Account for Y axis pos
756                     if (this.Get('chart.yaxispos') == 'center') {
757                         barWidth /= 2;
758                         barX += halfwidth;
759                     }
760
761                     // Set the fill color
762                     this.context.strokeStyle = this.Get('chart.strokestyle');
763                     this.context.fillStyle = this.Get('chart.colors')[0];
764
765                     this.context.strokeRect(barX, gutter + (i * height) + this.Get('chart.vmargin'), barWidth, barHeight);
766                     this.context.fillRect(barX, gutter + (i * height) + this.Get('chart.vmargin'), barWidth, barHeight);
767
768                     this.coords.push([x, y + vmargin, width, height - (2 * vmargin), this.Get('chart.colors')[0], this.data[i]]);
769
770                 /**
771                 * Stacked bar chart
772                 */
773                 } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') {
774
775                     var barHeight = height - (2 * vmargin);
776
777                     for (j=0; j<this.data[i].length; ++j) {
778
779                         // Set the fill/stroke colors
780                         this.context.strokeStyle = this.Get('chart.strokestyle');
781                         this.context.fillStyle = this.Get('chart.colors')[j];
782
783                         var width = (this.data[i][j] / this.max) * this.graphwidth;
784                         var totalWidth = (RGraph.array_sum(this.data[i]) / this.max) * this.graphwidth;
785
786                         this.context.strokeRect(x, gutter + this.Get('chart.vmargin') + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
787                         this.context.fillRect(x, gutter + this.Get('chart.vmargin') + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
788
789                         /**
790                         * Store the coords for tooltips
791                         */
792
793                         this.coords.push([x, y + vmargin, width, height - (2 * vmargin), this.Get('chart.colors')[j], RGraph.array_sum(this.data[i])]);
794
795                         x += width;
796                     }
797
798                 /**
799                 * A grouped bar chart
800                 */
801                 } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') {
802
803                     for (j=0; j<this.data[i].length; ++j) {
804
805                         /**
806                         * Turn on the shadow if need be
807                         */
808                         if (this.Get('chart.shadow')) {
809                             RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
810                         }
811
812                         // Set the fill/stroke colors
813                         this.context.strokeStyle = this.Get('chart.strokestyle');
814                         this.context.fillStyle = this.Get('chart.colors')[j];
815
816                         var width = (this.data[i][j] / this.max) * (this.canvas.width - (4 * gutter) );
817                         var individualBarHeight = (height - (2 * vmargin)) / this.data[i].length;
818
819                         var startX = gutter * 3;
820                         var startY = y + vmargin + (j * individualBarHeight);
821
822                         // Account for the Y axis being in the middle
823                         if (this.Get('chart.yaxispos') == 'center') {
824                             width  /= 2;
825                             startX += halfwidth;
826                         }
827                         
828                         if (width < 0) {
829                             startX += width;
830                             width *= -1;
831                         }
832
833                         this.context.strokeRect(startX, startY, width, individualBarHeight);
834                         this.context.fillRect(startX, startY, width, individualBarHeight);
835
836                         this.coords.push([startX, startY, width, individualBarHeight, this.Get('chart.colors')[j], this.data[i][j]]);
837                     }
838                 }
839
840             this.context.closePath();
841         }
842
843         this.context.fill();
844         this.context.stroke();
845
846
847
848         /**
849         * Now the bars are stroke()ed, turn off the shadow
850         */
851         RGraph.NoShadow(this);
852         
853         this.RedrawBars();
854     }
855     
856     
857     /**
858     * This function goes over the bars after they been drawn, so that upwards shadows are underneath the bars
859     */
860     RGraph.HBar.prototype.RedrawBars = function ()
861     {
862         var coords = this.coords;
863
864         RGraph.NoShadow(this);
865         this.context.strokeStyle = this.Get('chart.strokestyle');
866
867         for (var i=0; i<coords.length; ++i) {
868
869             this.context.beginPath();
870                 this.context.strokeStyle = this.Get('chart.strokestyle');
871                 this.context.fillStyle = coords[i][4];
872                 this.context.lineWidth = 1;
873                 this.context.strokeRect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
874                 this.context.fillRect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
875             this.context.fill();
876             this.context.stroke();
877
878             /**
879             * Draw labels "above" the bar
880             */
881             if (this.Get('chart.labels.above')) {
882
883                 this.context.fillStyle = this.Get('chart.text.color');
884                 this.context.strokeStyle = 'black';
885                 RGraph.NoShadow(this);
886
887                 var border = (coords[i][0] + coords[i][2] + 7 + this.context.measureText(this.Get('chart.units.pre') + this.coords[i][5] + this.Get('chart.units.post')).width) > this.canvas.width ? true : false;
888
889                 RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size'),coords[i][0] + coords[i][2] + (border ? -5 : 5),coords[i][1] + (coords[i][3] / 2),RGraph.number_format(this, (this.coords[i][5]).toFixed(this.Get('chart.labels.above.decimals')), this.Get('chart.units.pre'), this.Get('chart.units.post')),'center',border ? 'right' : 'left',border,null,border ? 'rgba(255,255,255,0.9)' : null);
890
891             }
892         }
893     }