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 || "";
231 var allx = Array.prototype.concat.apply([], opts.xvalues),
232 ally = Array.prototype.concat.apply([], opts.yvalues),
234 xdim = chartinst.snapEnds(
235 Math.min.apply(Math, allx),
236 Math.max.apply(Math, allx),
237 opts.xvalues[0].length - 1
241 ydim = chartinst.snapEnds(
242 Math.min.apply(Math, ally),
243 Math.max.apply(Math, ally),
244 opts.yvalues[0].length - 1
246 miny = Math.floor(typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin),
248 kx = (width - gutter * 2) / ((maxx - minx) || 1),
249 ky = (height - gutter * 2) / ((maxy - miny) || 1),
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,
260 if (Raphael.is(values[0], "array")) {
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);
272 for (var i = len; i--;) {
275 for (var j = values.length; j--;) {
276 tot +=+ values[j][i] || 0;
279 stacktotal.push(tot);
283 for (var i = values.length; i--;) {
284 if (values[i].length < len) {
285 for (var j = len; j--;) {
291 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
295 opts.axisystep = opts.axisystep || Math.floor((height - 2 * gutter) / 20);
298 var yrangedivs = Math.ceil((maxy - miny) / opts.axisystep) || 1;
299 var yrange = yrangedivs * opts.axisystep;
301 //Roo.log([total, opts.axisystep, yrangedivs , yrange]);
303 opts.axisystep = Math.ceil(total/yrangedivs);
305 total = maxy = miny + (yrangedivs * opts.axisystep);
307 total = (opts.to) || total;
311 var barwidth = (width - axwidth) / (len * (100 + gutter) + gutter) * 100,
312 barhgutter = barwidth * gutter / 100,
313 barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
315 X = x + barhgutter + axwidth_l,
316 Y = (height - 2 * barvgutter) / total;
317 //Roo.log([height , barvgutter , total]);
326 barhgutter = Math.round(barhgutter);
327 barwidth = Math.floor(barwidth);
330 !opts.stacked && (barwidth /= multi || 1);
337 for (var i = 0; i < len; i++) {
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];
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]);
356 bar.x = Math.round(X + barwidth / 2);
359 bar.value = multi ? values[j][i] : values[i];
361 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
362 bar.labels = paper.set();
363 var qtyLbl = paper.text(
366 opts.labels[i]).attr({
368 'text-anchor' : 'start',
369 'font-family' : 'Arial',
373 bar.labels.push(qtyLbl);
386 covers2.push(cvr = paper.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(chartinst.shim));
387 cvr.bars = paper.set();
391 for (var s = stack.length; s--;) {
395 for (var s = 0, ss = stack.length; s < ss; s++) {
398 h = (size + bar.value) * Y,
399 path = finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1, paper);
402 size && bar.attr({path: path});
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));
407 cover.value = bar.value;
421 for (var i = 0; i < len; i++) {
422 for (var j = 0; j < (multi || 1); j++) {
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;
434 //Roo.log("Final X: " + X);
438 var axis = paper.set();
439 // Roo.log(opts.axis);
452 opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
462 opts.axisystep || Math.floor((height - 2 * gutter) / 20),
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;
473 //Roo.log([width,len * (barwidth + barhgutter) + barhgutter])
474 // width from left to end right == (X-x)
479 y: y + height - gutter,
480 length : X - x, // total width
481 from: minx , // from -- infinity??? if no 'xvalues set'
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
489 loffset : Math.round(barhgutter + (barwidth / 2.0)),
490 roffset : Math.round(barhgutter + (barwidth / 2.0))
495 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
496 chartinst.axis( ax_args)
500 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
502 // vertical steps (eg. number of Lables) - should be related to the range.
505 //Roo.log(["Y axis:", maxy, miny, yrange, yrangedivs, opts.axisystep]);
507 //Roo.log(opts.axisystep);
528 chart.label = function (labels, isBottom) {
529 labels = labels || [];
530 this.labels = paper.set();
532 var L, l = -Infinity;
535 for (var i = 0; i < len; i++) {
538 for (var j = 0; j < (multi || 1); j++) {
539 tot += multi ? values[j][i] : values[i];
541 if (j == multi - 1) {
542 var label = paper.labelise(labels[i], tot, total);
544 L = paper.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).attr(txtattr).insertBefore(covers[i * (multi || 1) + j]);
546 var bb = L.getBBox();
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);
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]);
564 var bb = L.getBBox();
584 chart.hover = function (fin, fout) {
587 covers.mouseover(fin).mouseout(fout);
591 chart.hoverColumn = function (fin, fout) {
594 fout = fout || function () {};
595 covers2.mouseover(fin).mouseout(fout);
599 chart.click = function (f) {
606 chart.each = function (f) {
607 if (!Raphael.is(f, "function")) {
610 for (var i = covers.length; i--;) {
616 chart.eachColumn = function (f) {
617 if (!Raphael.is(f, "function")) {
620 for (var i = covers2.length; i--;) {
626 chart.clickColumn = function (f) {
633 chart.push(bars, covers, covers2);
635 chart.covers = covers;
640 var F = function() {};
641 F.prototype = Raphael.g;
642 HBarchart.prototype = VBarchart.prototype = new F; //prototype reused by hbarchart
644 Raphael.fn.barchart = function(x, y, width, height, values, opts) {
645 return new VBarchart(this, x, y, width, height, values, opts);
652 * Creates a horizontal bar chart
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
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?)
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
672 = (object) path element of the popup
674 | r.barchart(0, 0, 620, 260, [76, 70, 67, 71, 69], {})
677 function HBarchart(paper, x, y, width, height, values, opts) {
680 var chartinst = this,
681 type = opts.type || "square",
682 gutter = parseFloat(opts.gutter || "20%"),
685 covers = paper.set(),
686 covers2 = paper.set(),
687 total = Math.max.apply(Math, values),
690 colors = opts.colors || chartinst.colors,
693 if (Raphael.is(values[0], "array")) {
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);
705 for (var i = len; i--;) {
707 for (var j = values.length; j--;) {
708 tot +=+ values[j][i] || 0;
710 stacktotal.push(tot);
714 for (var i = values.length; i--;) {
715 if (values[i].length < len) {
716 for (var j = len; j--;) {
722 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
725 total = (opts.to) || total;
727 var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
728 bargutter = Math.floor(barheight * gutter / 100),
731 X = (width - 1) / total;
733 !opts.stacked && (barheight /= multi || 1);
735 for (var i = 0; i < len; i++) {
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]});
748 bar.x = x + Math.round(val * X);
749 bar.y = Y + barheight / 2;
750 bar.w = Math.round(val * X);
762 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
765 cvr.bars = paper.set();
769 for (var s = stack.length; s--;) {
773 for (var s = 0, ss = stack.length; s < ss; s++) {
776 val = Math.round((size + bar.value) * X),
777 path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
780 size && bar.attr({ path: path });
783 covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
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);
803 cover.bar = multi ? bars[j][i] : bars[i];
804 cover.value = cover.bar.value;
812 chart.label = function (labels, isRight) {
813 labels = labels || [];
814 this.labels = paper.set();
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",
823 this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
825 if (L.getBBox().x < x + 5) {
826 L.attr({x: x + 5, "text-anchor": "start"});
828 bars[i * (multi || 1) + j].label = L;
836 chart.hover = function (fin, fout) {
839 fout = fout || function () {};
840 covers.mouseover(fin).mouseout(fout);
844 chart.hoverColumn = function (fin, fout) {
847 fout = fout || function () {};
848 covers2.mouseover(fin).mouseout(fout);
852 chart.each = function (f) {
853 if (!Raphael.is(f, "function")) {
856 for (var i = covers.length; i--;) {
862 chart.eachColumn = function (f) {
863 if (!Raphael.is(f, "function")) {
866 for (var i = covers2.length; i--;) {
872 chart.click = function (f) {
879 chart.clickColumn = function (f) {
886 chart.push(bars, covers, covers2);
888 chart.covers = covers;
892 Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
893 return new HBarchart(this, x, y, width, height, values, opts);