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