g.circular.js
[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         
231         var allx = Array.prototype.concat.apply([], opts.xvalues),
232             ally = Array.prototype.concat.apply([], opts.yvalues),
233             
234             xdim = chartinst.snapEnds(
235                 Math.min.apply(Math, allx),
236                 Math.max.apply(Math, allx),
237                 opts.xvalues[0].length - 1
238             ),
239             minx = xdim.from,
240             maxx = xdim.to,
241             ydim = chartinst.snapEnds(
242                     Math.min.apply(Math, ally),
243                     Math.max.apply(Math, ally),
244                     opts.yvalues[0].length - 1
245             ),
246             miny = Math.floor(typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin),
247             maxy = ydim.to,
248             kx = (width - gutter * 2) / ((maxx - minx) || 1),
249             ky = (height - gutter * 2) / ((maxy - miny) || 1),
250             // axis...
251             ax = (opts.axis + "").split(/[,\s]+/),  // Value - "top right bottom left". If
252             axwidth_l = +ax[3] ? 20 : 0, // allow 20 for bar...
253             axwidth_r = +ax[1] ? 20 : 0,
254             axwidth = axwidth_l + axwidth_r,
255             
256             __enaxd__ = 0;
257         //Roo.log(ally);
258
259        
260         if (Raphael.is(values[0], "array")) {
261             total = [];
262             multi = len;
263             len = 0;
264
265             for (var i = values.length; i--;) {
266                 bars.push(paper.set());
267                 total.push(Math.max.apply(Math, values[i]));
268                 len = Math.max(len, values[i].length);
269             }
270
271             if (opts.stacked) {
272                 for (var i = len; i--;) {
273                     var tot = 0;
274
275                     for (var j = values.length; j--;) {
276                         tot +=+ values[j][i] || 0;
277                     }
278
279                     stacktotal.push(tot);
280                 }
281             }
282
283             for (var i = values.length; i--;) {
284                 if (values[i].length < len) {
285                     for (var j = len; j--;) {
286                         values[i].push(0);
287                     }
288                 }
289             }
290
291             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
292         }
293         // modify the total?
294         
295         opts.axisystep = opts.axisystep  || Math.floor((height - 2 * gutter) / 20);
296         
297         
298         var yrangedivs = Math.ceil((maxy - miny) / opts.axisystep) || 1; 
299         var yrange = yrangedivs * opts.axisystep;
300         
301         //Roo.log([total, opts.axisystep, yrangedivs , yrange]);
302         
303         opts.axisystep = Math.ceil(total/yrangedivs);
304          
305         total = maxy = miny + (yrangedivs * opts.axisystep);
306         
307         total = (opts.to) || total;
308         //Roo.log([total])
309         //
310         
311         var barwidth = (width - axwidth) / (len * (100 + gutter) + gutter) * 100,
312             barhgutter = barwidth * gutter / 100,
313             barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
314             stack = [],
315             X = x + barhgutter + axwidth_l,
316             Y = (height - 2 * barvgutter) / total;
317         //Roo.log([height , barvgutter ,  total]);
318
319         
320         
321         
322         
323         
324         
325         if (!opts.stretch) {
326             barhgutter = Math.round(barhgutter);
327             barwidth = Math.floor(barwidth);
328         }
329
330         !opts.stacked && (barwidth /= multi || 1);
331         
332         
333         
334         
335         
336          
337         for (var i = 0; i < len; i++) {
338             stack = [];
339
340             for (var j = 0; j < (multi || 1); j++) {
341                 //Roo.log( [ 'vbar-loop', multi ? values[j][i] : values[i],  Y]);
342                 var color = colors[multi ? j : i%colors.length];
343                 
344                 var h = Math.round((multi ? values[j][i] : values[i]) * Y),
345                     top = y + height - barvgutter - h,
346                     bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: color });
347                 //Roo.log([y , height , barvgutter ,h]);
348                  
349                 if (multi) {
350                     bars[j].push(bar);
351                 } else {
352                     bars.push(bar);
353                 }
354
355                 bar.y = top;
356                 bar.x = Math.round(X + barwidth / 2);
357                 bar.w = barwidth;
358                 bar.h = h;
359                 bar.value = multi ? values[j][i] : values[i];
360                 
361                  if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
362                     bar.labels = paper.set();
363                     var qtyLbl = paper.text(
364                             bar.x - 10,
365                             (bar.y - 10),
366                             opts.labels[i]).attr({
367                                 fill : '#000',
368                                 'text-anchor' : 'start',
369                                 'font-family' : 'Arial',
370                                 'font-size' : 10
371                             });
372
373                     bar.labels.push(qtyLbl);
374                 }
375                 
376                 if (!opts.stacked) {
377                     X += barwidth;
378                 } else {
379                     stack.push(bar);
380                 }
381             }
382
383             if (opts.stacked) {
384                 var cvr;
385
386                 covers2.push(cvr = paper.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(chartinst.shim));
387                 cvr.bars = paper.set();
388
389                 var size = 0;
390
391                 for (var s = stack.length; s--;) {
392                     stack[s].toFront();
393                 }
394
395                 for (var s = 0, ss = stack.length; s < ss; s++) {
396                     var bar = stack[s],
397                         cover,
398                         h = (size + bar.value) * Y,
399                         path = finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1, paper);
400
401                     cvr.bars.push(bar);
402                     size && bar.attr({path: path});
403                     bar.h = h;
404                     bar.y = y + height - barvgutter - !!size * .5 - h;
405                     covers.push(cover = paper.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(chartinst.shim));
406                     cover.bar = bar;
407                     cover.value = bar.value;
408                     size += bar.value;
409                 }
410
411                 X += barwidth;
412             }
413
414             X += barhgutter;
415         }
416
417         covers2.toFront();
418         X = x + barhgutter;
419
420         if (!opts.stacked) {
421             for (var i = 0; i < len; i++) {
422                 for (var j = 0; j < (multi || 1); j++) {
423                     var cover;
424
425                     covers.push(cover = paper.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(chartinst.shim));
426                     cover.bar = multi ? bars[j][i] : bars[i];
427                     cover.value = cover.bar.value;
428                     X += barwidth;
429                 }
430
431                 X += barhgutter;
432             }
433         }
434         //Roo.log("Final X: " + X);
435         
436         
437         if (opts.axis) {
438             var axis = paper.set();
439             // Roo.log(opts.axis);
440             
441             
442             
443             
444             // top axis
445             +ax[0] && axis.push(
446                     chartinst.axis(
447                         x + gutter,
448                         y + gutter,
449                         width - 2 * gutter,
450                         minx,
451                         maxx,
452                         opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
453                         2,
454                         paper));
455             // right axis
456             +ax[1] && axis.push(
457                     chartinst.axis(
458                         x + width - gutter,
459                         y + height - gutter,
460                         height - 2 * gutter,
461                         miny, maxy,
462                         opts.axisystep || Math.floor((height - 2 * gutter) / 20),
463                         3,
464                         paper
465                     ));
466             // bottom axis
467             opts.axisxlabels = opts.axisxlabels || [];
468             // add elements at beginning and end of array...
469             //opts.axisxlabels.unshift(' ');
470             //opts.axisxlabels.push(' ');
471             opts.axisxstep  = opts.axisxstep  || (opts.axisxlabels ? opts.axisxlabels.length -1   : false ) || len;
472             
473             //Roo.log([width,len * (barwidth + barhgutter) + barhgutter])
474             // width from left to end right == (X-x)
475             // each bar is 
476             
477             var ax_args = {
478                     x : x +  axwidth_l ,
479                     y: y + height - gutter,
480                     length : X  - x, // total width
481                     from: minx ,  // from -- infinity??? if no 'xvalues set'
482                     to : maxx, //to
483                     steps : opts.axisxstep , // 9 when we have 8 items?
484                     orientation : 0, // orientation
485                     labels : opts.axisxlabels || false, // labels
486                     type : "-", // type ofbarhgutter line
487                     dashsize : 5, // dash size
488                     paper: paper,
489                     loffset :   Math.round(barhgutter + (barwidth / 2.0)),
490                     roffset :   Math.round(barhgutter + (barwidth / 2.0))
491             };
492             //Roo.log(ax_args);
493             +ax[2] && axis.push(
494                 // bottom
495                 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
496                 chartinst.axis( ax_args)
497             );
498             // left axis
499             
500              // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
501             
502             // vertical steps (eg. number of Lables) - should be related to the range.
503             // eg. around 10? 
504             
505             //Roo.log(["Y axis:", maxy, miny, yrange, yrangedivs, opts.axisystep]);
506             
507             //Roo.log(opts.axisystep);
508             
509             
510             
511             +ax[3] && axis.push(
512                     chartinst.axis(
513                         x + gutter,
514                         y + height - gutter,
515                         height - 2 * gutter,
516                         miny,
517                         maxy ,
518                         opts.axisystep, 
519                         
520                         1,
521                         paper
522             ));
523         }
524         
525         
526         
527         
528         chart.label = function (labels, isBottom) {
529             labels = labels || [];
530             this.labels = paper.set();
531
532             var L, l = -Infinity;
533
534             if (opts.stacked) {
535                 for (var i = 0; i < len; i++) {
536                     var tot = 0;
537
538                     for (var j = 0; j < (multi || 1); j++) {
539                         tot += multi ? values[j][i] : values[i];
540
541                         if (j == multi - 1) {
542                             var label = paper.labelise(labels[i], tot, total);
543
544                             L = paper.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).attr(txtattr).insertBefore(covers[i * (multi || 1) + j]);
545
546                             var bb = L.getBBox();
547
548                             if (bb.x - 7 < l) {
549                                 L.remove();
550                             } else {
551                                 this.labels.push(L);
552                                 l = bb.x + bb.width;
553                             }
554                         }
555                     }
556                 }
557             } else {
558                 for (var i = 0; i < len; i++) {
559                     for (var j = 0; j < (multi || 1); j++) {
560                         var label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
561
562                         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]);
563
564                         var bb = L.getBBox();
565
566                         if (bb.x - 7 < l) {
567                             L.remove();
568                         } else {
569                             this.labels.push(L);
570                             l = bb.x + bb.width;
571                         }
572                     }
573                 }
574             }
575             return this;
576         };
577
578         
579         
580         
581         
582         
583         
584         chart.hover = function (fin, fout) {
585             covers2.hide();
586             covers.show();
587             covers.mouseover(fin).mouseout(fout);
588             return this;
589         };
590
591         chart.hoverColumn = function (fin, fout) {
592             covers.hide();
593             covers2.show();
594             fout = fout || function () {};
595             covers2.mouseover(fin).mouseout(fout);
596             return this;
597         };
598
599         chart.click = function (f) {
600             covers2.hide();
601             covers.show();
602             covers.click(f);
603             return this;
604         };
605
606         chart.each = function (f) {
607             if (!Raphael.is(f, "function")) {
608                 return this;
609             }
610             for (var i = covers.length; i--;) {
611                 f.call(covers[i]);
612             }
613             return this;
614         };
615
616         chart.eachColumn = function (f) {
617             if (!Raphael.is(f, "function")) {
618                 return this;
619             }
620             for (var i = covers2.length; i--;) {
621                 f.call(covers2[i]);
622             }
623             return this;
624         };
625
626         chart.clickColumn = function (f) {
627             covers.hide();
628             covers2.show();
629             covers2.click(f);
630             return this;
631         };
632
633         chart.push(bars, covers, covers2);
634         chart.bars = bars;
635         chart.covers = covers;
636         return chart;
637     };
638     
639     //inheritance
640     var F = function() {};
641     F.prototype = Raphael.g;
642     HBarchart.prototype = VBarchart.prototype = new F; //prototype reused by hbarchart
643     
644     Raphael.fn.barchart = function(x, y, width, height, values, opts) {
645         return new VBarchart(this, x, y, width, height, values, opts);
646     };
647     
648 /*\
649  * Paper.barchart
650  [ method ]
651  **
652  * Creates a horizontal bar chart
653  **
654  > Parameters
655  **
656  - x (number) x coordinate of the chart
657  - y (number) y coordinate of the chart
658  - width (number) width of the chart (respected by all elements in the set)
659  - height (number) height of the chart (respected by all elements in the set)
660  - values (array) values
661  - opts (object) options for the chart
662  o {
663  o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
664  o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
665  o vgutter (number)
666  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.
667  o stacked (boolean) whether or not to tread values as in a stacked bar chart
668  o to
669  o stretch (boolean)
670  o }
671  **
672  = (object) path element of the popup
673  > Usage
674  | r.barchart(0, 0, 620, 260, [76, 70, 67, 71, 69], {})
675  \*/
676  
677     function HBarchart(paper, x, y, width, height, values, opts) {
678         opts = opts || {};
679
680         var chartinst = this,
681             type = opts.type || "square",
682             gutter = parseFloat(opts.gutter || "20%"),
683             chart = paper.set(),
684             bars = paper.set(),
685             covers = paper.set(),
686             covers2 = paper.set(),
687             total = Math.max.apply(Math, values),
688             stacktotal = [],
689             multi = 0,
690             colors = opts.colors || chartinst.colors,
691             len = values.length;
692
693         if (Raphael.is(values[0], "array")) {
694             total = [];
695             multi = len;
696             len = 0;
697
698             for (var i = values.length; i--;) {
699                 bars.push(paper.set());
700                 total.push(Math.max.apply(Math, values[i]));
701                 len = Math.max(len, values[i].length);
702             }
703
704             if (opts.stacked) {
705                 for (var i = len; i--;) {
706                     var tot = 0;
707                     for (var j = values.length; j--;) {
708                         tot +=+ values[j][i] || 0;
709                     }
710                     stacktotal.push(tot);
711                 }
712             }
713
714             for (var i = values.length; i--;) {
715                 if (values[i].length < len) {
716                     for (var j = len; j--;) {
717                         values[i].push(0);
718                     }
719                 }
720             }
721
722             total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
723         }
724         
725         total = (opts.to) || total;
726
727         var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
728             bargutter = Math.floor(barheight * gutter / 100),
729             stack = [],
730             Y = y + bargutter,
731             X = (width - 1) / total;
732
733         !opts.stacked && (barheight /= multi || 1);
734
735         for (var i = 0; i < len; i++) {
736             stack = [];
737
738             for (var j = 0; j < (multi || 1); j++) {
739                 var val = multi ? values[j][i] : values[i],
740                     bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
741
742                 if (multi) {
743                     bars[j].push(bar);
744                 } else {
745                     bars.push(bar);
746                 }
747
748                 bar.x = x + Math.round(val * X);
749                 bar.y = Y + barheight / 2;
750                 bar.w = Math.round(val * X);
751                 bar.h = barheight;
752                 bar.value = +val;
753
754                 if (!opts.stacked) {
755                     Y += barheight;
756                 } else {
757                     stack.push(bar);
758                 }
759             }
760
761             if (opts.stacked) {
762                 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
763
764                 covers2.push(cvr);
765                 cvr.bars = paper.set();
766
767                 var size = 0;
768
769                 for (var s = stack.length; s--;) {
770                     stack[s].toFront();
771                 }
772
773                 for (var s = 0, ss = stack.length; s < ss; s++) {
774                     var bar = stack[s],
775                         cover,
776                         val = Math.round((size + bar.value) * X),
777                         path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
778
779                     cvr.bars.push(bar);
780                     size && bar.attr({ path: path });
781                     bar.w = val;
782                     bar.x = x + val;
783                     covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
784                     cover.bar = bar;
785                     size += bar.value;
786                 }
787
788                 Y += barheight;
789             }
790
791             Y += bargutter;
792         }
793
794         covers2.toFront();
795         Y = y + bargutter;
796
797         if (!opts.stacked) {
798             for (var i = 0; i < len; i++) {
799                 for (var j = 0; j < (multi || 1); j++) {
800                     var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
801
802                     covers.push(cover);
803                     cover.bar = multi ? bars[j][i] : bars[i];
804                     cover.value = cover.bar.value;
805                     Y += barheight;
806                 }
807
808                 Y += bargutter;
809             }
810         }
811
812         chart.label = function (labels, isRight) {
813             labels = labels || [];
814             this.labels = paper.set();
815
816             for (var i = 0; i < len; i++) {
817                 for (var j = 0; j < multi; j++) {
818                     var  label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
819                         X = isRight ? bars[i * (multi || 1) + j].x - barheight / 2 + 3 : x + 5,
820                         A = isRight ? "end" : "start",
821                         L;
822
823                     this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
824
825                     if (L.getBBox().x < x + 5) {
826                         L.attr({x: x + 5, "text-anchor": "start"});
827                     } else {
828                         bars[i * (multi || 1) + j].label = L;
829                     }
830                 }
831             }
832
833             return this;
834         };
835
836         chart.hover = function (fin, fout) {
837             covers2.hide();
838             covers.show();
839             fout = fout || function () {};
840             covers.mouseover(fin).mouseout(fout);
841             return this;
842         };
843
844         chart.hoverColumn = function (fin, fout) {
845             covers.hide();
846             covers2.show();
847             fout = fout || function () {};
848             covers2.mouseover(fin).mouseout(fout);
849             return this;
850         };
851
852         chart.each = function (f) {
853             if (!Raphael.is(f, "function")) {
854                 return this;
855             }
856             for (var i = covers.length; i--;) {
857                 f.call(covers[i]);
858             }
859             return this;
860         };
861
862         chart.eachColumn = function (f) {
863             if (!Raphael.is(f, "function")) {
864                 return this;
865             }
866             for (var i = covers2.length; i--;) {
867                 f.call(covers2[i]);
868             }
869             return this;
870         };
871
872         chart.click = function (f) {
873             covers2.hide();
874             covers.show();
875             covers.click(f);
876             return this;
877         };
878
879         chart.clickColumn = function (f) {
880             covers.hide();
881             covers2.show();
882             covers2.click(f);
883             return this;
884         };
885
886         chart.push(bars, covers, covers2);
887         chart.bars = bars;
888         chart.covers = covers;
889         return chart;
890     };
891     
892     Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
893         return new HBarchart(this, x, y, width, height, values, opts);
894     };
895     
896 })();