initial commit
[home-automation.git] / libraries / RGraph.bipolar.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 bi-polar/age frequency constructor.
19     * 
20     * @param string id The id of the canvas
21     * @param array  left  The left set of data points
22     * @param array  right The right set of data points
23     */
24     RGraph.Bipolar = function (id, left, right)
25     {
26         // Get the canvas and context objects
27         this.id                = id;
28         this.canvas            = document.getElementById(id);
29         this.context           = this.canvas.getContext('2d');
30         this.canvas.__object__ = this;
31         this.type              = 'bipolar';
32         this.coords            = [];
33         this.max               = 0;
34         this.isRGraph          = true;
35
36
37         /**
38         * Compatibility with older browsers
39         */
40         RGraph.OldBrowserCompat(this.context);
41
42         
43         // The left and right data respectively
44         this.left       = left;
45         this.right      = right;
46         this.data       = [left, right];
47
48         this.properties = {
49             'chart.margin':                 2,
50             'chart.xtickinterval':          null,
51             'chart.labels':                 [],
52             'chart.text.size':              10,
53             'chart.text.color':             'black',
54             'chart.text.font':              'Verdana',
55             'chart.title.left':             '',
56             'chart.title.right':            '',
57             'chart.gutter':                 25,
58             'chart.gutter.center':          60,
59             'chart.title':                  '',
60             'chart.title.background':       null,
61             'chart.title.hpos':             null,
62             'chart.title.vpos':             null,
63             'chart.colors':                 ['#0f0'],
64             'chart.contextmenu':            null,
65             'chart.tooltips':               null,
66             'chart.tooltips.effect':         'fade',
67             'chart.tooltips.css.class':      'RGraph_tooltip',
68             'chart.tooltips.highlight':     true,
69             'chart.units.pre':              '',
70             'chart.units.post':             '',
71             'chart.shadow':                 false,
72             'chart.shadow.color':           '#666',
73             'chart.shadow.offsetx':         3,
74             'chart.shadow.offsety':         3,
75             'chart.shadow.blur':            3,
76             'chart.annotatable':            false,
77             'chart.annotate.color':         'black',
78             'chart.xmax':                   null,
79             'chart.scale.decimals':         null,
80             'chart.scale.point':            '.',
81             'chart.scale.thousand':         ',',
82             'chart.axis.color':             'black',
83             'chart.zoom.factor':            1.5,
84             'chart.zoom.fade.in':           true,
85             'chart.zoom.fade.out':          true,
86             'chart.zoom.hdir':              'right',
87             'chart.zoom.vdir':              'down',
88             'chart.zoom.frames':            10,
89             'chart.zoom.delay':             50,
90             'chart.zoom.shadow':            true,
91             'chart.zoom.mode':              'canvas',
92             'chart.zoom.thumbnail.width':   75,
93             'chart.zoom.thumbnail.height':  75,
94             'chart.zoom.background':        true,
95             'chart.zoom.action':            'zoom',
96             'chart.resizable':              false,
97             'chart.strokestyle':            '#333'
98         }
99
100         // Pad the arrays so they're the same size
101         while (this.left.length < this.right.length) this.left.push(0);
102         while (this.left.length > this.right.length) this.right.push(0);
103
104         // Check the common library has been included
105         if (typeof(RGraph) == 'undefined') {
106             alert('[BIPOLAR] Fatal error: The common library does not appear to have been included');
107         }
108     }
109
110
111     /**
112     * The setter
113     * 
114     * @param name  string The name of the parameter to set
115     * @param value mixed  The value of the paraneter 
116     */
117     RGraph.Bipolar.prototype.Set = function (name, value)
118     {
119         this.properties[name.toLowerCase()] = value;
120     }
121
122
123     /**
124     * The getter
125     * 
126     * @param name string The name of the parameter to get
127     */
128     RGraph.Bipolar.prototype.Get = function (name)
129     {
130         return this.properties[name.toLowerCase()];
131     }
132
133     
134     /**
135     * Draws the graph
136     */
137     RGraph.Bipolar.prototype.Draw = function ()
138     {
139         /**
140         * Fire the onbeforedraw event
141         */
142         RGraph.FireCustomEvent(this, 'onbeforedraw');
143
144
145         /**
146         * Clear all of this canvases event handlers (the ones installed by RGraph)
147         */
148         RGraph.ClearEventListeners(this.id);
149
150
151         // Reset the data to what was initially supplied
152         this.left  = this.data[0];
153         this.right = this.data[1];
154
155         /**
156         * Reset the coords array
157         */
158         this.coords = [];
159
160         this.GetMax();
161         this.DrawAxes();
162         this.DrawTicks();
163         this.DrawLeftBars();
164         this.DrawRightBars();
165
166         if (this.Get('chart.axis.color') != 'black') {
167             this.DrawAxes(); // Draw the axes again (if the axes color is not black)
168         }
169
170         this.DrawLabels();
171         this.DrawTitles();
172         
173         /**
174         * Setup the context menu if required
175         */
176         if (this.Get('chart.contextmenu')) {
177             RGraph.ShowContext(this);
178         }
179
180
181         /**
182         * Install the on* event handlers
183         */
184         if (this.Get('chart.tooltips')) {
185
186
187             // Register the object so that it gets redrawn
188             RGraph.Register(this);
189
190
191             /**
192             * Install the window onclick handler
193             */
194             
195             /**
196             * Install the window event handler
197             */
198             var eventHandler_window_click = function ()
199             {
200                 RGraph.Redraw();
201             }
202             window.addEventListener('click', eventHandler_window_click, false);
203             RGraph.AddEventListener('window_' + this.id, 'click', eventHandler_window_click);
204
205
206
207             /**
208             * If the cursor is over a hotspot, change the cursor to a hand
209             */
210             var eventHandler_canvas_mousemove = function (e)
211             {
212                 e = RGraph.FixEventObject(e);
213
214                 var canvas = document.getElementById(this.id);
215                 var obj = canvas.__object__;
216
217                 /**
218                 * Get the mouse X/Y coordinates
219                 */
220                 var mouseCoords = RGraph.getMouseXY(e);
221
222                 /**
223                 * Loop through the bars determining if the mouse is over a bar
224                 */
225                 for (var i=0; i<obj.coords.length; i++) {
226
227                     var mouseX = mouseCoords[0];  // In relation to the canvas
228                     var mouseY = mouseCoords[1];  // In relation to the canvas
229                     var left   = obj.coords[i][0];
230                     var top    = obj.coords[i][1];
231                     var width  = obj.coords[i][2];
232                     var height = obj.coords[i][3];
233
234                     if (mouseX >= left && mouseX <= (left + width ) && mouseY >= top && mouseY <= (top + height) ) {
235                         canvas.style.cursor = 'pointer';
236                         return;
237                     }
238                 }
239                     
240                 canvas.style.cursor = 'default';
241             }
242             this.canvas.addEventListener('mousemove', eventHandler_canvas_mousemove, false);
243             RGraph.AddEventListener(this.id, 'mouseover', eventHandler_canvas_mousemove);
244
245
246             /**
247             * Install the onclick event handler for the tooltips
248             */
249             var eventHandler_canvas_click = function (e)
250             {
251                 e = RGraph.FixEventObject(e);
252
253                 var canvas = document.getElementById(this.id)
254                 var obj = canvas.__object__;
255
256                 /**
257                 * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
258                 * This "deselects" any already selected bar
259                 */
260                 RGraph.Clear(canvas);
261                 obj.Draw();
262     
263                 /**
264                 * Get the mouse X/Y coordinates
265                 */
266                 var mouseCoords = RGraph.getMouseXY(e);
267
268                 /**
269                 * Loop through the bars determining if the mouse is over a bar
270                 */
271                 for (var i=0; i<obj.coords.length; i++) {
272
273                     var mouseX = mouseCoords[0];  // In relation to the canvas
274                     var mouseY = mouseCoords[1];  // In relation to the canvas
275                     var left   = obj.coords[i][0];
276                     var top    = obj.coords[i][1];
277                     var width  = obj.coords[i][2];
278                     var height = obj.coords[i][3];
279
280                     if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
281                         
282     
283                         /**
284                         * Show a tooltip if it's defined
285                         * FIXME pageX and pageY not supported in MSIE
286                         */
287                         if (obj.Get('chart.tooltips')) {
288     
289                             /**
290                             * Get the tooltip text
291                             */
292                             if (typeof(obj.Get('chart.tooltips')) == 'function') {
293                                 var text = obj.Get('chart.tooltips')(i);
294
295                             } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'function') {
296                                 var text = obj.Get('chart.tooltips')[i](i);
297                             
298                             } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
299                                 var text = obj.Get('chart.tooltips')[i];
300     
301                             } else {
302                                 var text = '';
303                             }
304
305                             obj.context.beginPath();
306                             obj.context.strokeStyle = 'black';
307                             obj.context.fillStyle   = 'rgba(255,255,255,0.5)';
308                             obj.context.strokeRect(left, top, width, height);
309                             obj.context.fillRect(left, top, width, height);
310         
311                             obj.context.stroke();
312                             obj.context.fill();
313
314                             RGraph.Tooltip(canvas, text, e.pageX, e.pageY, i);
315                         }
316                     }
317                 }
318
319                 /**
320                 * Stop the event bubbling
321                 */
322                 e.stopPropagation();
323                 
324                 return false;
325             }
326             this.canvas.addEventListener('click', eventHandler_canvas_click, false);
327             RGraph.AddEventListener(this.id, 'click', eventHandler_canvas_click);
328
329             // This resets the bipolar graph
330             if (RGraph.Registry.Get('chart.tooltip')) {
331                 RGraph.Registry.Get('chart.tooltip').style.display = 'none';
332                 RGraph.Registry.Set('chart.tooltip', null)
333             }
334         }
335         
336         /**
337         * If the canvas is annotatable, do install the event handlers
338         */
339         if (this.Get('chart.annotatable')) {
340             RGraph.Annotate(this);
341         }
342         
343         /**
344         * This bit shows the mini zoom window if requested
345         */
346         if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
347             RGraph.ShowZoomWindow(this);
348         }
349
350         
351         /**
352         * This function enables resizing
353         */
354         if (this.Get('chart.resizable')) {
355             RGraph.AllowResizing(this);
356         }
357         
358         /**
359         * Fire the RGraph ondraw event
360         */
361         RGraph.FireCustomEvent(this, 'ondraw');
362     }
363
364
365     /**
366     * Draws the axes
367     */
368     RGraph.Bipolar.prototype.DrawAxes = function ()
369     {
370         // Draw the left set of axes
371         this.context.beginPath();
372         this.context.strokeStyle = this.Get('chart.axis.color');
373
374         this.axisWidth  = (this.canvas.width - this.Get('chart.gutter.center') ) / 2;
375         this.axisHeight = this.canvas.height - (2 * this.Get('chart.gutter'));
376
377         this.context.moveTo(this.Get('chart.gutter'), this.canvas.height - this.Get('chart.gutter'));
378         this.context.lineTo(this.axisWidth, this.canvas.height - this.Get('chart.gutter'));
379         this.context.lineTo(this.axisWidth, this.Get('chart.gutter'));
380         
381         this.context.stroke();
382
383         // Draw the right set of axes
384         this.context.beginPath();
385
386         this.axisWidth  = ((this.canvas.width - this.Get('chart.gutter.center')) / 2) + this.Get('chart.gutter.center');
387         
388         this.context.moveTo(this.axisWidth, this.Get('chart.gutter'));
389         this.context.lineTo(this.axisWidth, this.canvas.height - this.Get('chart.gutter'));
390         this.context.lineTo(this.canvas.width - this.Get('chart.gutter'), this.canvas.height - this.Get('chart.gutter'));
391
392         this.context.stroke();
393     }
394
395
396     /**
397     * Draws the tick marks on the axes
398     */
399     RGraph.Bipolar.prototype.DrawTicks = function ()
400     {
401         var numDataPoints = this.left.length;
402         var barHeight     = ( (this.canvas.height - (2 * this.Get('chart.gutter')))- (this.left.length * (this.Get('chart.margin') * 2) )) / numDataPoints;
403         
404         // Draw the left Y tick marks
405         for (var i = this.canvas.height - this.Get('chart.gutter'); i >= this.Get('chart.gutter'); i -= (barHeight + ( this.Get('chart.margin') * 2)) ) {
406             if (i < (this.canvas.height - this.Get('chart.gutter')) ) {
407                 this.context.beginPath();
408                 this.context.moveTo(this.axisWidth - this.Get('chart.gutter.center'), i);
409                 this.context.lineTo(this.axisWidth - this.Get('chart.gutter.center') + 3, i);
410                 this.context.stroke();
411             }
412         }
413
414         //Draw the right axis Y tick marks
415         for (var i = this.canvas.height - this.Get('chart.gutter'); i >= this.Get('chart.gutter'); i -= (barHeight + ( this.Get('chart.margin') * 2)) ) {
416             if (i < (this.canvas.height - this.Get('chart.gutter')) ) {
417                 this.context.beginPath();
418                 this.context.moveTo(this.axisWidth, i);
419                 this.context.lineTo(this.axisWidth - 3, i);
420                 this.context.stroke();
421             }
422         }
423         
424         // Draw the left sides X tick marks
425         var xInterval = (this.canvas.width - (2 * this.Get('chart.gutter')) - this.Get('chart.gutter.center')) / 10;
426
427         // Is chart.xtickinterval specified ? If so, use that.
428         if (typeof(this.Get('chart.xtickinterval')) == 'number') {
429             xInterval = this.Get('chart.xtickinterval');
430         }
431
432         for (i=this.Get('chart.gutter'); i<(this.canvas.width - this.Get('chart.gutter.center') ) / 2; i += xInterval) {
433             this.context.beginPath();
434             this.context.moveTo(i, this.canvas.height - this.Get('chart.gutter'));  // 4 is the tick height
435             this.context.lineTo(i, (this.canvas.height - this.Get('chart.gutter')) + 4);
436             this.context.closePath();
437             
438             this.context.stroke();
439         }
440
441         // Draw the right sides X tick marks
442         var stoppingPoint = (this.canvas.width - (2 * this.Get('chart.gutter')) - this.Get('chart.gutter.center')) / 2;
443         var stoppingPoint = stoppingPoint + this.Get('chart.gutter.center') + this.Get('chart.gutter')
444
445         for (i=this.canvas.width  - this.Get('chart.gutter'); i > stoppingPoint; i-=xInterval) {
446             this.context.beginPath();
447                 this.context.moveTo(i, this.canvas.height - this.Get('chart.gutter'));
448                 this.context.lineTo(i, (this.canvas.height - this.Get('chart.gutter')) + 4);
449             this.context.closePath();
450             
451             this.context.stroke();
452         }
453         
454         // Store this for later
455         this.barHeight = barHeight;
456     }
457
458
459     /**
460     * Figures out the maximum value, or if defined, uses xmax
461     */
462     RGraph.Bipolar.prototype.GetMax = function()
463     {
464         var max = 0;
465         var dec = this.Get('chart.scale.decimals');
466         
467         // chart.xmax defined
468         if (this.Get('chart.xmax')) {
469
470             max = this.Get('chart.xmax');
471             
472             this.scale    = [];
473             this.scale[0] = Number((max / 5) * 1).toFixed(dec);
474             this.scale[1] = Number((max / 5) * 2).toFixed(dec);
475             this.scale[2] = Number((max / 5) * 3).toFixed(dec);
476             this.scale[3] = Number((max / 5) * 4).toFixed(dec);
477             this.scale[4] = Number(max).toFixed(dec);
478
479             this.max = max;
480             
481
482         // Generate the scale ourselves
483         } else {
484             this.leftmax  = RGraph.array_max(this.left);
485             this.rightmax = RGraph.array_max(this.right);
486             max = Math.max(this.leftmax, this.rightmax);
487
488             this.scale    = RGraph.getScale(max, this);
489             this.scale[0] = Number(this.scale[0]).toFixed(dec);
490             this.scale[1] = Number(this.scale[1]).toFixed(dec);
491             this.scale[2] = Number(this.scale[2]).toFixed(dec);
492             this.scale[3] = Number(this.scale[3]).toFixed(dec);
493             this.scale[4] = Number(this.scale[4]).toFixed(dec);
494
495             this.max = this.scale[4];
496         }
497
498         // Don't need to return it as it is stored in this.max
499     }
500
501
502     /**
503     * Function to draw the left hand bars
504     */
505     RGraph.Bipolar.prototype.DrawLeftBars = function ()
506     {
507         // Set the stroke colour
508         this.context.strokeStyle = this.Get('chart.strokestyle');
509
510         for (i=0; i<this.left.length; ++i) {
511             
512             /**
513             * Turn on a shadow if requested
514             */
515             if (this.Get('chart.shadow')) {
516                 this.context.shadowColor   = this.Get('chart.shadow.color');
517                 this.context.shadowBlur    = this.Get('chart.shadow.blur');
518                 this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
519                 this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
520             }
521
522             this.context.beginPath();
523
524                 // Set the colour
525                 if (this.Get('chart.colors')[i]) {
526                     this.context.fillStyle = this.Get('chart.colors')[i];
527                 }
528                 
529                 /**
530                 * Work out the coordinates
531                 */
532                 var width = ( (this.left[i] / this.max) * ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter')) ) / 2) );
533                 var coords = [
534                               this.axisWidth - this.Get('chart.gutter.center') - width,
535                               this.Get('chart.margin') + (i * ( (this.canvas.height - (2 * this.Get('chart.gutter')) ) / this.left.length)) + this.Get('chart.gutter'),
536                               width,
537                               this.barHeight
538                              ];
539
540                 // Draw the IE shadow if necessary
541                 if (document.all && this.Get('chart.shadow')) {
542                     this.DrawIEShadow(coords);
543                 }
544     
545                 
546                 this.context.strokeRect(coords[0], coords[1], coords[2], coords[3]);
547                 this.context.fillRect(coords[0], coords[1], coords[2], coords[3]);
548
549             this.context.stroke();
550             this.context.fill();
551
552             /**
553             * Add the coordinates to the coords array
554             */
555             this.coords.push([
556                               coords[0],
557                               coords[1],
558                               coords[2],
559                               coords[3]
560                              ]);
561         }
562
563         /**
564         * Turn off any shadow
565         */
566         RGraph.NoShadow(this);
567     }
568
569
570     /**
571     * Function to draw the right hand bars
572     */
573     RGraph.Bipolar.prototype.DrawRightBars = function ()
574     {
575         // Set the stroke colour
576         this.context.strokeStyle = this.Get('chart.strokestyle');
577             
578         /**
579         * Turn on a shadow if requested
580         */
581         if (this.Get('chart.shadow')) {
582             this.context.shadowColor   = this.Get('chart.shadow.color');
583             this.context.shadowBlur    = this.Get('chart.shadow.blur');
584             this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
585             this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
586         }
587
588         for (var i=0; i<this.right.length; ++i) {
589             this.context.beginPath();
590
591             // Set the colour
592             if (this.Get('chart.colors')[i]) {
593                 this.context.fillStyle = this.Get('chart.colors')[i];
594             }
595
596
597             var width = ( (this.right[i] / this.max) * ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter')) ) / 2) );
598             var coords = [
599                           this.axisWidth,
600                           this.Get('chart.margin') + (i * ((this.canvas.height - (2 * this.Get('chart.gutter'))) / this.right.length)) + this.Get('chart.gutter'),
601                           width,
602                           this.barHeight
603                         ];
604
605                 // Draw the IE shadow if necessary
606                 if (document.all && this.Get('chart.shadow')) {
607                     this.DrawIEShadow(coords);
608                 }
609             this.context.strokeRect(coords[0], coords[1], coords[2], coords[3]);
610             this.context.fillRect(coords[0], coords[1], coords[2], coords[3]);
611
612             this.context.closePath();
613             
614             /**
615             * Add the coordinates to the coords array
616             */
617             this.coords.push([
618                               coords[0],
619                               coords[1],
620                               coords[2],
621                               coords[3]
622                              ]);
623         }
624         
625         this.context.stroke();
626
627         /**
628         * Turn off any shadow
629         */
630         RGraph.NoShadow(this);
631     }
632
633
634     /**
635     * Draws the titles
636     */
637     RGraph.Bipolar.prototype.DrawLabels = function ()
638     {
639         this.context.fillStyle = this.Get('chart.text.color');
640
641         var labelPoints = new Array();
642         var font = this.Get('chart.text.font');
643         var size = this.Get('chart.text.size');
644         
645         var max = Math.max(this.left.length, this.right.length);
646         
647         for (i=0; i<max; ++i) {
648             var barAreaHeight = this.canvas.height - (2 * this.Get('chart.gutter'));
649             var barHeight     = barAreaHeight / this.left.length;
650             var yPos          = (i * barAreaHeight) + this.Get('chart.gutter');
651
652             labelPoints.push(this.Get('chart.gutter') + (i * barHeight) + (barHeight / 2) + 5);
653         }
654
655         for (i=0; i<labelPoints.length; ++i) {
656             RGraph.Text(this.context, this.Get('chart.text.font'),
657                                         this.Get('chart.text.size'),
658                                         this.canvas.width / 2,
659                                         labelPoints[i],
660                                         String(this.Get('chart.labels')[i] ? this.Get('chart.labels')[i] : ''), null, 'center');
661         }
662
663         // Now draw the X labels for the left hand side
664         RGraph.Text(this.context, font, size, this.Get('chart.gutter'), this.canvas.height - this.Get('chart.gutter') + 14, RGraph.number_format(this, this.scale[4], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
665         RGraph.Text(this.context, font, size, this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (1/5), this.canvas.height - this.Get('chart.gutter') + 14, RGraph.number_format(this, this.scale[3], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
666         RGraph.Text(this.context, font, size, this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (2/5), this.canvas.height - this.Get('chart.gutter') + 14, RGraph.number_format(this, this.scale[2], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
667         RGraph.Text(this.context, font, size, this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (3/5), this.canvas.height - this.Get('chart.gutter') + 14, RGraph.number_format(this, this.scale[1], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
668         RGraph.Text(this.context, font, size, this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (4/5), this.canvas.height - this.Get('chart.gutter') + 14, RGraph.number_format(this, this.scale[0], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
669
670         // Now draw the X labels for the right hand side
671         RGraph.Text(this.context, font, size, this.canvas.width - this.Get('chart.gutter'), this.canvas.height - this.Get('chart.gutter') + 14, RGraph.number_format(this, this.scale[4], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
672         RGraph.Text(this.context, font, size, this.canvas.width - (this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (1/5)), this.canvas.height - this.Get('chart.gutter') + 14,RGraph.number_format(this, this.scale[3], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
673         RGraph.Text(this.context, font, size, this.canvas.width - (this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (2/5)), this.canvas.height - this.Get('chart.gutter') + 14,RGraph.number_format(this, this.scale[2], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
674         RGraph.Text(this.context, font, size, this.canvas.width - (this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (3/5)), this.canvas.height - this.Get('chart.gutter') + 14,RGraph.number_format(this, this.scale[1], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
675         RGraph.Text(this.context, font, size, this.canvas.width - (this.Get('chart.gutter') + ((this.canvas.width - this.Get('chart.gutter.center') - (2 * this.Get('chart.gutter'))) / 2) * (4/5)), this.canvas.height - this.Get('chart.gutter') + 14,RGraph.number_format(this, this.scale[0], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
676     }
677     
678     /**
679     * Draws the titles
680     */
681     RGraph.Bipolar.prototype.DrawTitles = function ()
682     {
683         RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), 30, (this.Get('chart.gutter') / 2) + 5, String(this.Get('chart.title.left')), 'center');
684         RGraph.Text(this.context,this.Get('chart.text.font'), this.Get('chart.text.size'), this.canvas.width - 30, (this.Get('chart.gutter') / 2) + 5, String(this.Get('chart.title.right')), 'center', 'right');
685         
686         // Draw the main title for the whole chart
687         RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.Get('chart.gutter'));
688     }
689
690
691     /**
692     * This function is used by MSIE only to manually draw the shadow
693     * 
694     * @param array coords The coords for the bar
695     */
696     RGraph.Bipolar.prototype.DrawIEShadow = function (coords)
697     {
698         var prevFillStyle = this.context.fillStyle;
699         var offsetx = this.Get('chart.shadow.offsetx');
700         var offsety = this.Get('chart.shadow.offsety');
701         
702         this.context.lineWidth = this.Get('chart.linewidth');
703         this.context.fillStyle = this.Get('chart.shadow.color');
704         this.context.beginPath();
705         
706     // Draw shadow here
707     this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2],coords[3]);
708
709         this.context.fill();
710         
711         // Change the fillstyle back to what it was
712         this.context.fillStyle = prevFillStyle;
713     }