2 * g.Raphael 0.51 - Charting library, based on Raphaƫl
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.
8 if (typeof(Raphael) == 'undefined') {
9 // support for seed/simple browser usage
10 importz = imports['seed/importz.js'].importz;
12 Raphael = importz('Raphael');
17 //chartinst = typeof(chartinst) != 'undefined' ? chartinst: (imports ? imports.chartinst.chartinst : {});
24 function finger(x, y, width, height, dir, ending, isPath, paper) {
25 //Roo.log(['finger' ,x, y, width, height, dir, ending, isPath, paper]);
27 ends = { round: 'round', sharp: 'sharp', soft: 'soft', square: 'square' };
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();
35 ending = ends[ending] || "square";
36 height = Math.round(height);
37 width = Math.round(width);
44 var r = ~~(height / 2);
49 "M", x + .5, y + .5 - ~~(height / 2),
51 "a", r, ~~(height / 2), 0, 0, 1, 0, height,
57 "M", x + .5, y + .5 - r,
59 "a", r, r, 0, 1, 1, 0, height,
70 "M", x - ~~(width / 2), y,
72 "a", ~~(width / 2), r, 0, 0, 1, width, 0,
80 "a", r, r, 0, 1, 1, width, 0,
89 var half = ~~(height / 2);
93 "l", 0, -height, mmax(width - half, 0), 0, mmin(half, width), half, -mmin(half, width), half + (half * 2 < height),
100 "l", -width, 0, 0, -mmax(height - half, 0), half, -mmin(half, height), half, mmin(half, height), half,
108 "M", x, y + ~~(height / 2),
109 "l", 0, -height, width, 0, 0, height,
114 "M", x + ~~(width / 2), y,
115 "l", 1 - width, 0, 0, -height, width - 1, 0,
122 r = mmin(width, Math.round(height / 5));
124 "M", x + .5, y + .5 - ~~(height / 2),
126 "a", r, r, 0, 0, 1, r, r,
127 "l", 0, height - r * 2,
128 "a", r, r, 0, 0, 1, -r, r,
133 r = mmin(Math.round(width / 5), height);
135 "M", x - ~~(width / 2), y,
137 "a", r, r, 0, 0, 1, r, -r,
138 "l", width - 2 * r, 0,
139 "a", r, r, 0, 0, 1, r, r,
147 return path.join(",");
149 //Roo.log("finger: paper.path: " + JSON.stringify(path));
150 return paper.path(path);
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]));
164 ret.push(parseInt(ar[i]));
176 * Creates a vertical bar chart
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
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?)
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
196 = (object) path element of the popup
198 | r.vbarchart(0, 0, 620, 260, [76, 70, 67, 71, 69], {})
202 function VBarchart(paper, x, y, width, height, values, opts) {
204 values = clearAr(values);
206 var chartinst = this,
207 type = opts.type || "square",
208 gutter = parseFloat(opts.gutter || "20%"),
211 covers = paper.set(),
212 covers2 = paper.set(),
213 total = Math.max.apply(Math, values),
216 colors = opts.colors || chartinst.colors,
220 opts.xvalues = opts.xvalues|| [];
221 if (!paper.raphael.is(opts.xvalues[0], "array")) {
222 opts.xvalues = [opts.xvalues];
224 opts.yvalues = opts.yvalues|| Array.prototype.concat.apply(values);
225 if (!paper.raphael.is(opts.yvalues[0], "array")) {
226 opts.yvalues = [opts.yvalues];
228 opts.axis = opts.axis || "";
230 var allx = Array.prototype.concat.apply([], opts.xvalues),
231 ally = Array.prototype.concat.apply([], opts.yvalues),
233 xdim = chartinst.snapEnds(
234 Math.min.apply(Math, allx),
235 Math.max.apply(Math, allx),
236 opts.xvalues[0].length - 1
240 ydim = chartinst.snapEnds(
241 Math.min.apply(Math, ally),
242 Math.max.apply(Math, ally),
243 opts.yvalues[0].length - 1
245 miny = Math.floor(typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin),
247 kx = (width - gutter * 2) / ((maxx - minx) || 1),
248 ky = (height - gutter * 2) / ((maxy - miny) || 1),
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,
259 if (Raphael.is(values[0], "array")) {
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);
271 for (var i = len; i--;) {
274 for (var j = values.length; j--;) {
275 tot +=+ values[j][i] || 0;
278 stacktotal.push(tot);
282 for (var i = values.length; i--;) {
283 if (values[i].length < len) {
284 for (var j = len; j--;) {
290 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
294 opts.axisystep = opts.axisystep || Math.floor((height - 2 * gutter) / 20);
297 var yrangedivs = Math.ceil((maxy - miny) / opts.axisystep) || 1;
298 var yrange = yrangedivs * opts.axisystep;
300 //Roo.log([total, opts.axisystep, yrangedivs , yrange]);
302 opts.axisystep = Math.ceil(total/yrangedivs);
304 total = maxy = miny + (yrangedivs * opts.axisystep);
306 total = (opts.to) || total;
310 var barwidth = (width - axwidth) / (len * (100 + gutter) + gutter) * 100,
311 barhgutter = barwidth * gutter / 100,
312 barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
314 X = x + barhgutter + axwidth_l,
315 Y = (height - 2 * barvgutter) / total;
316 //Roo.log([height , barvgutter , total]);
325 barhgutter = Math.round(barhgutter);
326 barwidth = Math.floor(barwidth);
329 !opts.stacked && (barwidth /= multi || 1);
336 for (var i = 0; i < len; i++) {
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];
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]);
355 bar.x = Math.round(X + barwidth / 2);
358 bar.value = multi ? values[j][i] : values[i];
360 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
361 bar.labels = paper.set();
362 var qtyLbl = paper.text(
365 opts.labels[i]).attr({
367 'text-anchor' : 'start',
368 'font-family' : 'Arial',
372 bar.labels.push(qtyLbl);
385 covers2.push(cvr = paper.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(chartinst.shim));
386 cvr.bars = paper.set();
390 for (var s = stack.length; s--;) {
394 for (var s = 0, ss = stack.length; s < ss; s++) {
397 h = (size + bar.value) * Y,
398 path = finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1, paper);
401 size && bar.attr({path: path});
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));
406 cover.value = bar.value;
420 for (var i = 0; i < len; i++) {
421 for (var j = 0; j < (multi || 1); j++) {
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;
433 //Roo.log("Final X: " + X);
437 var axis = paper.set();
438 // Roo.log(opts.axis);
451 opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
461 opts.axisystep || Math.floor((height - 2 * gutter) / 20),
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;
472 //Roo.log([width,len * (barwidth + barhgutter) + barhgutter])
473 // width from left to end right == (X-x)
478 y: y + height - gutter,
479 length : X - x, // total width
480 from: minx , // from -- infinity??? if no 'xvalues set'
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
488 loffset : Math.round(barhgutter + (barwidth / 2.0)),
489 roffset : Math.round(barhgutter + (barwidth / 2.0))
494 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
495 chartinst.axis( ax_args)
499 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
501 // vertical steps (eg. number of Lables) - should be related to the range.
504 //Roo.log(["Y axis:", maxy, miny, yrange, yrangedivs, opts.axisystep]);
506 //Roo.log(opts.axisystep);
527 chart.label = function (labels, isBottom) {
528 labels = labels || [];
529 this.labels = paper.set();
531 var L, l = -Infinity;
534 for (var i = 0; i < len; i++) {
537 for (var j = 0; j < (multi || 1); j++) {
538 tot += multi ? values[j][i] : values[i];
540 if (j == multi - 1) {
541 var label = paper.labelise(labels[i], tot, total);
543 L = paper.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).attr(txtattr).insertBefore(covers[i * (multi || 1) + j]);
545 var bb = L.getBBox();
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);
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]);
563 var bb = L.getBBox();
583 chart.hover = function (fin, fout) {
586 covers.mouseover(fin).mouseout(fout);
590 chart.hoverColumn = function (fin, fout) {
593 fout = fout || function () {};
594 covers2.mouseover(fin).mouseout(fout);
598 chart.click = function (f) {
605 chart.each = function (f) {
606 if (!Raphael.is(f, "function")) {
609 for (var i = covers.length; i--;) {
615 chart.eachColumn = function (f) {
616 if (!Raphael.is(f, "function")) {
619 for (var i = covers2.length; i--;) {
625 chart.clickColumn = function (f) {
632 chart.push(bars, covers, covers2);
634 chart.covers = covers;
639 var F = function() {};
640 F.prototype = Raphael.g;
641 HBarchart.prototype = VBarchart.prototype = new F; //prototype reused by hbarchart
643 Raphael.fn.barchart = function(x, y, width, height, values, opts) {
644 return new VBarchart(this, x, y, width, height, values, opts);
651 * Creates a horizontal bar chart
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
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?)
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
671 = (object) path element of the popup
673 | r.barchart(0, 0, 620, 260, [76, 70, 67, 71, 69], {})
676 function HBarchart(paper, x, y, width, height, values, opts) {
679 var chartinst = this,
680 type = opts.type || "square",
681 gutter = parseFloat(opts.gutter || "20%"),
684 covers = paper.set(),
685 covers2 = paper.set(),
686 total = Math.max.apply(Math, values),
689 colors = opts.colors || chartinst.colors,
692 if (Raphael.is(values[0], "array")) {
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);
704 for (var i = len; i--;) {
706 for (var j = values.length; j--;) {
707 tot +=+ values[j][i] || 0;
709 stacktotal.push(tot);
713 for (var i = values.length; i--;) {
714 if (values[i].length < len) {
715 for (var j = len; j--;) {
721 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
724 total = (opts.to) || total;
726 var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
727 bargutter = Math.floor(barheight * gutter / 100),
730 X = (width - 1) / total;
732 !opts.stacked && (barheight /= multi || 1);
734 for (var i = 0; i < len; i++) {
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]});
747 bar.x = x + Math.round(val * X);
748 bar.y = Y + barheight / 2;
749 bar.w = Math.round(val * X);
761 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
764 cvr.bars = paper.set();
768 for (var s = stack.length; s--;) {
772 for (var s = 0, ss = stack.length; s < ss; s++) {
775 val = Math.round((size + bar.value) * X),
776 path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
779 size && bar.attr({ path: path });
782 covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
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);
802 cover.bar = multi ? bars[j][i] : bars[i];
803 cover.value = cover.bar.value;
811 chart.label = function (labels, isRight) {
812 labels = labels || [];
813 this.labels = paper.set();
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",
822 this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
824 if (L.getBBox().x < x + 5) {
825 L.attr({x: x + 5, "text-anchor": "start"});
827 bars[i * (multi || 1) + j].label = L;
835 chart.hover = function (fin, fout) {
838 fout = fout || function () {};
839 covers.mouseover(fin).mouseout(fout);
843 chart.hoverColumn = function (fin, fout) {
846 fout = fout || function () {};
847 covers2.mouseover(fin).mouseout(fout);
851 chart.each = function (f) {
852 if (!Raphael.is(f, "function")) {
855 for (var i = covers.length; i--;) {
861 chart.eachColumn = function (f) {
862 if (!Raphael.is(f, "function")) {
865 for (var i = covers2.length; i--;) {
871 chart.click = function (f) {
878 chart.clickColumn = function (f) {
885 chart.push(bars, covers, covers2);
887 chart.covers = covers;
891 Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
892 return new HBarchart(this, x, y, width, height, values, opts);