2 * g.Raphael 0.5 - Charting library, based on Raphaƫl
4 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
5 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
11 function finger(x, y, width, height, dir, ending, isPath, paper) {
13 ends = { round: 'round', sharp: 'sharp', soft: 'soft', square: 'square' };
15 // dir 0 for horizontal and 1 for vertical
16 if ((dir && !height) || (!dir && !width)) {
17 return isPath ? "" : paper.path();
20 ending = ends[ending] || "square";
21 height = Math.round(height);
22 width = Math.round(width);
29 var r = ~~(height / 2);
34 "M", x + .5, y + .5 - ~~(height / 2),
36 "a", r, ~~(height / 2), 0, 0, 1, 0, height,
42 "M", x + .5, y + .5 - r,
44 "a", r, r, 0, 1, 1, 0, height,
55 "M", x - ~~(width / 2), y,
57 "a", ~~(width / 2), r, 0, 0, 1, width, 0,
65 "a", r, r, 0, 1, 1, width, 0,
74 var half = ~~(height / 2);
78 "l", 0, -height, mmax(width - half, 0), 0, mmin(half, width), half, -mmin(half, width), half + (half * 2 < height),
85 "l", -width, 0, 0, -mmax(height - half, 0), half, -mmin(half, height), half, mmin(half, height), half,
93 "M", x, y + ~~(height / 2),
94 "l", 0, -height, width, 0, 0, height,
99 "M", x + ~~(width / 2), y,
100 "l", 1 - width, 0, 0, -height, width - 1, 0,
107 r = mmin(width, Math.round(height / 5));
109 "M", x + .5, y + .5 - ~~(height / 2),
111 "a", r, r, 0, 0, 1, r, r,
112 "l", 0, height - r * 2,
113 "a", r, r, 0, 0, 1, -r, r,
118 r = mmin(Math.round(width / 5), height);
120 "M", x - ~~(width / 2), y,
122 "a", r, r, 0, 0, 1, r, -r,
123 "l", width - 2 * r, 0,
124 "a", r, r, 0, 0, 1, r, r,
132 return path.join(",");
134 return paper.path(path);
141 function VBarchart(paper, x, y, width, height, values, opts) {
145 var chartinst = this,
146 type = opts.type || "square",
147 gutter = parseFloat(opts.gutter || "20%"),
150 // covers = paper.set(),
151 // covers2 = paper.set(),
152 total = Math.max.apply(Math, values),
155 colors = opts.colors || chartinst.colors,
156 len = values.length + 2,
159 opts.xvalues = opts.xvalues|| [];
160 if (!paper.raphael.is(opts.xvalues[0], "array")) {
161 opts.xvalues = [opts.xvalues];
163 opts.yvalues = opts.yvalues|| [];
164 if (!paper.raphael.is(opts.yvalues[0], "array")) {
165 opts.yvalues = [opts.yvalues];
169 var allx = Array.prototype.concat.apply([], opts.xvalues),
170 ally = Array.prototype.concat.apply([], opts.yvalues),
171 xdim = chartinst.snapEnds(
172 Math.min.apply(Math, allx),
173 Math.max.apply(Math, allx),
174 opts.xvalues[0].length - 1
178 ydim = chartinst.snapEnds(
179 Math.min.apply(Math, ally),
180 Math.max.apply(Math, ally),
181 opts.yvalues[0].length - 1
183 miny = typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin,
185 kx = (width - gutter * 2) / ((maxx - minx) || 1),
186 ky = (height - gutter * 2) / ((maxy - miny) || 1);
188 var axis = paper.set();
192 // Roo.log(opts.axis);
194 // Value - "top right bottom left". If
195 var ax = (opts.axis + "").split(/[,\s]+/);
205 opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
215 opts.axisystep || Math.floor((height - 2 * gutter) / 20),
222 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
226 // width - 2 * gutter,
229 width - 20, // total width
234 opts.axisxstep || Math.floor((width - 2 * gutter) / 20), // data length
236 opts.axisxlabels || false,
249 // opts.axisystep || Math.floor((height - 2 * gutter) / 20),
256 if (Raphael.is(values[0], "array") && values.length > 1) {
261 for (var i = values.length; i--;) {
262 bars.push(paper.set());
263 total.push(Math.max.apply(Math, values[i]));
264 len = Math.max(len, values[i].length);
267 for (var i = values.length; i--;) {
268 if (values[i].length < len) {
269 for (var j = len; j--;) {
275 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
278 for (var i = 0; i < len; i++) {
280 values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
283 total = (opts.to) || total;
284 Roo.log([width, len, gutter]);
285 var barwidth = width / (len * (90 + gutter) + gutter) * 100,
286 barhgutter = barwidth * (gutter) / 100,
287 barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
290 Y = (height - 2 * barvgutter) / total;
293 barhgutter = Math.round(barhgutter);
294 barwidth = Math.floor(barwidth);
297 !opts.stacked && (barwidth /= multi || 1);
299 for (var i = 0; i < len; i++) {
305 for (var j = 0; j < (multi || 1); j++) {
306 var h = Math.round((multi ? values[j][i] : values[i]) * Y),
307 top = y + height - barvgutter - h,
308 bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] });
309 // bar = finger(x, Y + barwidth / 2, Math.round(8 * X), barwidth - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
310 if (multi && bars[j]) { // bars[j] did not appear to exist?
317 bar.x = Math.round(X + barwidth / 2);
320 bar.value = multi ? values[j][i] : values[i];
322 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
323 bar.labels = paper.set();
324 var qtyLbl = paper.text(
330 'text-anchor' : 'start',
334 bar.labels.push(qtyLbl);
344 X += barhgutter + 25;
350 * create the legend for Vbarchart
354 // var legend = function (labels, otherslabel, mark, dir) {
357 // labels = labels || [];
358 //// dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south";
359 // dir = "east"; // default set to east, do not support other position first...
360 // mark = paper[mark && mark.toLowerCase()] || "circle";
361 // chart.labels = paper.set();
363 // for (var i = 0; i < len; i++) {
366 // values[i].others && (labels[i] = otherslabel || "Others");
368 // labels[i] = chartinst.labelise(labels[i], values[i], sum);
369 // chart.labels.push(paper.set());
370 // chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
371 // 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"}));
372 // covers[i].label = chart.labels[i];
373 // h += txt.getBBox().height * 1.2;
377 // east: [width + 15, - Y +30]
378 //// west: [-bb.width - 2 * r - 20, -bb.height / 2],
379 //// north: [-r - bb.width / 2, -r - bb.height - 10],
380 //// south: [ x - 5, 10]
383 // chart.labels.translate.apply(chart.labels, tr);
384 // chart.push(chart.labels);
387 // if (opts.legend) {
388 // legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
393 // chart.hover = function (fin, fout) {
396 // covers.mouseover(fin).mouseout(fout);
400 // chart.hoverColumn = function (fin, fout) {
403 // fout = fout || function () {};
404 // covers2.mouseover(fin).mouseout(fout);
408 // chart.click = function (f) {
415 // chart.each = function (f) {
416 // if (!Raphael.is(f, "function")) {
419 // for (var i = covers.length; i--;) {
420 // f.call(covers[i]);
425 // chart.eachColumn = function (f) {
426 // if (!Raphael.is(f, "function")) {
429 // for (var i = covers2.length; i--;) {
430 // f.call(covers2[i]);
435 // chart.clickColumn = function (f) {
442 // chart.push(bars, covers, covers2);
444 // chart.covers = covers;
449 * Horizontal Barchart
451 function HBarchart(paper, x, y, width, height, values, opts) {
454 var chartinst = this,
455 type = opts.type || "square",
456 gutter = parseFloat(opts.gutter || "20%"),
459 covers = paper.set(),
460 covers2 = paper.set(),
461 total = Math.max.apply(Math, values),
464 colors = opts.colors || chartinst.colors,
469 if (Raphael.is(values[0], "array")) {
474 for (var i = values.length; i--;) {
475 bars.push(paper.set());
476 total.push(Math.max.apply(Math, values[i]));
477 len = Math.max(len, values[i].length);
481 for (var i = len; i--;) {
483 for (var j = values.length; j--;) {
484 tot +=+ values[j][i] || 0;
486 stacktotal.push(tot);
490 for (var i = values.length; i--;) {
491 if (values[i].length < len) {
492 for (var j = len; j--;) {
498 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
501 for (var i = 0; i < len; i++) {
503 values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
507 for (i = 0; i < len; i++) {
509 values[opts.cut].value += values[i];
510 values[opts.cut].others = true;
511 others = values[opts.cut].value;
515 len = Math.min(opts.cut + 1, values.length);
517 others && values.splice(len) && (values[opts.cut].others = true);
520 total = (opts.to) || Math.max.apply(Math, values);
522 var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
523 bargutter = Math.floor(barheight * gutter / 100),
526 X = (width - 1) / total;
528 !opts.stacked && (barheight /= multi || 1);
530 for (var i = 0; i < len; i++) {
533 for (var j = 0; j < (multi || 1); j++) {
534 var val = multi ? values[j][i] : values[i],
535 bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]});
543 bar.x = x + Math.round(val * X);
544 bar.y = Y + barheight / 2;
545 bar.w = Math.round(val * X);
557 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
560 cvr.bars = paper.set();
564 for (var s = stack.length; s--;) {
568 for (var s = 0, ss = stack.length; s < ss; s++) {
571 val = Math.round((size + bar.value) * X),
572 path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
575 size && bar.attr({ path: path });
578 covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
593 for (var i = 0; i < len; i++) {
594 for (var j = 0; j < (multi || 1); j++) {
595 var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
598 cover.bar = multi ? bars[j][i] : bars[i];
599 cover.value = cover.bar.value;
607 chart.label = function (labels, isRight) {
608 labels = labels || [];
609 this.labels = paper.set();
611 for (var i = 0; i < len; i++) {
612 for (var j = 0; j < (multi || 1); j++) {
613 //var label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
614 var label = Raphael.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
616 bars[i * (multi || 1) + j].x + 5 : // - barheight / 2 + 3 :
618 A = !isRight ? "end" : "start",
621 this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
623 if (L.getBBox().x < x + 5) {
624 L.attr({x: x + 5, "text-anchor": "start"});
626 bars[i * (multi || 1) + j].label = L;
634 var axis = paper.set();
637 // Roo.log(opts.axis);
639 // Value - "top right bottom left". If
640 var ax = (opts.axis + "").split(/[,\s]+/);
643 +ax[0] && axis.push(// not in used at the moment
650 opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
654 +ax[1] && axis.push(// not in used at the moment
661 opts.axisystep || Math.floor((height - 2 * gutter) / 20),
666 +ax[2] && axis.push(// not in used at the moment
668 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
672 // width - 2 * gutter,
675 width - 20, // total width
680 opts.axisxstep || Math.floor((width - 2 * gutter) / 20), // data length
682 opts.axisxlabels || false,
688 +ax[3] && axis.push(// just hardcode this
699 // opts.axisystep || Math.floor((height - 2 * gutter) / 20),
707 chart.hover = function (fin, fout) {
710 fout = fout || function () {};
711 covers.mouseover(fin).mouseout(fout);
715 chart.hoverColumn = function (fin, fout) {
718 fout = fout || function () {};
719 covers2.mouseover(fin).mouseout(fout);
723 chart.each = function (f) {
724 if (!Raphael.is(f, "function")) {
727 for (var i = covers.length; i--;) {
733 chart.eachColumn = function (f) {
734 if (!Raphael.is(f, "function")) {
737 for (var i = covers2.length; i--;) {
743 chart.click = function (f) {
750 chart.clickColumn = function (f) {
758 * create the legend for Hbarchart
762 var legend = function (labels, otherslabel, mark, dir) {
765 labels = labels || [];
766 // dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "south";
767 dir = "east"; // default set to east, do not support other position first...
768 mark = paper[mark && mark.toLowerCase()] || "circle";
769 chart.labels = paper.set();
771 for (var i = 0; i < len; i++) {
774 values[i].others && (labels[i] = otherslabel || "Others");
775 // Roo.log(values[i]);
776 labels[i] = chartinst.labelise(labels[i], values[i], sum);
777 chart.labels.push(paper.set());
778 chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ stroke: "none", fill: colors[i] }));
779 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"}));
780 covers[i].label = chart.labels[i];
781 h += txt.getBBox().height * 1.2;
785 east: [width + 25, - Y +30]
786 // west: [-bb.width - 2 * r - 20, -bb.height / 2],
787 // north: [-r - bb.width / 2, -r - bb.height - 10],
788 // south: [ x - 5, 10]
791 chart.labels.translate.apply(chart.labels, tr);
792 chart.push(chart.labels);
796 legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
799 chart.push(bars, covers, covers2);
801 chart.covers = covers;
806 var F = function() {};
807 F.prototype = Raphael.g;
808 HBarchart.prototype = VBarchart.prototype = new F;
810 Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
811 return new HBarchart(this, x, y, width, height, values, opts);
814 Raphael.fn.barchart = function(x, y, width, height, values, opts) {
815 return new VBarchart(this, x, y, width, height, values, opts);