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      * @param {Raphael} paper  The paper to render the graph on
141      * @param {int} x    left coord
142      * @param {int} y   right coord
143      * @param {int} width  width of graph
144      * @param {int} height heigt of graph
145      * @param {Array} values - single = [1,2,3] or multiple [ [1,2,3] , [1,2,3]]
146      * @param {Object} opts  - options
147      *    type :  {String} square | ???
148      *    gutter: {Number} percentage width default 20%
149      *    vgutter : {Number}  vertical gutter?
150      *    colors : {Array} list of colours - default chartinst.colors
151      *    xvalues : {Array} not sure - this looks like can be single or multi dimensional array 
152      *    yvalues : {Array} not sure - this looks like can be single or multi dimensional array
153      *    ymin :
154      *    axis: {String} eg. "top right bottom left" - which sides to show an axis eg. "0 0 1 1"
155      *    axisxstep
156      *    axisystep
157      *    axisxlabels: {Array} - note - first element appears left of first bar..
158      *    stacked
159      *    to
160      *    stretch
161      *    labels : {Array} labels to go above the chart..
162      *    
163      */
164     function VBarchart(paper, x, y, width, height, values, opts) {
165         opts = opts || {};
166         opts.xvalues 
167         
168         var chartinst = this,
169             type = opts.type || "square",
170             gutter = parseFloat(opts.gutter || "20%"),
171             chart = paper.set(),
172             bars = paper.set(),
173 //            covers = paper.set(),
174 //            covers2 = paper.set(),
175             total = Math.max.apply(Math, values),
176             stacktotal = [],
177             multi = 0,
178             colors = opts.colors || chartinst.colors,
179             len = values.length + 2,
180             sum = 0;
181             
182         opts.xvalues = opts.xvalues|| [];
183         if (!paper.raphael.is(opts.xvalues[0], "array")) {
184             opts.xvalues = [opts.xvalues];
185         }
186         opts.yvalues = opts.yvalues|| [];
187         if (!paper.raphael.is(opts.yvalues[0], "array")) {
188             opts.yvalues = [opts.yvalues];
189         }
190         
191         
192         var allx = Array.prototype.concat.apply([], opts.xvalues),
193             ally = Array.prototype.concat.apply([], opts.yvalues),
194             xdim = chartinst.snapEnds(
195                 Math.min.apply(Math, allx),
196                 Math.max.apply(Math, allx),
197                 opts.xvalues[0].length - 1
198             ),
199             minx = xdim.from,
200             maxx = xdim.to,
201             ydim = chartinst.snapEnds(
202                     Math.min.apply(Math, ally),
203                     Math.max.apply(Math, ally),
204                     opts.yvalues[0].length - 1
205             ),
206             miny = typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin,
207             maxy = ydim.to,
208             kx = (width - gutter * 2) / ((maxx - minx) || 1),
209             ky = (height - gutter * 2) / ((maxy - miny) || 1);
210             
211        
212         
213         if (Raphael.is(values[0], "array") && values.length > 1) {
214             total = [];
215             multi = len;
216             len = 0;
217
218             for (var i = values.length; i--;) {
219                 bars.push(paper.set());
220                 total.push(Math.max.apply(Math, values[i]));
221                 len = Math.max(len, values[i].length);
222             }
223
224             for (var i = values.length; i--;) {
225                 if (values[i].length < len) {
226                     for (var j = len; j--;) {
227                         values[i].push(0);
228                     }
229                 }
230             }
231
232             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
233         }
234         
235         for (var i = 0; i < len; i++) {
236             sum += values[i];
237             values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
238         }
239         
240         total = (opts.to) || total;
241         Roo.log([width, len, gutter]);
242         var barwidth = width / (len * (90 + gutter) + gutter) * 100,
243             barhgutter = barwidth * (gutter) / 100,
244             barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
245             stack = [],
246             X = x + barhgutter,
247             Y = (height - 2 * barvgutter) / total;
248         Roo.log([barwidth, X, barhgutter]);
249         if (!opts.stretch) {
250             barhgutter = Math.round(barhgutter);
251             barwidth = Math.floor(barwidth);
252         }
253         
254         
255          var axis = paper.set();
256         
257         if (opts.axis) {
258             
259             // Roo.log(opts.axis);
260             
261             // Value - "top right bottom left". If
262             var ax = (opts.axis + "").split(/[,\s]+/);
263             
264             // top axis
265             +ax[0] && axis.push(
266                     chartinst.axis(
267                         x + gutter,
268                         y + gutter,
269                         width - 2 * gutter,
270                         minx,
271                         maxx,
272                         opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
273                         2,
274                         paper));
275             // right axis
276             +ax[1] && axis.push(
277                     chartinst.axis(
278                         x + width - gutter,
279                         y + height - gutter,
280                         height - 2 * gutter,
281                         miny, maxy,
282                         opts.axisystep || Math.floor((height - 2 * gutter) / 20),
283                         3,
284                         paper
285                     ));
286             // bottom axis
287             opts.axisxlabels = opts.axisxlabels || [];
288             // add elements at beginning and end of array...
289             opts.axisxlabels.unshift('');
290             opts.axisxlabels.push('');
291             opts.axisxstep  = opts.axisxstep  || (opts.axisxlabels ? opts.axisxlabels.length -2   : false ) || len;
292             
293             +ax[2] && axis.push(
294                 // bottom
295                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
296                 chartinst.axis(
297                     x + gutter ,
298                     y + height - gutter,
299                     width - gutter, // total width
300                     minx ,  // from
301                     maxx, //to
302                     opts.axisxstep ,
303                     0, // orientation
304                     opts.axisxlabels || false, // labels
305                     "-", // type
306                     5, // dash size
307                     paper
308                 ));
309             // left axis
310             +ax[3] && axis.push(
311                     chartinst.axis(
312                         x + gutter,
313                         y + height - gutter,
314                         height - 2 * gutter,
315                         miny,
316                         maxy ,
317 //                        opts.axisystep || Math.floor((height - 2 * gutter) / 20),
318                         10,
319                         1,
320                         paper
321             ));
322         }
323         
324         
325         !opts.stacked && (barwidth /= multi || 1);
326         
327         for (var i = 0; i < len; i++) {
328             stack = [];
329             if(i == 0){
330                 X += barwidth;
331             }
332             Roo.log( {X : X});
333             for (var j = 0; j < (multi || 1); j++) {
334                 var h = Math.round((multi ? values[j][i] : values[i]) * Y),
335                     top = y + height - barvgutter - h,
336                     bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] });
337                     
338 //                    bar = finger(x, Y + barwidth / 2, Math.round(8 * X), barwidth - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
339                 
340                 
341                 if (multi && bars[j]) { // bars[j] did not appear to exist?
342                     bars[j].push(bar);
343                 } else {
344                     bars.push(bar);
345                 }
346                 
347                 bar.y = top;
348                 bar.x = Math.round(X + barwidth / 2);
349                 bar.w = barwidth;
350                 bar.h = h;
351                 bar.value = multi ? values[j][i] : values[i];
352                 
353                 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
354                     bar.labels = paper.set();
355                     var qtyLbl = paper.text(
356                             bar.x - 10,
357                             (bar.y - 10),
358                             opts.labels[i])
359                             .attr({
360                                 fill : '#000',
361                                 'text-anchor' : 'start',
362                                 font : '10px Arial'
363                             });
364
365                     bar.labels.push(qtyLbl);
366                 }
367                 
368                 if (!opts.stacked) {
369                     X += barwidth;
370                 } else {
371                     stack.push(bar);
372                 }
373             }
374
375             //X += barhgutter + 25; -- why add 25??
376             X += barhgutter;
377         }
378         X = x + barhgutter;
379         
380         
381         /*
382          * create the legend for Vbarchart
383          * 
384          */
385         
386 //        var legend = function (labels, otherslabel, mark, dir) {
387 //            var h = Y + 10;
388 //            
389 //            labels = labels || [];
390 ////            dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south"; 
391 //            dir = "east"; // default set to east, do not support other position first...
392 //            mark = paper[mark && mark.toLowerCase()] || "circle";
393 //            chart.labels = paper.set();
394 //            
395 //            for (var i = 0; i < len; i++) {
396 //                var txt;
397 //                
398 //                values[i].others && (labels[i] = otherslabel || "Others");
399 //                
400 //                labels[i] = chartinst.labelise(labels[i], values[i], sum);
401 //                chart.labels.push(paper.set());
402 //                chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
403 //                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"}));
404 //                covers[i].label = chart.labels[i];
405 //                h += txt.getBBox().height * 1.2;
406 //            }
407 //            
408 //            var tr = {
409 //                    east: [width + 15, - Y +30]
410 ////                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
411 ////                    north: [-r - bb.width / 2, -r - bb.height - 10],
412 ////                    south: [ x - 5, 10]
413 //                }[dir];
414 //                
415 //            chart.labels.translate.apply(chart.labels, tr);
416 //            chart.push(chart.labels);
417 //        };
418 //
419 //        if (opts.legend) {
420 //            legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
421 //        }
422         
423         
424         
425 //        chart.hover = function (fin, fout) {
426 //            covers2.hide();
427 //            covers.show();
428 //            covers.mouseover(fin).mouseout(fout);
429 //            return this;
430 //        };
431 //
432 //        chart.hoverColumn = function (fin, fout) {
433 //            covers.hide();
434 //            covers2.show();
435 //            fout = fout || function () {};
436 //            covers2.mouseover(fin).mouseout(fout);
437 //            return this;
438 //        };
439 //
440 //        chart.click = function (f) {
441 //            covers2.hide();
442 //            covers.show();
443 //            covers.click(f);
444 //            return this;
445 //        };
446
447 //        chart.each = function (f) {
448 //            if (!Raphael.is(f, "function")) {
449 //                return this;
450 //            }
451 //            for (var i = covers.length; i--;) {
452 //                f.call(covers[i]);
453 //            }
454 //            return this;
455 //        };
456 //
457 //        chart.eachColumn = function (f) {
458 //            if (!Raphael.is(f, "function")) {
459 //                return this;
460 //            }
461 //            for (var i = covers2.length; i--;) {
462 //                f.call(covers2[i]);
463 //            }
464 //            return this;
465 //        };
466 //
467 //        chart.clickColumn = function (f) {
468 //            covers.hide();
469 //            covers2.show();
470 //            covers2.click(f);
471 //            return this;
472 //        };
473         
474 //        chart.push(bars, covers, covers2);
475         chart.bars = bars;
476 //        chart.covers = covers;
477         return chart;
478     };
479
480     /**
481      * Horizontal Barchart
482      */
483     function HBarchart(paper, x, y, width, height, values, opts) {
484         opts = opts || {};
485         
486         var chartinst = this,
487             type = opts.type || "square",
488             gutter = parseFloat(opts.gutter || "20%"),
489             chart = paper.set(),
490             bars = paper.set(),
491             covers = paper.set(),
492             covers2 = paper.set(),
493             total = Math.max.apply(Math, values),
494             stacktotal = [],
495             multi = 0,
496             colors = opts.colors || chartinst.colors,
497             len = values.length,
498             others = 0,
499             sum = 0;
500             
501         if (Raphael.is(values[0], "array")) {
502             total = [];
503             multi = len;
504             len = 0;
505
506             for (var i = values.length; i--;) {
507                 bars.push(paper.set());
508                 total.push(Math.max.apply(Math, values[i]));
509                 len = Math.max(len, values[i].length);
510             }
511
512             if (opts.stacked) {
513                 for (var i = len; i--;) {
514                     var tot = 0;
515                     for (var j = values.length; j--;) {
516                         tot +=+ values[j][i] || 0;
517                     }
518                     stacktotal.push(tot);
519                 }
520             }
521
522             for (var i = values.length; i--;) {
523                 if (values[i].length < len) {
524                     for (var j = len; j--;) {
525                         values[i].push(0);
526                     }
527                 }
528             }
529
530             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
531         }
532         
533         for (var i = 0; i < len; i++) {
534             sum += values[i];
535             values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
536         }
537         
538         if(opts.cut){
539            for (i = 0; i < len; i++) {
540                 if (i > opts.cut) {
541                     values[opts.cut].value += values[i];
542                     values[opts.cut].others = true;
543                     others = values[opts.cut].value;
544                 }
545             }
546             
547             len = Math.min(opts.cut + 1, values.length);
548             
549             others && values.splice(len) && (values[opts.cut].others = true);
550         }
551         
552         total = (opts.to) || Math.max.apply(Math, values);
553
554         var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
555             bargutter = Math.floor(barheight * gutter / 100),
556             stack = [],
557             Y = y + bargutter,
558             X = (width - 1) / total;
559
560         !opts.stacked && (barheight /= multi || 1);
561     
562         for (var i = 0; i < len; i++) {
563             stack = [];
564
565             for (var j = 0; j < (multi || 1); j++) {
566                 var val = multi ? values[j][i] : values[i],
567                     bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
568
569                 if (multi) {
570                     bars[j].push(bar);
571                 } else {
572                     bars.push(bar);
573                 }
574
575                 bar.x = x + Math.round(val * X);
576                 bar.y = Y + barheight / 2;
577                 bar.w = Math.round(val * X);
578                 bar.h = barheight;
579                 bar.value = +val;
580
581                 if (!opts.stacked) {
582                     Y += barheight;
583                 } else {
584                     stack.push(bar);
585                 }
586             }
587             
588             if (opts.stacked) {
589                 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
590
591                 covers2.push(cvr);
592                 cvr.bars = paper.set();
593
594                 var size = 0;
595
596                 for (var s = stack.length; s--;) {
597                     stack[s].toFront();
598                 }
599
600                 for (var s = 0, ss = stack.length; s < ss; s++) {
601                     var bar = stack[s],
602                         cover,
603                         val = Math.round((size + bar.value) * X),
604                         path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
605
606                     cvr.bars.push(bar);
607                     size && bar.attr({ path: path });
608                     bar.w = val;
609                     bar.x = x + val;
610                     covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
611                     cover.bar = bar;
612                     size += bar.value;
613                 }
614
615                 Y += barheight;
616             }
617
618             Y += bargutter;
619         }
620
621         covers2.toFront();
622         Y = y + bargutter;
623
624         if (!opts.stacked) {
625             for (var i = 0; i < len; i++) {
626                 for (var j = 0; j < (multi || 1); j++) {
627                     var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
628
629                     covers.push(cover);
630                     cover.bar = multi ? bars[j][i] : bars[i];
631                     cover.value = cover.bar.value;
632                     Y += barheight;
633                 }
634
635                 Y += bargutter;
636             }
637         }
638
639         chart.label = function (labels, isRight) {
640             labels = labels || [];
641             this.labels = paper.set();
642             var txtattr =  { };
643             for (var i = 0; i < len; i++) {
644                 for (var j = 0; j < (multi || 1); j++) {
645                     //var  label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
646                     var  label = Raphael.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
647                         X = isRight ?
648                                 bars[i * (multi || 1) + j].x + 5 : // - barheight / 2 + 3 :
649                                 x + 5,
650                         A = !isRight ? "end" : "start",
651                         L;
652
653                     this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
654
655                     if (L.getBBox().x < x + 5) {
656                         L.attr({x: x + 5, "text-anchor": "start"});
657                     } else {
658                         bars[i * (multi || 1) + j].label = L;
659                     }
660                 }
661             }
662
663             return this;
664         };
665         
666         var axis = paper.set();
667         
668         if (opts.axis) {    
669             // Roo.log(opts.axis);
670             
671             // Value - "top right bottom left". If
672             var ax = (opts.axis + "").split(/[,\s]+/);
673             
674             // top axis
675             +ax[0] && axis.push(// not in used at the moment
676                     chartinst.axis(
677                         x + gutter,
678                         y + gutter,
679                         width - 2 * gutter,
680                         0,
681                         0,
682                         opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
683                         2,
684                         paper));
685             // right axis
686             +ax[1] && axis.push(// not in used at the moment
687                     chartinst.axis(
688                         x + width - gutter,
689                         y + height - gutter,
690                         height - 2 * gutter,
691                         0,
692                         0,
693                         opts.axisystep || Math.floor((height - 2 * gutter) / 20),
694                         3,
695                         paper
696                     ));
697             // bottom axis
698             +ax[2] && axis.push(// not in used at the moment
699                 // bottom
700                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
701                 chartinst.axis(
702                     x + gutter,
703                     y + height - gutter,
704 //                    width - 2 * gutter,
705 //                    20,
706 //                    50,
707                     width - 20, // total width
708                     0,
709                     0,
710 //                    20,
711 //                    50,
712                     opts.axisxstep || Math.floor((width - 2 * gutter) / 20), // data length
713                     0,
714                     opts.axisxlabels || false,
715                     "-",
716                     5,
717                     paper
718                 ));
719             // left axis
720             +ax[3] && axis.push(// just hardcode this
721                 chartinst.axis(
722                     19, // x pos 
723                     240, // y pos
724                     220,// length
725                     0, // from
726                     0, // to
727                     0,// steps
728                     1,// orientation
729                     false, // labels
730                     "-",// type
731 //                        opts.axisystep || Math.floor((height - 2 * gutter) / 20),
732                     100,// dashsize
733                     paper
734             ));
735         }
736         
737         
738
739         chart.hover = function (fin, fout) {
740             covers2.hide();
741             covers.show();
742             fout = fout || function () {};
743             covers.mouseover(fin).mouseout(fout);
744             return this;
745         };
746
747         chart.hoverColumn = function (fin, fout) {
748             covers.hide();
749             covers2.show();
750             fout = fout || function () {};
751             covers2.mouseover(fin).mouseout(fout);
752             return this;
753         };
754
755         chart.each = function (f) {
756             if (!Raphael.is(f, "function")) {
757                 return this;
758             }
759             for (var i = covers.length; i--;) {
760                 f.call(covers[i]);
761             }
762             return this;
763         };
764
765         chart.eachColumn = function (f) {
766             if (!Raphael.is(f, "function")) {
767                 return this;
768             }
769             for (var i = covers2.length; i--;) {
770                 f.call(covers2[i]);
771             }
772             return this;
773         };
774
775         chart.click = function (f) {
776             covers2.hide();
777             covers.show();
778             covers.click(f);
779             return this;
780         };
781
782         chart.clickColumn = function (f) {
783             covers.hide();
784             covers2.show();
785             covers2.click(f);
786             return this;
787         };
788         
789         /*
790          * create the legend for Hbarchart
791          * 
792          */
793         
794         var legend = function (labels, otherslabel, mark, dir) {
795             var h = Y + 10;
796             
797             labels = labels || [];
798 //            dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south"; 
799             dir = "east"; // default set to east, do not support other position first...
800             mark = paper[mark && mark.toLowerCase()] || "circle";
801             chart.labels = paper.set();
802             
803             for (var i = 0; i < len; i++) {
804                 var txt;
805                 
806                 values[i].others && (labels[i] = otherslabel || "Others");
807 //                Roo.log(values[i]);
808                 labels[i] = chartinst.labelise(labels[i], values[i], sum);
809                 chart.labels.push(paper.set());
810                 chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
811                 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"}));
812                 covers[i].label = chart.labels[i];
813                 h += txt.getBBox().height * 1.2;
814             }
815 //            Roo.log(labels)
816             var tr = {
817                     east: [width + 25, - Y +30]
818 //                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
819 //                    north: [-r - bb.width / 2, -r - bb.height - 10],
820 //                    south: [ x - 5, 10]
821                 }[dir];
822                 
823             chart.labels.translate.apply(chart.labels, tr);
824             chart.push(chart.labels);
825         };
826
827         if (opts.legend) {
828             legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
829         }
830         
831         chart.push(bars, covers, covers2);
832         chart.bars = bars;
833         chart.covers = covers;
834         return chart;
835     };
836     
837     //inheritance
838     var F = function() {};
839     F.prototype = Raphael.g;
840     HBarchart.prototype = VBarchart.prototype = new F;
841     
842     Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
843         return new HBarchart(this, x, y, width, height, values, opts);
844     };
845     
846     Raphael.fn.barchart = function(x, y, width, height, values, opts) {
847         return new VBarchart(this, x, y, width, height, values, opts);
848     };
849 })();