merged cont.
[opensuse:yast-rest-service.git] / webyast / public / javascripts / plugin / jqplot.pieRenderer.js
1 /**
2  * Copyright (c) 2009 Chris Leonello
3  * jqPlot is currently available for use in all personal or commercial projects 
4  * under both the MIT and GPL version 2.0 licenses. This means that you can 
5  * choose the license that best suits your project and use it accordingly. 
6  *
7  * The author would appreciate an email letting him know of any substantial
8  * use of jqPlot.  You can reach the author at: chris dot leonello at gmail 
9  * dot com or see http://www.jqplot.com/info.php .  This is, of course, 
10  * not required.
11  *
12  * If you are feeling kind and generous, consider supporting the project by
13  * making a donation at: http://www.jqplot.com/donate.php .
14  *
15  * Thanks for using jqPlot!
16  * 
17  */
18 (function($) {
19     /**
20      * Class: $.jqplot.PieRenderer
21      * Plugin renderer to draw a pie chart.
22      * Pie charts will draw only the first series.  Other series are ignored.
23      * x values, if present, will be used as slice labels.
24      * y values give slice size.
25      */
26     $.jqplot.PieRenderer = function(){
27         $.jqplot.LineRenderer.call(this);
28     };
29     
30     $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
31     $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
32     
33     // called with scope of a series
34     $.jqplot.PieRenderer.prototype.init = function(options) {
35         // Group: Properties
36         //
37         // prop: diameter
38         // diameter of the pie, auto computed by default
39         this.diameter = null;
40         // prop: padding
41         // padding between the pie and plot edges, legend, etc.
42         this.padding = 20;
43         // prop: sliceMargin
44         // pixels spacing between pie slices.
45         this.sliceMargin = 0;
46         // prop: fill
47         // true or false, wether to fil the slices.
48         this.fill = true;
49         // prop: shadowOffset
50         // offset of the shadow from the slice and offset of 
51         // each succesive stroke of the shadow from the last.
52         this.shadowOffset = 2;
53         // prop: shadowAlpha
54         // transparency of the shadow (0 = transparent, 1 = opaque)
55         this.shadowAlpha = 0.07;
56         // prop: shadowDepth
57         // number of strokes to apply to the shadow, 
58         // each stroke offset shadowOffset from the last.
59         this.shadowDepth = 5;
60         this.tickRenderer = $.jqplot.PieTickRenderer;
61         $.extend(true, this, options);
62         if (this.diameter != null) {
63             this.diameter = this.diameter - this.sliceMargin;
64         }
65         this._diameter = null;
66     };
67     
68     $.jqplot.PieRenderer.prototype.setGridData = function(plot) {
69         // this is a no-op
70     };
71     
72     $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) {
73         var stack = [];
74         var td = [];
75         for (var i=0; i<data.length; i++){
76             stack.push(data[i][1]);
77             td.push([data[i][0]]);
78             if (i>0) {
79                 stack[i] += stack[i-1];
80             }
81         }
82         var fact = Math.PI*2/stack[stack.length - 1];
83         
84         for (var i=0; i<stack.length; i++) {
85             td[i][1] = stack[i] * fact;
86         }
87         return td;
88     };
89     
90     $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
91         var r = this._diameter / 2;
92         var fill = this.fill;
93         var lineWidth = this.lineWidth;
94         ctx.save();
95         ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2));
96         
97         if (isShadow) {
98             for (var i=0; i<this.shadowDepth; i++) {
99                 ctx.save();
100                 ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
101                 doDraw();
102             }
103         }
104         
105         else {
106             doDraw();
107         }
108         
109         function doDraw () {
110             // Fix for IE and Chrome that can't seem to draw circles correctly.
111             // ang2 should always be <= 2 pi since that is the way the data is converted.
112              if (ang2 > 6.282) {
113                 ang2 = 6.282;
114                 if (ang1 > ang2) {
115                     ang1 = 6.281;
116                 }
117             }
118             ctx.beginPath();  
119             ctx.moveTo(0, 0);
120             ctx.fillStyle = color;
121             ctx.strokeStyle = color;
122             ctx.lineWidth = lineWidth;
123             ctx.arc(0, 0, r, ang1, ang2, false);
124             ctx.closePath();
125             if (fill) {
126                 ctx.fill();
127             }
128             else {
129                 ctx.stroke();
130             }
131         }
132         
133         if (isShadow) {
134             for (var i=0; i<this.shadowDepth; i++) {
135                 ctx.restore();
136             }
137         }
138         
139         ctx.restore();        
140     };
141     
142     // called with scope of series
143     $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options) {
144         var i;
145         var opts = (options != undefined) ? options : {};
146         // offset and direction of offset due to legend placement
147         var offx = 0;
148         var offy = 0;
149         var trans = 1;
150         var colorGenerator = new this.colorGenerator(this.seriesColors);
151         if (options.legendInfo) {
152             var li = options.legendInfo;
153             switch (li.location) {
154                 case 'nw':
155                     offx = li.width + li.xoffset;
156                     break;
157                 case 'w':
158                     offx = li.width + li.xoffset;
159                     break;
160                 case 'sw':
161                     offx = li.width + li.xoffset;
162                     break;
163                 case 'ne':
164                     offx = li.width + li.xoffset;
165                     trans = -1;
166                     break;
167                 case 'e':
168                     offx = li.width + li.xoffset;
169                     trans = -1;
170                     break;
171                 case 'se':
172                     offx = li.width + li.xoffset;
173                     trans = -1;
174                     break;
175                 case 'n':
176                     offy = li.height + li.yoffset;
177                     break;
178                 case 's':
179                     offy = li.height + li.yoffset;
180                     trans = -1;
181                     break;
182                 default:
183                     break;
184             }
185         }
186         
187         var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
188         var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
189         var fill = (opts.fill != undefined) ? opts.fill : this.fill;
190         var cw = ctx.canvas.width;
191         var ch = ctx.canvas.height;
192         var w = cw - offx - 2 * this.padding;
193         var h = ch - offy - 2 * this.padding;
194         var d = Math.min(w,h);
195         this._diameter = this.diameter  || d - this.sliceMargin;
196         // this.diameter -= this.sliceMargin;
197         var r = this._diameter/2;
198         ctx.save();
199         ctx.translate((cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy);
200         
201         if (this.shadow) {
202             var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
203             for (var i=0; i<gd.length; i++) {
204                 var ang1 = (i == 0) ? 0 : gd[i-1][1];
205                 this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1], shadowColor, true);
206             }
207             
208         }
209         for (var i=0; i<gd.length; i++) {
210             var ang1 = (i == 0) ? 0 : gd[i-1][1];
211             this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1], colorGenerator.next());
212         }
213         
214         ctx.restore();        
215     };
216     
217     $.jqplot.PieAxisRenderer = function() {
218         $.jqplot.LinearAxisRenderer.call(this);
219     };
220     
221     $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
222     $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
223         
224     
225     // There are no traditional axes on a pie chart.  We just need to provide
226     // dummy objects with properties so the plot will render.
227     // called with scope of axis object.
228     $.jqplot.PieAxisRenderer.prototype.init = function(options){
229         //
230         this.tickRenderer = $.jqplot.PieTickRenderer;
231         $.extend(true, this, options);
232         // I don't think I'm going to need _dataBounds here.
233         // have to go Axis scaling in a way to fit chart onto plot area
234         // and provide u2p and p2u functionality for mouse cursor, etc.
235         // for convienence set _dataBounds to 0 and 100 and
236         // set min/max to 0 and 100.
237         this._dataBounds = {min:0, max:100};
238         this.min = 0;
239         this.max = 100;
240         this.showTicks = false;
241         this.ticks = [];
242         this.showMark = false;
243         this.show = false; 
244     };
245     
246     $.jqplot.PieLegendRenderer = function() {
247         $.jqplot.TableLegendRenderer.call(this);
248     };
249     
250     $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
251     $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer;
252     
253     // called with context of legend
254     $.jqplot.PieLegendRenderer.prototype.draw = function() {
255         var legend = this;
256         if (this.show) {
257             var series = this._series;
258             // make a table.  one line label per row.
259             var ss = 'position:absolute;';
260             ss += (this.background) ? 'background:'+this.background+';' : '';
261             ss += (this.border) ? 'border:'+this.border+';' : '';
262             ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
263             ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
264             ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
265             this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
266         
267             var pad = false;
268             var s = series[0];
269             var colorGenerator = new s.colorGenerator(s.seriesColors);
270             if (s.show) {
271                 var pd = s.data;
272                 for (var i=0; i<pd.length; i++){
273                     var lt = pd[i][0].toString();
274                     if (lt) {
275                         this.renderer.addrow.call(this, lt, colorGenerator.next(), pad);
276                         pad = true;
277                     }  
278                 }
279             }
280         }        
281         return this._elem;
282     };
283     
284     // setup default renderers for axes and legend so user doesn't have to
285     // called with scope of plot
286     function preInit(target, data, options) {
287         options = options || {};
288         options.axesDefaults = options.axesDefaults || {};
289         options.legend = options.legend || {};
290         options.seriesDefaults = options.seriesDefaults || {};
291         // only set these if there is a pie series
292         var setopts = false;
293         if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
294             setopts = true;
295         }
296         else if (options.series) {
297             for (var i=0; i < options.series.length; i++) {
298                 if (options.series[i].renderer == $.jqplot.PieRenderer) {
299                     setopts = true;
300                 }
301             }
302         }
303         
304         if (setopts) {
305             options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
306             options.legend.renderer = $.jqplot.PieLegendRenderer;
307             options.legend.preDraw = true;
308             // options.seriesDefaults.colorGenerator = this.colorGenerator;
309             // options.seriesDefaults.seriesColors = this.seriesColors;
310         }
311     }
312     
313     // called with scope of plot
314     function postParseOptions(options) {
315         for (var i=0; i<this.series.length; i++) {
316             this.series[i].seriesColors = this.seriesColors;
317             this.series[i].colorGenerator = this.colorGenerator;
318         }
319     }
320     
321     $.jqplot.preInitHooks.push(preInit);
322     $.jqplot.postParseOptionsHooks.push(postParseOptions);
323     
324     $.jqplot.PieTickRenderer = function() {
325         $.jqplot.AxisTickRenderer.call(this);
326     };
327     
328     $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
329     $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;
330     
331 })(jQuery);
332     
333