Prechádzať zdrojové kódy

Starting to use RapahelJS for charts rendering

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@2048 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 13 rokov pred
rodič
commit
02cfe9ff07
5 zmenil súbory, kde vykonal 1561 pridanie a 0 odobranie
  1. 255 0
      js/charts.js
  2. 156 0
      js/g.dot.js
  3. 255 0
      js/g.pie.js
  4. 887 0
      js/g.raphael.js
  5. 8 0
      js/raphael-min.js

+ 255 - 0
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;
+		}
+	});	
+});

+ 156 - 0
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);
+    }
+})();

+ 255 - 0
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);
+    }
+    
+})();

+ 887 - 0
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);
+        }
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 8 - 0
js/raphael-min.js


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov