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