initial commit
[home-automation.git] / libraries / RGraph.bar.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 bar chart constructor
19     * 
20     * @param object canvas The canvas object
21     * @param array  data   The chart data
22     */
23     RGraph.Bar = function (id, data)
24     {
25         // Get the canvas and context objects
26         this.id                = id;
27         this.canvas            = document.getElementById(id);
28         this.context           = this.canvas.getContext ? this.canvas.getContext("2d") : null;
29         this.canvas.__object__ = this;
30         this.type              = 'bar';
31         this.max               = 0;
32         this.stackedOrGrouped  = false;
33         this.isRGraph          = true;
34
35         /**
36         * Compatibility with older browsers
37         */
38         RGraph.OldBrowserCompat(this.context);
39
40
41         // Various config type stuff
42         this.properties = {
43             'chart.background.barcolor1':   'rgba(0,0,0,0)',
44             'chart.background.barcolor2':   'rgba(0,0,0,0)',
45             'chart.background.grid':        true,
46             'chart.background.grid.color':  '#ddd',
47             'chart.background.grid.width':  1,
48             'chart.background.grid.hsize':  20,
49             'chart.background.grid.vsize':  20,
50             'chart.background.grid.vlines': true,
51             'chart.background.grid.hlines': true,
52             'chart.background.grid.border': true,
53             'chart.background.grid.autofit':false,
54             'chart.background.grid.autofit.numhlines': 7,
55             'chart.background.grid.autofit.numvlines': 20,
56             'chart.ytickgap':               20,
57             'chart.smallyticks':            3,
58             'chart.largeyticks':            5,
59             'chart.numyticks':              10,
60             'chart.hmargin':                5,
61             'chart.strokecolor':            '#666',
62             'chart.axis.color':             'black',
63             'chart.gutter':                 25,
64             'chart.labels':                 null,
65             'chart.labels.ingraph':         null,
66             'chart.labels.above':           false,
67             'chart.labels.above.decimals':  0,
68             'chart.labels.above.size':      null,
69             'chart.ylabels':                true,
70             'chart.ylabels.count':          5,
71             'chart.ylabels.inside':         false,
72             'chart.xlabels.offset':         0,
73             'chart.xaxispos':               'bottom',
74             'chart.yaxispos':               'left',
75             'chart.text.color':             'black',
76             'chart.text.size':              10,
77             'chart.text.angle':             0,
78             'chart.text.font':              'Verdana',
79             'chart.ymax':                   null,
80             'chart.title':                  '',
81             'chart.title.background':       null,
82             'chart.title.hpos':             null,
83             'chart.title.vpos':             null,
84             'chart.title.xaxis':            '',
85             'chart.title.yaxis':            '',
86             'chart.title.xaxis.pos':        0.25,
87             'chart.title.yaxis.pos':        0.25,
88             'chart.colors':                 ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'],
89             'chart.grouping':               'grouped',
90             'chart.variant':                'bar',
91             'chart.shadow':                 false,
92             'chart.shadow.color':           '#666',
93             'chart.shadow.offsetx':         3,
94             'chart.shadow.offsety':         3,
95             'chart.shadow.blur':            3,
96             'chart.tooltips':               null,
97             'chart.tooltips.effect':        'fade',
98             'chart.tooltips.css.class':     'RGraph_tooltip',
99             'chart.tooltips.event':         'onclick',
100             'chart.tooltips.coords.adjust': [0,0],
101             'chart.tooltips.highlight':     true,
102             'chart.background.hbars':       null,
103
104             'chart.key':                    [],
105             'chart.key.background':         'white',
106             'chart.key.position':           'graph',
107             'chart.key.shadow':             false,
108             'chart.key.shadow.color':       '#666',
109             'chart.key.shadow.blur':        3,
110             'chart.key.shadow.offsetx':     2,
111             'chart.key.shadow.offsety':     2,
112             'chart.key.position.gutter.boxed': true,
113             'chart.key.position.x':         null,
114             'chart.key.position.y':         null,
115             'chart.key.color.shape':        'square',
116             'chart.key.rounded':            true,
117             'chart.key.text.size':          10,
118
119             'chart.contextmenu':            null,
120             'chart.line':                   null,
121             'chart.units.pre':              '',
122             'chart.units.post':             '',
123             'chart.scale.decimals':         0,
124             'chart.scale.point':            '.',
125             'chart.scale.thousand':         ',',
126             'chart.crosshairs':             false,
127             'chart.crosshairs.color':       '#333',
128             'chart.linewidth':              1,
129             'chart.annotatable':            false,
130             'chart.annotate.color':         'black',
131             'chart.zoom.factor':            1.5,
132             'chart.zoom.fade.in':           true,
133             'chart.zoom.fade.out':          true,
134             'chart.zoom.hdir':              'right',
135             'chart.zoom.vdir':              'down',
136             'chart.zoom.frames':            10,
137             'chart.zoom.delay':             50,
138             'chart.zoom.shadow':            true,
139             'chart.zoom.mode':              'canvas',
140             'chart.zoom.thumbnail.width':   75,
141             'chart.zoom.thumbnail.height':  75,
142             'chart.zoom.background':        true,
143             'chart.resizable':              false,
144             'chart.adjustable':             false
145         }
146
147         // Check for support
148         if (!this.canvas) {
149             alert('[BAR] No canvas support');
150             return;
151         }
152         
153         // Check the common library has been included
154         if (typeof(RGraph) == 'undefined') {
155             alert('[BAR] Fatal error: The common library does not appear to have been included');
156         }
157
158         /**
159         * Determine whether the chart will contain stacked or grouped bars
160         */
161         for (i=0; i<data.length; ++i) {
162             if (typeof(data[i]) == 'object') {
163                 this.stackedOrGrouped = true;
164             }
165         }
166
167         // Store the data
168         this.data = data;
169         
170         // Used to store the coords of the bars
171         this.coords = [];
172     }
173
174
175     /**
176     * A setter
177     * 
178     * @param name  string The name of the property to set
179     * @param value mixed  The value of the property
180     */
181     RGraph.Bar.prototype.Set = function (name, value)
182     {
183         name = name.toLowerCase();
184
185         if (name == 'chart.labels.abovebar') {
186             name = 'chart.labels.above';
187         }
188         
189         if (name == 'chart.strokestyle') {
190             name = 'chart.strokecolor';
191         }
192
193         this.properties[name] = value;
194     }
195
196
197     /**
198     * A getter
199     * 
200     * @param name  string The name of the property to get
201     */
202     RGraph.Bar.prototype.Get = function (name)
203     {
204         if (name == 'chart.labels.abovebar') {
205             name = 'chart.labels.above';
206         }
207
208         return this.properties[name];
209     }
210
211
212     /**
213     * The function you call to draw the bar chart
214     */
215     RGraph.Bar.prototype.Draw = function ()
216     {
217         /**
218         * Fire the onbeforedraw event
219         */
220         RGraph.FireCustomEvent(this, 'onbeforedraw');
221
222         /**
223         * Clear all of this canvases event handlers (the ones installed by RGraph)
224         */
225         RGraph.ClearEventListeners(this.id);
226         
227         /**
228         * Convert any null values to 0. Won't make any difference to the bar (as opposed to the line chart)
229         */
230         for (var i=0; i<this.data.length; ++i) {
231             if (this.data[i] == null) {
232                 this.data[i] = 0;
233             }
234         }
235
236
237         // Cache this in a class variable as it's used rather a lot
238         this.gutter = this.Get('chart.gutter');
239
240         /**
241         * Check for tooltips and alert the user that they're not supported with pyramid charts
242         */
243         if (   (this.Get('chart.variant') == 'pyramid' || this.Get('chart.variant') == 'dot')
244             && typeof(this.Get('chart.tooltips')) == 'object'
245             && this.Get('chart.tooltips')
246             && this.Get('chart.tooltips').length > 0) {
247
248             alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts');
249         }
250
251         /**
252         * Stop the coords array from growing uncontrollably
253         */
254         this.coords = [];
255
256         /**
257         * Work out a few things. They need to be here because they depend on things you can change before you
258         * call Draw() but after you instantiate the object
259         */
260         this.max            = 0;
261         this.grapharea      = this.canvas.height - ( (2 * this.gutter));
262         this.halfgrapharea  = this.grapharea / 2;
263         this.halfTextHeight = this.Get('chart.text.size') / 2;
264
265         // Progressively Draw the chart
266         RGraph.background.Draw(this);
267
268
269         //If it's a sketch chart variant, draw the axes first
270         if (this.Get('chart.variant') == 'sketch') {
271             this.DrawAxes();
272             this.Drawbars();
273         } else {
274             this.Drawbars();
275             this.DrawAxes();
276         }
277
278         this.DrawLabels();
279
280
281         // Draw the key if necessary
282         if (this.Get('chart.key').length) {
283             RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
284         }
285         
286         
287         /**
288         * Setup the context menu if required
289         */
290         if (this.Get('chart.contextmenu')) {
291             RGraph.ShowContext(this);
292         }
293
294
295         /**
296         * Is a line is defined, draw it
297         */
298         var line = this.Get('chart.line');
299
300         if (line) {
301             
302             // Check the length of the data(s)
303             if (line.original_data[0].length != this.data.length) {
304                 alert("[BAR] You're adding a line with a differing amount of data points to the bar chart - this is not permitted");
305             }
306             
307             // Check the X axis positions
308             if (this.Get('chart.xaxispos') != line.Get('chart.xaxispos')) {
309                 alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised");
310             }
311
312             line.Set('chart.gutter', this.Get('chart.gutter'));
313             line.Set('chart.noaxes', true);
314             line.Set('chart.background.barcolor1', 'rgba(0,0,0,0)');
315             line.Set('chart.background.barcolor2', 'rgba(0,0,0,0)');
316             line.Set('chart.background.grid', false);
317             line.Set('chart.ylabels', false);
318             line.Set('chart.hmargin', (this.canvas.width - (2 * this.gutter)) / (line.original_data[0].length * 2));
319             
320             // If a custom yMax is set, use that
321             if (this.Get('chart.ymax')) {
322                 line.Set('chart.ymax', this.Get('chart.ymax'));
323             }
324
325             line.Draw();
326         }
327
328
329         /**
330         * Draw "in graph" labels
331         */
332         if (this.Get('chart.labels.ingraph')) {
333             RGraph.DrawInGraphLabels(this);
334         }
335         
336         /**
337         * Draw crosschairs
338         */
339         if (this.Get('chart.crosshairs')) {
340             RGraph.DrawCrosshairs(this);
341         }
342         
343         /**
344         * If the canvas is annotatable, do install the event handlers
345         */
346         if (this.Get('chart.annotatable')) {
347             RGraph.Annotate(this);
348         }
349         
350         /**
351         * This bit shows the mini zoom window if requested
352         */
353         if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
354             RGraph.ShowZoomWindow(this);
355         }
356
357         
358         /**
359         * This function enables resizing
360         */
361         if (this.Get('chart.resizable')) {
362             RGraph.AllowResizing(this);
363         }
364
365
366         /**
367         * This function enables adjusting
368         */
369         if (this.Get('chart.adjustable')) {
370             RGraph.AllowAdjusting(this);
371         }
372         
373         /**
374         * Fire the RGraph ondraw event
375         */
376         RGraph.FireCustomEvent(this, 'ondraw');
377     }
378
379     
380     /**
381     * Draws the charts axes
382     */
383     RGraph.Bar.prototype.DrawAxes = function ()
384     {
385         var gutter   = this.gutter;
386         var xaxispos = this.Get('chart.xaxispos');
387         var yaxispos = this.Get('chart.yaxispos');
388
389         this.context.beginPath();
390         this.context.strokeStyle = this.Get('chart.axis.color');
391         this.context.lineWidth   = 1;
392
393         // Draw the Y axis
394         if (yaxispos == 'right') {
395             this.context.moveTo(this.canvas.width - gutter, gutter);
396             this.context.lineTo(this.canvas.width - gutter, this.canvas.height - gutter);
397         } else {
398             this.context.moveTo(gutter, gutter);
399             this.context.lineTo(gutter, this.canvas.height - gutter);
400         }
401         
402         // Draw the X axis
403         this.context.moveTo(gutter, (xaxispos == 'center' ? this.canvas.height / 2 : this.canvas.height - gutter));
404         this.context.lineTo(this.canvas.width - gutter, xaxispos == 'center' ? this.canvas.height / 2 : this.canvas.height - gutter);
405
406         var numYTicks = this.Get('chart.numyticks');
407
408         // Draw the Y tickmarks
409         var yTickGap = (this.canvas.height - (2 * gutter)) / numYTicks;
410         var xpos     = yaxispos == 'left' ? gutter : this.canvas.width - gutter;
411
412         for (y=gutter;
413              xaxispos == 'center' ? y <= (this.canvas.height - gutter) : y < (this.canvas.height - gutter);
414              y += yTickGap) {
415
416             if (xaxispos == 'center' && y == (this.canvas.height / 2)) continue;
417             
418             this.context.moveTo(xpos, y);
419             this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), y);
420         }
421
422         // Draw the X tickmarks
423         xTickGap = (this.canvas.width - (2 * gutter) ) / this.data.length;
424         yStart   = this.canvas.height - gutter;
425         yEnd     = (this.canvas.height - gutter) + 3;
426         
427         //////////////// X TICKS ////////////////
428
429         // Now move the Y start end positions down if the axis is set to center
430         if (xaxispos == 'center') {
431             yStart = (this.canvas.height / 2) + 3;
432             yEnd   = (this.canvas.height / 2) - 3;
433         }
434
435         for (x=gutter + (yaxispos == 'left' ? xTickGap : 0); x<this.canvas.width - gutter + (yaxispos == 'left' ? 5 : 0); x+=xTickGap) {
436             this.context.moveTo(x, yStart);
437             this.context.lineTo(x, yEnd);
438         }
439
440         //////////////// X TICKS ////////////////
441
442         this.context.stroke();
443     }
444
445
446     /**
447     * Draws the bars
448     */
449     RGraph.Bar.prototype.Drawbars = function ()
450     {
451         this.context.lineWidth   = this.Get('chart.linewidth');
452         this.context.strokeStyle = this.Get('chart.strokecolor');
453         this.context.fillStyle   = this.Get('chart.colors')[0];
454         var prevX                = 0;
455         var prevY                = 0;
456         var gutter               = this.gutter;
457         var decimals             = this.Get('chart.scale.decimals');
458
459         /**
460         * Work out the max value
461         */
462         if (this.Get('chart.ymax')) {
463             this.max = this.Get('chart.ymax');
464
465             this.scale = [
466                           (this.max * (1/5)).toFixed(decimals),
467                           (this.max * (2/5)).toFixed(decimals),
468                           (this.max * (3/5)).toFixed(decimals),
469                           (this.max * (4/5)).toFixed(decimals),
470                           this.max.toFixed(decimals)
471                          ];
472         } else {
473             for (i=0; i<this.data.length; ++i) {
474                 if (typeof(this.data[i]) == 'object') {
475                     var value = this.Get('chart.grouping') == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;
476
477                 } else {
478                     var value = Number(this.data[i]);
479                 }
480
481                 this.max = Math.max(Math.abs(this.max), Math.abs(value));
482             }
483
484             this.scale = RGraph.getScale(this.max, this);
485             this.max   = this.scale[4];
486
487             if (this.Get('chart.scale.decimals')) {
488                 var decimals = this.Get('chart.scale.decimals');
489
490                 this.scale[0] = Number(this.scale[0]).toFixed(decimals);
491                 this.scale[1] = Number(this.scale[1]).toFixed(decimals);
492                 this.scale[2] = Number(this.scale[2]).toFixed(decimals);
493                 this.scale[3] = Number(this.scale[3]).toFixed(decimals);
494                 this.scale[4] = Number(this.scale[4]).toFixed(decimals);
495             }
496         }
497
498         /**
499         * Draw horizontal bars here
500         */
501         if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0) {
502             RGraph.DrawBars(this);
503         }
504
505         var variant = this.Get('chart.variant');
506         
507         /**
508         * Draw the 3D axes is necessary
509         */
510         if (variant == '3d') {
511             RGraph.Draw3DAxes(this);
512         }
513
514         /**
515         * Get the variant once, and draw the bars, be they regular, stacked or grouped
516         */
517         
518         // Get these variables outside of the loop
519         var xaxispos      = this.Get('chart.xaxispos');
520         var width         = (this.canvas.width - (2 * gutter) ) / this.data.length;
521         var orig_height   = height;
522         var hmargin       = this.Get('chart.hmargin');
523         var shadow        = this.Get('chart.shadow');
524         var shadowColor   = this.Get('chart.shadow.color');
525         var shadowBlur    = this.Get('chart.shadow.blur');
526         var shadowOffsetX = this.Get('chart.shadow.offsetx');
527         var shadowOffsetY = this.Get('chart.shadow.offsety');
528         var strokeStyle   = this.Get('chart.strokecolor');
529         var colors        = this.Get('chart.colors');
530
531         for (i=0; i<this.data.length; ++i) {
532
533             // Work out the height
534             //The width is up outside the loop
535             var height      = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - (2 * gutter) );
536
537             // Half the height if the Y axis is at the center
538             if (xaxispos == 'center') {
539                 height /= 2;
540             }
541
542             var x = (i * width) + gutter;
543             var y = xaxispos == 'center' ? (this.canvas.height / 2) - height : this.canvas.height - height - gutter;
544             
545
546             // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
547             if (height < 0) {
548                 y += height;
549                 height = Math.abs(height);
550             }
551
552             /**
553             * Turn on the shadow if need be
554             */
555             if (shadow) {
556                 this.context.shadowColor   = shadowColor;
557                 this.context.shadowBlur    = shadowBlur;
558                 this.context.shadowOffsetX = shadowOffsetX;
559                 this.context.shadowOffsetY = shadowOffsetY;
560             }
561
562             /**
563             * Draw the bar
564             */
565             this.context.beginPath();
566                 if (typeof(this.data[i]) == 'number') {
567                     
568                     var barWidth = width - (2 * hmargin);
569                     
570                     // Set the fill color
571                     this.context.strokeStyle = strokeStyle;
572                     this.context.fillStyle = colors[0];
573
574                     if (variant == 'sketch') {
575
576                         this.context.lineCap = 'round';
577                         
578                         var sketchOffset = 3;
579
580                         this.context.beginPath();
581
582                         this.context.strokeStyle = colors[0];
583
584                         // Left side
585                         this.context.moveTo(x + hmargin + 2, y + height - 2);
586                         this.context.lineTo(x + hmargin , y - 2);
587
588                         // The top
589                         this.context.moveTo(x + hmargin - 3, y + -2 + (this.data[i] < 0 ? height : 0));
590                         this.context.bezierCurveTo(x + ((hmargin + width) * 0.33),y + 5 + (this.data[i] < 0 ? height - 10: 0),x + ((hmargin + width) * 0.66),y + 5 + (this.data[i] < 0 ? height - 10 : 0),x + hmargin + width + -1, y + 0 + (this.data[i] < 0 ? height : 0));
591
592
593                         // The right side
594                         this.context.moveTo(x + hmargin + width - 2, y + -2);
595                         this.context.lineTo(x + hmargin + width - 3, y + height - 3);
596
597                         for (var r=0.2; r<=0.8; r+=0.2) {
598                             this.context.moveTo(x + hmargin + width + (r > 0.4 ? -1 : 3) - (r * width),y - 1);
599                             this.context.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2));
600                         }
601
602                         this.context.stroke();
603
604                     // Regular bar
605                     } else if (variant == 'bar' || variant == '3d' || variant == 'glass') {
606                     
607                         if (document.all && shadow) {
608                             this.DrawIEShadow([x + hmargin, y, barWidth, height]);
609                         }
610                         
611                         if (variant == 'glass') {
612                             RGraph.filledCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
613                             RGraph.strokedCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
614                         } else {
615                             this.context.strokeRect(x + hmargin, y, barWidth, height);
616                             this.context.fillRect(x + hmargin, y, barWidth, height);
617                         }
618
619                         
620                         // This bit draws the text labels that appear above the bars if requested
621                         if (this.Get('chart.labels.above')) {
622
623                             // Turn off any shadow
624                             if (shadow) {
625                                 RGraph.NoShadow(this);
626                             }
627
628                             var yPos = y - 3;
629
630                             // Account for negative bars
631                             if (this.data[i] < 0) {
632                                 yPos += height + 6 + (this.Get('chart.text.size') - 4);
633                             }
634
635                             this.context.fillStyle = this.Get('chart.text.color');
636                             RGraph.Text(this.context, this.Get('chart.text.font'), typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3, x + hmargin + (barWidth / 2), yPos, RGraph.number_format(this, Number(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')),this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
637                         }
638
639                         // 3D effect
640                         if (variant == '3d') {
641
642                             var prevStrokeStyle = this.context.strokeStyle;
643                             var prevFillStyle   = this.context.fillStyle;
644
645                             // Draw the top
646                             this.context.beginPath();
647                                 this.context.moveTo(x + hmargin, y);
648                                 this.context.lineTo(x + hmargin + 10, y - 5);
649                                 this.context.lineTo(x + hmargin + 10 + barWidth, y - 5);
650                                 this.context.lineTo(x + hmargin + barWidth, y);
651                             this.context.closePath();
652
653                             this.context.stroke();
654                             this.context.fill();
655
656                             // Draw the right hand side
657                             this.context.beginPath();
658                                 this.context.moveTo(x + hmargin + barWidth, y);
659                                 this.context.lineTo(x + hmargin + barWidth + 10, y - 5);
660                                 this.context.lineTo(x + hmargin + barWidth + 10, y + height - 5);
661                                 this.context.lineTo(x + hmargin + barWidth, y + height);
662                             this.context.closePath();
663     
664                             this.context.stroke();                        
665                             this.context.fill();
666
667                             // Draw the darker top section
668                             this.context.beginPath();
669                                 this.context.fillStyle = 'rgba(255,255,255,0.3)';
670                                 this.context.moveTo(x + hmargin, y);
671                                 this.context.lineTo(x + hmargin + 10, y - 5);
672                                 this.context.lineTo(x + hmargin + 10 + barWidth, y - 5);
673                                 this.context.lineTo(x + hmargin + barWidth, y);
674                                 this.context.lineTo(x + hmargin, y);
675                             this.context.closePath();
676     
677                             this.context.stroke();
678                             this.context.fill();
679
680                             // Draw the darker right side section
681                             this.context.beginPath();
682                                 this.context.fillStyle = 'rgba(0,0,0,0.4)';
683                                 this.context.moveTo(x + hmargin + barWidth, y);
684                                 this.context.lineTo(x + hmargin + barWidth + 10, y - 5);
685                                 this.context.lineTo(x + hmargin + barWidth + 10, y - 5 + height);
686                                 this.context.lineTo(x + hmargin + barWidth, y + height);
687                                 this.context.lineTo(x + hmargin + barWidth, y);
688                             this.context.closePath();
689
690                             this.context.stroke();
691                             this.context.fill();
692
693                             this.context.strokeStyle = prevStrokeStyle;
694                             this.context.fillStyle   = prevFillStyle;
695                         
696                         // Glass variant
697                         } else if (variant == 'glass') {
698  
699                             var grad = this.context.createLinearGradient(
700                                                                          x + hmargin,
701                                                                          y,
702                                                                          x + hmargin + (barWidth / 2),
703                                                                          y
704                                                                         );
705                             grad.addColorStop(0, 'rgba(255,255,255,0.9)');
706                             grad.addColorStop(1, 'rgba(255,255,255,0.5)');
707
708                             this.context.beginPath();
709                             this.context.fillStyle = grad;
710                             this.context.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2);
711                             this.context.fill();
712                         }
713                     
714                     // Dot chart
715                     } else if (variant == 'dot') {
716
717                         this.context.beginPath();
718                         this.context.moveTo(x + (width / 2), y);
719                         this.context.lineTo(x + (width / 2), y + height);
720                         this.context.stroke();
721                         
722                         this.context.beginPath();
723                         this.context.fillStyle = this.Get('chart.colors')[i];
724                         this.context.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0);
725                         
726                         // Set the colour for the dots
727                         this.context.fillStyle = this.Get('chart.colors')[0];
728
729                         this.context.stroke();
730                         this.context.fill();
731                     
732                     // Pyramid chart
733                     } else if (variant == 'pyramid') {
734
735                         this.context.beginPath();
736                             var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.Get('chart.gutter')));
737                         
738                             this.context.moveTo(x + hmargin, startY);
739                             this.context.lineTo(
740                                                 x + hmargin + (barWidth / 2),
741                                                 y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)
742                                                );
743                             this.context.lineTo(x + hmargin + barWidth, startY);
744                         
745                         this.context.closePath();
746                         
747                         this.context.stroke();
748                         this.context.fill();
749                     
750                     // Arrow chart
751                     } else if (variant == 'arrow') {
752                         var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.gutter));
753
754                         this.context.lineWidth = this.Get('chart.linewidth') ? this.Get('chart.linewidth') : 1;
755                         this.context.lineCap = 'round';
756
757                         this.context.beginPath();
758
759                             this.context.moveTo(x + hmargin + (barWidth / 2), startY);
760                             this.context.lineTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0));
761                             this.context.arc(x + hmargin + (barWidth / 2),
762                                              y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0),
763                                              5,
764                                              this.data[i] > 0 ? 0.78 : 5.6,
765                                              this.data[i] > 0 ? 0.79 : 5.48,
766                                              this.data[i] < 0);
767
768                             this.context.moveTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0));
769                             this.context.arc(x + hmargin + (barWidth / 2),
770                                              y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0),
771                                              5,
772                                              this.data[i] > 0 ? 2.355 : 4,
773                                              this.data[i] > 0 ? 2.4 : 3.925,
774                                              this.data[i] < 0);
775
776                         this.context.stroke();
777                         
778                         this.context.lineWidth = 1;
779
780                     // Unknown variant type
781                     } else {
782                         alert('[BAR] Warning! Unknown chart.variant: ' + variant);
783                     }
784
785                     this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
786
787
788                 /**
789                 * Stacked bar
790                 */
791                 } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') {
792                     
793                     var barWidth     = width - (2 * hmargin);
794                     var redrawCoords = [];// Necessary to draw if the shadow is enabled
795                     var startY       = 0;
796
797                     for (j=0; j<this.data[i].length; ++j) {
798                     
799                         // Stacked bar chart and X axis pos in the middle - poitless since negative values are not permitted
800                         if (xaxispos == 'center') {
801                             alert("[BAR] It's fruitless having the X axis position at the center on a stacked bar chart.");
802                             return;
803                         }
804
805                         // Negative values not permitted for the stacked chart
806                         if (this.data[i][j] < 0) {
807                             alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');
808                             return;
809                         }
810
811                         // Set the fill and stroke colors
812                         this.context.strokeStyle = strokeStyle
813                         this.context.fillStyle = colors[j];
814
815                         var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.gutter) );
816
817                         // If the X axis pos is in the center, we need to half the  height
818                         if (xaxispos == 'center') {
819                             height /= 2;
820                         }
821
822                         var totalHeight = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - hmargin - (2 * this.gutter));
823
824                         /**
825                         * Store the coords for tooltips
826                         */
827                         this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
828
829                         // MSIE shadow
830                         if (document.all && shadow) {
831                             this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]);
832                         }
833
834                         this.context.strokeRect(x + hmargin, y, width - (2 * hmargin), height);
835                         this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height);
836
837                         
838                         if (j == 0) {
839                             var startY = y;
840                             var startX = x;
841                         }
842
843                         /**
844                         * Store the redraw coords if the shadow is enabled
845                         */
846                         if (shadow) {
847                             redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, colors[j]]);
848                         }
849
850                         /**
851                         * Stacked 3D effect
852                         */
853                         if (variant == '3d') {
854
855                             var prevFillStyle = this.context.fillStyle;
856                             var prevStrokeStyle = this.context.strokeStyle;
857
858     
859                             // Draw the top side
860                             if (j == 0) {
861                                 this.context.beginPath();
862                                     this.context.moveTo(startX + hmargin, y);
863                                     this.context.lineTo(startX + 10 + hmargin, y - 5);
864                                     this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5);
865                                     this.context.lineTo(startX + barWidth + hmargin, y);
866                                 this.context.closePath();
867                                 
868                                 this.context.fill();
869                                 this.context.stroke();
870                             }
871
872                             // Draw the side section
873                             this.context.beginPath();
874                                 this.context.moveTo(startX + barWidth + hmargin, y);
875                                 this.context.lineTo(startX + barWidth + hmargin + 10, y - 5);
876                                 this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height);
877                                 this.context.lineTo(startX + barWidth + hmargin , y + height);
878                             this.context.closePath();
879                             
880                             this.context.fill();
881                             this.context.stroke();
882
883                             // Draw the darker top side
884                             if (j == 0) {
885                                 this.context.fillStyle = 'rgba(255,255,255,0.3)';
886                                 this.context.beginPath();
887                                     this.context.moveTo(startX + hmargin, y);
888                                     this.context.lineTo(startX + 10 + hmargin, y - 5);
889                                     this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5);
890                                     this.context.lineTo(startX + barWidth + hmargin, y);
891                                 this.context.closePath();
892                                 
893                                 this.context.fill();
894                                 this.context.stroke();
895                             }
896
897                             // Draw the darker side section
898                             this.context.fillStyle = 'rgba(0,0,0,0.4)';
899                             this.context.beginPath();
900                                 this.context.moveTo(startX + barWidth + hmargin, y);
901                                 this.context.lineTo(startX + barWidth + hmargin + 10, y - 5);
902                                 this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height);
903                                 this.context.lineTo(startX + barWidth + hmargin , y + height);
904                             this.context.closePath();
905                             
906                             this.context.fill();
907                             this.context.stroke();
908
909                             this.context.strokeStyle = prevStrokeStyle;
910                             this.context.fillStyle = prevFillStyle;
911                         }
912
913                         y += height;
914                     }
915
916                     // This bit draws the text labels that appear above the bars if requested
917                     if (this.Get('chart.labels.above')) {
918
919                         // Turn off any shadow
920                         RGraph.NoShadow(this);
921
922                         this.context.fillStyle = this.Get('chart.text.color');
923                         RGraph.Text(this.context,this.Get('chart.text.font'),typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,startX + (barWidth / 2) + this.Get('chart.hmargin'),startY - (this.Get('chart.shadow') && this.Get('chart.shadow.offsety') < 0 ? 7 : 4),String(this.Get('chart.units.pre') + RGraph.array_sum(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')) + this.Get('chart.units.post')),null,'center');
924                       
925                         // Turn any shadow back on
926                         if (shadow) {
927                             this.context.shadowColor   = shadowColor;
928                             this.context.shadowBlur    = shadowBlur;
929                             this.context.shadowOffsetX = shadowOffsetX;
930                             this.context.shadowOffsetY = shadowOffsetY;
931                         }
932                     }
933                     
934
935                     /**
936                     * Redraw the bars if the shadow is enabled due to hem being drawn from the bottom up, and the
937                     * shadow spilling over to higher up bars
938                     */
939                     if (shadow) {
940
941                         RGraph.NoShadow(this);
942
943                         for (k=0; k<redrawCoords.length; ++k) {
944                             this.context.strokeStyle = strokeStyle;
945                             this.context.fillStyle = redrawCoords[k][4];
946                             this.context.strokeRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
947                             this.context.fillRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
948
949                             this.context.stroke();
950                             this.context.fill();
951                         }
952                         
953                         // Reset the redraw coords to be empty
954                         redrawCoords = [];
955                     }
956                 /**
957                 * Grouped bar
958                 */
959                 } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') {
960
961                     var redrawCoords = [];
962                     this.context.lineWidth = this.Get('chart.linewidth');
963
964                     for (j=0; j<this.data[i].length; ++j) {
965                         // Set the fill and stroke colors
966                         this.context.strokeStyle = strokeStyle;
967                         this.context.fillStyle   = colors[j];
968
969                         var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length;
970                         var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.gutter) );
971
972                         // If the X axis pos is in the center, we need to half the  height
973                         if (xaxispos == 'center') {
974                             height /= 2;
975                         }
976
977                         var startX = x + hmargin + (j * individualBarWidth);
978                         var startY = (xaxispos == 'bottom' ? this.canvas.height : (this.canvas.height / 2) + this.gutter) - this.gutter - height;
979
980                         // Account for a bug in chrome that doesn't allow negative heights
981                         if (height < 0) {
982                             startY += height;
983                             height = Math.abs(height);
984                         }
985
986                         /**
987                         * Draw MSIE shadow
988                         */
989                         if (document.all && shadow) {
990                             this.DrawIEShadow([startX, startY, individualBarWidth, height]);
991                         }
992
993                         this.context.strokeRect(startX, startY, individualBarWidth, height);
994                         this.context.fillRect(startX, startY, individualBarWidth, height);
995                         y += height;
996
997                         // This bit draws the text labels that appear above the bars if requested
998                         if (this.Get('chart.labels.above')) {
999                         
1000                             this.context.strokeStyle = 'rgba(0,0,0,0)';
1001
1002                             // Turn off any shadow
1003                             if (shadow) {
1004                                 RGraph.NoShadow(this);
1005                             }
1006
1007                             var yPos = y - 3;
1008
1009                             // Account for negative bars
1010                             if (this.data[i][j] < 0) {
1011                                 yPos += height + 6 + (this.Get('chart.text.size') - 4);
1012                             }
1013
1014                             this.context.fillStyle = this.Get('chart.text.color');
1015                             RGraph.Text(this.context,this.Get('chart.text.font'),typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,startX + (individualBarWidth / 2),startY - 2,RGraph.number_format(this, this.data[i][j].toFixed(this.Get('chart.labels.above.decimals'))),null,'center');
1016                           
1017                             // Turn any shadow back on
1018                             if (shadow) {
1019                                 this.context.shadowColor   = shadowColor;
1020                                 this.context.shadowBlur    = shadowBlur;
1021                                 this.context.shadowOffsetX = shadowOffsetX;
1022                                 this.context.shadowOffsetY = shadowOffsetY;
1023                             }
1024                         }
1025
1026                         /**
1027                         * Grouped 3D effect
1028                         */
1029                         if (variant == '3d') {
1030                             var prevFillStyle = this.context.fillStyle;
1031                             var prevStrokeStyle = this.context.strokeStyle;
1032                             
1033                             // Draw the top side
1034                             this.context.beginPath();
1035                                 this.context.moveTo(startX, startY);
1036                                 this.context.lineTo(startX + 10, startY - 5);
1037                                 this.context.lineTo(startX + 10 + individualBarWidth, startY - 5);
1038                                 this.context.lineTo(startX + individualBarWidth, startY);
1039                             this.context.closePath();
1040                             
1041                             this.context.fill();
1042                             this.context.stroke();
1043                             
1044                             // Draw the side section
1045                             this.context.beginPath();
1046                                 this.context.moveTo(startX + individualBarWidth, startY);
1047                                 this.context.lineTo(startX + individualBarWidth + 10, startY - 5);
1048                                 this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height);
1049                                 this.context.lineTo(startX + individualBarWidth , startY + height);
1050                             this.context.closePath();
1051                             
1052                             this.context.fill();
1053                             this.context.stroke();
1054
1055
1056                             // Draw the darker top side
1057                             this.context.fillStyle = 'rgba(255,255,255,0.3)';
1058                             this.context.beginPath();
1059                                 this.context.moveTo(startX, startY);
1060                                 this.context.lineTo(startX + 10, startY - 5);
1061                                 this.context.lineTo(startX + 10 + individualBarWidth, startY - 5);
1062                                 this.context.lineTo(startX + individualBarWidth, startY);
1063                             this.context.closePath();
1064                             
1065                             this.context.fill();
1066                             this.context.stroke();
1067                             
1068                             // Draw the darker side section
1069                             this.context.fillStyle = 'rgba(0,0,0,0.4)';
1070                             this.context.beginPath();
1071                                 this.context.moveTo(startX + individualBarWidth, startY);
1072                                 this.context.lineTo(startX + individualBarWidth + 10, startY - 5);
1073                                 this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height);
1074                                 this.context.lineTo(startX + individualBarWidth , startY + height);
1075                             this.context.closePath();
1076                             
1077                             this.context.fill();
1078                             this.context.stroke();
1079
1080                             this.context.strokeStyle = prevStrokeStyle;
1081                             this.context.fillStyle   = prevFillStyle;
1082                         }
1083
1084                         this.coords.push([startX, startY, individualBarWidth, height]);
1085
1086                         // Facilitate shadows going to the left
1087                         if (this.Get('chart.shadow')) {
1088                             redrawCoords.push([startX, startY, individualBarWidth, height]);
1089                         }
1090                     }
1091                     
1092                     /**
1093                     * Redraw the bar if shadows are going to the left
1094                     */
1095                     if (redrawCoords.length) {
1096
1097                         RGraph.NoShadow(this);
1098                         
1099                         this.context.lineWidth = this.Get('chart.linewidth');
1100
1101                         this.context.beginPath();
1102                             for (var j=0; j<redrawCoords.length; ++j) {
1103
1104                                 this.context.fillStyle   = this.Get('chart.colors')[j];
1105                                 this.context.strokeStyle = this.Get('chart.strokecolor');
1106
1107                                 this.context.fillRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
1108                                 this.context.strokeRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
1109                             }
1110                         this.context.fill();
1111                         this.context.stroke();
1112
1113                         redrawCoords = [];
1114                     }
1115                 }
1116
1117             this.context.closePath();
1118         }
1119
1120         /**
1121         * Turn off any shadow
1122         */
1123         RGraph.NoShadow(this);
1124
1125
1126         /**
1127         * Install the onclick event handler
1128         */
1129         if (this.Get('chart.tooltips')) {
1130         
1131             // Need to register this object for redrawing
1132             RGraph.Register(this);
1133
1134             /**
1135             * Install the window onclick handler
1136             */
1137             var window_onclick_func = function (){RGraph.Redraw();};
1138             window.addEventListener('click', window_onclick_func, false);
1139             RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func);
1140
1141
1142
1143             /**
1144             * If the cursor is over a hotspot, change the cursor to a hand. Bar chart tooltips can now
1145             * be based around the onmousemove event
1146             */
1147             canvas_onmousemove = function (e)
1148             {
1149                 e = RGraph.FixEventObject(e);
1150
1151                 var canvas    = document.getElementById(e.target.id);
1152                 var obj       = canvas.__object__;
1153                 var barCoords = obj.getBar(e);
1154
1155                 /**
1156                 * If there are bar coords AND the bar has height
1157                 */
1158                 if (barCoords && barCoords[4] > 0) {
1159
1160                     /**
1161                     * Get the tooltip text
1162                     */
1163                     if (typeof(obj.Get('chart.tooltips')) == 'function') {
1164                         var text = String(obj.Get('chart.tooltips')(barCoords[5]));
1165                     
1166                     } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'function') {
1167                         var text = String(obj.Get('chart.tooltips')[barCoords[5]](barCoords[5]));
1168                     
1169                     } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'string' || typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'number')) {
1170                         var text = String(obj.Get('chart.tooltips')[barCoords[5]]);
1171                 
1172                     } else {
1173                         var text = null;
1174                     }
1175
1176                     if (text) {
1177                         canvas.style.cursor = 'pointer';
1178                     } else {
1179                         canvas.style.cursor = 'default';
1180                     }
1181                     
1182                     /**
1183                     * Hide the currently displayed tooltip if the index is the same
1184                     */
1185                     if (   RGraph.Registry.Get('chart.tooltip')
1186                         && RGraph.Registry.Get('chart.tooltip').__canvas__.id != obj.id
1187                         && obj.Get('chart.tooltips.event') == 'onmousemove') {
1188
1189                         RGraph.Redraw();
1190                         RGraph.HideTooltip();
1191                     }
1192
1193                     /**
1194                     * This facilitates the tooltips using the onmousemove event
1195                     */
1196
1197                     if (   obj.Get('chart.tooltips.event') == 'onmousemove'
1198                         && (
1199                                (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ != barCoords[5])
1200                             || !RGraph.Registry.Get('chart.tooltip')
1201                            )
1202                         && text) {
1203                         /**
1204                         * Show a tooltip if it's defined
1205                         */
1206                         RGraph.Redraw(obj);
1207
1208                         obj.context.beginPath();
1209                         obj.context.strokeStyle = 'black';
1210                         obj.context.fillStyle   = 'rgba(255,255,255,0.5)';
1211                         obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
1212                         obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
1213                 
1214                         obj.context.stroke();
1215                         obj.context.fill();
1216
1217                         RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]);
1218                     }
1219                 } else {
1220                     canvas.style.cursor = 'default';
1221                 }
1222             }
1223             RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove);
1224             this.canvas.addEventListener('mousemove', canvas_onmousemove, false);
1225
1226
1227             /**
1228             * Install the onclick event handler for the tooltips
1229             */
1230             if (this.Get('chart.tooltips.event') == 'onclick') {
1231
1232                 canvas_onclick = function (e)
1233                 {
1234                     var e = RGraph.FixEventObject(e);
1235     
1236                     // If the button pressed isn't the left, we're not interested
1237                     if (e.button != 0) return;
1238     
1239                     e = RGraph.FixEventObject(e);
1240     
1241                     var canvas    = document.getElementById(this.id);
1242                     var obj       = canvas.__object__;
1243                     var barCoords = obj.getBar(e);
1244
1245                     /**
1246                     * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
1247                     * This "deselects" any already selected bar
1248                     */
1249                     RGraph.Redraw();
1250
1251                     /**
1252                     * Loop through the bars determining if the mouse is over a bar
1253                     */
1254                     if (barCoords) {
1255
1256                         /**
1257                         * Get the tooltip text
1258                         */
1259                         if (typeof(obj.Get('chart.tooltips')) == 'function') {
1260                             var text = String(obj.Get('chart.tooltips')(barCoords[5]));
1261                         
1262                         } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[barCoords[5]]) == 'function') {
1263                             var text = String(obj.Get('chart.tooltips')[barCoords[5]](barCoords[5]));
1264                         
1265                         } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
1266                             var text = String(obj.Get('chart.tooltips')[barCoords[5]]);
1267
1268                         } else {
1269                             var text = null;
1270                         }
1271
1272                         /**
1273                         * Show a tooltip if it's defined
1274                         */
1275                         if (text && text != 'undefined') {
1276
1277                             // [TODO] Allow customisation of the highlight colors
1278                             obj.context.beginPath();
1279                             obj.context.strokeStyle = 'black';
1280                             obj.context.fillStyle   = 'rgba(255,255,255,0.5)';
1281                             obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
1282                             obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
1283         
1284                             obj.context.stroke();
1285                             obj.context.fill();
1286
1287                             RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]);
1288                         }
1289                     }
1290     
1291                     /**
1292                     * Stop the event bubbling
1293                     */
1294                     e.stopPropagation();
1295                 }
1296                 RGraph.AddEventListener(this.id, 'click', canvas_onclick);
1297                 this.canvas.addEventListener('click', canvas_onclick, false);
1298             }
1299
1300
1301             // This resets the bar graph
1302             // 8th August 2010 : Is this redundant
1303             //if (typeof(obj) != 'undefined' && obj == RGraph.Registry.Get('chart.tooltip')) {
1304             //    obj.style.display = 'none';
1305             //    RGraph.Registry.Set('chart.tooltip', null)
1306             //}
1307         }
1308     }
1309
1310     /**
1311     * Draws the labels for the graph
1312     */
1313     RGraph.Bar.prototype.DrawLabels = function ()
1314     {
1315         var context    = this.context;
1316         var gutter     = this.gutter;
1317         var text_angle = this.Get('chart.text.angle');
1318         var text_size  = this.Get('chart.text.size');
1319         var labels     = this.Get('chart.labels');
1320
1321
1322         // Draw the Y axis labels:
1323         if (this.Get('chart.ylabels')) {
1324             this.Drawlabels_center();
1325             this.Drawlabels_bottom();
1326         }
1327
1328         /**
1329         * The X axis labels
1330         */
1331         if (typeof(labels) == 'object' && labels) {
1332
1333             var yOffset = 13 + Number(this.Get('chart.xlabels.offset'));
1334
1335             /**
1336             * Text angle
1337             */
1338             var angle  = 0;
1339             var halign = 'center';
1340
1341             if (text_angle > 0) {
1342                 angle  = -1 * text_angle;
1343                 halign   = 'right';
1344                 yOffset -= 5;
1345             }
1346
1347             // Draw the X axis labels
1348             context.fillStyle = this.Get('chart.text.color');
1349             
1350             // How wide is each bar
1351             var barWidth = (this.canvas.width - (2 * gutter) ) / labels.length;
1352             
1353             // Reset the xTickGap
1354             xTickGap = (this.canvas.width - (2 * gutter)) / labels.length
1355
1356             // Draw the X tickmarks
1357             var i=0;
1358             var font = this.Get('chart.text.font');
1359
1360             for (x=gutter + (xTickGap / 2); x<=this.canvas.width - gutter; x+=xTickGap) {
1361                 RGraph.Text(context, font,
1362                                       text_size,
1363                                       x + (this.Get('chart.text.angle') == 90 ? 0: 0),
1364                                       (this.canvas.height - gutter) + yOffset,
1365                                       String(labels[i++]),
1366                                       (this.Get('chart.text.angle') == 90 ? 'center' : null),
1367                                       halign,
1368                                       null,
1369                                       angle);
1370             }
1371         }
1372     }
1373
1374     /**
1375     * Draws the X axis in the middle
1376     */
1377     RGraph.Bar.prototype.Drawlabels_center = function ()
1378     {
1379         var font       = this.Get('chart.text.font');
1380         var numYLabels = this.Get('chart.ylabels.count');
1381
1382         this.context.fillStyle = this.Get('chart.text.color');
1383
1384         if (this.Get('chart.xaxispos') == 'center') {
1385
1386             /**
1387             * Draw the top labels
1388             */
1389             var interval   = (this.grapharea * (1/10) );
1390             var text_size  = this.Get('chart.text.size');
1391             var gutter     = this.gutter;
1392             var units_pre  = this.Get('chart.units.pre');
1393             var units_post = this.Get('chart.units.post');
1394             var context = this.context;
1395             var align   = '';
1396             var xpos    = 0;
1397             var boxed   = false;
1398
1399             this.context.fillStyle = this.Get('chart.text.color');
1400             this.context.strokeStyle = 'black';
1401
1402             if (this.Get('chart.ylabels.inside') == true) {
1403                 var xpos  = this.Get('chart.yaxispos') == 'left' ? gutter + 5 : this.canvas.width - gutter - 5;
1404                 var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right';
1405                 var boxed = true;
1406             } else {
1407                 var xpos  = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : this.canvas.width - gutter + 5;
1408                 var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
1409                 var boxed = false;
1410             }
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423             /**
1424             * Draw specific Y labels here so that the local variables can be reused
1425             */
1426             if (typeof(this.Get('chart.ylabels.specific')) == 'object') {
1427
1428                 var labels = this.Get('chart.ylabels.specific');
1429                 var grapharea = this.canvas.height - (2 * gutter);
1430
1431                 // Draw the top halves labels
1432                 for (var i=0; i<labels.length; ++i) {
1433                     var y = gutter + (grapharea * (i / (labels.length * 2) ));
1434                     
1435                     RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed);
1436                 }
1437
1438                 // Draw the bottom halves labels
1439                 for (var i=labels.length-1; i>=0; --i) {
1440                     var y = gutter + (grapharea * ( (i+1) / (labels.length * 2) )) + (grapharea / 2);
1441
1442                     RGraph.Text(context, font, text_size, xpos, y, labels[labels.length - i - 1], 'center', align, boxed);
1443                 }
1444
1445                 return;
1446             }
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459             if (numYLabels == 3 || numYLabels == 5) {
1460                 RGraph.Text(context, font, text_size, xpos, gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
1461     
1462                 if (numYLabels == 5) {
1463                     RGraph.Text(context, font, text_size, xpos, (1*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
1464                     RGraph.Text(context, font, text_size, xpos, (3*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
1465                 }
1466                 
1467                 if (numYLabels == 3 || numYLabels == 5) {
1468                     RGraph.Text(context, font, text_size, xpos, (4*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
1469                     RGraph.Text(context, font, text_size, xpos, (2*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
1470                 }
1471             } else if (numYLabels == 10) {
1472                 // 10Y labels
1473                 interval = (this.grapharea / numYLabels) / 2;
1474             
1475                 for (var i=0; i<numYLabels; ++i) {
1476                     RGraph.Text(context, font, text_size, xpos,gutter + ((this.grapharea / (numYLabels * 2)) * i),RGraph.number_format(this, ((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed);
1477                 }
1478             }
1479             ///////////////////////////////////////////////////////////////////////////////////
1480
1481             /**
1482             * Draw the bottom (X axis) labels
1483             */
1484             var interval = (this.grapharea) / 10;
1485
1486             if (numYLabels == 3 || numYLabels == 5) {
1487                 if (numYLabels == 3 || numYLabels == 5) {
1488                     RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - (4 * interval), '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
1489                     RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - (2 * interval), '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
1490                 }
1491     
1492                 if (numYLabels == 5) {
1493                     RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - (3 * interval), '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
1494                     RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - interval, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
1495                 }
1496     
1497                 RGraph.Text(context, font, text_size, xpos,  this.grapharea + gutter + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
1498
1499             } else if (numYLabels == 10) {
1500
1501                 // Arbitrary number of Y labels
1502                 interval = (this.grapharea / numYLabels) / 2;
1503             
1504                 for (var i=0; i<numYLabels; ++i) {
1505                     RGraph.Text(context, font, text_size, xpos,this.Get('chart.gutter') + (this.grapharea / 2) + ((this.grapharea / (numYLabels * 2)) * i) + (this.grapharea / (numYLabels * 2)),RGraph.number_format(this, ((this.scale[4] / numYLabels) * (i+1)).toFixed((this.Get('chart.scale.decimals'))), '-' + units_pre, units_post),'center', align, boxed);
1506                 }
1507             }
1508
1509
1510
1511         }
1512     }
1513
1514     /**
1515     * Draws the X axdis at the bottom (the default)
1516     */
1517     RGraph.Bar.prototype.Drawlabels_bottom = function ()
1518     {
1519         this.context.beginPath();
1520         this.context.fillStyle = this.Get('chart.text.color');
1521         this.context.strokeStyle = 'black';
1522
1523         if (this.Get('chart.xaxispos') != 'center') {
1524             
1525             var interval   = (this.grapharea * (1/5) );
1526             var text_size  = this.Get('chart.text.size');
1527             var units_pre  = this.Get('chart.units.pre');
1528             var units_post = this.Get('chart.units.post');
1529             var gutter     = this.gutter;
1530             var context    = this.context;
1531             var align      = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
1532             var font       = this.Get('chart.text.font');
1533             var numYLabels = this.Get('chart.ylabels.count');
1534
1535             if (this.Get('chart.ylabels.inside') == true) {
1536                 var xpos  = this.Get('chart.yaxispos') == 'left' ? gutter + 5 : this.canvas.width - gutter - 5;
1537                 var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right';
1538                 var boxed = true;
1539             } else {
1540                 var xpos  = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : this.canvas.width - gutter + 5;
1541                 var boxed = false;
1542             }
1543             
1544             /**
1545             * Draw specific Y labels here so that the local variables can be reused
1546             */
1547             if (typeof(this.Get('chart.ylabels.specific')) == 'object') {
1548                 
1549                 var labels = this.Get('chart.ylabels.specific');
1550                 var grapharea = this.canvas.height - (2 * gutter);
1551
1552                 for (var i=0; i<labels.length; ++i) {
1553                     var y = gutter + (grapharea * (i / labels.length));
1554                     
1555                     RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed);
1556                 }
1557
1558                 return;
1559             }
1560
1561             // 1 label
1562             if (numYLabels == 3 || numYLabels == 5) {
1563                 RGraph.Text(context, font, text_size, xpos, gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
1564     
1565                 // 5 labels
1566                 if (numYLabels == 5) {
1567                     RGraph.Text(context, font, text_size, xpos, (1*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
1568                     RGraph.Text(context, font, text_size, xpos, (3*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
1569                 
1570                 }
1571                 
1572                 // 3 labels
1573                 if (numYLabels == 3 || numYLabels == 5) {
1574                     RGraph.Text(context, font, text_size, xpos, (2*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
1575                     RGraph.Text(context, font, text_size, xpos, (4*interval) + gutter + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
1576                 }
1577             }
1578             
1579             // 10 Y labels
1580             if (numYLabels == 10) {
1581
1582                 interval   = (this.grapharea / numYLabels );
1583
1584                 for (var i=0; i<numYLabels; ++i) {
1585                     RGraph.Text(context, font, text_size, xpos, this.Get('chart.gutter') + ((this.grapharea / numYLabels) * i), RGraph.number_format(this,((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed);
1586                 }
1587             }
1588         }
1589         
1590         this.context.fill();
1591         this.context.stroke();
1592     }
1593
1594
1595     /**
1596     * This function is used by MSIE only to manually draw the shadow
1597     * 
1598     * @param array coords The coords for the bar
1599     */
1600     RGraph.Bar.prototype.DrawIEShadow = function (coords)
1601     {
1602         var prevFillStyle = this.context.fillStyle;
1603         var offsetx       = this.Get('chart.shadow.offsetx');
1604         var offsety       = this.Get('chart.shadow.offsety');
1605         
1606         this.context.lineWidth = this.Get('chart.linewidth');
1607         this.context.fillStyle = this.Get('chart.shadow.color');
1608         this.context.beginPath();
1609         
1610         // Draw shadow here
1611         this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]);
1612
1613         this.context.fill();
1614         
1615         // Change the fillstyle back to what it was
1616         this.context.fillStyle = prevFillStyle;
1617     }
1618
1619
1620     /**
1621     * Not used by the class during creating the graph, but is used by event handlers
1622     * to get the coordinates (if any) of the selected bar
1623     */
1624     RGraph.Bar.prototype.getBar = function (e)
1625     {
1626         var canvas      = e.target;
1627         var obj         = e.target.__object__;
1628         var mouseCoords = RGraph.getMouseXY(e);
1629
1630         /**
1631         * Loop through the bars determining if the mouse is over a bar
1632         */
1633         for (var i=0; i<obj.coords.length; i++) {
1634
1635             var mouseX = mouseCoords[0];
1636             var mouseY = mouseCoords[1];
1637
1638             var left   = obj.coords[i][0];
1639             var top    = obj.coords[i][1];
1640             var width  = obj.coords[i][2];
1641             var height = obj.coords[i][3];
1642
1643             if (   mouseX >= (left + obj.Get('chart.tooltips.coords.adjust')[0])
1644                 && mouseX <= (left + width+ obj.Get('chart.tooltips.coords.adjust')[0])
1645                 && mouseY >= (top + obj.Get('chart.tooltips.coords.adjust')[1])
1646                 && mouseY <= (top + height + obj.Get('chart.tooltips.coords.adjust')[1]) ) {
1647                 
1648                 return [obj, left, top, width, height, i];
1649             }
1650         }
1651         
1652         return null;
1653     }