From 264d10ffd7af4d7d6c38d3787dadf091fb2017c2 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 May 2012 11:55:32 +0000 Subject: [PATCH] Starting to use RapahelJS for charts rendering SVN:trunk[2048] --- js/charts.js | 255 +++++++++++++ js/g.dot.js | 156 ++++++++ js/g.pie.js | 255 +++++++++++++ js/g.raphael.js | 887 ++++++++++++++++++++++++++++++++++++++++++++++ js/raphael-min.js | 9 + 5 files changed, 1562 insertions(+) create mode 100644 js/charts.js create mode 100755 js/g.dot.js create mode 100755 js/g.pie.js create mode 100755 js/g.raphael.js create mode 100755 js/raphael-min.js diff --git a/js/charts.js b/js/charts.js new file mode 100644 index 000000000..c5d62f442 --- /dev/null +++ b/js/charts.js @@ -0,0 +1,255 @@ +// jQuery UI style "widget" for charts +Raphael.fn.ball = function (x, y, r, hue) +{ + hue = hue || 0; + return this.set( + this.ellipse(x, y + r - r / 3, r, r / 2).attr({fill: "rhsb(" + hue + ", 0, .25)-hsb(" + hue + ", 0, .25)", stroke: "none", opacity: 0}), + this.ellipse(x, y, r, r).attr({fill: "0-#000-#ccc-#000", stroke: "none"}), + this.ellipse(x, y, r*0.95, r*0.95 ).attr({fill: "r(.5,.9)hsb(" + hue + ", 0, .75)-hsb(" + hue + ", 0, .25)", stroke: "none"}), + this.ellipse(x, y, r - r / 5, r - r / 20).attr({stroke: "none", fill: "r(.5,.1)#ccc-#ccc", opacity: 0}) + ); +}; + +$(function() +{ + // the widget definition, where "itop" is the namespace, + // "pie_chart" the widget name + $.widget( "itop.pie_chart", + { + // default options + options: + { + chart_id: '', + chart_label: '', + values: [], + labels: [], + hrefs: [] + }, + + // the constructor + _create: function() + { + var me = this; + + this.element + .addClass('itop-pie_chart'); + + this.oR = Raphael(this.element.get(0), this.element.width(), this.element.height()); + $(window).bind('resize.pie_chart', function() { me._refresh(); }); + this._refresh(); + }, + _clear_r: function() + { + this.oR.clear(); + }, + // called when created, and later when changing options + _refresh: function() + { + this._clear_r(); + + var me = this; + this._compute_size(); + this.oR.ball(this.x, this.y, this.r, 0); + + var aColors = []; + var hue = 0; + var brightness = 1; + for(index = 0; index < 30; index++) + { + hue = (hue+137) % 360; + brightness = 1-((Math.floor(index / 3) % 4) / 8); + aColors.push('hsba('+(hue/360.0)+',1,'+brightness+',0.6)'); + //aColors.push('hsba('+(hue/360.0)+',0.5,0.5,0.4)'); + } + + + var aVals = this.options.values.slice(0); // Clone the array since the pie function will alter it + this.pie = this.oR.piechart(this.x, this.y, this.r, aVals, { legend: this.options.labels, legendpos: "east", href: this.options.hrefs, colors: aColors }); + this.oR.text(this.x, 10, this.options.chart_label).attr({ font: "20px 'Fontin Sans', Fontin-Sans, sans-serif" }); + this.pie.hover( + function () + { + var positiveAngle = (360 + this.mangle) % 360; + this.sector.attr({opacity: 0.5}); + //this.sector.stop(); + //this.sector.scale(1.1, 1.1, this.cx, this.cy); + + + if (this.label) + { + //this.label[0].stop(); + //this.label[0].attr({ r: 7.5 }); + this.label[1].attr({ "font-weight": 800 }); + + } + if (this.label_highlight == undefined) + { + var oBBox = this.label.getBBox(); + this.label_highlight = this.label_highlight || me.oR.rect(oBBox.x - 2, oBBox.y - 2, oBBox.width + 4, oBBox.height + 4, 4).attr({'stroke': '#ccc', fill: '#ccc'}).toBack(); + } + this.label_highlight.show(); + + //this.marker = this.marker || r.label(this.mx, this.my, this.value.value, 0, 12); + var alpha = 2*Math.PI * this.mangle / 360; + var iDir = Math.floor(((45 + 360 + this.mangle) % 360) / 90); + var aDirs = ['right', 'up', 'left', 'down']; + var sDir = aDirs[iDir]; + this.marker = this.marker || me.oR.popup(this.cx + Math.cos(alpha) *(this.r), this.cy - Math.sin(alpha) *(this.r), me.options.labels[this.value.order]+': '+this.value.valueOf(), sDir); + this.marker.show(); + + }, + function () + { + this.sector.attr({opacity:1}); + //this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500); + if (this.label) + { + //this.label[0].animate({ r: 5 }, 200, "bounce"); + this.label[1].attr({ "font-weight": 400 }); + } + this.marker && this.marker.hide(); + this.label_highlight && this.label_highlight.hide(); + + }); + }, + // events bound via _bind are removed automatically + // revert other modifications here + destroy: function() + { + this.element + .removeClass('itop-pie_chart'); + + $(window).unbind('resize.pie_chart'); + + // call the original destroy method since we overwrote it + $.Widget.prototype.destroy.call( this ); + }, + // _setOptions is called with a hash of all options that are changing + _setOptions: function() + { + // in 1.9 would use _superApply + $.Widget.prototype._setOptions.apply( this, arguments ); + }, + // _setOption is called for each individual option that is changing + _setOption: function( key, value ) + { + // in 1.9 would use _super + $.Widget.prototype._setOption.call( this, key, value ); + }, + _compute_size: function() + { + var legendWidth = 100; + var titleHeight = 20; + var iW = this.element.width(); + var iH = this.element.height(); + + if (iH == 0) + { + iH = 0.75*iW; + this.element.height(iH); + } + this.r = (6*Math.min(iW-legendWidth, iH-titleHeight)/7) / 2; // 1/6 is for the drop shadow + + this.x = (iW-legendWidth) / 2; + this.y = titleHeight+(iH-titleHeight) / 2; + }, + _draw_ball: function(x, y, r) + { + return this.oR.set( + this.oR.ellipse(x, y + r - r / 3, r, r / 2).attr({fill: "rhsb(1, 0, .25)-hsb(1, 0, .25)", stroke: "none", opacity: 0}), + this.oR.ellipse(x, y, r, r).attr({fill: "0-#000-#ccc-#000", stroke: "none"}), + this.oR.ellipse(x, y, r*0.95, r*0.95 ).attr({fill: "r(.5,.9)hsb(1, 0, .75)-hsb(1, 0, .25)", stroke: "none"}), + this.oR.ellipse(x, y, r - r / 5, r - r / 20).attr({stroke: "none", fill: "r(.5,.1)#ccc-#ccc", opacity: 0}) + ); + } + }); +}); + +$(function() +{ + // the widget definition, where "itop" is the namespace, + // "heatmap_chart" the widget name + $.widget( "itop.heatmap_chart", + { + // default options + options: + { + chart_id: '', + chart_label: '', + values: [], + labels: [], + hrefs: [] + }, + + // the constructor + _create: function() + { + var me = this; + + this.element + .addClass('itop-heatmap_chart'); + + this.oR = Raphael(this.element.get(0), this.element.width(), this.element.height()); + this._compute_size(); + this.oR.text(this.x, 10, this.options.chart_label).attr({ font: "20px 'Fontin Sans', Fontin-Sans, sans-serif" }); + + xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], + ys = [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + data = [294, 300, 204, 255, 348, 383, 334, 217, 114, 33, 44, 26, 41, 39, 52, 17, 13, 2, 0, 2, 5, 6, 64, 153, 294, 313, 195, 280, 365, 392, 340, 184, 87, 35, 43, 55, 53, 79, 49, 19, 6, 1, 0, 1, 1, 10, 50, 181, 246, 246, 220, 249, 355, 373, 332, 233, 85, 54, 28, 33, 45, 72, 54, 28, 5, 5, 0, 1, 2, 3, 58, 167, 206, 245, 194, 207, 334, 290, 261, 160, 61, 28, 11, 26, 33, 46, 36, 5, 6, 0, 0, 0, 0, 0, 0, 9, 9, 10, 7, 10, 14, 3, 3, 7, 0, 3, 4, 4, 6, 28, 24, 3, 5, 0, 0, 0, 0, 0, 0, 4, 3, 4, 4, 3, 4, 13, 10, 7, 2, 3, 6, 1, 9, 33, 32, 6, 2, 1, 3, 0, 0, 4, 40, 128, 212, 263, 202, 248, 307, 306, 284, 222, 79, 39, 26, 33, 40, 61, 54, 17, 3, 0, 0, 0, 3, 7, 70, 199], + axisy = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], + axisx = ["12am", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12pm", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]; + + this.oR.dotchart(10, this.y, this.width, this.height, xs, ys, data, {symbol: "o", max: 10, heat: true, axis: "0 0 1 1", axisxstep: 23, axisystep: 6, axisxlabels: axisx, axisxtype: " ", axisytype: " ", axisylabels: axisy}).hover(function () { + this.marker = this.marker || me.oR.tag(this.x, this.y, this.value, 0, this.r + 2).insertBefore(this); + this.marker.show(); + }, function () { + this.marker && this.marker.hide(); + }); + + + }, + + // called when created, and later when changing options + _refresh: function() + { + }, + // events bound via _bind are removed automatically + // revert other modifications here + destroy: function() + { + this.element + .removeClass('itop-heatmap_chart'); + + // call the original destroy method since we overwrote it + $.Widget.prototype.destroy.call( this ); + }, + // _setOptions is called with a hash of all options that are changing + _setOptions: function() + { + // in 1.9 would use _superApply + $.Widget.prototype._setOptions.apply( this, arguments ); + }, + // _setOption is called for each individual option that is changing + _setOption: function( key, value ) + { + // in 1.9 would use _super + $.Widget.prototype._setOption.call( this, key, value ); + }, + _compute_size: function() + { + var titleHeight = 20; + var iW = this.element.width(); + var iH = this.element.height(); + + if (iH == 0) + { + iH = 0.75*iW; + this.element.height(iH); + } + this.x = (iW) / 2; + this.y = titleHeight; + this.width = iW; + this.height = iH - titleHeight; + } + }); +}); \ No newline at end of file diff --git a/js/g.dot.js b/js/g.dot.js new file mode 100755 index 000000000..17f4c965b --- /dev/null +++ b/js/g.dot.js @@ -0,0 +1,156 @@ +/*! + * 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 colorValue = function (value, total, s, b) { + return 'hsb(' + [Math.min((1 - value / total) * .4, 1), s || .75, b || .75] + ')'; + }; + + function Dotchart(paper, x, y, width, height, valuesx, valuesy, size, opts) { + + var chartinst = this; + + function drawAxis(ax) { + +ax[0] && (ax[0] = chartinst.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlabels || null, opts.axisxtype || "t", null, paper)); + +ax[1] && (ax[1] = chartinst.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.axisylabels || null, opts.axisytype || "t", null, paper)); + +ax[2] && (ax[2] = chartinst.axis(x + gutter, y + height - gutter + maxR, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlabels || null, opts.axisxtype || "t", null, paper)); + +ax[3] && (ax[3] = chartinst.axis(x + gutter - maxR, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.axisylabels || null, opts.axisytype || "t", null, paper)); + } + + opts = opts || {}; + var xdim = chartinst.snapEnds(Math.min.apply(Math, valuesx), Math.max.apply(Math, valuesx), valuesx.length - 1), + minx = xdim.from, + maxx = xdim.to, + gutter = opts.gutter || 10, + ydim = chartinst.snapEnds(Math.min.apply(Math, valuesy), Math.max.apply(Math, valuesy), valuesy.length - 1), + miny = ydim.from, + maxy = ydim.to, + len = Math.max(valuesx.length, valuesy.length, size.length), + symbol = paper[opts.symbol] || "circle", + res = paper.set(), + series = paper.set(), + max = opts.max || 100, + top = Math.max.apply(Math, size), + R = [], + k = Math.sqrt(top / Math.PI) * 2 / max; + + for (var i = 0; i < len; i++) { + R[i] = Math.min(Math.sqrt(size[i] / Math.PI) * 2 / k, max); + } + + gutter = Math.max.apply(Math, R.concat(gutter)); + + var axis = paper.set(), + maxR = Math.max.apply(Math, R); + + if (opts.axis) { + var ax = (opts.axis + "").split(/[,\s]+/); + + drawAxis.call(chartinst, ax); + + var g = [], b = []; + + for (var i = 0, ii = ax.length; i < ii; i++) { + var bb = ax[i].all ? ax[i].all.getBBox()[["height", "width"][i % 2]] : 0; + + g[i] = bb + gutter; + b[i] = bb; + } + + gutter = Math.max.apply(Math, g.concat(gutter)); + + for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) { + ax[i].remove(); + ax[i] = 1; + } + + drawAxis.call(chartinst, ax); + + for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) { + axis.push(ax[i].all); + } + + res.axis = axis; + } + + var kx = (width - gutter * 2) / ((maxx - minx) || 1), + ky = (height - gutter * 2) / ((maxy - miny) || 1); + + for (var i = 0, ii = valuesy.length; i < ii; i++) { + var sym = paper.raphael.is(symbol, "array") ? symbol[i] : symbol, + X = x + gutter + (valuesx[i] - minx) * kx, + Y = y + height - gutter - (valuesy[i] - miny) * ky; + + sym && R[i] && series.push(paper[sym](X, Y, R[i]).attr({ fill: opts.heat ? colorValue(R[i], maxR) : chartinst.colors[0], "fill-opacity": opts.opacity ? R[i] / max : 1, stroke: "none" })); + } + + var covers = paper.set(); + + for (var i = 0, ii = valuesy.length; i < ii; i++) { + var X = x + gutter + (valuesx[i] - minx) * kx, + Y = y + height - gutter - (valuesy[i] - miny) * ky; + + covers.push(paper.circle(X, Y, maxR).attr(chartinst.shim)); + opts.href && opts.href[i] && covers[i].attr({href: opts.href[i]}); + covers[i].r = +R[i].toFixed(3); + covers[i].x = +X.toFixed(3); + covers[i].y = +Y.toFixed(3); + covers[i].X = valuesx[i]; + covers[i].Y = valuesy[i]; + covers[i].value = size[i] || 0; + covers[i].dot = series[i]; + } + + res.covers = covers; + res.series = series; + res.push(series, axis, covers); + + res.hover = function (fin, fout) { + covers.mouseover(fin).mouseout(fout); + return this; + }; + + res.click = function (f) { + covers.click(f); + return this; + }; + + res.each = function (f) { + if (!paper.raphael.is(f, "function")) { + return this; + } + + for (var i = covers.length; i--;) { + f.call(covers[i]); + } + + return this; + }; + + res.href = function (map) { + var cover; + + for (var i = covers.length; i--;) { + cover = covers[i]; + + if (cover.X == map.x && cover.Y == map.y && cover.value == map.value) { + cover.attr({href: map.href}); + } + } + }; + return res; + }; + + //inheritance + var F = function() {}; + F.prototype = Raphael.g + Dotchart.prototype = new F; + + //public + Raphael.fn.dotchart = function(x, y, width, height, valuesx, valuesy, size, opts) { + return new Dotchart(this, x, y, width, height, valuesx, valuesy, size, opts); + } +})(); diff --git a/js/g.pie.js b/js/g.pie.js new file mode 100755 index 000000000..99f4e0f75 --- /dev/null +++ b/js/g.pie.js @@ -0,0 +1,255 @@ +/* + * 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 Piechart(paper, cx, cy, r, values, opts) { + opts = opts || {}; + + var chartinst = this, + sectors = [], + covers = paper.set(), + chart = paper.set(), + series = paper.set(), + order = [], + len = values.length, + angle = 0, + total = 0, + others = 0, + cut = 9, + defcut = true; + + function sector(cx, cy, r, startAngle, endAngle, fill) { + var rad = Math.PI / 180, + x1 = cx + r * Math.cos(-startAngle * rad), + x2 = cx + r * Math.cos(-endAngle * rad), + xm = cx + r / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad), + y1 = cy + r * Math.sin(-startAngle * rad), + y2 = cy + r * Math.sin(-endAngle * rad), + ym = cy + r / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad), + res = [ + "M", cx, cy, + "L", x1, y1, + "A", r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2, + "z" + ]; + + res.middle = { x: xm, y: ym }; + return res; + } + + chart.covers = covers; + + if (len == 1) { + series.push(paper.circle(cx, cy, r).attr({ fill: chartinst.colors[0], stroke: opts.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth })); + covers.push(paper.circle(cx, cy, r).attr(chartinst.shim)); + total = values[0]; + values[0] = { value: values[0], order: 0, valueOf: function () { return this.value; } }; + series[0].middle = {x: cx, y: cy}; + series[0].mangle = 180; + } else { + for (var i = 0; i < len; i++) { + total += values[i]; + values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } }; + } + + values.sort(function (a, b) { + return b.value - a.value; + }); + + for (i = 0; i < len; i++) { + if (defcut && values[i] * 360 / total <= 1.5) { + cut = i; + defcut = false; + } + + if (i > cut) { + defcut = false; + values[cut].value += values[i]; + values[cut].others = true; + others = values[cut].value; + } + } + + len = Math.min(cut + 1, values.length); + others && values.splice(len) && (values[cut].others = true); + + for (i = 0; i < len; i++) { + var mangle = angle - 360 * values[i] / total / 2; + + if (!i) { + angle = 90 - mangle; + mangle = angle - 360 * values[i] / total / 2; + } + + if (opts.init) { + var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(","); + } + + var path = sector(cx, cy, r, angle, angle -= 360 * values[i] / total); + var p = paper.path(opts.init ? ipath : path).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#666", stroke: opts.stroke || "#fff", "stroke-width": (opts.strokewidth == null ? 1 : opts.strokewidth), "stroke-linejoin": "round" }); + + p.value = values[i]; + p.middle = path.middle; + p.mangle = mangle; + sectors.push(p); + series.push(p); + opts.init && p.animate({ path: path.join(",") }, (+opts.init - 1) || 1000, ">"); + } + + for (i = 0; i < len; i++) { + p = paper.path(sectors[i].attr("path")).attr(chartinst.shim); + p.attr = function () {}; + opts.href && opts.href[i] && p.attr({ href: opts.href[i] }); + covers.push(p); + series.push(p); + } + } + + chart.hover = function (fin, fout) { + fout = fout || function () {}; + + var that = this; + + for (var i = 0; i < len; i++) { + (function (sector, cover, j) { + var o = { + sector: sector, + cover: cover, + cx: cx, + cy: cy, + mx: sector.middle.x, + my: sector.middle.y, + mangle: sector.mangle, + r: r, + value: values[j], + total: total, + label: that.labels && that.labels[j] + }; + cover.mouseover(function () { + fin.call(o); + }).mouseout(function () { + fout.call(o); + }); + })(series[i], covers[i], i); + } + return this; + }; + + // x: where label could be put + // y: where label could be put + // value: value to show + // total: total number to count % + chart.each = function (f) { + var that = this; + + for (var i = 0; i < len; i++) { + (function (sector, cover, j) { + var o = { + sector: sector, + cover: cover, + cx: cx, + cy: cy, + x: sector.middle.x, + y: sector.middle.y, + mangle: sector.mangle, + r: r, + value: values[j], + total: total, + label: that.labels && that.labels[j] + }; + f.call(o); + })(series[i], covers[i], i); + } + return this; + }; + + chart.click = function (f) { + var that = this; + + for (var i = 0; i < len; i++) { + (function (sector, cover, j) { + var o = { + sector: sector, + cover: cover, + cx: cx, + cy: cy, + mx: sector.middle.x, + my: sector.middle.y, + mangle: sector.mangle, + r: r, + value: values[j], + total: total, + label: that.labels && that.labels[j] + }; + cover.click(function () { f.call(o); }); + })(series[i], covers[i], i); + } + return this; + }; + + chart.inject = function (element) { + element.insertBefore(covers[0]); + }; + + var legend = function (labels, otherslabel, mark, dir) { + var x = cx + r + r / 5, + y = cy, + h = y + 10; + + labels = labels || []; + dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east"; + mark = paper[mark && mark.toLowerCase()] || "circle"; + chart.labels = paper.set(); + + for (var i = 0; i < len; i++) { + var clr = series[i].attr("fill"), + j = values[i].order, + txt; + + values[i].others && (labels[j] = otherslabel || "Others"); + labels[j] = chartinst.labelise(labels[j], values[i], total); + chart.labels.push(paper.set()); + chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ fill: clr, stroke: "none" })); + chart.labels[i].push(txt = paper.text(x + 20, h, labels[j] || values[j]).attr(chartinst.txtattr).attr({ fill: opts.legendcolor || "#000", "text-anchor": "start"})); + covers[i].label = chart.labels[i]; + h += txt.getBBox().height * 1.2; + } + + var bb = chart.labels.getBBox(), + tr = { + east: [0, -bb.height / 2], + west: [-bb.width - 2 * r - 20, -bb.height / 2], + north: [-r - bb.width / 2, -r - bb.height - 10], + south: [-r - bb.width / 2, r + 10] + }[dir]; + + chart.labels.translate.apply(chart.labels, tr); + chart.push(chart.labels); + }; + + if (opts.legend) { + legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos); + } + + chart.push(series, covers); + chart.series = series; + chart.covers = covers; + + return chart; + }; + + //inheritance + var F = function() {}; + F.prototype = Raphael.g; + Piechart.prototype = new F; + + //public + Raphael.fn.piechart = function(cx, cy, r, values, opts) { + return new Piechart(this, cx, cy, r, values, opts); + } + +})(); diff --git a/js/g.raphael.js b/js/g.raphael.js new file mode 100755 index 000000000..f49ffb510 --- /dev/null +++ b/js/g.raphael.js @@ -0,0 +1,887 @@ +/*! + * 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. + */ + +/* + * Tooltips on Element prototype + */ +/*\ + * Element.popup + [ method ] + ** + * Puts the context Element in a 'popup' tooltip. Can also be used on sets. + ** + > Parameters + ** + - dir (string) location of Element relative to the tail: `'down'`, `'left'`, `'up'` [default], or `'right'`. + - size (number) amount of bevel/padding around the Element, as well as half the width and height of the tail [default: `5`] + - x (number) x coordinate of the popup's tail [default: Element's `x` or `cx`] + - y (number) y coordinate of the popup's tail [default: Element's `y` or `cy`] + ** + = (object) path element of the popup + > Usage + | paper.circle(50, 50, 5).attr({ + | stroke: "#fff", + | fill: "0-#c9de96-#8ab66b:44-#398235" + | }).popup(); + \*/ +Raphael.el.popup = function (dir, size, x, y) { + var paper = this.paper || this[0].paper, + bb, xy, center, cw, ch; + + if (!paper) return; + + switch (this.type) { + case 'text': + case 'circle': + case 'ellipse': center = true; break; + default: center = false; + } + + dir = dir == null ? 'up' : dir; + size = size || 5; + bb = this.getBBox(); + + x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x); + y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y); + cw = Math.max(bb.width / 2 - size, 0); + ch = Math.max(bb.height / 2 - size, 0); + + this.translate(x - bb.x - (center ? bb.width / 2 : 0), y - bb.y - (center ? bb.height / 2 : 0)); + bb = this.getBBox(); + + var paths = { + up: [ + 'M', x, y, + 'l', -size, -size, -cw, 0, + 'a', size, size, 0, 0, 1, -size, -size, + 'l', 0, -bb.height, + 'a', size, size, 0, 0, 1, size, -size, + 'l', size * 2 + cw * 2, 0, + 'a', size, size, 0, 0, 1, size, size, + 'l', 0, bb.height, + 'a', size, size, 0, 0, 1, -size, size, + 'l', -cw, 0, + 'z' + ].join(','), + down: [ + 'M', x, y, + 'l', size, size, cw, 0, + 'a', size, size, 0, 0, 1, size, size, + 'l', 0, bb.height, + 'a', size, size, 0, 0, 1, -size, size, + 'l', -(size * 2 + cw * 2), 0, + 'a', size, size, 0, 0, 1, -size, -size, + 'l', 0, -bb.height, + 'a', size, size, 0, 0, 1, size, -size, + 'l', cw, 0, + 'z' + ].join(','), + left: [ + 'M', x, y, + 'l', -size, size, 0, ch, + 'a', size, size, 0, 0, 1, -size, size, + 'l', -bb.width, 0, + 'a', size, size, 0, 0, 1, -size, -size, + 'l', 0, -(size * 2 + ch * 2), + 'a', size, size, 0, 0, 1, size, -size, + 'l', bb.width, 0, + 'a', size, size, 0, 0, 1, size, size, + 'l', 0, ch, + 'z' + ].join(','), + right: [ + 'M', x, y, + 'l', size, -size, 0, -ch, + 'a', size, size, 0, 0, 1, size, -size, + 'l', bb.width, 0, + 'a', size, size, 0, 0, 1, size, size, + 'l', 0, size * 2 + ch * 2, + 'a', size, size, 0, 0, 1, -size, size, + 'l', -bb.width, 0, + 'a', size, size, 0, 0, 1, -size, -size, + 'l', 0, -ch, + 'z' + ].join(',') + }; + + xy = { + up: { x: -!center * (bb.width / 2), y: -size * 2 - (center ? bb.height / 2 : bb.height) }, + down: { x: -!center * (bb.width / 2), y: size * 2 + (center ? bb.height / 2 : bb.height) }, + left: { x: -size * 2 - (center ? bb.width / 2 : bb.width), y: -!center * (bb.height / 2) }, + right: { x: size * 2 + (center ? bb.width / 2 : bb.width), y: -!center * (bb.height / 2) } + }[dir]; + + this.translate(xy.x, xy.y); + return paper.path(paths[dir]).attr({ fill: "#000", stroke: "none" }).insertBefore(this.node ? this : this[0]); +}; + +/*\ + * Element.tag + [ method ] + ** + * Puts the context Element in a 'tag' tooltip. Can also be used on sets. + ** + > Parameters + ** + - angle (number) angle of orientation in degrees [default: `0`] + - r (number) radius of the loop [default: `5`] + - x (number) x coordinate of the center of the tag loop [default: Element's `x` or `cx`] + - y (number) y coordinate of the center of the tag loop [default: Element's `x` or `cx`] + ** + = (object) path element of the tag + > Usage + | paper.circle(50, 50, 15).attr({ + | stroke: "#fff", + | fill: "0-#c9de96-#8ab66b:44-#398235" + | }).tag(60); + \*/ +Raphael.el.tag = function (angle, r, x, y) { + var d = 3, + paper = this.paper || this[0].paper; + + if (!paper) return; + + var p = paper.path().attr({ fill: '#000', stroke: '#000' }), + bb = this.getBBox(), + dx, R, center, tmp; + + switch (this.type) { + case 'text': + case 'circle': + case 'ellipse': center = true; break; + default: center = false; + } + + angle = angle || 0; + x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x); + y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y); + r = r == null ? 5 : r; + R = .5522 * r; + + if (bb.height >= r * 2) { + p.attr({ + path: [ + "M", x, y + r, + "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2, + "m", 0, -r * 2 -d, + "a", r + d, r + d, 0, 1, 0, 0, (r + d) * 2, + "L", x + r + d, y + bb.height / 2 + d, + "l", bb.width + 2 * d, 0, 0, -bb.height - 2 * d, -bb.width - 2 * d, 0, + "L", x, y - r - d + ].join(",") + }); + } else { + dx = Math.sqrt(Math.pow(r + d, 2) - Math.pow(bb.height / 2 + d, 2)); + p.attr({ + path: [ + "M", x, y + r, + "c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r, + "M", x + dx, y - bb.height / 2 - d, + "a", r + d, r + d, 0, 1, 0, 0, bb.height + 2 * d, + "l", r + d - dx + bb.width + 2 * d, 0, 0, -bb.height - 2 * d, + "L", x + dx, y - bb.height / 2 - d + ].join(",") + }); + } + + angle = 360 - angle; + p.rotate(angle, x, y); + + if (this.attrs) { + //elements + this.attr(this.attrs.x ? 'x' : 'cx', x + r + d + (!center ? this.type == 'text' ? bb.width : 0 : bb.width / 2)).attr('y', center ? y : y - bb.height / 2); + this.rotate(angle, x, y); + angle > 90 && angle < 270 && this.attr(this.attrs.x ? 'x' : 'cx', x - r - d - (!center ? bb.width : bb.width / 2)).rotate(180, x, y); + } else { + //sets + if (angle > 90 && angle < 270) { + this.translate(x - bb.x - bb.width - r - d, y - bb.y - bb.height / 2); + this.rotate(angle - 180, bb.x + bb.width + r + d, bb.y + bb.height / 2); + } else { + this.translate(x - bb.x + r + d, y - bb.y - bb.height / 2); + this.rotate(angle, bb.x - r - d, bb.y + bb.height / 2); + } + } + + return p.insertBefore(this.node ? this : this[0]); +}; + +/*\ + * Element.drop + [ method ] + ** + * Puts the context Element in a 'drop' tooltip. Can also be used on sets. + ** + > Parameters + ** + - angle (number) angle of orientation in degrees [default: `0`] + - x (number) x coordinate of the drop's point [default: Element's `x` or `cx`] + - y (number) y coordinate of the drop's point [default: Element's `x` or `cx`] + ** + = (object) path element of the drop + > Usage + | paper.circle(50, 50, 8).attr({ + | stroke: "#fff", + | fill: "0-#c9de96-#8ab66b:44-#398235" + | }).drop(60); + \*/ +Raphael.el.drop = function (angle, x, y) { + var bb = this.getBBox(), + paper = this.paper || this[0].paper, + center, size, p, dx, dy; + + if (!paper) return; + + switch (this.type) { + case 'text': + case 'circle': + case 'ellipse': center = true; break; + default: center = false; + } + + angle = angle || 0; + + x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x); + y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y); + size = Math.max(bb.width, bb.height) + Math.min(bb.width, bb.height); + p = paper.path([ + "M", x, y, + "l", size, 0, + "A", size * .4, size * .4, 0, 1, 0, x + size * .7, y - size * .7, + "z" + ]).attr({fill: "#000", stroke: "none"}).rotate(22.5 - angle, x, y); + + angle = (angle + 90) * Math.PI / 180; + dx = (x + size * Math.sin(angle)) - (center ? 0 : bb.width / 2); + dy = (y + size * Math.cos(angle)) - (center ? 0 : bb.height / 2); + + this.attrs ? + this.attr(this.attrs.x ? 'x' : 'cx', dx).attr(this.attrs.y ? 'y' : 'cy', dy) : + this.translate(dx - bb.x, dy - bb.y); + + return p.insertBefore(this.node ? this : this[0]); +}; + +/*\ + * Element.flag + [ method ] + ** + * Puts the context Element in a 'flag' tooltip. Can also be used on sets. + ** + > Parameters + ** + - angle (number) angle of orientation in degrees [default: `0`] + - x (number) x coordinate of the flag's point [default: Element's `x` or `cx`] + - y (number) y coordinate of the flag's point [default: Element's `x` or `cx`] + ** + = (object) path element of the flag + > Usage + | paper.circle(50, 50, 10).attr({ + | stroke: "#fff", + | fill: "0-#c9de96-#8ab66b:44-#398235" + | }).flag(60); + \*/ +Raphael.el.flag = function (angle, x, y) { + var d = 3, + paper = this.paper || this[0].paper; + + if (!paper) return; + + var p = paper.path().attr({ fill: '#000', stroke: '#000' }), + bb = this.getBBox(), + h = bb.height / 2, + center; + + switch (this.type) { + case 'text': + case 'circle': + case 'ellipse': center = true; break; + default: center = false; + } + + angle = angle || 0; + x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x); + y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2: bb.y); + + p.attr({ + path: [ + "M", x, y, + "l", h + d, -h - d, bb.width + 2 * d, 0, 0, bb.height + 2 * d, -bb.width - 2 * d, 0, + "z" + ].join(",") + }); + + angle = 360 - angle; + p.rotate(angle, x, y); + + if (this.attrs) { + //elements + this.attr(this.attrs.x ? 'x' : 'cx', x + h + d + (!center ? this.type == 'text' ? bb.width : 0 : bb.width / 2)).attr('y', center ? y : y - bb.height / 2); + this.rotate(angle, x, y); + angle > 90 && angle < 270 && this.attr(this.attrs.x ? 'x' : 'cx', x - h - d - (!center ? bb.width : bb.width / 2)).rotate(180, x, y); + } else { + //sets + if (angle > 90 && angle < 270) { + this.translate(x - bb.x - bb.width - h - d, y - bb.y - bb.height / 2); + this.rotate(angle - 180, bb.x + bb.width + h + d, bb.y + bb.height / 2); + } else { + this.translate(x - bb.x + h + d, y - bb.y - bb.height / 2); + this.rotate(angle, bb.x - h - d, bb.y + bb.height / 2); + } + } + + return p.insertBefore(this.node ? this : this[0]); +}; + +/*\ + * Element.label + [ method ] + ** + * Puts the context Element in a 'label' tooltip. Can also be used on sets. + ** + = (object) path element of the label. + > Usage + | paper.circle(50, 50, 10).attr({ + | stroke: "#fff", + | fill: "0-#c9de96-#8ab66b:44-#398235" + | }).label(); + \*/ +Raphael.el.label = function () { + var bb = this.getBBox(), + paper = this.paper || this[0].paper, + r = Math.min(20, bb.width + 10, bb.height + 10) / 2; + + if (!paper) return; + + return paper.rect(bb.x - r / 2, bb.y - r / 2, bb.width + r, bb.height + r, r).attr({ stroke: 'none', fill: '#000' }).insertBefore(this.node ? this : this[0]); +}; + +/*\ + * Element.blob + [ method ] + ** + * Puts the context Element in a 'blob' tooltip. Can also be used on sets. + ** + > Parameters + ** + - angle (number) angle of orientation in degrees [default: `0`] + - x (number) x coordinate of the blob's tail [default: Element's `x` or `cx`] + - y (number) y coordinate of the blob's tail [default: Element's `x` or `cx`] + ** + = (object) path element of the blob + > Usage + | paper.circle(50, 50, 8).attr({ + | stroke: "#fff", + | fill: "0-#c9de96-#8ab66b:44-#398235" + | }).blob(60); + \*/ +Raphael.el.blob = function (angle, x, y) { + var bb = this.getBBox(), + rad = Math.PI / 180, + paper = this.paper || this[0].paper, + p, center, size; + + if (!paper) return; + + switch (this.type) { + case 'text': + case 'circle': + case 'ellipse': center = true; break; + default: center = false; + } + + p = paper.path().attr({ fill: "#000", stroke: "none" }); + angle = (+angle + 1 ? angle : 45) + 90; + size = Math.min(bb.height, bb.width); + x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x); + y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y); + + var w = Math.max(bb.width + size, size * 25 / 12), + h = Math.max(bb.height + size, size * 25 / 12), + x2 = x + size * Math.sin((angle - 22.5) * rad), + y2 = y + size * Math.cos((angle - 22.5) * rad), + x1 = x + size * Math.sin((angle + 22.5) * rad), + y1 = y + size * Math.cos((angle + 22.5) * rad), + dx = (x1 - x2) / 2, + dy = (y1 - y2) / 2, + rx = w / 2, + ry = h / 2, + k = -Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * dy * dy - ry * ry * dx * dx) / (rx * rx * dy * dy + ry * ry * dx * dx)), + cx = k * rx * dy / ry + (x1 + x2) / 2, + cy = k * -ry * dx / rx + (y1 + y2) / 2; + + p.attr({ + x: cx, + y: cy, + path: [ + "M", x, y, + "L", x1, y1, + "A", rx, ry, 0, 1, 1, x2, y2, + "z" + ].join(",") + }); + + this.translate(cx - bb.x - bb.width / 2, cy - bb.y - bb.height / 2); + + return p.insertBefore(this.node ? this : this[0]); +}; + +/* + * Tooltips on Paper prototype + */ +/*\ + * Paper.label + [ method ] + ** + * Puts the given `text` into a 'label' tooltip. The text is given a default style according to @g.txtattr. See @Element.label + ** + > Parameters + ** + - x (number) x coordinate of the center of the label + - y (number) y coordinate of the center of the label + - text (string) text to place inside the label + ** + = (object) set containing the label path and the text element + > Usage + | paper.label(50, 50, "$9.99"); + \*/ +Raphael.fn.label = function (x, y, text) { + var set = this.set(); + + text = this.text(x, y, text).attr(Raphael.g.txtattr); + return set.push(text.label(), text); +}; + +/*\ + * Paper.popup + [ method ] + ** + * Puts the given `text` into a 'popup' tooltip. The text is given a default style according to @g.txtattr. See @Element.popup + * + * Note: The `dir` parameter has changed from g.Raphael 0.4.1 to 0.5. The options `0`, `1`, `2`, and `3` has been changed to `'down'`, `'left'`, `'up'`, and `'right'` respectively. + ** + > Parameters + ** + - x (number) x coordinate of the popup's tail + - y (number) y coordinate of the popup's tail + - text (string) text to place inside the popup + - dir (string) location of the text relative to the tail: `'down'`, `'left'`, `'up'` [default], or `'right'`. + - size (number) amount of padding around the Element [default: `5`] + ** + = (object) set containing the popup path and the text element + > Usage + | paper.popup(50, 50, "$9.99", 'down'); + \*/ +Raphael.fn.popup = function (x, y, text, dir, size) { + var set = this.set(); + + text = this.text(x, y, text).attr(Raphael.g.txtattr); + return set.push(text.popup(dir, size), text); +}; + +/*\ + * Paper.tag + [ method ] + ** + * Puts the given text into a 'tag' tooltip. The text is given a default style according to @g.txtattr. See @Element.tag + ** + > Parameters + ** + - x (number) x coordinate of the center of the tag loop + - y (number) y coordinate of the center of the tag loop + - text (string) text to place inside the tag + - angle (number) angle of orientation in degrees [default: `0`] + - r (number) radius of the loop [default: `5`] + ** + = (object) set containing the tag path and the text element + > Usage + | paper.tag(50, 50, "$9.99", 60); + \*/ +Raphael.fn.tag = function (x, y, text, angle, r) { + var set = this.set(); + + text = this.text(x, y, text).attr(Raphael.g.txtattr); + return set.push(text.tag(angle, r), text); +}; + +/*\ + * Paper.flag + [ method ] + ** + * Puts the given `text` into a 'flag' tooltip. The text is given a default style according to @g.txtattr. See @Element.flag + ** + > Parameters + ** + - x (number) x coordinate of the flag's point + - y (number) y coordinate of the flag's point + - text (string) text to place inside the flag + - angle (number) angle of orientation in degrees [default: `0`] + ** + = (object) set containing the flag path and the text element + > Usage + | paper.flag(50, 50, "$9.99", 60); + \*/ +Raphael.fn.flag = function (x, y, text, angle) { + var set = this.set(); + + text = this.text(x, y, text).attr(Raphael.g.txtattr); + return set.push(text.flag(angle), text); +}; + +/*\ + * Paper.drop + [ method ] + ** + * Puts the given text into a 'drop' tooltip. The text is given a default style according to @g.txtattr. See @Element.drop + ** + > Parameters + ** + - x (number) x coordinate of the drop's point + - y (number) y coordinate of the drop's point + - text (string) text to place inside the drop + - angle (number) angle of orientation in degrees [default: `0`] + ** + = (object) set containing the drop path and the text element + > Usage + | paper.drop(50, 50, "$9.99", 60); + \*/ +Raphael.fn.drop = function (x, y, text, angle) { + var set = this.set(); + + text = this.text(x, y, text).attr(Raphael.g.txtattr); + return set.push(text.drop(angle), text); +}; + +/*\ + * Paper.blob + [ method ] + ** + * Puts the given text into a 'blob' tooltip. The text is given a default style according to @g.txtattr. See @Element.blob + ** + > Parameters + ** + - x (number) x coordinate of the blob's tail + - y (number) y coordinate of the blob's tail + - text (string) text to place inside the blob + - angle (number) angle of orientation in degrees [default: `0`] + ** + = (object) set containing the blob path and the text element + > Usage + | paper.blob(50, 50, "$9.99", 60); + \*/ +Raphael.fn.blob = function (x, y, text, angle) { + var set = this.set(); + + text = this.text(x, y, text).attr(Raphael.g.txtattr); + return set.push(text.blob(angle), text); +}; + +/** + * Brightness functions on the Element prototype + */ +/*\ + * Element.lighter + [ method ] + ** + * Makes the context element lighter by increasing the brightness and reducing the saturation by a given factor. Can be called on Sets. + ** + > Parameters + ** + - times (number) adjustment factor [default: `2`] + ** + = (object) Element + > Usage + | paper.circle(50, 50, 20).attr({ + | fill: "#ff0000", + | stroke: "#fff", + | "stroke-width": 2 + | }).lighter(6); + \*/ +Raphael.el.lighter = function (times) { + times = times || 2; + + var fs = [this.attrs.fill, this.attrs.stroke]; + + this.fs = this.fs || [fs[0], fs[1]]; + + fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex); + fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex); + fs[0].b = Math.min(fs[0].b * times, 1); + fs[0].s = fs[0].s / times; + fs[1].b = Math.min(fs[1].b * times, 1); + fs[1].s = fs[1].s / times; + + this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"}); + return this; +}; + +/*\ + * Element.darker + [ method ] + ** + * Makes the context element darker by decreasing the brightness and increasing the saturation by a given factor. Can be called on Sets. + ** + > Parameters + ** + - times (number) adjustment factor [default: `2`] + ** + = (object) Element + > Usage + | paper.circle(50, 50, 20).attr({ + | fill: "#ff0000", + | stroke: "#fff", + | "stroke-width": 2 + | }).darker(6); + \*/ +Raphael.el.darker = function (times) { + times = times || 2; + + var fs = [this.attrs.fill, this.attrs.stroke]; + + this.fs = this.fs || [fs[0], fs[1]]; + + fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex); + fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex); + fs[0].s = Math.min(fs[0].s * times, 1); + fs[0].b = fs[0].b / times; + fs[1].s = Math.min(fs[1].s * times, 1); + fs[1].b = fs[1].b / times; + + this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"}); + return this; +}; + +/*\ + * Element.resetBrightness + [ method ] + ** + * Resets brightness and saturation levels to their original values. See @Element.lighter and @Element.darker. Can be called on Sets. + ** + = (object) Element + > Usage + | paper.circle(50, 50, 20).attr({ + | fill: "#ff0000", + | stroke: "#fff", + | "stroke-width": 2 + | }).lighter(6).resetBrightness(); + \*/ +Raphael.el.resetBrightness = function () { + if (this.fs) { + this.attr({ fill: this.fs[0], stroke: this.fs[1] }); + delete this.fs; + } + return this; +}; + +//alias to set prototype +(function () { + var brightness = ['lighter', 'darker', 'resetBrightness'], + tooltips = ['popup', 'tag', 'flag', 'label', 'drop', 'blob']; + + for (var f in tooltips) (function (name) { + Raphael.st[name] = function () { + return Raphael.el[name].apply(this, arguments); + }; + })(tooltips[f]); + + for (var f in brightness) (function (name) { + Raphael.st[name] = function () { + for (var i = 0; i < this.length; i++) { + this[i][name].apply(this[i], arguments); + } + + return this; + }; + })(brightness[f]); +})(); + +//chart prototype for storing common functions +Raphael.g = { + /*\ + * g.shim + [ object ] + ** + * An attribute object that charts will set on all generated shims (shims being the invisible objects that mouse events are bound to) + ** + > Default value + | { stroke: 'none', fill: '#000', 'fill-opacity': 0 } + \*/ + shim: { stroke: 'none', fill: '#000', 'fill-opacity': 0 }, + + /*\ + * g.txtattr + [ object ] + ** + * An attribute object that charts and tooltips will set on any generated text + ** + > Default value + | { font: '12px Arial, sans-serif', fill: '#fff' } + \*/ + txtattr: { font: '12px Arial, sans-serif', fill: '#fff' }, + + /*\ + * g.colors + [ array ] + ** + * An array of color values that charts will iterate through when drawing chart data values. + ** + \*/ + colors: (function () { + var hues = [.6, .2, .05, .1333, .75, 0], + colors = []; + + for (var i = 0; i < 10; i++) { + if (i < hues.length) { + colors.push('hsb(' + hues[i] + ',.75, .75)'); + } else { + colors.push('hsb(' + hues[i - hues.length] + ', 1, .5)'); + } + } + + return colors; + })(), + + snapEnds: function(from, to, steps) { + var f = from, + t = to; + + if (f == t) { + return {from: f, to: t, power: 0}; + } + + function round(a) { + return Math.abs(a - .5) < .25 ? ~~(a) + .5 : Math.round(a); + } + + var d = (t - f) / steps, + r = ~~(d), + R = r, + i = 0; + + if (r) { + while (R) { + i--; + R = ~~(d * Math.pow(10, i)) / Math.pow(10, i); + } + + i ++; + } else { + while (!r) { + i = i || 1; + r = ~~(d * Math.pow(10, i)) / Math.pow(10, i); + i++; + } + + i && i--; + } + + t = round(to * Math.pow(10, i)) / Math.pow(10, i); + + if (t < to) { + t = round((to + .5) * Math.pow(10, i)) / Math.pow(10, i); + } + + f = round((from - (i > 0 ? 0 : .5)) * Math.pow(10, i)) / Math.pow(10, i); + return { from: f, to: t, power: i }; + }, + + axis: function (x, y, length, from, to, steps, orientation, labels, type, dashsize, paper) { + dashsize = dashsize == null ? 2 : dashsize; + type = type || "t"; + steps = steps || 10; + paper = arguments[arguments.length-1] //paper is always last argument + + var path = type == "|" || type == " " ? ["M", x + .5, y, "l", 0, .001] : orientation == 1 || orientation == 3 ? ["M", x + .5, y, "l", 0, -length] : ["M", x, y + .5, "l", length, 0], + ends = this.snapEnds(from, to, steps), + f = ends.from, + t = ends.to, + i = ends.power, + j = 0, + txtattr = { font: "11px 'Fontin Sans', Fontin-Sans, sans-serif" }, + text = paper.set(), + d; + + d = (t - f) / steps; + + var label = f, + rnd = i > 0 ? i : 0; + dx = length / steps; + + if (+orientation == 1 || +orientation == 3) { + var Y = y, + addon = (orientation - 1 ? 1 : -1) * (dashsize + 3 + !!(orientation - 1)); + + while (Y >= y - length) { + type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), Y + .5, "l", dashsize * 2 + 1, 0])); + text.push(paper.text(x + addon, Y, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr).attr({ "text-anchor": orientation - 1 ? "start" : "end" })); + label += d; + Y -= dx; + } + + if (Math.round(Y + dx - (y - length))) { + type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), y - length + .5, "l", dashsize * 2 + 1, 0])); + text.push(paper.text(x + addon, y - length, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr).attr({ "text-anchor": orientation - 1 ? "start" : "end" })); + } + } else { + label = f; + rnd = (i > 0) * i; + addon = (orientation ? -1 : 1) * (dashsize + 9 + !orientation); + + var X = x, + dx = length / steps, + txt = 0, + prev = 0; + + while (X <= x + length) { + type != "-" && type != " " && (path = path.concat(["M", X + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1])); + text.push(txt = paper.text(X, y + addon, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr)); + + var bb = txt.getBBox(); + + if (prev >= bb.x - 5) { + text.pop(text.length - 1).remove(); + } else { + prev = bb.x + bb.width; + } + + label += d; + X += dx; + } + + if (Math.round(X - dx - x - length)) { + type != "-" && type != " " && (path = path.concat(["M", x + length + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1])); + text.push(paper.text(x + length, y + addon, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(txtattr)); + } + } + + var res = paper.path(path); + + res.text = text; + res.all = paper.set([res, text]); + res.remove = function () { + this.text.remove(); + this.constructor.prototype.remove.call(this); + }; + + return res; + }, + + labelise: function(label, val, total) { + if (label) { + return (label + "").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g, function (all, value, percent) { + if (value) { + return (+val).toFixed(value.replace(/^#+\.?/g, "").length); + } + if (percent) { + return (val * 100 / total).toFixed(percent.replace(/^%+\.?/g, "").length) + "%"; + } + }); + } else { + return (+val).toFixed(0); + } + } +} diff --git a/js/raphael-min.js b/js/raphael-min.js new file mode 100755 index 000000000..39da02126 --- /dev/null +++ b/js/raphael-min.js @@ -0,0 +1,9 @@ +// ┌────────────────────────────────────────────────────────────────────┐ \\ +// │ Raphaël 2.0.2 - JavaScript Vector Library │ \\ +// ├────────────────────────────────────────────────────────────────────┤ \\ +// │ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com) │ \\ +// │ Copyright © 2008-2012 Sencha Labs (http://sencha.com) │ \\ +// ├────────────────────────────────────────────────────────────────────┤ \\ +// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\ +// └────────────────────────────────────────────────────────────────────┘ \\ +(function(a){var b="0.3.4",c="hasOwnProperty",d=/[\.\/]/,e="*",f=function(){},g=function(a,b){return a-b},h,i,j={n:{}},k=function(a,b){var c=j,d=i,e=Array.prototype.slice.call(arguments,2),f=k.listeners(a),l=0,m=!1,n,o=[],p={},q=[],r=h,s=[];h=a,i=0;for(var t=0,u=f.length;tf*b.top){e=b.percents[y],p=b.percents[y-1]||0,t=t/b.top*(e-p),o=b.percents[y+1],j=b.anim[e];break}f&&d.attr(b.anim[b.percents[y]])}if(!!j){if(!k){for(var A in j)if(j[g](A))if(U[g](A)||d.paper.customAttributes[g](A)){u[A]=d.attr(A),u[A]==null&&(u[A]=T[A]),v[A]=j[A];switch(U[A]){case C:w[A]=(v[A]-u[A])/t;break;case"colour":u[A]=a.getRGB(u[A]);var B=a.getRGB(v[A]);w[A]={r:(B.r-u[A].r)/t,g:(B.g-u[A].g)/t,b:(B.b-u[A].b)/t};break;case"path":var D=bH(u[A],v[A]),E=D[1];u[A]=D[0],w[A]=[];for(y=0,z=u[A].length;yd)return d;while(cf?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function ce(){return this.x+q+this.y+q+this.width+" × "+this.height}function cd(){return this.x+q+this.y}function bR(a,b,c,d,e,f){a!=null?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function bx(a,b){var c=[];for(var d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function bw(){return this.hex}function bu(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),h=d.cache=d.cache||{},i=d.count=d.count||[];if(h[g](f)){bt(i,f);return c?c(h[f]):h[f]}i.length>=1e3&&delete h[i.shift()],i.push(f),h[f]=a[m](b,e);return c?c(h[f]):h[f]}return d}function bt(a,b){for(var c=0,d=a.length;c',bl=bk.firstChild,bl.style.behavior="url(#default#VML)";if(!bl||typeof bl.adj!="object")return a.type=p;bk=null}a.svg=!(a.vml=a.type=="VML"),a._Paper=j,a.fn=k=j.prototype=a.prototype,a._id=0,a._oid=0,a.is=function(a,b){b=v.call(b);if(b=="finite")return!M[g](+a);if(b=="array")return a instanceof Array;return b=="null"&&a===null||b==typeof a&&a!==null||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||H.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return(180+w.atan2(-i,-h)*180/B+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*B/180},a.deg=function(a){return a*180/B%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,E)){var e=b.length;while(e--)if(z(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(fb-d)return c-f+b}return c};var bm=a.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=w.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)});a.setWindow=function(b){eve("setWindow",a,h.win,b),h.win=b,h.doc=h.win.document,a._engine.initWin&&a._engine.initWin(h.win)};var bn=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write(""),e.close(),d=e.body}catch(f){d=createPopup().document.body}var g=d.createTextRange();bn=bu(function(a){try{d.style.color=r(a).replace(c,p);var b=g.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=h.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",h.doc.body.appendChild(i),bn=bu(function(a){i.style.color=a;return h.doc.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bn(b)},bo=function(){return"hsb("+[this.h,this.s,this.b]+")"},bp=function(){return"hsl("+[this.h,this.s,this.l]+")"},bq=function(){return this.hex},br=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,D)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;return[b,c,d]},bs=function(b,c,d,e){b*=255,c*=255,d*=255;var f={r:b,g:c,b:d,hex:a.rgb(b,c,d),toString:bq};a.is(e,"finite")&&(f.opacity=e);return f};a.color=function(b){var c;a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b?(c=a.hsb2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b?(c=a.hsl2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):(a.is(b,"string")&&(b=a.getRGB(b)),a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b?(c=a.rgb2hsl(b),b.h=c.h,b.s=c.s,b.l=c.l,c=a.rgb2hsb(b),b.v=c.b):(b={hex:"none"},b.r=b.g=b.b=b.h=b.s=b.v=b.l=-1)),b.toString=bq;return b},a.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,f,g,h,i;a=a%360/60,i=c*b,h=i*(1-z(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bs(e,f,g,d)},a.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h);if(a>1||b>1||c>1)a/=360,b/=100,c/=100;a*=360;var e,f,g,h,i;a=a%360/60,i=2*b*(c<.5?c:1-c),h=i*(1-z(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bs(e,f,g,d)},a.rgb2hsb=function(a,b,c){c=br(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;f=x(a,b,c),g=f-y(a,b,c),d=g==0?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=g==0?0:g/f;return{h:d,s:e,b:f,toString:bo}},a.rgb2hsl=function(a,b,c){c=br(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;g=x(a,b,c),h=y(a,b,c),i=g-h,d=i==0?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=i==0?0:f<.5?i/(2*f):i/(2-2*f);return{h:d,s:e,l:f,toString:bp}},a._path2string=function(){return this.join(",").replace(Y,"$1")};var bv=a._preload=function(a,b){var c=h.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,h.doc.body.removeChild(this)},c.onerror=function(){h.doc.body.removeChild(this)},h.doc.body.appendChild(c),c.src=a};a.getRGB=bu(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bw};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none",toString:bw};!X[g](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bn(b));var c,d,e,f,h,i,j,k=b.match(L);if(k){k[2]&&(f=R(k[2].substring(5),16),e=R(k[2].substring(3,5),16),d=R(k[2].substring(1,3),16)),k[3]&&(f=R((i=k[3].charAt(3))+i,16),e=R((i=k[3].charAt(2))+i,16),d=R((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,f,h)}if(k[6]){j=k[6][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,f,h)}k={r:d,g:e,b:f,toString:bw},k.hex="#"+(16777216|f|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bw}},a),a.hsb=bu(function(b,c,d){return a.hsb2rgb(b,c,d).hex}),a.hsl=bu(function(b,c,d){return a.hsl2rgb(b,c,d).hex}),a.rgb=bu(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=bu(function(b){if(!b)return null;var c={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bz(b)),d.length||r(b).replace(Z,function(a,b,e){var f=[],g=b.toLowerCase();e.replace(_,function(a,b){b&&f.push(+b)}),g=="m"&&f.length>2&&(d.push([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");if(g=="r")d.push([b][n](f));else while(f.length>=c[g]){d.push([b][n](f.splice(0,c[g])));if(!c[g])break}}),d.toString=a._path2string;return d}),a.parseTransformString=bu(function(b){if(!b)return null;var c={r:3,s:4,t:2,m:6},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bz(b)),d.length||r(b).replace($,function(a,b,c){var e=[],f=v.call(b);c.replace(_,function(a,b){b&&e.push(+b)}),d.push([b][n](e))}),d.toString=a._path2string;return d}),a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=A(j,3),l=A(j,2),m=i*i,n=m*i,o=k*a+l*3*i*c+j*3*i*i*e+n*g,p=k*b+l*3*i*d+j*3*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,x=j*e+i*g,y=j*f+i*h,z=90-w.atan2(q-s,r-t)*180/B;(q>s||r1&&(v=w.sqrt(v),c=v*c,d=v*d);var x=c*c,y=d*d,A=(f==g?-1:1)*w.sqrt(z((x*y-x*u*u-y*t*t)/(x*u*u+y*t*t))),C=A*c*u/d+(a+h)/2,D=A*-d*t/c+(b+i)/2,E=w.asin(((b-D)/d).toFixed(9)),F=w.asin(((i-D)/d).toFixed(9));E=aF&&(E=E-B*2),!g&&F>E&&(F=F-B*2)}else E=j[0],F=j[1],C=j[2],D=j[3];var G=F-E;if(z(G)>k){var H=F,I=h,J=i;F=E+k*(g&&F>E?1:-1),h=C+c*w.cos(F),i=D+d*w.sin(F),m=bE(h,i,c,d,e,0,g,I,J,[F,H,C,D])}G=F-E;var K=w.cos(E),L=w.sin(E),M=w.cos(F),N=w.sin(F),O=w.tan(G/4),P=4/3*c*O,Q=4/3*d*O,R=[a,b],S=[a+P*L,b-Q*K],T=[h+P*N,i-Q*M],U=[h,i];S[0]=2*R[0]-S[0],S[1]=2*R[1]-S[1];if(j)return[S,T,U][n](m);m=[S,T,U][n](m).join()[s](",");var V=[];for(var W=0,X=m.length;W"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bF(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bF(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bF(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bF(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y));return{min:{x:y[m](0,p),y:y[m](0,o)},max:{x:x[m](0,p),y:x[m](0,o)}}}),bH=a._path2curve=bu(function(a,b){var c=bB(a),d=b&&bB(b),e={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bE[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bD(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bD(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](bC(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](bC(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](bC(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](bC(b.x,b.y,b.X,b.Y))}return a},h=function(a,b){if(a[b].length>7){a[b].shift();var e=a[b];while(e.length)a.splice(b++,0,["C"][n](e.splice(0,6)));a.splice(b,1),k=x(c.length,d&&d.length||0)}},i=function(a,b,e,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),e.bx=0,e.by=0,e.x=a[g][1],e.y=a[g][2],k=x(c.length,d&&d.length||0))};for(var j=0,k=x(c.length,d&&d.length||0);j=j)return p;o=p}if(j==null)return k},ch=function(b,c){return function(d,e,f){d=bH(d);var g,h,i,j,k="",l={},m,n=0;for(var o=0,p=d.length;oe){if(c&&!l.start){m=cg(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C"+m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M"+m.x,m.y+"C"+m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cg(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i.shift()+i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},ci=ch(1),cj=ch(),ck=ch(0,1);a.getTotalLength=ci,a.getPointAtLength=cj,a.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return ck(a,b).end;var d=ck(a,c,1);return b?ck(d,b).end:d},b_.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return ci(this.attrs.path)}},b_.getPointAtLength=function(a){if(this.type=="path")return cj(this.attrs.path,a)},b_.getSubpath=function(b,c){if(this.type=="path")return a.getSubpath(this.attrs.path,b,c)};var cl=a.easing_formulas={linear:function(a){return a},"<":function(a){return A(a,1.7)},">":function(a){return A(a,.48)},"<>":function(a){var b=.48-a/1.04,c=w.sqrt(.1734+b*b),d=c-b,e=A(z(d),1/3)*(d<0?-1:1),f=-c-b,g=A(z(f),1/3)*(f<0?-1:1),h=e+g+.5;return(1-h)*3*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==!!a)return a;return A(2,-10*a)*w.sin((a-.075)*2*B/.3)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};cl.easeIn=cl["ease-in"]=cl["<"],cl.easeOut=cl["ease-out"]=cl[">"],cl.easeInOut=cl["ease-in-out"]=cl["<>"],cl["back-in"]=cl.backIn,cl["back-out"]=cl.backOut;var cm=[],cn=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},co=function(){var b=+(new Date),c=0;for(;c1&&!d.next){for(s in k)k[g](s)&&(r[s]=d.totalOrigin[s]);d.el.attr(r),cs(d.anim,d.el,d.anim.percents[0],null,d.totalOrigin,d.repeat-1)}d.next&&!d.stop&&cs(d.anim,d.el,d.next,null,d.totalOrigin,d.repeat)}}a.svg&&m&&m.paper&&m.paper.safari(),cm.length&&cn(co)},cp=function(a){return a>255?255:a<0?0:a};b_.animateWith=function(b,c,d,e,f,g){var h=this;if(h.removed){g&&g.call(h);return h}var i=d instanceof cr?d:a.animation(d,e,f,g),j,k;cs(i,h,i.percents[0],null,h.attr());for(var l=0,m=cm.length;l.5)*2-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&n!=.5&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/);if(j=="linear"){var t=e.shift();t=-d(t);if(isNaN(t))return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=a._parseDots(e);if(!w)return null;k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient);if(!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,j=="radial"?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;x1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(p),i.setAttribute(o,G.hex),o=="stroke"&&G[b]("opacity")&&q(i,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity}),o=="stroke"&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":(d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),H&&(I=H.getElementsByTagName("stop"),q(I[I.length-1],{"stop-opacity":p}));break};default:o=="font-size"&&(p=e(p,10)+"px");var J=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[J]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if(d.type=="text"&&!!(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){g.text=f.text;while(h.firstChild)h.removeChild(h.firstChild);var j=c(f.text).split("\n"),k=[],m;for(var n=0,o=j.length;n"));var $=X.getBoundingClientRect();t.W=m.w=($.right-$.left)/Y,t.H=m.h=($.bottom-$.top)/Y,t.X=m.x,t.Y=m.y+t.H/2,("x"in i||"y"in i)&&(t.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));var _=["x","y","text","font","font-family","font-weight","font-style","font-size"];for(var ba=0,bb=_.length;ba.25&&(c=e.sqrt(.25-i(b-.5,2))*((c>.5)*2-1)+.5),m=b+n+c);return o}),f=f.split(/\s*\-\s*/);if(l=="linear"){var p=f.shift();p=-d(p);if(isNaN(p))return null}var q=a._parseDots(f);if(!q)return null;b=b.shape||b.node;if(q.length){b.removeChild(g),g.on=!0,g.method="none",g.color=q[0].color,g.color2=q[q.length-1].color;var r=[];for(var s=0,t=q.length;s')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e,f=b.width,g=b.x,h=b.y;if(!c)throw new Error("VML container not found.");var i=new a._Paper,j=i.canvas=a._g.doc.createElement("div"),k=j.style;g=g||0,h=h||0,f=f||512,d=d||342,i.width=f,i.height=d,f==+f&&(f+="px"),d==+d&&(d+="px"),i.coordsize=u*1e3+n+u*1e3,i.coordorigin="0 0",i.span=a._g.doc.createElement("span"),i.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",j.appendChild(i.span),k.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(a._g.doc.body.appendChild(j),k.left=g+"px",k.top=h+"px",k.position="absolute"):c.firstChild?c.insertBefore(j,c.firstChild):c.appendChild(j),i.renderfix=function(){};return i},a.prototype.clear=function(){a.eve("clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}(window.Raphael) \ No newline at end of file