2 * o------------------------------------------------------------------------------o
3 * | This file is part of the RGraph package - you can learn more at: |
5 * | http://www.rgraph.net |
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: |
11 * | http://www.rgraph.net/LICENSE.txt |
12 * o------------------------------------------------------------------------------o
15 if (typeof(RGraph) == 'undefined') RGraph = {};
18 * The bar chart constructor
20 * @param object canvas The canvas object
21 * @param array data The chart data
23 RGraph.Bar = function (id, data)
25 // Get the canvas and context objects
27 this.canvas = document.getElementById(id);
28 this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
29 this.canvas.__object__ = this;
32 this.stackedOrGrouped = false;
36 * Compatibility with older browsers
38 RGraph.OldBrowserCompat(this.context);
41 // Various config type stuff
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,
57 'chart.smallyticks': 3,
58 'chart.largeyticks': 5,
59 'chart.numyticks': 10,
61 'chart.strokecolor': '#666',
62 'chart.axis.color': 'black',
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',
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,
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,
119 'chart.contextmenu': 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
149 alert('[BAR] No canvas support');
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');
159 * Determine whether the chart will contain stacked or grouped bars
161 for (i=0; i<data.length; ++i) {
162 if (typeof(data[i]) == 'object') {
163 this.stackedOrGrouped = true;
170 // Used to store the coords of the bars
178 * @param name string The name of the property to set
179 * @param value mixed The value of the property
181 RGraph.Bar.prototype.Set = function (name, value)
183 name = name.toLowerCase();
185 if (name == 'chart.labels.abovebar') {
186 name = 'chart.labels.above';
189 if (name == 'chart.strokestyle') {
190 name = 'chart.strokecolor';
193 this.properties[name] = value;
200 * @param name string The name of the property to get
202 RGraph.Bar.prototype.Get = function (name)
204 if (name == 'chart.labels.abovebar') {
205 name = 'chart.labels.above';
208 return this.properties[name];
213 * The function you call to draw the bar chart
215 RGraph.Bar.prototype.Draw = function ()
218 * Fire the onbeforedraw event
220 RGraph.FireCustomEvent(this, 'onbeforedraw');
223 * Clear all of this canvases event handlers (the ones installed by RGraph)
225 RGraph.ClearEventListeners(this.id);
228 * Convert any null values to 0. Won't make any difference to the bar (as opposed to the line chart)
230 for (var i=0; i<this.data.length; ++i) {
231 if (this.data[i] == null) {
237 // Cache this in a class variable as it's used rather a lot
238 this.gutter = this.Get('chart.gutter');
241 * Check for tooltips and alert the user that they're not supported with pyramid charts
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) {
248 alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts');
252 * Stop the coords array from growing uncontrollably
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
261 this.grapharea = this.canvas.height - ( (2 * this.gutter));
262 this.halfgrapharea = this.grapharea / 2;
263 this.halfTextHeight = this.Get('chart.text.size') / 2;
265 // Progressively Draw the chart
266 RGraph.background.Draw(this);
269 //If it's a sketch chart variant, draw the axes first
270 if (this.Get('chart.variant') == 'sketch') {
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'));
288 * Setup the context menu if required
290 if (this.Get('chart.contextmenu')) {
291 RGraph.ShowContext(this);
296 * Is a line is defined, draw it
298 var line = this.Get('chart.line');
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");
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");
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));
320 // If a custom yMax is set, use that
321 if (this.Get('chart.ymax')) {
322 line.Set('chart.ymax', this.Get('chart.ymax'));
330 * Draw "in graph" labels
332 if (this.Get('chart.labels.ingraph')) {
333 RGraph.DrawInGraphLabels(this);
339 if (this.Get('chart.crosshairs')) {
340 RGraph.DrawCrosshairs(this);
344 * If the canvas is annotatable, do install the event handlers
346 if (this.Get('chart.annotatable')) {
347 RGraph.Annotate(this);
351 * This bit shows the mini zoom window if requested
353 if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
354 RGraph.ShowZoomWindow(this);
359 * This function enables resizing
361 if (this.Get('chart.resizable')) {
362 RGraph.AllowResizing(this);
367 * This function enables adjusting
369 if (this.Get('chart.adjustable')) {
370 RGraph.AllowAdjusting(this);
374 * Fire the RGraph ondraw event
376 RGraph.FireCustomEvent(this, 'ondraw');
381 * Draws the charts axes
383 RGraph.Bar.prototype.DrawAxes = function ()
385 var gutter = this.gutter;
386 var xaxispos = this.Get('chart.xaxispos');
387 var yaxispos = this.Get('chart.yaxispos');
389 this.context.beginPath();
390 this.context.strokeStyle = this.Get('chart.axis.color');
391 this.context.lineWidth = 1;
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);
398 this.context.moveTo(gutter, gutter);
399 this.context.lineTo(gutter, this.canvas.height - gutter);
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);
406 var numYTicks = this.Get('chart.numyticks');
408 // Draw the Y tickmarks
409 var yTickGap = (this.canvas.height - (2 * gutter)) / numYTicks;
410 var xpos = yaxispos == 'left' ? gutter : this.canvas.width - gutter;
413 xaxispos == 'center' ? y <= (this.canvas.height - gutter) : y < (this.canvas.height - gutter);
416 if (xaxispos == 'center' && y == (this.canvas.height / 2)) continue;
418 this.context.moveTo(xpos, y);
419 this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), y);
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;
427 //////////////// X TICKS ////////////////
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;
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);
440 //////////////// X TICKS ////////////////
442 this.context.stroke();
449 RGraph.Bar.prototype.Drawbars = function ()
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];
456 var gutter = this.gutter;
457 var decimals = this.Get('chart.scale.decimals');
460 * Work out the max value
462 if (this.Get('chart.ymax')) {
463 this.max = this.Get('chart.ymax');
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)
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])) ;
478 var value = Number(this.data[i]);
481 this.max = Math.max(Math.abs(this.max), Math.abs(value));
484 this.scale = RGraph.getScale(this.max, this);
485 this.max = this.scale[4];
487 if (this.Get('chart.scale.decimals')) {
488 var decimals = this.Get('chart.scale.decimals');
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);
499 * Draw horizontal bars here
501 if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0) {
502 RGraph.DrawBars(this);
505 var variant = this.Get('chart.variant');
508 * Draw the 3D axes is necessary
510 if (variant == '3d') {
511 RGraph.Draw3DAxes(this);
515 * Get the variant once, and draw the bars, be they regular, stacked or grouped
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');
531 for (i=0; i<this.data.length; ++i) {
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) );
537 // Half the height if the Y axis is at the center
538 if (xaxispos == 'center') {
542 var x = (i * width) + gutter;
543 var y = xaxispos == 'center' ? (this.canvas.height / 2) - height : this.canvas.height - height - gutter;
546 // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
549 height = Math.abs(height);
553 * Turn on the shadow if need be
556 this.context.shadowColor = shadowColor;
557 this.context.shadowBlur = shadowBlur;
558 this.context.shadowOffsetX = shadowOffsetX;
559 this.context.shadowOffsetY = shadowOffsetY;
565 this.context.beginPath();
566 if (typeof(this.data[i]) == 'number') {
568 var barWidth = width - (2 * hmargin);
570 // Set the fill color
571 this.context.strokeStyle = strokeStyle;
572 this.context.fillStyle = colors[0];
574 if (variant == 'sketch') {
576 this.context.lineCap = 'round';
578 var sketchOffset = 3;
580 this.context.beginPath();
582 this.context.strokeStyle = colors[0];
585 this.context.moveTo(x + hmargin + 2, y + height - 2);
586 this.context.lineTo(x + hmargin , y - 2);
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));
594 this.context.moveTo(x + hmargin + width - 2, y + -2);
595 this.context.lineTo(x + hmargin + width - 3, y + height - 3);
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));
602 this.context.stroke();
605 } else if (variant == 'bar' || variant == '3d' || variant == 'glass') {
607 if (document.all && shadow) {
608 this.DrawIEShadow([x + hmargin, y, barWidth, height]);
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);
615 this.context.strokeRect(x + hmargin, y, barWidth, height);
616 this.context.fillRect(x + hmargin, y, barWidth, height);
620 // This bit draws the text labels that appear above the bars if requested
621 if (this.Get('chart.labels.above')) {
623 // Turn off any shadow
625 RGraph.NoShadow(this);
630 // Account for negative bars
631 if (this.data[i] < 0) {
632 yPos += height + 6 + (this.Get('chart.text.size') - 4);
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');
640 if (variant == '3d') {
642 var prevStrokeStyle = this.context.strokeStyle;
643 var prevFillStyle = this.context.fillStyle;
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();
653 this.context.stroke();
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();
664 this.context.stroke();
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();
677 this.context.stroke();
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();
690 this.context.stroke();
693 this.context.strokeStyle = prevStrokeStyle;
694 this.context.fillStyle = prevFillStyle;
697 } else if (variant == 'glass') {
699 var grad = this.context.createLinearGradient(
702 x + hmargin + (barWidth / 2),
705 grad.addColorStop(0, 'rgba(255,255,255,0.9)');
706 grad.addColorStop(1, 'rgba(255,255,255,0.5)');
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);
715 } else if (variant == 'dot') {
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();
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);
726 // Set the colour for the dots
727 this.context.fillStyle = this.Get('chart.colors')[0];
729 this.context.stroke();
733 } else if (variant == 'pyramid') {
735 this.context.beginPath();
736 var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.Get('chart.gutter')));
738 this.context.moveTo(x + hmargin, startY);
740 x + hmargin + (barWidth / 2),
741 y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)
743 this.context.lineTo(x + hmargin + barWidth, startY);
745 this.context.closePath();
747 this.context.stroke();
751 } else if (variant == 'arrow') {
752 var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.gutter));
754 this.context.lineWidth = this.Get('chart.linewidth') ? this.Get('chart.linewidth') : 1;
755 this.context.lineCap = 'round';
757 this.context.beginPath();
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),
764 this.data[i] > 0 ? 0.78 : 5.6,
765 this.data[i] > 0 ? 0.79 : 5.48,
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),
772 this.data[i] > 0 ? 2.355 : 4,
773 this.data[i] > 0 ? 2.4 : 3.925,
776 this.context.stroke();
778 this.context.lineWidth = 1;
780 // Unknown variant type
782 alert('[BAR] Warning! Unknown chart.variant: ' + variant);
785 this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
791 } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') {
793 var barWidth = width - (2 * hmargin);
794 var redrawCoords = [];// Necessary to draw if the shadow is enabled
797 for (j=0; j<this.data[i].length; ++j) {
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.");
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.');
811 // Set the fill and stroke colors
812 this.context.strokeStyle = strokeStyle
813 this.context.fillStyle = colors[j];
815 var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.gutter) );
817 // If the X axis pos is in the center, we need to half the height
818 if (xaxispos == 'center') {
822 var totalHeight = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - hmargin - (2 * this.gutter));
825 * Store the coords for tooltips
827 this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
830 if (document.all && shadow) {
831 this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]);
834 this.context.strokeRect(x + hmargin, y, width - (2 * hmargin), height);
835 this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height);
844 * Store the redraw coords if the shadow is enabled
847 redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, colors[j]]);
853 if (variant == '3d') {
855 var prevFillStyle = this.context.fillStyle;
856 var prevStrokeStyle = this.context.strokeStyle;
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();
869 this.context.stroke();
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();
881 this.context.stroke();
883 // Draw the darker top side
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();
894 this.context.stroke();
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();
907 this.context.stroke();
909 this.context.strokeStyle = prevStrokeStyle;
910 this.context.fillStyle = prevFillStyle;
916 // This bit draws the text labels that appear above the bars if requested
917 if (this.Get('chart.labels.above')) {
919 // Turn off any shadow
920 RGraph.NoShadow(this);
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');
925 // Turn any shadow back on
927 this.context.shadowColor = shadowColor;
928 this.context.shadowBlur = shadowBlur;
929 this.context.shadowOffsetX = shadowOffsetX;
930 this.context.shadowOffsetY = shadowOffsetY;
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
941 RGraph.NoShadow(this);
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]);
949 this.context.stroke();
953 // Reset the redraw coords to be empty
959 } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') {
961 var redrawCoords = [];
962 this.context.lineWidth = this.Get('chart.linewidth');
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];
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) );
972 // If the X axis pos is in the center, we need to half the height
973 if (xaxispos == 'center') {
977 var startX = x + hmargin + (j * individualBarWidth);
978 var startY = (xaxispos == 'bottom' ? this.canvas.height : (this.canvas.height / 2) + this.gutter) - this.gutter - height;
980 // Account for a bug in chrome that doesn't allow negative heights
983 height = Math.abs(height);
989 if (document.all && shadow) {
990 this.DrawIEShadow([startX, startY, individualBarWidth, height]);
993 this.context.strokeRect(startX, startY, individualBarWidth, height);
994 this.context.fillRect(startX, startY, individualBarWidth, height);
997 // This bit draws the text labels that appear above the bars if requested
998 if (this.Get('chart.labels.above')) {
1000 this.context.strokeStyle = 'rgba(0,0,0,0)';
1002 // Turn off any shadow
1004 RGraph.NoShadow(this);
1009 // Account for negative bars
1010 if (this.data[i][j] < 0) {
1011 yPos += height + 6 + (this.Get('chart.text.size') - 4);
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');
1017 // Turn any shadow back on
1019 this.context.shadowColor = shadowColor;
1020 this.context.shadowBlur = shadowBlur;
1021 this.context.shadowOffsetX = shadowOffsetX;
1022 this.context.shadowOffsetY = shadowOffsetY;
1029 if (variant == '3d') {
1030 var prevFillStyle = this.context.fillStyle;
1031 var prevStrokeStyle = this.context.strokeStyle;
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();
1041 this.context.fill();
1042 this.context.stroke();
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();
1052 this.context.fill();
1053 this.context.stroke();
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();
1065 this.context.fill();
1066 this.context.stroke();
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();
1077 this.context.fill();
1078 this.context.stroke();
1080 this.context.strokeStyle = prevStrokeStyle;
1081 this.context.fillStyle = prevFillStyle;
1084 this.coords.push([startX, startY, individualBarWidth, height]);
1086 // Facilitate shadows going to the left
1087 if (this.Get('chart.shadow')) {
1088 redrawCoords.push([startX, startY, individualBarWidth, height]);
1093 * Redraw the bar if shadows are going to the left
1095 if (redrawCoords.length) {
1097 RGraph.NoShadow(this);
1099 this.context.lineWidth = this.Get('chart.linewidth');
1101 this.context.beginPath();
1102 for (var j=0; j<redrawCoords.length; ++j) {
1104 this.context.fillStyle = this.Get('chart.colors')[j];
1105 this.context.strokeStyle = this.Get('chart.strokecolor');
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]);
1110 this.context.fill();
1111 this.context.stroke();
1117 this.context.closePath();
1121 * Turn off any shadow
1123 RGraph.NoShadow(this);
1127 * Install the onclick event handler
1129 if (this.Get('chart.tooltips')) {
1131 // Need to register this object for redrawing
1132 RGraph.Register(this);
1135 * Install the window onclick handler
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);
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
1147 canvas_onmousemove = function (e)
1149 e = RGraph.FixEventObject(e);
1151 var canvas = document.getElementById(e.target.id);
1152 var obj = canvas.__object__;
1153 var barCoords = obj.getBar(e);
1156 * If there are bar coords AND the bar has height
1158 if (barCoords && barCoords[4] > 0) {
1161 * Get the tooltip text
1163 if (typeof(obj.Get('chart.tooltips')) == 'function') {
1164 var text = String(obj.Get('chart.tooltips')(barCoords[5]));
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]));
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]]);
1177 canvas.style.cursor = 'pointer';
1179 canvas.style.cursor = 'default';
1183 * Hide the currently displayed tooltip if the index is the same
1185 if ( RGraph.Registry.Get('chart.tooltip')
1186 && RGraph.Registry.Get('chart.tooltip').__canvas__.id != obj.id
1187 && obj.Get('chart.tooltips.event') == 'onmousemove') {
1190 RGraph.HideTooltip();
1194 * This facilitates the tooltips using the onmousemove event
1197 if ( obj.Get('chart.tooltips.event') == 'onmousemove'
1199 (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ != barCoords[5])
1200 || !RGraph.Registry.Get('chart.tooltip')
1204 * Show a tooltip if it's defined
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]);
1214 obj.context.stroke();
1217 RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]);
1220 canvas.style.cursor = 'default';
1223 RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove);
1224 this.canvas.addEventListener('mousemove', canvas_onmousemove, false);
1228 * Install the onclick event handler for the tooltips
1230 if (this.Get('chart.tooltips.event') == 'onclick') {
1232 canvas_onclick = function (e)
1234 var e = RGraph.FixEventObject(e);
1236 // If the button pressed isn't the left, we're not interested
1237 if (e.button != 0) return;
1239 e = RGraph.FixEventObject(e);
1241 var canvas = document.getElementById(this.id);
1242 var obj = canvas.__object__;
1243 var barCoords = obj.getBar(e);
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
1252 * Loop through the bars determining if the mouse is over a bar
1257 * Get the tooltip text
1259 if (typeof(obj.Get('chart.tooltips')) == 'function') {
1260 var text = String(obj.Get('chart.tooltips')(barCoords[5]));
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]));
1265 } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
1266 var text = String(obj.Get('chart.tooltips')[barCoords[5]]);
1273 * Show a tooltip if it's defined
1275 if (text && text != 'undefined') {
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]);
1284 obj.context.stroke();
1287 RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]);
1292 * Stop the event bubbling
1294 e.stopPropagation();
1296 RGraph.AddEventListener(this.id, 'click', canvas_onclick);
1297 this.canvas.addEventListener('click', canvas_onclick, false);
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)
1311 * Draws the labels for the graph
1313 RGraph.Bar.prototype.DrawLabels = function ()
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');
1322 // Draw the Y axis labels:
1323 if (this.Get('chart.ylabels')) {
1324 this.Drawlabels_center();
1325 this.Drawlabels_bottom();
1331 if (typeof(labels) == 'object' && labels) {
1333 var yOffset = 13 + Number(this.Get('chart.xlabels.offset'));
1339 var halign = 'center';
1341 if (text_angle > 0) {
1342 angle = -1 * text_angle;
1347 // Draw the X axis labels
1348 context.fillStyle = this.Get('chart.text.color');
1350 // How wide is each bar
1351 var barWidth = (this.canvas.width - (2 * gutter) ) / labels.length;
1353 // Reset the xTickGap
1354 xTickGap = (this.canvas.width - (2 * gutter)) / labels.length
1356 // Draw the X tickmarks
1358 var font = this.Get('chart.text.font');
1360 for (x=gutter + (xTickGap / 2); x<=this.canvas.width - gutter; x+=xTickGap) {
1361 RGraph.Text(context, font,
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),
1375 * Draws the X axis in the middle
1377 RGraph.Bar.prototype.Drawlabels_center = function ()
1379 var font = this.Get('chart.text.font');
1380 var numYLabels = this.Get('chart.ylabels.count');
1382 this.context.fillStyle = this.Get('chart.text.color');
1384 if (this.Get('chart.xaxispos') == 'center') {
1387 * Draw the top labels
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;
1399 this.context.fillStyle = this.Get('chart.text.color');
1400 this.context.strokeStyle = 'black';
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';
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';
1424 * Draw specific Y labels here so that the local variables can be reused
1426 if (typeof(this.Get('chart.ylabels.specific')) == 'object') {
1428 var labels = this.Get('chart.ylabels.specific');
1429 var grapharea = this.canvas.height - (2 * gutter);
1431 // Draw the top halves labels
1432 for (var i=0; i<labels.length; ++i) {
1433 var y = gutter + (grapharea * (i / (labels.length * 2) ));
1435 RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed);
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);
1442 RGraph.Text(context, font, text_size, xpos, y, labels[labels.length - i - 1], 'center', align, boxed);
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);
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);
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);
1471 } else if (numYLabels == 10) {
1473 interval = (this.grapharea / numYLabels) / 2;
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);
1479 ///////////////////////////////////////////////////////////////////////////////////
1482 * Draw the bottom (X axis) labels
1484 var interval = (this.grapharea) / 10;
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);
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);
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);
1499 } else if (numYLabels == 10) {
1501 // Arbitrary number of Y labels
1502 interval = (this.grapharea / numYLabels) / 2;
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);
1515 * Draws the X axdis at the bottom (the default)
1517 RGraph.Bar.prototype.Drawlabels_bottom = function ()
1519 this.context.beginPath();
1520 this.context.fillStyle = this.Get('chart.text.color');
1521 this.context.strokeStyle = 'black';
1523 if (this.Get('chart.xaxispos') != 'center') {
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');
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';
1540 var xpos = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : this.canvas.width - gutter + 5;
1545 * Draw specific Y labels here so that the local variables can be reused
1547 if (typeof(this.Get('chart.ylabels.specific')) == 'object') {
1549 var labels = this.Get('chart.ylabels.specific');
1550 var grapharea = this.canvas.height - (2 * gutter);
1552 for (var i=0; i<labels.length; ++i) {
1553 var y = gutter + (grapharea * (i / labels.length));
1555 RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed);
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);
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);
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);
1580 if (numYLabels == 10) {
1582 interval = (this.grapharea / numYLabels );
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);
1590 this.context.fill();
1591 this.context.stroke();
1596 * This function is used by MSIE only to manually draw the shadow
1598 * @param array coords The coords for the bar
1600 RGraph.Bar.prototype.DrawIEShadow = function (coords)
1602 var prevFillStyle = this.context.fillStyle;
1603 var offsetx = this.Get('chart.shadow.offsetx');
1604 var offsety = this.Get('chart.shadow.offsety');
1606 this.context.lineWidth = this.Get('chart.linewidth');
1607 this.context.fillStyle = this.Get('chart.shadow.color');
1608 this.context.beginPath();
1611 this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]);
1613 this.context.fill();
1615 // Change the fillstyle back to what it was
1616 this.context.fillStyle = prevFillStyle;
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
1624 RGraph.Bar.prototype.getBar = function (e)
1626 var canvas = e.target;
1627 var obj = e.target.__object__;
1628 var mouseCoords = RGraph.getMouseXY(e);
1631 * Loop through the bars determining if the mouse is over a bar
1633 for (var i=0; i<obj.coords.length; i++) {
1635 var mouseX = mouseCoords[0];
1636 var mouseY = mouseCoords[1];
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];
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]) ) {
1648 return [obj, left, top, width, height, i];