merged cont.
[opensuse:yast-rest-service.git] / webservice / public / javascripts / plugin / jqplot.logAxisRenderer.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.LogAxisRenderer
21     *  A plugin for a jqPlot to render a logarithmic axis.
22     * 
23     *  To use this renderer, include the plugin in your source
24     *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.logAxisRenderer.js"></script>
25     *  
26     *  and supply the appropriate options to your plot
27     *  
28     *  > {axes:{xaxis:{renderer:$.jqplot.LogAxisRenderer}}}
29     **/ 
30     $.jqplot.LogAxisRenderer = function() {
31         $.jqplot.LinearAxisRenderer.call(this);
32         // prop: axisDefaults
33         // Default properties which will be applied directly to the series.
34         //
35         // Group: Properties
36         //
37         // Properties
38         //
39         /// base - the logarithmic base, commonly 2, 10 or Math.E
40         // tickDistribution - 'even' or 'power'.  'even' gives equal pixel
41         // spacing of the ticks on the plot.  'power' gives ticks in powers
42         // of 10.
43         this.axisDefaults = {
44             base : 10,
45             tickDistribution :'even'
46         };
47     };
48     
49     $.jqplot.LogAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
50     $.jqplot.LogAxisRenderer.prototype.constructor = $.jqplot.LogAxisRenderer;
51     
52     $.jqplot.LogAxisRenderer.prototype.init = function(options) {
53         // prop: tickRenderer
54         // A class of a rendering engine for creating the ticks labels displayed on the plot, 
55         // See <$.jqplot.AxisTickRenderer>.
56         // this.tickRenderer = $.jqplot.AxisTickRenderer;
57         // this.labelRenderer = $.jqplot.AxisLabelRenderer;
58         $.extend(true, this.renderer, options);
59         for (var d in this.renderer.axisDefaults) {
60             if (this[d] == null) {
61                 this[d] = this.renderer.axisDefaults[d];
62             }
63         }
64         var db = this._dataBounds;
65         // Go through all the series attached to this axis and find
66         // the min/max bounds for this axis.
67         for (var i=0; i<this._series.length; i++) {
68             var s = this._series[i];
69             var d = s.data;
70             
71             for (var j=0; j<d.length; j++) { 
72                 if (this.name == 'xaxis' || this.name == 'x2axis') {
73                     if (d[j][0] > db.max || db.max == null) {
74                         db.max = d[j][0];
75                     }
76                     if (d[j][0] > db.max || db.max == null) {
77                         db.max = d[j][0];
78                     }
79                 }              
80                 else {
81                     if (d[j][1] < db.min || db.min == null) {
82                         db.min = d[j][1];
83                     }
84                     if (d[j][1] > db.max || db.max == null) {
85                         db.max = d[j][1];
86                     }
87                 }              
88             }
89         }
90     };
91     
92     $.jqplot.LogAxisRenderer.prototype.createTicks = function() {
93         // we're are operating on an axis here
94         var ticks = this._ticks;
95         var userTicks = this.ticks;
96         var name = this.name;
97         var db = this._dataBounds;
98         var dim, interval;
99         var min, max;
100         var pos1, pos2;
101         var tt, i;
102
103         // if we already have ticks, use them.
104         // ticks must be in order of increasing value.
105         if (userTicks.length) {
106             // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
107             for (i=0; i<userTicks.length; i++){
108                 var ut = userTicks[i];
109                 var t = new this.tickRenderer(this.tickOptions);
110                 if (ut.constructor == Array) {
111                     t.value = ut[0];
112                     t.label = ut[1];
113                     if (!this.showTicks) {
114                         t.showLabel = false;
115                         t.showMark = false;
116                     }
117                     else if (!this.showTickMarks) {
118                         t.showMark = false;
119                     }
120                     t.setTick(ut[0], this.name);
121                     this._ticks.push(t);
122                 }
123                 
124                 else {
125                     t.value = ut;
126                     if (!this.showTicks) {
127                         t.showLabel = false;
128                         t.showMark = false;
129                     }
130                     else if (!this.showTickMarks) {
131                         t.showMark = false;
132                     }
133                     t.setTick(ut, this.name);
134                     this._ticks.push(t);
135                 }
136             }
137             this.numberTicks = userTicks.length;
138             this.min = this._ticks[0].value;
139             this.max = this._ticks[this.numberTicks-1].value;
140         }
141         
142         // we don't have any ticks yet, let's make some!
143         else {
144             if (name == 'xaxis' || name == 'x2axis') {
145                 dim = this._plotDimensions.width;
146             }
147             else {
148                 dim = this._plotDimensions.height;
149             }
150         
151             min = ((this.min != null) ? this.min : db.min);
152             max = ((this.max != null) ? this.max : db.max);
153             
154             // if min and max are same, space them out a bit
155             if (min == max) {
156                 var adj = 0.05;
157                 min = min*(1-adj);
158                 max = max*(1+adj);
159             }
160             
161             // perform some checks
162             if (this.min != null && this.min <= 0) {
163                 throw('log axis minimum must be greater than 0');
164             }
165             if (this.max != null && this.max <= 0) {
166                 throw('log axis maximum must be greater than 0');
167             }
168             // if (this.pad >1.99) this.pad = 1.99;
169             var range = max - min;
170             var rmin, rmax;
171
172             if (this.tickDistribution == 'even') {                    
173                 rmin = (this.min != null) ? this.min : min - min*((this.padMin-1)/2);
174                 rmax = (this.max != null) ? this.max : max + max*((this.padMax-1)/2);
175                 this.min = rmin;
176                 this.max = rmax;
177                 range = this.max - this.min;            
178         
179                 if (this.numberTicks == null){
180                     if (dim > 100) {
181                         this.numberTicks = parseInt(3+(dim-100)/75, 10);
182                     }
183                     else {
184                         this.numberTicks = 2;
185                     }
186                 }
187     
188                 var u = Math.pow(this.base, (1/(this.numberTicks-1)*Math.log(this.max/this.min)/Math.log(this.base)));
189                 for (var i=0; i<this.numberTicks; i++){
190                     tt = this.min * Math.pow(u, i);
191                     var t = new this.tickRenderer(this.tickOptions);
192                     if (!this.showTicks) {
193                         t.showLabel = false;
194                         t.showMark = false;
195                     }
196                     else if (!this.showTickMarks) {
197                         t.showMark = false;
198                     }
199                     t.setTick(tt, this.name);
200                     this._ticks.push(t);
201                 }
202                 
203             }
204             
205             else if (this.tickDistribution == 'power'){
206                 // for power distribution, open up range to get a nice power of axis.renderer.base.
207                 // power distribution won't respect the user's min/max settings.
208                 rmin = Math.pow(this.base, Math.ceil(Math.log(min*(2-this.padMin))/Math.log(this.base))-1);
209                 rmax = Math.pow(this.base, Math.floor(Math.log(max*this.padMax)/Math.log(this.base))+1);
210                 this.min = rmin;
211                 this.max = rmax;
212                 range = this.max - this.min;            
213         
214                 var fittedTicks = 0;
215                 var minorTicks = 0;
216                 if (this.numberTicks == null){
217                     if (dim > 100) {
218                         this.numberTicks = Math.round(Math.log(this.max/this.min)/Math.log(this.base) + 1);
219                         if (this.numberTicks < 2) {
220                             this.numberTicks = 2;
221                         }
222                         fittedTicks = parseInt(3+(dim-100)/75, 10);
223                     }
224                     else {
225                         this.numberTicks = 2;
226                         fittedTicks = 2;
227                     }
228                     // if we don't have enough ticks, add some intermediate ticks
229                     // how many to have between major ticks.
230                     if (this.numberTicks < fittedTicks-1) {
231                         minorTicks = Math.floor(fittedTicks/this.numberTicks);
232                     }
233                 }
234
235                 for (var i=0; i<this.numberTicks; i++){
236                     tt = Math.pow(this.base, i - this.numberTicks + 1) * this.max;
237                     var t = new this.tickRenderer(this.tickOptions);
238                     if (!this.showTicks) {
239                         t.showLabel = false;
240                         t.showMark = false;
241                     }
242                     else if (!this.showTickMarks) {
243                         t.showMark = false;
244                     }
245                     t.setTick(tt, this.name);
246                     this._ticks.push(t);
247             
248                     if (minorTicks && i<this.numberTicks-1) {
249                         var tt1 = Math.pow(this.base, i - this.numberTicks + 2) * this.max;
250                         var spread = tt1 - tt;
251                         var interval = tt1 / (minorTicks+1);
252                         for (var j=minorTicks-1; j>=0; j--) {
253                             var val = tt1-interval*(j+1);
254                             var t = new this.tickRenderer(this.tickOptions);
255                             if (!this.showTicks) {
256                                 t.showLabel = false;
257                                 t.showMark = false;
258                             }
259                             else if (!this.showTickMarks) {
260                                 t.showMark = false;
261                             }
262                             t.setTick(val, this.name);
263                             this._ticks.push(t);
264                         }
265                     }       
266                 }                    
267             }       
268         }
269     };
270     
271     $.jqplot.LogAxisRenderer.prototype.pack = function(pos, offsets) {
272         var lb = parseInt(this.base, 10);
273         var ticks = this._ticks;
274         var trans = function (v) { return Math.log(v)/Math.log(lb); };
275         var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); };
276         max = trans(this.max);
277         min = trans(this.min);
278         var offmax = offsets.max;
279         var offmin = offsets.min;
280         var lshow = (this._label == null) ? false : this._label.show;
281         
282         for (var p in pos) {
283             this._elem.css(p, pos[p]);
284         }
285         
286         this._offsets = offsets;
287         // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
288         var pixellength = offmax - offmin;
289         var unitlength = max - min;
290         
291         // point to unit and unit to point conversions references to Plot DOM element top left corner.
292         this.p2u = function(p){
293             return invtrans((p - offmin) * unitlength / pixellength + min);
294         };
295         
296         this.u2p = function(u){
297             return (trans(u) - min) * pixellength / unitlength + offmin;
298         };
299         
300         if (this.name == 'xaxis' || this.name == 'x2axis'){
301             this.series_u2p = function(u){
302                 return (trans(u) - min) * pixellength / unitlength;
303             };
304             this.series_p2u = function(p){
305                 return invtrans(p * unitlength / pixellength + min);
306             };
307         }
308         // yaxis is max at top of canvas.
309         else {
310             this.series_u2p = function(u){
311                 return (trans(u) - max) * pixellength / unitlength;
312             };
313             this.series_p2u = function(p){
314                 return invtrans(p * unitlength / pixellength + max);
315             };
316         }
317         
318         if (this.show) {
319             if (this.name == 'xaxis' || this.name == 'x2axis') {
320                 for (i=0; i<ticks.length; i++) {
321                     var t = ticks[i];
322                     if (t.show && t.showLabel) {
323                         var shim;
324                         
325                         if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
326                             switch (t.labelPosition) {
327                                 case 'auto':
328                                     // position at end
329                                     if (t.angle < 0) {
330                                         shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
331                                     }
332                                     // position at start
333                                     else {
334                                         shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
335                                     }
336                                     break;
337                                 case 'end':
338                                     shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
339                                     break;
340                                 case 'start':
341                                     shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
342                                     break;
343                                 case 'middle':
344                                     shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
345                                     break;
346                                 default:
347                                     shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
348                                     break;
349                             }
350                         }
351                         else {
352                             shim = -t.getWidth()/2;
353                         }
354                         // var shim = t.getWidth()/2;
355                         var val = this.u2p(t.value) + shim + 'px';
356                         t._elem.css('left', val);
357                         t.pack();
358                     }
359                 }
360                 if (lshow) {
361                     var w = this._label._elem.outerWidth(true);
362                     this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
363                     if (this.name == 'xaxis') {
364                         this._label._elem.css('bottom', '0px');
365                     }
366                     else {
367                         this._label._elem.css('top', '0px');
368                     }
369                     this._label.pack();
370                 }
371             }
372             else {
373                 for (i=0; i<ticks.length; i++) {
374                     var t = ticks[i];
375                     if (t.show && t.showLabel) {                        
376                         var shim;
377                         if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
378                             switch (t.labelPosition) {
379                                 case 'auto':
380                                     // position at end
381                                 case 'end':
382                                     if (t.angle < 0) {
383                                         shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
384                                     }
385                                     else {
386                                         shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
387                                     }
388                                     break;
389                                 case 'start':
390                                     if (t.angle > 0) {
391                                         shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
392                                     }
393                                     else {
394                                         shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
395                                     }
396                                     break;
397                                 case 'middle':
398                                     // if (t.angle > 0) {
399                                     //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
400                                     // }
401                                     // else {
402                                     //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
403                                     // }
404                                     shim = -t.getHeight()/2;
405                                     break;
406                                 default:
407                                     shim = -t.getHeight()/2;
408                                     break;
409                             }
410                         }
411                         else {
412                             shim = -t.getHeight()/2;
413                         }
414                         
415                         var val = this.u2p(t.value) + shim + 'px';
416                         t._elem.css('top', val);
417                         t.pack();
418                     }
419                 }
420                 if (lshow) {
421                     var h = this._label._elem.outerHeight(true);
422                     this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
423                     if (this.name == 'yaxis') {
424                         this._label._elem.css('left', '0px');
425                     }
426                     else {
427                         this._label._elem.css('right', '0px');
428                     }   
429                     this._label.pack();
430                 }
431             }
432         }        
433     };
434 })(jQuery);