fix #8056 - more refinements to checking data
[g.raphael] / g.pie.js
1 /*
2  * g.Raphael 0.5 - Charting library, based on RaphaĆ«l
3  *
4  * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 Raphael = typeof(Raphael) != 'undefined' ? Raphael :  (imports ? imports.seed.Raphael.Raphael : {});
8 Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
9 //chartinst = typeof(chartinst) != 'undefined' ? chartinst:  (imports ? imports.chartinst.chartinst : {});
10  
11
12
13 (function () {
14
15     /**
16      * @param {Raphael} paper to draw on
17      * @param {int} cx - centre X
18      * @param {int} cy - centre Y
19      * @param {int} r - radius
20      * @param {Array} values
21      * @param {Object} opts options
22      *   cut : after this meany items - do not show a pie element?
23      *   
24      *   
25      * 
26      */
27
28     function Piechart(paper, cx, cy, r, values, opts) {
29         
30         opts = opts || {};
31
32         var chartinst = this,
33             sectors = [],
34             covers = paper.set(),
35             chart = paper.set(),
36             series = paper.set(),
37             order = [],
38             len = values.length,
39             angle = 0,
40             total = 0,
41             others = 0,
42             cut = opts.cut || 9,
43             defcut = true;
44             
45         function sector(cx, cy, r, startAngle, endAngle, fill) {
46             var rad = Math.PI / 180,
47                 x1 = cx + r * Math.cos(-startAngle * rad),
48                 x2 = cx + r * Math.cos(-endAngle * rad),
49                 xm = cx + r / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad),
50                 y1 = cy + r * Math.sin(-startAngle * rad),
51                 y2 = cy + r * Math.sin(-endAngle * rad),
52                 ym = cy + r / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad),
53                 res = [
54                     "M", cx, cy,
55                     "L", x1, y1,
56                     "A", r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2,
57                     "z"
58                 ];
59
60             res.middle = { x: xm, y: ym };
61             return res;
62         }
63         
64         chart.covers = covers;
65         
66         if (len == 1) {
67             series.push(paper.circle(cx, cy, r).attr({ fill: opts.colors && opts.colors[0] || chartinst.colors[0] || "#666", stroke: opts.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth }));
68             covers.push(paper.circle(cx, cy, r).attr(chartinst.shim));
69             total = values[0] * 1;
70             values[0] = { value: values[0], order: 0, valueOf: function () { return this.value; } };
71             series[0].middle = {x: cx, y: cy};
72             series[0].mangle = 180;
73         } else {
74             for (var i = 0; i < len; i++) {
75                 total += values[i] * 1;
76                 values[i] = {
77                         value: values[i],
78                         order: i,
79                         valueOf: function () { return this.value; }
80                 };
81             }
82             //Roo.log(total);
83             if (!opts.no_sort) {
84                 values.sort(function (a, b) {
85                     return b.value - a.value;
86                 });
87             }
88             for (i = 0; i < len; i++) {
89                 if (defcut && values[i] * 360 / total <= 1.5) {
90                     cut = i;
91                     defcut = false;
92                 }
93
94                 if (i > cut) {
95                     defcut = false;
96                     values[cut].value += values[i];
97                     values[cut].others = true;
98                     others = values[cut].value;
99                 }
100             }
101             
102             len = Math.min(cut + 1, values.length);
103             others && values.splice(len) && (values[cut].others = true);
104
105             for (i = 0; i < len; i++) {
106                 var mangle = angle - 360 * values[i] / total / 2;
107
108                 if (!i) {
109                     // start..
110                     if (typeof(opts.start_angle) == 'undefined') {
111                         angle = 90 - mangle;
112                     } else {
113                         angle = opts.start_angle;
114                     }
115                     
116                     //Roo.log('start angle: ' + angle);
117                     mangle = angle - 360 * values[i] / total / 2;
118                 }
119
120                 if (opts.init) {
121                     var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(",");
122                 }
123
124                 var path = sector(cx, cy, r, angle, angle -= 360 * values[i] / total);
125                 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" });
126
127                 p.value = values[i];
128                 p.middle = path.middle;
129                 p.mangle = mangle;
130                 sectors.push(p);
131                 series.push(p);
132                 opts.init && p.animate({ path: path.join(",") }, (+opts.init - 1) || 1000, ">");
133             }
134
135             for (i = 0; i < len; i++) {
136                 p = paper.path(sectors[i].attr("path")).attr(chartinst.shim);
137                 opts.href && opts.href[i] && p.attr({ href: opts.href[i] });
138                 p.attr = function () {};
139                 covers.push(p);
140                 series.push(p);
141             }
142         }
143
144         chart.hover = function (fin, fout) {
145             fout = fout || function () {};
146
147             var that = this;
148
149             for (var i = 0; i < len; i++) {
150                 (function (sector, cover, j) {
151                     var o = {
152                         sector: sector,
153                         cover: cover,
154                         cx: cx,
155                         cy: cy,
156                         mx: sector.middle.x,
157                         my: sector.middle.y,
158                         mangle: sector.mangle,
159                         r: r,
160                         value: values[j],
161                         j : j,
162                         total: total,
163                         label: that.labels && that.labels[j]
164                     };
165                     cover.mouseover(function () {
166                         fin.call(o);
167                     }).mouseout(function () {
168                         fout.call(o);
169                     });
170                 })(series[i], covers[i], i);
171             }
172             return this;
173         };
174
175         // x: where label could be put
176         // y: where label could be put
177         // value: value to show
178         // total: total number to count %
179         chart.each = function (f) {
180             var that = this;
181
182             for (var i = 0; i < len; i++) {
183                 (function (sector, cover, j) {
184                     var o = {
185                         sector: sector,
186                         cover: cover,
187                         cx: cx,
188                         cy: cy,
189                         x: sector.middle.x,
190                         y: sector.middle.y,
191                         mangle: sector.mangle,
192                         r: r,
193                          j : j,
194                         value: values[j],
195                         total: total,
196                         label: that.labels && that.labels[j]
197                     };
198                     f.call(o);
199                 })(series[i], covers[i], i);
200             }
201             return this;
202         };
203
204         chart.click = function (f) {
205             var that = this;
206
207             for (var i = 0; i < len; i++) {
208                 (function (sector, cover, j) {
209                     var o = {
210                         sector: sector,
211                         cover: cover,
212                         cx: cx,
213                         cy: cy,
214                         mx: sector.middle.x,
215                         my: sector.middle.y,
216                         mangle: sector.mangle,
217                         r: r,
218                         j : j,
219                         value: values[j],
220                         total: total,
221                         label: that.labels && that.labels[j]
222                     };
223                     cover.click(function () { f.call(o); });
224                 })(series[i], covers[i], i);
225             }
226             return this;
227         };
228
229         chart.inject = function (element) {
230             element.insertBefore(covers[0]);
231         };
232
233         var legend = function (labels, otherslabel, mark, dir) {
234             var x = cx + r + r / 5,
235                 y = cy,
236                 h = y + 10;
237             
238             labels = labels || [];
239             dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east";
240             mark = paper[mark && mark.toLowerCase()] || "circle";
241             chart.labels = paper.set();
242
243             for (var i = 0; i < len; i++) {
244                 var clr = series[i].attr("fill"),
245                     j = values[i].order,
246                     txt;
247
248                 values[i].others && (labels[j] = otherslabel || "Others");
249                 labels[j] = chartinst.labelise(labels[j], values[i], total);
250                 chart.labels.push(paper.set());
251                 chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ fill: clr, stroke: "none" }));
252                 chart.labels[i].push(
253                     txt = paper.text(x + 20, h, labels[j] || values[j]).attr(chartinst.txtattr).attr({ fill: opts.legendcolor || "#000", "text-anchor": "start"}));
254                 covers[i].label = chart.labels[i];
255                // Roo.log(JSON.stringify(txt.getBBox()));
256                 h += txt.getBBox().height * 1.2;
257             }
258
259             var bb = chart.labels.getBBox(),
260                 tr = {
261                     east: [0, -bb.height / 2],
262                     west: [-bb.width - 2 * r - 20, -bb.height / 2],
263                     north: [-r - bb.width / 2, -r - bb.height - 10],
264                     south: [-r - bb.width / 2, r + 10]
265                 }[dir];
266
267             chart.labels.translate.apply(chart.labels, tr);
268             chart.push(chart.labels);
269         };
270
271         if (opts.legend) {
272             legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
273         }
274
275         chart.push(series, covers);
276         chart.series = series;
277         chart.covers = covers;
278         
279         chart.sector = sector;
280         chart.cx = cx;
281         chart.cy = cy;
282         chart.r = r;
283         return chart;
284     };
285     
286     //inheritance
287     var F = function() {};
288     F.prototype = Raphael.g;
289     Piechart.prototype = new F;
290     
291     //public
292     Raphael.fn.piechart = function(cx, cy, r, values, opts) {
293         return new Piechart(this, cx, cy, r, values, opts);
294     }
295     
296 })();