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
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             +ax[2] && axis.push(
288                 // bottom
289                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
290                 chartinst.axis(
291                     x + gutter,
292                     y + height - gutter,
293                     width - gutter, // total width
294                     minx ,  // from
295                     maxx, //to
296                     opts.axisxstep || len, // steps
297                     0, // orientation
298                     opts.axisxlabels || false, // labels
299                     "-", // type
300                     5, // dash size
301                     paper
302                 ));
303             // left axis
304             +ax[3] && axis.push(
305                     chartinst.axis(
306                         x + gutter,
307                         y + height - gutter,
308                         height - 2 * gutter,
309                         miny,
310                         maxy ,
311 //                        opts.axisystep || Math.floor((height - 2 * gutter) / 20),
312                         10,
313                         1,
314                         paper
315             ));
316         }
317         
318         
319         !opts.stacked && (barwidth /= multi || 1);
320         
321         for (var i = 0; i < len; i++) {
322             stack = [];
323             if(i == 0){
324                 X += barwidth;
325             }
326             Roo.log( {X : X});
327             for (var j = 0; j < (multi || 1); j++) {
328                 var h = Math.round((multi ? values[j][i] : values[i]) * Y),
329                     top = y + height - barvgutter - h,
330                     bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] });
331                     
332 //                    bar = finger(x, Y + barwidth / 2, Math.round(8 * X), barwidth - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
333                 
334                 
335                 if (multi && bars[j]) { // bars[j] did not appear to exist?
336                     bars[j].push(bar);
337                 } else {
338                     bars.push(bar);
339                 }
340                 
341                 bar.y = top;
342                 bar.x = Math.round(X + barwidth / 2);
343                 bar.w = barwidth;
344                 bar.h = h;
345                 bar.value = multi ? values[j][i] : values[i];
346                 
347                 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
348                     bar.labels = paper.set();
349                     var qtyLbl = paper.text(
350                             bar.x - 10,
351                             (bar.y - 10),
352                             opts.labels[i])
353                             .attr({
354                                 fill : '#000',
355                                 'text-anchor' : 'start',
356                                 font : '10px Arial'
357                             });
358
359                     bar.labels.push(qtyLbl);
360                 }
361                 
362                 if (!opts.stacked) {
363                     X += barwidth;
364                 } else {
365                     stack.push(bar);
366                 }
367             }
368
369             //X += barhgutter + 25; -- why add 25??
370             X += barhgutter;
371         }
372         X = x + barhgutter;
373         
374         
375         /*
376          * create the legend for Vbarchart
377          * 
378          */
379         
380 //        var legend = function (labels, otherslabel, mark, dir) {
381 //            var h = Y + 10;
382 //            
383 //            labels = labels || [];
384 ////            dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south"; 
385 //            dir = "east"; // default set to east, do not support other position first...
386 //            mark = paper[mark && mark.toLowerCase()] || "circle";
387 //            chart.labels = paper.set();
388 //            
389 //            for (var i = 0; i < len; i++) {
390 //                var txt;
391 //                
392 //                values[i].others && (labels[i] = otherslabel || "Others");
393 //                
394 //                labels[i] = chartinst.labelise(labels[i], values[i], sum);
395 //                chart.labels.push(paper.set());
396 //                chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
397 //                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"}));
398 //                covers[i].label = chart.labels[i];
399 //                h += txt.getBBox().height * 1.2;
400 //            }
401 //            
402 //            var tr = {
403 //                    east: [width + 15, - Y +30]
404 ////                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
405 ////                    north: [-r - bb.width / 2, -r - bb.height - 10],
406 ////                    south: [ x - 5, 10]
407 //                }[dir];
408 //                
409 //            chart.labels.translate.apply(chart.labels, tr);
410 //            chart.push(chart.labels);
411 //        };
412 //
413 //        if (opts.legend) {
414 //            legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
415 //        }
416         
417         
418         
419 //        chart.hover = function (fin, fout) {
420 //            covers2.hide();
421 //            covers.show();
422 //            covers.mouseover(fin).mouseout(fout);
423 //            return this;
424 //        };
425 //
426 //        chart.hoverColumn = function (fin, fout) {
427 //            covers.hide();
428 //            covers2.show();
429 //            fout = fout || function () {};
430 //            covers2.mouseover(fin).mouseout(fout);
431 //            return this;
432 //        };
433 //
434 //        chart.click = function (f) {
435 //            covers2.hide();
436 //            covers.show();
437 //            covers.click(f);
438 //            return this;
439 //        };
440
441 //        chart.each = function (f) {
442 //            if (!Raphael.is(f, "function")) {
443 //                return this;
444 //            }
445 //            for (var i = covers.length; i--;) {
446 //                f.call(covers[i]);
447 //            }
448 //            return this;
449 //        };
450 //
451 //        chart.eachColumn = function (f) {
452 //            if (!Raphael.is(f, "function")) {
453 //                return this;
454 //            }
455 //            for (var i = covers2.length; i--;) {
456 //                f.call(covers2[i]);
457 //            }
458 //            return this;
459 //        };
460 //
461 //        chart.clickColumn = function (f) {
462 //            covers.hide();
463 //            covers2.show();
464 //            covers2.click(f);
465 //            return this;
466 //        };
467         
468 //        chart.push(bars, covers, covers2);
469         chart.bars = bars;
470 //        chart.covers = covers;
471         return chart;
472     };
473
474     /**
475      * Horizontal Barchart
476      */
477     function HBarchart(paper, x, y, width, height, values, opts) {
478         opts = opts || {};
479         
480         var chartinst = this,
481             type = opts.type || "square",
482             gutter = parseFloat(opts.gutter || "20%"),
483             chart = paper.set(),
484             bars = paper.set(),
485             covers = paper.set(),
486             covers2 = paper.set(),
487             total = Math.max.apply(Math, values),
488             stacktotal = [],
489             multi = 0,
490             colors = opts.colors || chartinst.colors,
491             len = values.length,
492             others = 0,
493             sum = 0;
494             
495         if (Raphael.is(values[0], "array")) {
496             total = [];
497             multi = len;
498             len = 0;
499
500             for (var i = values.length; i--;) {
501                 bars.push(paper.set());
502                 total.push(Math.max.apply(Math, values[i]));
503                 len = Math.max(len, values[i].length);
504             }
505
506             if (opts.stacked) {
507                 for (var i = len; i--;) {
508                     var tot = 0;
509                     for (var j = values.length; j--;) {
510                         tot +=+ values[j][i] || 0;
511                     }
512                     stacktotal.push(tot);
513                 }
514             }
515
516             for (var i = values.length; i--;) {
517                 if (values[i].length < len) {
518                     for (var j = len; j--;) {
519                         values[i].push(0);
520                     }
521                 }
522             }
523
524             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
525         }
526         
527         for (var i = 0; i < len; i++) {
528             sum += values[i];
529             values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
530         }
531         
532         if(opts.cut){
533            for (i = 0; i < len; i++) {
534                 if (i > opts.cut) {
535                     values[opts.cut].value += values[i];
536                     values[opts.cut].others = true;
537                     others = values[opts.cut].value;
538                 }
539             }
540             
541             len = Math.min(opts.cut + 1, values.length);
542             
543             others && values.splice(len) && (values[opts.cut].others = true);
544         }
545         
546         total = (opts.to) || Math.max.apply(Math, values);
547
548         var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
549             bargutter = Math.floor(barheight * gutter / 100),
550             stack = [],
551             Y = y + bargutter,
552             X = (width - 1) / total;
553
554         !opts.stacked && (barheight /= multi || 1);
555     
556         for (var i = 0; i < len; i++) {
557             stack = [];
558
559             for (var j = 0; j < (multi || 1); j++) {
560                 var val = multi ? values[j][i] : values[i],
561                     bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
562
563                 if (multi) {
564                     bars[j].push(bar);
565                 } else {
566                     bars.push(bar);
567                 }
568
569                 bar.x = x + Math.round(val * X);
570                 bar.y = Y + barheight / 2;
571                 bar.w = Math.round(val * X);
572                 bar.h = barheight;
573                 bar.value = +val;
574
575                 if (!opts.stacked) {
576                     Y += barheight;
577                 } else {
578                     stack.push(bar);
579                 }
580             }
581             
582             if (opts.stacked) {
583                 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
584
585                 covers2.push(cvr);
586                 cvr.bars = paper.set();
587
588                 var size = 0;
589
590                 for (var s = stack.length; s--;) {
591                     stack[s].toFront();
592                 }
593
594                 for (var s = 0, ss = stack.length; s < ss; s++) {
595                     var bar = stack[s],
596                         cover,
597                         val = Math.round((size + bar.value) * X),
598                         path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
599
600                     cvr.bars.push(bar);
601                     size && bar.attr({ path: path });
602                     bar.w = val;
603                     bar.x = x + val;
604                     covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
605                     cover.bar = bar;
606                     size += bar.value;
607                 }
608
609                 Y += barheight;
610             }
611
612             Y += bargutter;
613         }
614
615         covers2.toFront();
616         Y = y + bargutter;
617
618         if (!opts.stacked) {
619             for (var i = 0; i < len; i++) {
620                 for (var j = 0; j < (multi || 1); j++) {
621                     var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
622
623                     covers.push(cover);
624                     cover.bar = multi ? bars[j][i] : bars[i];
625                     cover.value = cover.bar.value;
626                     Y += barheight;
627                 }
628
629                 Y += bargutter;
630             }
631         }
632
633         chart.label = function (labels, isRight) {
634             labels = labels || [];
635             this.labels = paper.set();
636             var txtattr =  { };
637             for (var i = 0; i < len; i++) {
638                 for (var j = 0; j < (multi || 1); j++) {
639                     //var  label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
640                     var  label = Raphael.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
641                         X = isRight ?
642                                 bars[i * (multi || 1) + j].x + 5 : // - barheight / 2 + 3 :
643                                 x + 5,
644                         A = !isRight ? "end" : "start",
645                         L;
646
647                     this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
648
649                     if (L.getBBox().x < x + 5) {
650                         L.attr({x: x + 5, "text-anchor": "start"});
651                     } else {
652                         bars[i * (multi || 1) + j].label = L;
653                     }
654                 }
655             }
656
657             return this;
658         };
659         
660         var axis = paper.set();
661         
662         if (opts.axis) {    
663             // Roo.log(opts.axis);
664             
665             // Value - "top right bottom left". If
666             var ax = (opts.axis + "").split(/[,\s]+/);
667             
668             // top axis
669             +ax[0] && axis.push(// not in used at the moment
670                     chartinst.axis(
671                         x + gutter,
672                         y + gutter,
673                         width - 2 * gutter,
674                         0,
675                         0,
676                         opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
677                         2,
678                         paper));
679             // right axis
680             +ax[1] && axis.push(// not in used at the moment
681                     chartinst.axis(
682                         x + width - gutter,
683                         y + height - gutter,
684                         height - 2 * gutter,
685                         0,
686                         0,
687                         opts.axisystep || Math.floor((height - 2 * gutter) / 20),
688                         3,
689                         paper
690                     ));
691             // bottom axis
692             +ax[2] && axis.push(// not in used at the moment
693                 // bottom
694                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
695                 chartinst.axis(
696                     x + gutter,
697                     y + height - gutter,
698 //                    width - 2 * gutter,
699 //                    20,
700 //                    50,
701                     width - 20, // total width
702                     0,
703                     0,
704 //                    20,
705 //                    50,
706                     opts.axisxstep || Math.floor((width - 2 * gutter) / 20), // data length
707                     0,
708                     opts.axisxlabels || false,
709                     "-",
710                     5,
711                     paper
712                 ));
713             // left axis
714             +ax[3] && axis.push(// just hardcode this
715                 chartinst.axis(
716                     19, // x pos 
717                     240, // y pos
718                     220,// length
719                     0, // from
720                     0, // to
721                     0,// steps
722                     1,// orientation
723                     false, // labels
724                     "-",// type
725 //                        opts.axisystep || Math.floor((height - 2 * gutter) / 20),
726                     100,// dashsize
727                     paper
728             ));
729         }
730         
731         
732
733         chart.hover = function (fin, fout) {
734             covers2.hide();
735             covers.show();
736             fout = fout || function () {};
737             covers.mouseover(fin).mouseout(fout);
738             return this;
739         };
740
741         chart.hoverColumn = function (fin, fout) {
742             covers.hide();
743             covers2.show();
744             fout = fout || function () {};
745             covers2.mouseover(fin).mouseout(fout);
746             return this;
747         };
748
749         chart.each = function (f) {
750             if (!Raphael.is(f, "function")) {
751                 return this;
752             }
753             for (var i = covers.length; i--;) {
754                 f.call(covers[i]);
755             }
756             return this;
757         };
758
759         chart.eachColumn = function (f) {
760             if (!Raphael.is(f, "function")) {
761                 return this;
762             }
763             for (var i = covers2.length; i--;) {
764                 f.call(covers2[i]);
765             }
766             return this;
767         };
768
769         chart.click = function (f) {
770             covers2.hide();
771             covers.show();
772             covers.click(f);
773             return this;
774         };
775
776         chart.clickColumn = function (f) {
777             covers.hide();
778             covers2.show();
779             covers2.click(f);
780             return this;
781         };
782         
783         /*
784          * create the legend for Hbarchart
785          * 
786          */
787         
788         var legend = function (labels, otherslabel, mark, dir) {
789             var h = Y + 10;
790             
791             labels = labels || [];
792 //            dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south"; 
793             dir = "east"; // default set to east, do not support other position first...
794             mark = paper[mark && mark.toLowerCase()] || "circle";
795             chart.labels = paper.set();
796             
797             for (var i = 0; i < len; i++) {
798                 var txt;
799                 
800                 values[i].others && (labels[i] = otherslabel || "Others");
801 //                Roo.log(values[i]);
802                 labels[i] = chartinst.labelise(labels[i], values[i], sum);
803                 chart.labels.push(paper.set());
804                 chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
805                 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"}));
806                 covers[i].label = chart.labels[i];
807                 h += txt.getBBox().height * 1.2;
808             }
809 //            Roo.log(labels)
810             var tr = {
811                     east: [width + 25, - Y +30]
812 //                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
813 //                    north: [-r - bb.width / 2, -r - bb.height - 10],
814 //                    south: [ x - 5, 10]
815                 }[dir];
816                 
817             chart.labels.translate.apply(chart.labels, tr);
818             chart.push(chart.labels);
819         };
820
821         if (opts.legend) {
822             legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
823         }
824         
825         chart.push(bars, covers, covers2);
826         chart.bars = bars;
827         chart.covers = covers;
828         return chart;
829     };
830     
831     //inheritance
832     var F = function() {};
833     F.prototype = Raphael.g;
834     HBarchart.prototype = VBarchart.prototype = new F;
835     
836     Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
837         return new HBarchart(this, x, y, width, height, values, opts);
838     };
839     
840     Raphael.fn.barchart = function(x, y, width, height, values, opts) {
841         return new VBarchart(this, x, y, width, height, values, opts);
842     };
843 })();