g.bar.js
[g.raphael] / g.bar.js
1 /*!
2  * g.Raphael 0.5 - Charting library, based on RaphaĆ«l
3  *
4  * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 (function () {
8     var mmin = Math.min,
9         mmax = Math.max;
10
11     function finger(x, y, width, height, dir, ending, isPath, paper) {
12         var path,
13             ends = { round: 'round', sharp: 'sharp', soft: 'soft', square: 'square' };
14
15         // dir 0 for horizontal and 1 for vertical
16         if ((dir && !height) || (!dir && !width)) {
17             return isPath ? "" : paper.path();
18         }
19
20         ending = ends[ending] || "square";
21         height = Math.round(height);
22         width = Math.round(width);
23         x = Math.round(x);
24         y = Math.round(y);
25
26         switch (ending) {
27             case "round":
28                 if (!dir) {
29                     var r = ~~(height / 2);
30
31                     if (width < r) {
32                         r = width;
33                         path = [
34                             "M", x + .5, y + .5 - ~~(height / 2),
35                             "l", 0, 0,
36                             "a", r, ~~(height / 2), 0, 0, 1, 0, height,
37                             "l", 0, 0,
38                             "z"
39                         ];
40                     } else {
41                         path = [
42                             "M", x + .5, y + .5 - r,
43                             "l", width - r, 0,
44                             "a", r, r, 0, 1, 1, 0, height,
45                             "l", r - width, 0,
46                             "z"
47                         ];
48                     }
49                 } else {
50                     r = ~~(width / 2);
51
52                     if (height < r) {
53                         r = height;
54                         path = [
55                             "M", x - ~~(width / 2), y,
56                             "l", 0, 0,
57                             "a", ~~(width / 2), r, 0, 0, 1, width, 0,
58                             "l", 0, 0,
59                             "z"
60                         ];
61                     } else {
62                         path = [
63                             "M", x - r, y,
64                             "l", 0, r - height,
65                             "a", r, r, 0, 1, 1, width, 0,
66                             "l", 0, height - r,
67                             "z"
68                         ];
69                     }
70                 }
71                 break;
72             case "sharp":
73                 if (!dir) {
74                     var half = ~~(height / 2);
75
76                     path = [
77                         "M", x, y + half,
78                         "l", 0, -height, mmax(width - half, 0), 0, mmin(half, width), half, -mmin(half, width), half + (half * 2 < height),
79                         "z"
80                     ];
81                 } else {
82                     half = ~~(width / 2);
83                     path = [
84                         "M", x + half, y,
85                         "l", -width, 0, 0, -mmax(height - half, 0), half, -mmin(half, height), half, mmin(half, height), half,
86                         "z"
87                     ];
88                 }
89                 break;
90             case "square":
91                 if (!dir) {
92                     path = [
93                         "M", x, y + ~~(height / 2),
94                         "l", 0, -height, width, 0, 0, height,
95                         "z"
96                     ];
97                 } else {
98                     path = [
99                         "M", x + ~~(width / 2), y,
100                         "l", 1 - width, 0, 0, -height, width - 1, 0,
101                         "z"
102                     ];
103                 }
104                 break;
105             case "soft":
106                 if (!dir) {
107                     r = mmin(width, Math.round(height / 5));
108                     path = [
109                         "M", x + .5, y + .5 - ~~(height / 2),
110                         "l", width - r, 0,
111                         "a", r, r, 0, 0, 1, r, r,
112                         "l", 0, height - r * 2,
113                         "a", r, r, 0, 0, 1, -r, r,
114                         "l", r - width, 0,
115                         "z"
116                     ];
117                 } else {
118                     r = mmin(Math.round(width / 5), height);
119                     path = [
120                         "M", x - ~~(width / 2), y,
121                         "l", 0, r - height,
122                         "a", r, r, 0, 0, 1, r, -r,
123                         "l", width - 2 * r, 0,
124                         "a", r, r, 0, 0, 1, r, r,
125                         "l", 0, height - r,
126                         "z"
127                     ];
128                 }
129         }
130
131         if (isPath) {
132             return path.join(",");
133         } else {
134             return paper.path(path);
135         }
136     }
137
138     /*
139      * Vertical Barchart
140      */
141     function VBarchart(paper, x, y, width, height, values, opts) {
142         opts = opts || {};
143         opts.xvalues 
144         
145         var chartinst = this,
146             type = opts.type || "square",
147             gutter = parseFloat(opts.gutter || "20%"),
148             chart = paper.set(),
149             bars = paper.set(),
150 //            covers = paper.set(),
151 //            covers2 = paper.set(),
152             total = Math.max.apply(Math, values),
153             stacktotal = [],
154             multi = 0,
155             colors = opts.colors || chartinst.colors,
156             len = values.length + 2,
157             sum = 0;
158             
159         opts.xvalues = opts.xvalues|| [];
160         if (!paper.raphael.is(opts.xvalues[0], "array")) {
161             opts.xvalues = [opts.xvalues];
162         }
163         opts.yvalues = opts.yvalues|| [];
164         if (!paper.raphael.is(opts.yvalues[0], "array")) {
165             opts.yvalues = [opts.yvalues];
166         }
167         
168         
169         var allx = Array.prototype.concat.apply([], opts.xvalues),
170             ally = Array.prototype.concat.apply([], opts.yvalues),
171             xdim = chartinst.snapEnds(
172                 Math.min.apply(Math, allx),
173                 Math.max.apply(Math, allx),
174                 opts.xvalues[0].length - 1
175             ),
176             minx = xdim.from,
177             maxx = xdim.to,
178             ydim = chartinst.snapEnds(
179                     Math.min.apply(Math, ally),
180                     Math.max.apply(Math, ally),
181                     opts.yvalues[0].length - 1
182             ),
183             miny = typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin,
184             maxy = ydim.to,
185             kx = (width - gutter * 2) / ((maxx - minx) || 1),
186             ky = (height - gutter * 2) / ((maxy - miny) || 1);
187             
188         var axis = paper.set();
189         
190         if (opts.axis) {
191             
192             // Roo.log(opts.axis);
193             
194             // Value - "top right bottom left". If
195             var ax = (opts.axis + "").split(/[,\s]+/);
196             
197             // top axis
198             +ax[0] && axis.push(
199                     chartinst.axis(
200                         x + gutter,
201                         y + gutter,
202                         width - 2 * gutter,
203                         minx,
204                         maxx,
205                         opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
206                         2,
207                         paper));
208             // right axis
209             +ax[1] && axis.push(
210                     chartinst.axis(
211                         x + width - gutter,
212                         y + height - gutter,
213                         height - 2 * gutter,
214                         miny, maxy,
215                         opts.axisystep || Math.floor((height - 2 * gutter) / 20),
216                         3,
217                         paper
218                     ));
219             // bottom axis
220             +ax[2] && axis.push(
221                 // bottom
222                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
223                 chartinst.axis(
224                     x + gutter,
225                     y + height - gutter,
226 //                    width - 2 * gutter,
227 //                    20,
228 //                    50,
229                     width - 20, // total width
230                     minx,
231                     maxx,
232 //                    20,
233 //                    50,
234                     opts.axisxstep || Math.floor((width - 2 * gutter) / 20), // data length
235                     0,
236                     opts.axisxlabels || false,
237                     "-",
238                     5,
239                     paper
240                 ));
241             // left axis
242             +ax[3] && axis.push(
243                     chartinst.axis(
244                         x + gutter,
245                         y + height - gutter,
246                         height - 2 * gutter,
247                         miny,
248                         maxy ,
249 //                        opts.axisystep || Math.floor((height - 2 * gutter) / 20),
250                         10,
251                         1,
252                         paper
253             ));
254         }
255         
256         if (Raphael.is(values[0], "array") && values.length > 1) {
257             total = [];
258             multi = len;
259             len = 0;
260
261             for (var i = values.length; i--;) {
262                 bars.push(paper.set());
263                 total.push(Math.max.apply(Math, values[i]));
264                 len = Math.max(len, values[i].length);
265             }
266
267             for (var i = values.length; i--;) {
268                 if (values[i].length < len) {
269                     for (var j = len; j--;) {
270                         values[i].push(0);
271                     }
272                 }
273             }
274
275             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
276         }
277         
278         for (var i = 0; i < len; i++) {
279             sum += values[i];
280             values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
281         }
282         
283         total = (opts.to) || total;
284         Roo.log([width, len, gutter]);
285         var barwidth = width / (len * (90 + gutter) + gutter) * 100,
286             barhgutter = barwidth * (gutter) / 100,
287             barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
288             stack = [],
289             X = x + barhgutter,
290             Y = (height - 2 * barvgutter) / total;
291         Roo.log([barwidth, X, barhgutter]);
292         if (!opts.stretch) {
293             barhgutter = Math.round(barhgutter);
294             barwidth = Math.floor(barwidth);
295         }
296         
297         !opts.stacked && (barwidth /= multi || 1);
298         
299         for (var i = 0; i < len; i++) {
300             stack = [];
301             if(i == 0){
302                 X += barwidth;
303             }
304             
305             for (var j = 0; j < (multi || 1); j++) {
306                 var h = Math.round((multi ? values[j][i] : values[i]) * Y),
307                     top = y + height - barvgutter - h,
308                     bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] });
309 //                    bar = finger(x, Y + barwidth / 2, Math.round(8 * X), barwidth - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
310                 if (multi && bars[j]) { // bars[j] did not appear to exist?
311                     bars[j].push(bar);
312                 } else {
313                     bars.push(bar);
314                 }
315                 
316                 bar.y = top;
317                 bar.x = Math.round(X + barwidth / 2);
318                 bar.w = barwidth;
319                 bar.h = h;
320                 bar.value = multi ? values[j][i] : values[i];
321                 
322                 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
323                     bar.labels = paper.set();
324                     var qtyLbl = paper.text(
325                             bar.x - 10,
326                             (bar.y - 10),
327                             opts.labels[i])
328                             .attr({
329                                 fill : '#000',
330                                 'text-anchor' : 'start',
331                                 font : '10px Arial'
332                             });
333
334                     bar.labels.push(qtyLbl);
335                 }
336                 
337                 if (!opts.stacked) {
338                     X += barwidth;
339                 } else {
340                     stack.push(bar);
341                 }
342             }
343
344             X += barhgutter + 25;
345         }
346         X = x + barhgutter;
347         
348         
349         /*
350          * create the legend for Vbarchart
351          * 
352          */
353         
354 //        var legend = function (labels, otherslabel, mark, dir) {
355 //            var h = Y + 10;
356 //            
357 //            labels = labels || [];
358 ////            dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south"; 
359 //            dir = "east"; // default set to east, do not support other position first...
360 //            mark = paper[mark && mark.toLowerCase()] || "circle";
361 //            chart.labels = paper.set();
362 //            
363 //            for (var i = 0; i < len; i++) {
364 //                var txt;
365 //                
366 //                values[i].others && (labels[i] = otherslabel || "Others");
367 //                
368 //                labels[i] = chartinst.labelise(labels[i], values[i], sum);
369 //                chart.labels.push(paper.set());
370 //                chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
371 //                chart.labels[i].push(txt = paper.text(x + 20, h, labels[i] || values[i]).attr(chartinst.txtattr).attr({ fill: opts.legendcolor || "#000", "text-anchor": "start"}));
372 //                covers[i].label = chart.labels[i];
373 //                h += txt.getBBox().height * 1.2;
374 //            }
375 //            
376 //            var tr = {
377 //                    east: [width + 15, - Y +30]
378 ////                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
379 ////                    north: [-r - bb.width / 2, -r - bb.height - 10],
380 ////                    south: [ x - 5, 10]
381 //                }[dir];
382 //                
383 //            chart.labels.translate.apply(chart.labels, tr);
384 //            chart.push(chart.labels);
385 //        };
386 //
387 //        if (opts.legend) {
388 //            legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
389 //        }
390         
391         
392         
393 //        chart.hover = function (fin, fout) {
394 //            covers2.hide();
395 //            covers.show();
396 //            covers.mouseover(fin).mouseout(fout);
397 //            return this;
398 //        };
399 //
400 //        chart.hoverColumn = function (fin, fout) {
401 //            covers.hide();
402 //            covers2.show();
403 //            fout = fout || function () {};
404 //            covers2.mouseover(fin).mouseout(fout);
405 //            return this;
406 //        };
407 //
408 //        chart.click = function (f) {
409 //            covers2.hide();
410 //            covers.show();
411 //            covers.click(f);
412 //            return this;
413 //        };
414
415 //        chart.each = function (f) {
416 //            if (!Raphael.is(f, "function")) {
417 //                return this;
418 //            }
419 //            for (var i = covers.length; i--;) {
420 //                f.call(covers[i]);
421 //            }
422 //            return this;
423 //        };
424 //
425 //        chart.eachColumn = function (f) {
426 //            if (!Raphael.is(f, "function")) {
427 //                return this;
428 //            }
429 //            for (var i = covers2.length; i--;) {
430 //                f.call(covers2[i]);
431 //            }
432 //            return this;
433 //        };
434 //
435 //        chart.clickColumn = function (f) {
436 //            covers.hide();
437 //            covers2.show();
438 //            covers2.click(f);
439 //            return this;
440 //        };
441         
442 //        chart.push(bars, covers, covers2);
443         chart.bars = bars;
444 //        chart.covers = covers;
445         return chart;
446     };
447
448     /**
449      * Horizontal Barchart
450      */
451     function HBarchart(paper, x, y, width, height, values, opts) {
452         opts = opts || {};
453         
454         var chartinst = this,
455             type = opts.type || "square",
456             gutter = parseFloat(opts.gutter || "20%"),
457             chart = paper.set(),
458             bars = paper.set(),
459             covers = paper.set(),
460             covers2 = paper.set(),
461             total = Math.max.apply(Math, values),
462             stacktotal = [],
463             multi = 0,
464             colors = opts.colors || chartinst.colors,
465             len = values.length,
466             others = 0,
467             sum = 0;
468             
469         if (Raphael.is(values[0], "array")) {
470             total = [];
471             multi = len;
472             len = 0;
473
474             for (var i = values.length; i--;) {
475                 bars.push(paper.set());
476                 total.push(Math.max.apply(Math, values[i]));
477                 len = Math.max(len, values[i].length);
478             }
479
480             if (opts.stacked) {
481                 for (var i = len; i--;) {
482                     var tot = 0;
483                     for (var j = values.length; j--;) {
484                         tot +=+ values[j][i] || 0;
485                     }
486                     stacktotal.push(tot);
487                 }
488             }
489
490             for (var i = values.length; i--;) {
491                 if (values[i].length < len) {
492                     for (var j = len; j--;) {
493                         values[i].push(0);
494                     }
495                 }
496             }
497
498             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
499         }
500         
501         for (var i = 0; i < len; i++) {
502             sum += values[i];
503             values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
504         }
505         
506         if(opts.cut){
507            for (i = 0; i < len; i++) {
508                 if (i > opts.cut) {
509                     values[opts.cut].value += values[i];
510                     values[opts.cut].others = true;
511                     others = values[opts.cut].value;
512                 }
513             }
514             
515             len = Math.min(opts.cut + 1, values.length);
516             
517             others && values.splice(len) && (values[opts.cut].others = true);
518         }
519         
520         total = (opts.to) || Math.max.apply(Math, values);
521
522         var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
523             bargutter = Math.floor(barheight * gutter / 100),
524             stack = [],
525             Y = y + bargutter,
526             X = (width - 1) / total;
527
528         !opts.stacked && (barheight /= multi || 1);
529     
530         for (var i = 0; i < len; i++) {
531             stack = [];
532
533             for (var j = 0; j < (multi || 1); j++) {
534                 var val = multi ? values[j][i] : values[i],
535                     bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
536
537                 if (multi) {
538                     bars[j].push(bar);
539                 } else {
540                     bars.push(bar);
541                 }
542
543                 bar.x = x + Math.round(val * X);
544                 bar.y = Y + barheight / 2;
545                 bar.w = Math.round(val * X);
546                 bar.h = barheight;
547                 bar.value = +val;
548
549                 if (!opts.stacked) {
550                     Y += barheight;
551                 } else {
552                     stack.push(bar);
553                 }
554             }
555             
556             if (opts.stacked) {
557                 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
558
559                 covers2.push(cvr);
560                 cvr.bars = paper.set();
561
562                 var size = 0;
563
564                 for (var s = stack.length; s--;) {
565                     stack[s].toFront();
566                 }
567
568                 for (var s = 0, ss = stack.length; s < ss; s++) {
569                     var bar = stack[s],
570                         cover,
571                         val = Math.round((size + bar.value) * X),
572                         path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
573
574                     cvr.bars.push(bar);
575                     size && bar.attr({ path: path });
576                     bar.w = val;
577                     bar.x = x + val;
578                     covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
579                     cover.bar = bar;
580                     size += bar.value;
581                 }
582
583                 Y += barheight;
584             }
585
586             Y += bargutter;
587         }
588
589         covers2.toFront();
590         Y = y + bargutter;
591
592         if (!opts.stacked) {
593             for (var i = 0; i < len; i++) {
594                 for (var j = 0; j < (multi || 1); j++) {
595                     var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
596
597                     covers.push(cover);
598                     cover.bar = multi ? bars[j][i] : bars[i];
599                     cover.value = cover.bar.value;
600                     Y += barheight;
601                 }
602
603                 Y += bargutter;
604             }
605         }
606
607         chart.label = function (labels, isRight) {
608             labels = labels || [];
609             this.labels = paper.set();
610             var txtattr =  { };
611             for (var i = 0; i < len; i++) {
612                 for (var j = 0; j < (multi || 1); j++) {
613                     //var  label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
614                     var  label = Raphael.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
615                         X = isRight ?
616                                 bars[i * (multi || 1) + j].x + 5 : // - barheight / 2 + 3 :
617                                 x + 5,
618                         A = !isRight ? "end" : "start",
619                         L;
620
621                     this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
622
623                     if (L.getBBox().x < x + 5) {
624                         L.attr({x: x + 5, "text-anchor": "start"});
625                     } else {
626                         bars[i * (multi || 1) + j].label = L;
627                     }
628                 }
629             }
630
631             return this;
632         };
633         
634         var axis = paper.set();
635         
636         if (opts.axis) {    
637             // Roo.log(opts.axis);
638             
639             // Value - "top right bottom left". If
640             var ax = (opts.axis + "").split(/[,\s]+/);
641             
642             // top axis
643             +ax[0] && axis.push(// not in used at the moment
644                     chartinst.axis(
645                         x + gutter,
646                         y + gutter,
647                         width - 2 * gutter,
648                         0,
649                         0,
650                         opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
651                         2,
652                         paper));
653             // right axis
654             +ax[1] && axis.push(// not in used at the moment
655                     chartinst.axis(
656                         x + width - gutter,
657                         y + height - gutter,
658                         height - 2 * gutter,
659                         0,
660                         0,
661                         opts.axisystep || Math.floor((height - 2 * gutter) / 20),
662                         3,
663                         paper
664                     ));
665             // bottom axis
666             +ax[2] && axis.push(// not in used at the moment
667                 // bottom
668                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
669                 chartinst.axis(
670                     x + gutter,
671                     y + height - gutter,
672 //                    width - 2 * gutter,
673 //                    20,
674 //                    50,
675                     width - 20, // total width
676                     0,
677                     0,
678 //                    20,
679 //                    50,
680                     opts.axisxstep || Math.floor((width - 2 * gutter) / 20), // data length
681                     0,
682                     opts.axisxlabels || false,
683                     "-",
684                     5,
685                     paper
686                 ));
687             // left axis
688             +ax[3] && axis.push(// just hardcode this
689                 chartinst.axis(
690                     19, // x pos 
691                     240, // y pos
692                     220,// length
693                     0, // from
694                     0, // to
695                     0,// steps
696                     1,// orientation
697                     false, // labels
698                     "-",// type
699 //                        opts.axisystep || Math.floor((height - 2 * gutter) / 20),
700                     100,// dashsize
701                     paper
702             ));
703         }
704         
705         
706
707         chart.hover = function (fin, fout) {
708             covers2.hide();
709             covers.show();
710             fout = fout || function () {};
711             covers.mouseover(fin).mouseout(fout);
712             return this;
713         };
714
715         chart.hoverColumn = function (fin, fout) {
716             covers.hide();
717             covers2.show();
718             fout = fout || function () {};
719             covers2.mouseover(fin).mouseout(fout);
720             return this;
721         };
722
723         chart.each = function (f) {
724             if (!Raphael.is(f, "function")) {
725                 return this;
726             }
727             for (var i = covers.length; i--;) {
728                 f.call(covers[i]);
729             }
730             return this;
731         };
732
733         chart.eachColumn = function (f) {
734             if (!Raphael.is(f, "function")) {
735                 return this;
736             }
737             for (var i = covers2.length; i--;) {
738                 f.call(covers2[i]);
739             }
740             return this;
741         };
742
743         chart.click = function (f) {
744             covers2.hide();
745             covers.show();
746             covers.click(f);
747             return this;
748         };
749
750         chart.clickColumn = function (f) {
751             covers.hide();
752             covers2.show();
753             covers2.click(f);
754             return this;
755         };
756         
757         /*
758          * create the legend for Hbarchart
759          * 
760          */
761         
762         var legend = function (labels, otherslabel, mark, dir) {
763             var h = Y + 10;
764             
765             labels = labels || [];
766 //            dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south"; 
767             dir = "east"; // default set to east, do not support other position first...
768             mark = paper[mark && mark.toLowerCase()] || "circle";
769             chart.labels = paper.set();
770             
771             for (var i = 0; i < len; i++) {
772                 var txt;
773                 
774                 values[i].others && (labels[i] = otherslabel || "Others");
775 //                Roo.log(values[i]);
776                 labels[i] = chartinst.labelise(labels[i], values[i], sum);
777                 chart.labels.push(paper.set());
778                 chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
779                 chart.labels[i].push(txt = paper.text(x + 20, h, labels[i] || values[i]).attr(chartinst.txtattr).attr({ fill: opts.legendcolor || "#000", "text-anchor": "start"}));
780                 covers[i].label = chart.labels[i];
781                 h += txt.getBBox().height * 1.2;
782             }
783 //            Roo.log(labels)
784             var tr = {
785                     east: [width + 25, - Y +30]
786 //                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
787 //                    north: [-r - bb.width / 2, -r - bb.height - 10],
788 //                    south: [ x - 5, 10]
789                 }[dir];
790                 
791             chart.labels.translate.apply(chart.labels, tr);
792             chart.push(chart.labels);
793         };
794
795         if (opts.legend) {
796             legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
797         }
798         
799         chart.push(bars, covers, covers2);
800         chart.bars = bars;
801         chart.covers = covers;
802         return chart;
803     };
804     
805     //inheritance
806     var F = function() {};
807     F.prototype = Raphael.g;
808     HBarchart.prototype = VBarchart.prototype = new F;
809     
810     Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
811         return new HBarchart(this, x, y, width, height, values, opts);
812     };
813     
814     Raphael.fn.barchart = function(x, y, width, height, values, opts) {
815         return new VBarchart(this, x, y, width, height, values, opts);
816     };
817 })();