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.
7 Raphael = typeof(Raphael) != 'undefined' ? Raphael : (imports ? imports.seed.Raphael.Raphael : {});
8 Roo = typeof(Roo) != 'undefined' ? Roo: (imports ? imports.seed.Roo.Roo: {});
9 //chartinst = typeof(chartinst) != 'undefined' ? chartinst: (imports ? imports.chartinst.chartinst : {});
16 function finger(x, y, width, height, dir, ending, isPath, paper) {
17 //Roo.log(['finger' ,x, y, width, height, dir, ending, isPath, paper]);
19 ends = { round: 'round', sharp: 'sharp', soft: 'soft', square: 'square' };
21 // dir 0 for horizontal and 1 for vertical
22 if ((dir && !height) || (!dir && !width)) {
23 //Roo.log("finger call blan paper path");
24 return isPath ? "" : paper.path();
27 ending = ends[ending] || "square";
28 height = Math.round(height);
29 width = Math.round(width);
36 var r = ~~(height / 2);
41 "M", x + .5, y + .5 - ~~(height / 2),
43 "a", r, ~~(height / 2), 0, 0, 1, 0, height,
49 "M", x + .5, y + .5 - r,
51 "a", r, r, 0, 1, 1, 0, height,
62 "M", x - ~~(width / 2), y,
64 "a", ~~(width / 2), r, 0, 0, 1, width, 0,
72 "a", r, r, 0, 1, 1, width, 0,
81 var half = ~~(height / 2);
85 "l", 0, -height, mmax(width - half, 0), 0, mmin(half, width), half, -mmin(half, width), half + (half * 2 < height),
92 "l", -width, 0, 0, -mmax(height - half, 0), half, -mmin(half, height), half, mmin(half, height), half,
100 "M", x, y + ~~(height / 2),
101 "l", 0, -height, width, 0, 0, height,
106 "M", x + ~~(width / 2), y,
107 "l", 1 - width, 0, 0, -height, width - 1, 0,
114 r = mmin(width, Math.round(height / 5));
116 "M", x + .5, y + .5 - ~~(height / 2),
118 "a", r, r, 0, 0, 1, r, r,
119 "l", 0, height - r * 2,
120 "a", r, r, 0, 0, 1, -r, r,
125 r = mmin(Math.round(width / 5), height);
127 "M", x - ~~(width / 2), y,
129 "a", r, r, 0, 0, 1, r, -r,
130 "l", width - 2 * r, 0,
131 "a", r, r, 0, 0, 1, r, r,
139 return path.join(",");
141 //Roo.log("finger: paper.path: " + JSON.stringify(path));
142 return paper.path(path);
146 // might not be needed - but Math.max.apply kept returning Nan?
147 function clearAr(ar) {
148 var ret = new Array();
149 //Roo.log(JSON.stringify(ar));
150 for (var i = 0; i < ar.length;i++) {
151 //print(typeof(ar[i]));
152 if (Raphael.is(ar[i], "object")) {
153 ret.push(clearAr(ar[i]));
156 ret.push(parseInt(ar[i]));
168 * Creates a vertical bar chart
172 - x (number) x coordinate of the chart
173 - y (number) y coordinate of the chart
174 - width (number) width of the chart (respected by all elements in the set)
175 - height (number) height of the chart (respected by all elements in the set)
176 - values (array) values
177 - opts (object) options for the chart
179 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
180 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
182 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.
183 o stacked (boolean) whether or not to tread values as in a stacked bar chart
188 = (object) path element of the popup
190 | r.vbarchart(0, 0, 620, 260, [76, 70, 67, 71, 69], {})
194 function VBarchart(paper, x, y, width, height, values, opts) {
196 values = clearAr(values);
198 var chartinst = this,
199 type = opts.type || "square",
200 gutter = parseFloat(opts.gutter || "20%"),
203 covers = paper.set(),
204 covers2 = paper.set(),
205 total = Math.max.apply(Math, values),
208 colors = opts.colors || chartinst.colors,
212 opts.xvalues = opts.xvalues|| [];
213 if (!paper.raphael.is(opts.xvalues[0], "array")) {
214 opts.xvalues = [opts.xvalues];
216 opts.yvalues = opts.yvalues|| Array.prototype.concat.apply(values);
217 if (!paper.raphael.is(opts.yvalues[0], "array")) {
218 opts.yvalues = [opts.yvalues];
220 opts.axis = opts.axis || "";
223 var allx = Array.prototype.concat.apply([], opts.xvalues),
224 ally = Array.prototype.concat.apply([], opts.yvalues),
226 xdim = chartinst.snapEnds(
227 Math.min.apply(Math, allx),
228 Math.max.apply(Math, allx),
229 opts.xvalues[0].length - 1
233 ydim = chartinst.snapEnds(
234 Math.min.apply(Math, ally),
235 Math.max.apply(Math, ally),
236 opts.yvalues[0].length - 1
238 miny = Math.floor(typeof(opts.ymin) == 'undefined' ? ydim.from : opts.ymin),
240 kx = (width - gutter * 2) / ((maxx - minx) || 1),
241 ky = (height - gutter * 2) / ((maxy - miny) || 1),
243 ax = (opts.axis + "").split(/[,\s]+/), // Value - "top right bottom left". If
244 axwidth_l = +ax[3] ? 20 : 0, // allow 20 for bar...
245 axwidth_r = +ax[1] ? 20 : 0,
246 axwidth = axwidth_l + axwidth_r,
252 if (Raphael.is(values[0], "array")) {
257 for (var i = values.length; i--;) {
258 bars.push(paper.set());
259 total.push(Math.max.apply(Math, values[i]));
260 len = Math.max(len, values[i].length);
264 for (var i = len; i--;) {
267 for (var j = values.length; j--;) {
268 tot +=+ values[j][i] || 0;
271 stacktotal.push(tot);
275 for (var i = values.length; i--;) {
276 if (values[i].length < len) {
277 for (var j = len; j--;) {
283 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
287 opts.axisystep = opts.axisystep || Math.floor((height - 2 * gutter) / 20);
290 var yrangedivs = Math.ceil((maxy - miny) / opts.axisystep) || 1;
291 var yrange = yrangedivs * opts.axisystep;
293 //Roo.log([total, opts.axisystep, yrangedivs , yrange]);
295 opts.axisystep = Math.ceil(total/yrangedivs);
297 total = maxy = miny + (yrangedivs * opts.axisystep);
299 total = (opts.to) || total;
303 var barwidth = (width - axwidth) / (len * (100 + gutter) + gutter) * 100,
304 barhgutter = barwidth * gutter / 100,
305 barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
307 X = x + barhgutter + axwidth_l,
308 Y = (height - 2 * barvgutter) / total;
309 //Roo.log([height , barvgutter , total]);
318 barhgutter = Math.round(barhgutter);
319 barwidth = Math.floor(barwidth);
322 !opts.stacked && (barwidth /= multi || 1);
329 for (var i = 0; i < len; i++) {
332 for (var j = 0; j < (multi || 1); j++) {
333 //Roo.log( [ 'vbar-loop', multi ? values[j][i] : values[i], Y]);
336 var h = Math.round((multi ? values[j][i] : values[i]) * Y),
337 top = y + height - barvgutter - h,
338 bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] });
339 //Roo.log([y , height , barvgutter ,h]);
348 bar.x = Math.round(X + barwidth / 2);
351 bar.value = multi ? values[j][i] : values[i];
353 if(!isNaN(bar.y) && opts.labels && opts.labels[i]){
354 bar.labels = paper.set();
355 var qtyLbl = paper.text(
358 opts.labels[i]).attr({
360 'text-anchor' : 'start',
361 'font-family' : 'Arial',
365 bar.labels.push(qtyLbl);
378 covers2.push(cvr = paper.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(chartinst.shim));
379 cvr.bars = paper.set();
383 for (var s = stack.length; s--;) {
387 for (var s = 0, ss = stack.length; s < ss; s++) {
390 h = (size + bar.value) * Y,
391 path = finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1, paper);
394 size && bar.attr({path: path});
396 bar.y = y + height - barvgutter - !!size * .5 - h;
397 covers.push(cover = paper.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(chartinst.shim));
399 cover.value = bar.value;
413 for (var i = 0; i < len; i++) {
414 for (var j = 0; j < (multi || 1); j++) {
417 covers.push(cover = paper.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(chartinst.shim));
418 cover.bar = multi ? bars[j][i] : bars[i];
419 cover.value = cover.bar.value;
426 //Roo.log("Final X: " + X);
430 var axis = paper.set();
441 opts.axisxstep || Math.floor((width - 2 * gutter) / 20),
451 opts.axisystep || Math.floor((height - 2 * gutter) / 20),
456 opts.axisxlabels = opts.axisxlabels || [];
457 // add elements at beginning and end of array...
458 //opts.axisxlabels.unshift(' ');
459 //opts.axisxlabels.push(' ');
460 opts.axisxstep = opts.axisxstep || (opts.axisxlabels ? opts.axisxlabels.length -1 : false ) || len;
462 //Roo.log([width,len * (barwidth + barhgutter) + barhgutter])
463 // width from left to end right == (X-x)
468 y: y + height - gutter,
469 length : X - x, // total width
470 from: minx , // from -- infinity??? if no 'xvalues set'
472 steps : opts.axisxstep , // 9 when we have 8 items?
473 orientation : 0, // orientation
474 labels : opts.axisxlabels || false, // labels
475 type : "-", // type ofbarhgutter line
476 dashsize : 5, // dash size
478 loffset : Math.round(barhgutter + (barwidth / 2.0)),
479 roffset : Math.round(barhgutter + (barwidth / 2.0))
486 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
487 chartinst.axis( ax_args)
491 // x, y, length, from, to, steps, orientation, labels, type, dashsize, paper
493 // vertical steps (eg. number of Lables) - should be related to the range.
496 //Roo.log(["Y axis:", maxy, miny, yrange, yrangedivs, opts.axisystep]);
498 //Roo.log(opts.axisystep);
502 // +ax[3] && axis.push(
505 // y + height - gutter,
506 // height - 2 * gutter,
520 chart.label = function (labels, isBottom) {
521 labels = labels || [];
522 this.labels = paper.set();
524 var L, l = -Infinity;
527 for (var i = 0; i < len; i++) {
530 for (var j = 0; j < (multi || 1); j++) {
531 tot += multi ? values[j][i] : values[i];
533 if (j == multi - 1) {
534 var label = paper.labelise(labels[i], tot, total);
536 L = paper.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).attr(txtattr).insertBefore(covers[i * (multi || 1) + j]);
538 var bb = L.getBBox();
550 for (var i = 0; i < len; i++) {
551 for (var j = 0; j < (multi || 1); j++) {
552 var label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
554 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]);
556 var bb = L.getBBox();
576 chart.hover = function (fin, fout) {
579 covers.mouseover(fin).mouseout(fout);
583 chart.hoverColumn = function (fin, fout) {
586 fout = fout || function () {};
587 covers2.mouseover(fin).mouseout(fout);
591 chart.click = function (f) {
598 chart.each = function (f) {
599 if (!Raphael.is(f, "function")) {
602 for (var i = covers.length; i--;) {
608 chart.eachColumn = function (f) {
609 if (!Raphael.is(f, "function")) {
612 for (var i = covers2.length; i--;) {
618 chart.clickColumn = function (f) {
625 chart.push(bars, covers, covers2);
627 chart.covers = covers;
632 var F = function() {};
633 F.prototype = Raphael.g;
634 HBarchart.prototype = VBarchart.prototype = new F; //prototype reused by hbarchart
636 Raphael.fn.barchart = function(x, y, width, height, values, opts) {
637 return new VBarchart(this, x, y, width, height, values, opts);
644 * Creates a horizontal bar chart
648 - x (number) x coordinate of the chart
649 - y (number) y coordinate of the chart
650 - width (number) width of the chart (respected by all elements in the set)
651 - height (number) height of the chart (respected by all elements in the set)
652 - values (array) values
653 - opts (object) options for the chart
655 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
656 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
658 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.
659 o stacked (boolean) whether or not to tread values as in a stacked bar chart
664 = (object) path element of the popup
666 | r.barchart(0, 0, 620, 260, [76, 70, 67, 71, 69], {})
669 function HBarchart(paper, x, y, width, height, values, opts) {
672 var chartinst = this,
673 type = opts.type || "square",
674 gutter = parseFloat(opts.gutter || "20%"),
677 covers = paper.set(),
678 covers2 = paper.set(),
679 total = Math.max.apply(Math, values),
682 colors = opts.colors || chartinst.colors,
685 if (Raphael.is(values[0], "array")) {
690 for (var i = values.length; i--;) {
691 bars.push(paper.set());
692 total.push(Math.max.apply(Math, values[i]));
693 len = Math.max(len, values[i].length);
697 for (var i = len; i--;) {
699 for (var j = values.length; j--;) {
700 tot +=+ values[j][i] || 0;
702 stacktotal.push(tot);
706 for (var i = values.length; i--;) {
707 if (values[i].length < len) {
708 for (var j = len; j--;) {
714 total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
717 total = (opts.to) || total;
719 var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
720 bargutter = Math.floor(barheight * gutter / 100),
723 X = (width - 1) / total;
725 !opts.stacked && (barheight /= multi || 1);
727 for (var i = 0; i < len; i++) {
730 for (var j = 0; j < (multi || 1); j++) {
731 var val = multi ? values[j][i] : values[i],
732 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 bar.x = x + Math.round(val * X);
741 bar.y = Y + barheight / 2;
742 bar.w = Math.round(val * X);
754 var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim);
757 cvr.bars = paper.set();
761 for (var s = stack.length; s--;) {
765 for (var s = 0, ss = stack.length; s < ss; s++) {
768 val = Math.round((size + bar.value) * X),
769 path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper);
772 size && bar.attr({ path: path });
775 covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim));
790 for (var i = 0; i < len; i++) {
791 for (var j = 0; j < (multi || 1); j++) {
792 var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim);
795 cover.bar = multi ? bars[j][i] : bars[i];
796 cover.value = cover.bar.value;
804 chart.label = function (labels, isRight) {
805 labels = labels || [];
806 this.labels = paper.set();
808 for (var i = 0; i < len; i++) {
809 for (var j = 0; j < multi; j++) {
810 var label = paper.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total),
811 X = isRight ? bars[i * (multi || 1) + j].x - barheight / 2 + 3 : x + 5,
812 A = isRight ? "end" : "start",
815 this.labels.push(L = paper.text(X, bars[i * (multi || 1) + j].y, label).attr(txtattr).attr({ "text-anchor": A }).insertBefore(covers[0]));
817 if (L.getBBox().x < x + 5) {
818 L.attr({x: x + 5, "text-anchor": "start"});
820 bars[i * (multi || 1) + j].label = L;
828 chart.hover = function (fin, fout) {
831 fout = fout || function () {};
832 covers.mouseover(fin).mouseout(fout);
836 chart.hoverColumn = function (fin, fout) {
839 fout = fout || function () {};
840 covers2.mouseover(fin).mouseout(fout);
844 chart.each = function (f) {
845 if (!Raphael.is(f, "function")) {
848 for (var i = covers.length; i--;) {
854 chart.eachColumn = function (f) {
855 if (!Raphael.is(f, "function")) {
858 for (var i = covers2.length; i--;) {
864 chart.click = function (f) {
871 chart.clickColumn = function (f) {
878 chart.push(bars, covers, covers2);
880 chart.covers = covers;
884 Raphael.fn.hbarchart = function(x, y, width, height, values, opts) {
885 return new HBarchart(this, x, y, width, height, values, opts);