/*! * 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 () { function shrink(values, dim) { var k = values.length / dim, j = 0, l = k, sum = 0, res = []; while (j < values.length) { l--; if (l < 0) { sum += values[j] * (1 + l); res.push(sum / k); sum = values[j++] * -l; l += k; } else { sum += values[j++]; } } return res; } function getAnchors(p1x, p1y, p2x, p2y, p3x, p3y) { var l1 = (p2x - p1x) / 2, l2 = (p3x - p2x) / 2, a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)), b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y)); a = p1y < p2y ? Math.PI - a : a; b = p3y < p2y ? Math.PI - b : b; var alpha = Math.PI / 2 - ((a + b) % (Math.PI * 2)) / 2, dx1 = l1 * Math.sin(alpha + a), dy1 = l1 * Math.cos(alpha + a), dx2 = l2 * Math.sin(alpha + b), dy2 = l2 * Math.cos(alpha + b); return { x1: p2x - dx1, y1: p2y + dy1, x2: p2x + dx2, y2: p2y + dy2 }; } function Linechart(paper, x, y, width, height, valuesx, valuesy, opts) { var chartinst = this; opts = opts || {}; if (!paper.raphael.is(valuesx[0], "array")) { valuesx = [valuesx]; } if (!paper.raphael.is(valuesy[0], "array")) { valuesy = [valuesy]; } var gutter = opts.gutter || 10, len = Math.max(valuesx[0].length, valuesy[0].length), symbol = opts.symbol || "", colors = opts.colors || chartinst.colors, columns = null, dots = null, chart = paper.set(), path = []; for (var i = 0, ii = valuesy.length; i < ii; i++) { len = Math.max(len, valuesy[i].length); } var shades = paper.set(); for (i = 0, ii = valuesy.length; i < ii; i++) { if (opts.shade) { shades.push(paper.path().attr({ stroke: "none", fill: colors[i], opacity: opts.nostroke ? 1 : .3 })); } if (valuesy[i].length > width - 2 * gutter) { valuesy[i] = shrink(valuesy[i], width - 2 * gutter); len = width - 2 * gutter; } if (valuesx[i] && valuesx[i].length > width - 2 * gutter) { valuesx[i] = shrink(valuesx[i], width - 2 * gutter); } } var allx = Array.prototype.concat.apply([], valuesx), ally = Array.prototype.concat.apply([], valuesy), xdim = chartinst.snapEnds(Math.min.apply(Math, allx), Math.max.apply(Math, allx), valuesx[0].length - 1), minx = xdim.from, maxx = xdim.to, ydim = chartinst.snapEnds(Math.min.apply(Math, ally), Math.max.apply(Math, ally), valuesy[0].length - 1), miny = ydim.from, maxy = ydim.to, kx = (width - gutter * 2) / ((maxx - minx) || 1), ky = (height - gutter * 2) / ((maxy - miny) || 1); var axis = paper.set(); if (opts.axis) { miny = 0; maxy = ydim.to; ky = (height - gutter * 2) / ((maxy - miny) || 1); var ax = (opts.axis + "").split(/[,\s]+/); +ax[0] && axis.push(chartinst.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlables, paper)); +ax[1] && axis.push(chartinst.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, ydim.from, ydim.to, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, paper)); +ax[2] && axis.push(chartinst.axis(x + gutter, y + height - gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlables, paper)); //+ax[3] && axis.push(chartinst.axis(x + gutter, y + height - gutter, height - 2 * gutter, ydim.from, ydim.to, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, paper)); +ax[3] && axis.push(chartinst.axis(x + gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, paper)); } var lines = paper.set(), symbols = paper.set(), line; for (i = 0, ii = valuesy.length; i < ii; i++) { if (!opts.nostroke) { lines.push(line = paper.path().attr({ stroke: colors[i], "stroke-width": opts.width || 2, "stroke-linejoin": "round", "stroke-linecap": "round", "stroke-dasharray": opts.dash || "" })); } var sym = Raphael.is(symbol, "array") ? symbol[i] : symbol, symset = paper.set(); path = []; for (var j = 0, jj = valuesy[i].length; j < jj; j++) { var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx, Y = y + height - gutter - (valuesy[i][j] - miny) * ky; (Raphael.is(sym, "array") ? sym[j] : sym) && symset.push(paper[Raphael.is(sym, "array") ? sym[j] : sym](X, Y, (opts.width || 2) * 3).attr({ fill: colors[i], stroke: "none" })); if (opts.smooth) { if (j && j != jj - 1) { var X0 = x + gutter + ((valuesx[i] || valuesx[0])[j - 1] - minx) * kx, Y0 = y + height - gutter - (valuesy[i][j - 1] - miny) * ky, X2 = x + gutter + ((valuesx[i] || valuesx[0])[j + 1] - minx) * kx, Y2 = y + height - gutter - (valuesy[i][j + 1] - miny) * ky, a = getAnchors(X0, Y0, X, Y, X2, Y2); path = path.concat([a.x1, a.y1, X, Y, a.x2, a.y2]); } if (!j) { path = ["M", X, Y, "C", X, Y]; } } else { path = path.concat([j ? "L" : "M", X, Y]); } } if (opts.smooth) { path = path.concat([X, Y, X, Y]); } symbols.push(symset); if (opts.shade) { shades[i].attr({ path: path.concat(["L", X, y + height - gutter, "L", x + gutter + ((valuesx[i] || valuesx[0])[0] - minx) * kx, y + height - gutter, "z"]).join(",") }); } !opts.nostroke && line.attr({ path: path.join(",") }); } function createColumns(f) { // unite Xs together var Xs = []; for (var i = 0, ii = valuesx.length; i < ii; i++) { Xs = Xs.concat(valuesx[i]); } Xs.sort(function(a,b){return a-b;}); // remove duplicates var Xs2 = [], xs = []; for (i = 0, ii = Xs.length; i < ii; i++) { Xs[i] != Xs[i - 1] && Xs2.push(Xs[i]) && xs.push(x + gutter + (Xs[i] - minx) * kx); } Xs = Xs2; ii = Xs.length; var cvrs = f || paper.set(); for (i = 0; i < ii; i++) { var X = xs[i] - (xs[i] - (xs[i - 1] || x)) / 2, w = ((xs[i + 1] || x + width) - xs[i]) / 2 + (xs[i] - (xs[i - 1] || x)) / 2, C; f ? (C = {}) : cvrs.push(C = paper.rect(X - 1, y, Math.max(w + 1, 1), height).attr({ stroke: "none", fill: "#000", opacity: 0 })); C.values = []; C.symbols = paper.set(); C.y = []; C.x = xs[i]; C.axis = Xs[i]; for (var j = 0, jj = valuesy.length; j < jj; j++) { Xs2 = valuesx[j] || valuesx[0]; for (var k = 0, kk = Xs2.length; k < kk; k++) { if (Xs2[k] == Xs[i]) { C.values.push(valuesy[j][k]); C.y.push(y + height - gutter - (valuesy[j][k] - miny) * ky); C.symbols.push(chart.symbols[j][k]); } } } f && f.call(C); } !f && (columns = cvrs); } function createDots(f) { var cvrs = f || paper.set(), C; for (var i = 0, ii = valuesy.length; i < ii; i++) { for (var j = 0, jj = valuesy[i].length; j < jj; j++) { var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx, nearX = x + gutter + ((valuesx[i] || valuesx[0])[j ? j - 1 : 1] - minx) * kx, Y = y + height - gutter - (valuesy[i][j] - miny) * ky; f ? (C = {}) : cvrs.push(C = paper.circle(X, Y, Math.abs(nearX - X) / 2).attr({ stroke: "none", fill: "#000", opacity: 0 })); C.x = X; C.y = Y; C.value = valuesy[i][j]; C.line = chart.lines[i]; C.shade = chart.shades[i]; C.symbol = chart.symbols[i][j]; C.symbols = chart.symbols[i]; C.axis = (valuesx[i] || valuesx[0])[j]; f && f.call(C); } } !f && (dots = cvrs); } chart.push(lines, shades, symbols, axis, columns, dots); chart.lines = lines; chart.shades = shades; chart.symbols = symbols; chart.axis = axis; chart.hoverColumn = function (fin, fout) { !columns && createColumns(); columns.mouseover(fin).mouseout(fout); return this; }; chart.clickColumn = function (f) { !columns && createColumns(); columns.click(f); return this; }; chart.hrefColumn = function (cols) { var hrefs = paper.raphael.is(arguments[0], "array") ? arguments[0] : arguments; if (!(arguments.length - 1) && typeof cols == "object") { for (var x in cols) { for (var i = 0, ii = columns.length; i < ii; i++) if (columns[i].axis == x) { columns[i].attr("href", cols[x]); } } } !columns && createColumns(); for (i = 0, ii = hrefs.length; i < ii; i++) { columns[i] && columns[i].attr("href", hrefs[i]); } return this; }; chart.hover = function (fin, fout) { !dots && createDots(); dots.mouseover(fin).mouseout(fout); return this; }; chart.click = function (f) { !dots && createDots(); dots.click(f); return this; }; chart.each = function (f) { createDots(f); return this; }; chart.eachColumn = function (f) { createColumns(f); return this; }; return chart; }; //inheritance var F = function() {}; F.prototype = Raphael.g; Linechart.prototype = new F; //public Raphael.fn.linechart = function(x, y, width, height, valuesx, valuesy, opts) { return new Linechart(this, x, y, width, height, valuesx, valuesy, opts); } })();