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