/*! * g.Raphael 0.5 - Charting library, based on Raphaƫl * * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. */ (function () { var mmin = Math.min, mmax = Math.max; function finger(x, y, width, height, dir, ending, isPath, paper) { var path, ends = { round: 'round', sharp: 'sharp', soft: 'soft', square: 'square' }; // dir 0 for horizontal and 1 for vertical if ((dir && !height) || (!dir && !width)) { return isPath ? "" : paper.path(); } ending = ends[ending] || "square"; height = Math.round(height); width = Math.round(width); x = Math.round(x); y = Math.round(y); switch (ending) { case "round": if (!dir) { var r = ~~(height / 2); if (width < r) { r = width; path = [ "M", x + .5, y + .5 - ~~(height / 2), "l", 0, 0, "a", r, ~~(height / 2), 0, 0, 1, 0, height, "l", 0, 0, "z" ]; } else { path = [ "M", x + .5, y + .5 - r, "l", width - r, 0, "a", r, r, 0, 1, 1, 0, height, "l", r - width, 0, "z" ]; } } else { r = ~~(width / 2); if (height < r) { r = height; path = [ "M", x - ~~(width / 2), y, "l", 0, 0, "a", ~~(width / 2), r, 0, 0, 1, width, 0, "l", 0, 0, "z" ]; } else { path = [ "M", x - r, y, "l", 0, r - height, "a", r, r, 0, 1, 1, width, 0, "l", 0, height - r, "z" ]; } } break; case "sharp": if (!dir) { var half = ~~(height / 2); path = [ "M", x, y + half, "l", 0, -height, mmax(width - half, 0), 0, mmin(half, width), half, -mmin(half, width), half + (half * 2 < height), "z" ]; } else { half = ~~(width / 2); path = [ "M", x + half, y, "l", -width, 0, 0, -mmax(height - half, 0), half, -mmin(half, height), half, mmin(half, height), half, "z" ]; } break; case "square": if (!dir) { path = [ "M", x, y + ~~(height / 2), "l", 0, -height, width, 0, 0, height, "z" ]; } else { path = [ "M", x + ~~(width / 2), y, "l", 1 - width, 0, 0, -height, width - 1, 0, "z" ]; } break; case "soft": if (!dir) { r = mmin(width, Math.round(height / 5)); path = [ "M", x + .5, y + .5 - ~~(height / 2), "l", width - r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r * 2, "a", r, r, 0, 0, 1, -r, r, "l", r - width, 0, "z" ]; } else { r = mmin(Math.round(width / 5), height); path = [ "M", x - ~~(width / 2), y, "l", 0, r - height, "a", r, r, 0, 0, 1, r, -r, "l", width - 2 * r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r, "z" ]; } } if (isPath) { return path.join(","); } else { return paper.path(path); } } /* * Vertical Barchart */ function VBarchart(paper, x, y, width, height, values, opts) { opts = opts || {}; var chartinst = this, type = opts.type || "square", gutter = parseFloat(opts.gutter || "20%"), chart = paper.set(), bars = paper.set(), covers = paper.set(), covers2 = paper.set(), total = Math.max.apply(Math, values), stacktotal = [], multi = 0, colors = opts.colors || chartinst.colors, len = values.length; if (Raphael.is(values[0], "array")) { total = []; multi = len; len = 0; for (var i = values.length; i--;) { bars.push(paper.set()); total.push(Math.max.apply(Math, values[i])); len = Math.max(len, values[i].length); } if (opts.stacked) { for (var i = len; i--;) { var tot = 0; for (var j = values.length; j--;) { tot +=+ values[j][i] || 0; } stacktotal.push(tot); } } for (var i = values.length; i--;) { if (values[i].length < len) { for (var j = len; j--;) { values[i].push(0); } } } total = Math.max.apply(Math, opts.stacked ? stacktotal : total); } total = (opts.to) || total; var barwidth = width / (len * (100 + gutter) + gutter) * 100, barhgutter = barwidth * gutter / 100, barvgutter = opts.vgutter == null ? 20 : opts.vgutter, stack = [], X = x + barhgutter, Y = (height - 2 * barvgutter) / total; if (!opts.stretch) { barhgutter = Math.round(barhgutter); barwidth = Math.floor(barwidth); } !opts.stacked && (barwidth /= multi || 1); for (var i = 0; i < len; i++) { stack = []; for (var j = 0; j < (multi || 1); j++) { var h = Math.round((multi ? values[j][i] : values[i]) * Y), top = y + height - barvgutter - h, bar = finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, null, paper).attr({ stroke: "none", fill: colors[multi ? j : i] }); if (multi) { bars[j].push(bar); } else { bars.push(bar); } bar.y = top; bar.x = Math.round(X + barwidth / 2); bar.w = barwidth; bar.h = h; bar.value = multi ? values[j][i] : values[i]; if (!opts.stacked) { X += barwidth; } else { stack.push(bar); } } if (opts.stacked) { var cvr; covers2.push(cvr = paper.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(chartinst.shim)); cvr.bars = paper.set(); var size = 0; for (var s = stack.length; s--;) { stack[s].toFront(); } for (var s = 0, ss = stack.length; s < ss; s++) { var bar = stack[s], cover, h = (size + bar.value) * Y, path = finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1, paper); cvr.bars.push(bar); size && bar.attr({path: path}); bar.h = h; bar.y = y + height - barvgutter - !!size * .5 - h; covers.push(cover = paper.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(chartinst.shim)); cover.bar = bar; cover.value = bar.value; size += bar.value; } X += barwidth; } X += barhgutter; } covers2.toFront(); X = x + barhgutter; if (!opts.stacked) { for (var i = 0; i < len; i++) { for (var j = 0; j < (multi || 1); j++) { var cover; covers.push(cover = paper.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(chartinst.shim)); cover.bar = multi ? bars[j][i] : bars[i]; cover.value = cover.bar.value; X += barwidth; } X += barhgutter; } } var xdim = chartinst.snapEnds(0, len, 1), minx = xdim.from, maxx = xdim.to, ydim = chartinst.snapEnds(0, total, 1), miny = ydim.from, maxy = ydim.to; var axis = paper.set(); if (opts.axis) { var ax = (opts.axis + "").split(/[,\s]+/); // +ax[0] && axis.push(chartinst.axis(x, y + gutter, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 2, paper)); +ax[0] && axis.push(paper.path(["M", x, y + gutter + 0.5,"h", width])); +ax[1] && axis.push(chartinst.axis(x + width, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 3, paper)); // +ax[2] && axis.push(chartinst.axis(x, y + height - gutter, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 0, paper)); +ax[2] && axis.push(paper.path(["M", x, y + height - gutter + 0.5,"h", width])); +ax[3] && axis.push(chartinst.axis(x, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 1, paper)); } chart.label = function (labels, isBottom) { labels = labels || []; this.labels = paper.set(); var L, l = -Infinity; if (opts.stacked) { for (var i = 0; i < len; i++) { var tot = 0; for (var j = 0; j < (multi || 1); j++) { tot += multi ? values[j][i] : values[i]; var bar = multi ? bars[j][i] : bars[i]; if (j == multi - 1) { var label = chartinst.labelise(labels[i], tot, total); L = paper.text(bar.x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]); var bb = L.getBBox(); if (bb.x - 7 < l) { L.remove(); } else { this.labels.push(L); l = bb.x + bb.width; } } } } } else { for (var i = 0; i < len; i++) { for (var j = 0; j < (multi || 1); j++) { var label = chartinst.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total); var bar = multi ? bars[j][i] : bars[i]; L = paper.text(bar.x, isBottom ? y + height - barvgutter / 2 : bar.y - 10, label).insertBefore(covers[i * (multi || 1) + j]); var bb = L.getBBox(); if (bb.x - 7 < l) { L.remove(); } else { this.labels.push(L); l = bb.x + bb.width; } } } } return this; }; chart.hover = function (fin, fout) { covers2.hide(); covers.show(); covers.mouseover(fin).mouseout(fout); return this; }; chart.hoverColumn = function (fin, fout) { covers.hide(); covers2.show(); fout = fout || function () {}; covers2.mouseover(fin).mouseout(fout); return this; }; chart.click = function (f) { covers2.hide(); covers.show(); covers.click(f); return this; }; chart.each = function (f) { if (!Raphael.is(f, "function")) { return this; } for (var i = covers.length; i--;) { f.call(covers[i]); } return this; }; chart.eachColumn = function (f) { if (!Raphael.is(f, "function")) { return this; } for (var i = covers2.length; i--;) { f.call(covers2[i]); } return this; }; chart.clickColumn = function (f) { covers.hide(); covers2.show(); covers2.click(f); return this; }; chart.push(bars, covers, covers2); chart.bars = bars; chart.covers = covers; chart.axis = axis; return chart; }; /** * Horizontal Barchart */ function HBarchart(paper, x, y, width, height, values, opts) { opts = opts || {}; var chartinst = this, type = opts.type || "square", gutter = parseFloat(opts.gutter || "20%"), chart = paper.set(), bars = paper.set(), covers = paper.set(), covers2 = paper.set(), total = Math.max.apply(Math, values), stacktotal = [], multi = 0, colors = opts.colors || chartinst.colors, len = values.length; if (Raphael.is(values[0], "array")) { total = []; multi = len; len = 0; for (var i = values.length; i--;) { bars.push(paper.set()); total.push(Math.max.apply(Math, values[i])); len = Math.max(len, values[i].length); } if (opts.stacked) { for (var i = len; i--;) { var tot = 0; for (var j = values.length; j--;) { tot +=+ values[j][i] || 0; } stacktotal.push(tot); } } for (var i = values.length; i--;) { if (values[i].length < len) { for (var j = len; j--;) { values[i].push(0); } } } total = Math.max.apply(Math, opts.stacked ? stacktotal : total); } total = (opts.to) || total; var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100), bargutter = Math.floor(barheight * gutter / 100), stack = [], Y = y + bargutter, X = (width - 1) / total; !opts.stacked && (barheight /= multi || 1); for (var i = 0; i < len; i++) { stack = []; for (var j = 0; j < (multi || 1); j++) { var val = multi ? values[j][i] : values[i], bar = finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, null, paper).attr({stroke: "none", fill: colors[multi ? j : i]}); if (multi) { bars[j].push(bar); } else { bars.push(bar); } bar.x = x + Math.round(val * X); bar.y = Y + barheight / 2; bar.w = Math.round(val * X); bar.h = barheight; bar.value = +val; if (!opts.stacked) { Y += barheight; } else { stack.push(bar); } } if (opts.stacked) { var cvr = paper.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(chartinst.shim); covers2.push(cvr); cvr.bars = paper.set(); var size = 0; for (var s = stack.length; s--;) { stack[s].toFront(); } for (var s = 0, ss = stack.length; s < ss; s++) { var bar = stack[s], cover, val = Math.round((size + bar.value) * X), path = finger(x, bar.y, val, barheight - 1, false, type, 1, paper); cvr.bars.push(bar); size && bar.attr({ path: path }); bar.w = val; bar.x = x + val; covers.push(cover = paper.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(chartinst.shim)); cover.bar = bar; size += bar.value; } Y += barheight; } Y += bargutter; } covers2.toFront(); Y = y + bargutter; if (!opts.stacked) { for (var i = 0; i < len; i++) { for (var j = 0; j < (multi || 1); j++) { var cover = paper.rect(x, Y, width, barheight).attr(chartinst.shim); covers.push(cover); cover.bar = multi ? bars[j][i] : bars[i]; cover.value = cover.bar.value; Y += barheight; } Y += bargutter; } } var xdim = chartinst.snapEnds(0, total, 1), minx = xdim.from, maxx = xdim.to, ydim = chartinst.snapEnds(0, len, 1), miny = ydim.from, maxy = ydim.to; var axis = paper.set(); if (opts.axis) { var ax = (opts.axis + "").split(/[,\s]+/); +ax[0] && axis.push(chartinst.axis(x, y, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 2, paper)); //+ax[1] && axis.push(chartinst.axis(x + width, y + height, height, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 3, paper)); +ax[1] && axis.push(paper.path(["M", x+width+ 0.5, y,"v", height])); +ax[2] && axis.push(chartinst.axis(x, y + height, width, minx, maxx, opts.axisxstep || Math.floor((width) / 20), 0, paper)); //+ax[3] && axis.push(chartinst.axis(x, y + height, height, miny, maxy, opts.axisystep || Math.floor((height - gutter) / 20), 1, paper)); +ax[3] && axis.push(paper.path(["M", x+ 0.5, y,"v", height])); } chart.label = function (labels, isRight) { labels = labels || []; this.labels = paper.set(); for (var i = 0; i < len; i++) { for (var j = 0; j < multi; j++) { var bar = multi ? bars[j][i] : bars[i]; var label = chartinst.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total), X = isRight ? bar.x - barheight / 2 + 3 : x + 5, A = isRight ? "end" : "start", L; this.labels.push(L = paper.text(X, bar.y, label).attr({ "text-anchor": A }).insertBefore(covers[i * (multi || 1) + j])); if (L.getBBox().x < x + 5) { L.attr({x: x + 5, "text-anchor": "start"}); } else { bar.label = L; } } } return this; }; chart.hover = function (fin, fout) { covers2.hide(); covers.show(); fout = fout || function () {}; covers.mouseover(fin).mouseout(fout); return this; }; chart.hoverColumn = function (fin, fout) { covers.hide(); covers2.show(); fout = fout || function () {}; covers2.mouseover(fin).mouseout(fout); return this; }; chart.each = function (f) { if (!Raphael.is(f, "function")) { return this; } for (var i = covers.length; i--;) { f.call(covers[i]); } return this; }; chart.eachColumn = function (f) { if (!Raphael.is(f, "function")) { return this; } for (var i = covers2.length; i--;) { f.call(covers2[i]); } return this; }; chart.click = function (f) { covers2.hide(); covers.show(); covers.click(f); return this; }; chart.clickColumn = function (f) { covers.hide(); covers2.show(); covers2.click(f); return this; }; chart.push(bars, covers, covers2); chart.bars = bars; chart.covers = covers; return chart; }; //inheritance var F = function() {}; F.prototype = Raphael.g; HBarchart.prototype = VBarchart.prototype = new F; Raphael.fn.hbarchart = function(x, y, width, height, values, opts) { return new HBarchart(this, x, y, width, height, values, opts); }; Raphael.fn.barchart = function(x, y, width, height, values, opts) { return new VBarchart(this, x, y, width, height, values, opts); }; })();