]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojox/charting/axis2d/Default.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojox / charting / axis2d / Default.js
1 if(!dojo._hasResource["dojox.charting.axis2d.Default"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.charting.axis2d.Default"] = true;
3 dojo.provide("dojox.charting.axis2d.Default");
4
5 dojo.require("dojox.charting.scaler");
6 dojo.require("dojox.charting.axis2d.common");
7 dojo.require("dojox.charting.axis2d.Base");
8
9 dojo.require("dojo.colors");
10 dojo.require("dojox.gfx");
11 dojo.require("dojox.lang.functional");
12 dojo.require("dojox.lang.utils");
13
14 (function(){
15         var dc = dojox.charting, 
16                 df = dojox.lang.functional, 
17                 du = dojox.lang.utils, 
18                 g = dojox.gfx,
19                 labelGap = 4;   // in pixels
20                 
21         var eq = function(/* Number */ a, /* Number */ b){
22                 // summary: compare two FP numbers for equality
23                 return Math.abs(a - b) <= 1e-6 * (Math.abs(a) + Math.abs(b));   // Boolean
24         };
25
26         dojo.declare("dojox.charting.axis2d.Default", dojox.charting.axis2d.Base, {
27                  defaultParams: {
28                         vertical:    false,             // true for vertical axis
29                         fixUpper:    "none",    // align the upper on ticks: "major", "minor", "micro", "none"
30                         fixLower:    "none",    // align the lower on ticks: "major", "minor", "micro", "none"
31                         natural:     false,             // all tick marks should be made on natural numbers
32                         leftBottom:  true,              // position of the axis, used with "vertical"
33                         includeZero: false,             // 0 should be included
34                         fixed:       true,              // all labels are fixed numbers
35                         majorLabels: true,              // draw major labels
36                         minorTicks:  true,              // draw minor ticks
37                         minorLabels: true,              // draw minor labels
38                         microTicks:  false,             // draw micro ticks
39                         htmlLabels:  true               // use HTML to draw labels
40                 },
41                 optionalParams: {
42                         "min":           0,             // minimal value on this axis
43                         "max":           1,             // maximal value on this axis
44                         "majorTickStep": 4,             // major tick step
45                         "minorTickStep": 2,             // minor tick step
46                         "microTickStep": 1,             // micro tick step
47                         "labels":        [],    // array of labels for major ticks
48                                                                         // with corresponding numeric values
49                                                                         // ordered by values
50                         // theme components
51                         "stroke":        {},    // stroke for an axis
52                         "majorTick":     {},    // stroke + length for a tick
53                         "minorTick":     {},    // stroke + length for a tick
54                         "font":          "",    // font for labels
55                         "fontColor":     ""             // color for labels as a string
56                 },
57
58                 constructor: function(chart, kwArgs){
59                         this.opt = dojo.clone(this.defaultParams);
60                         du.updateWithObject(this.opt, kwArgs);
61                         du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
62                 },
63                 dependOnData: function(){
64                         return !("min" in this.opt) || !("max" in this.opt);
65                 },
66                 clear: function(){
67                         delete this.scaler;
68                         this.dirty = true;
69                         return this;
70                 },
71                 initialized: function(){
72                         return "scaler" in this;
73                 },
74                 calculate: function(min, max, span, labels){
75                         if(this.initialized()){ return this; }
76                         this.labels = "labels" in this.opt ? this.opt.labels : labels;
77                         if("min" in this.opt){ min = this.opt.min; }
78                         if("max" in this.opt){ max = this.opt.max; }
79                         if(this.opt.includeZero){
80                                 if(min > 0){ min = 0; }
81                                 if(max < 0){ max = 0; }
82                         }
83                         var minMinorStep = 0, ta = this.chart.theme.axis, 
84                                 taFont = "font" in this.opt ? this.opt.font : ta.font,
85                                 size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0;
86                         if(this.vertical){
87                                 if(size){
88                                         minMinorStep = size + labelGap;
89                                 }
90                         }else{
91                                 if(size){
92                                         var labelWidth, i;
93                                         if(this.labels){
94                                                 labelWidth = df.foldl(df.map(this.labels, function(label){
95                                                         return dojox.gfx._base._getTextBox(label.text, {font: taFont}).w;
96                                                 }), "Math.max(a, b)", 0);
97                                         }else{
98                                                 var labelLength = Math.ceil(Math.log(Math.max(Math.abs(min), Math.abs(max))) / Math.LN10), t = [];
99                                                 if(min < 0 || max < 0){ t.push("-"); }
100                                                 for(i = 0; i < labelLength; ++i){ t.push("9"); }
101                                                 var precision = Math.floor(Math.log(max - min) / Math.LN10);
102                                                 if(precision > 0){
103                                                         t.push(".");
104                                                         for(i = 0; i < precision; ++i){ t.push("9"); }
105                                                 }
106                                                 labelWidth = dojox.gfx._base._getTextBox(t.join(""), {font: taFont}).w;
107                                         }
108                                         minMinorStep = labelWidth + labelGap;
109                                 }
110                         }
111                         var kwArgs = {
112                                 fixUpper: this.opt.fixUpper, 
113                                 fixLower: this.opt.fixLower, 
114                                 natural:  this.opt.natural
115                         };
116                         if("majorTickStep" in this.opt){ kwArgs.majorTick = this.opt.majorTickStep; }
117                         if("minorTickStep" in this.opt){ kwArgs.minorTick = this.opt.minorTickStep; }
118                         if("microTickStep" in this.opt){ kwArgs.microTick = this.opt.microTickStep; }
119                         this.scaler = dojox.charting.scaler(min, max, span, kwArgs);
120                         this.scaler.minMinorStep = minMinorStep;
121                         return this;
122                 },
123                 getScaler: function(){
124                         return this.scaler;
125                 },
126                 getOffsets: function(){
127                         var offsets = {l: 0, r: 0, t: 0, b: 0}, s, labelWidth, gtb, a, b, c, d;
128                         var offset = 0, ta = this.chart.theme.axis,
129                                 taFont = "font" in this.opt ? this.opt.font : ta.font,
130                                 taMajorTick = "majorTick" in this.opt ? this.opt.majorTick : ta.majorTick,
131                                 taMinorTick = "minorTick" in this.opt ? this.opt.minorTick : ta.minorTick,
132                                 size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0;
133                         if(this.vertical){
134                                 if(size){
135                                         s = this.scaler;
136                                         if(this.labels){
137                                                 labelWidth = df.foldl(df.map(this.labels, function(label){
138                                                         return dojox.gfx._base._getTextBox(label.text, {font: taFont}).w;
139                                                 }), "Math.max(a, b)", 0);
140                                         }else{
141                                                 gtb = dojox.gfx._base._getTextBox;
142                                                 a = gtb(this._getLabel(s.major.start, s.major.prec), {font: taFont}).w;
143                                                 b = gtb(this._getLabel(s.major.start + s.major.count * s.major.tick, s.major.prec), {font: taFont}).w;
144                                                 c = gtb(this._getLabel(s.minor.start, s.minor.prec), {font: taFont}).w;
145                                                 d = gtb(this._getLabel(s.minor.start + s.minor.count * s.minor.tick, s.minor.prec), {font: taFont}).w;
146                                                 labelWidth = Math.max(a, b, c, d);
147                                         }
148                                         offset = labelWidth + labelGap;
149                                 }
150                                 offset += labelGap + Math.max(taMajorTick.length, taMinorTick.length);
151                                 offsets[this.opt.leftBottom ? "l" : "r"] = offset;
152                                 offsets.t = offsets.b = size / 2;
153                         }else{
154                                 if(size){
155                                         offset = size + labelGap;
156                                 }
157                                 offset += labelGap + Math.max(taMajorTick.length, taMinorTick.length);
158                                 offsets[this.opt.leftBottom ? "b" : "t"] = offset;
159                                 if(size){
160                                         s = this.scaler;
161                                         if(this.labels){
162                                                 labelWidth = df.foldl(df.map(this.labels, function(label){
163                                                         return dojox.gfx._base._getTextBox(label.text, {font: taFont}).w;
164                                                 }), "Math.max(a, b)", 0);
165                                         }else{
166                                                 gtb = dojox.gfx._base._getTextBox;
167                                                 a = gtb(this._getLabel(s.major.start, s.major.prec), {font: taFont}).w;
168                                                 b = gtb(this._getLabel(s.major.start + s.major.count * s.major.tick, s.major.prec), {font: taFont}).w;
169                                                 c = gtb(this._getLabel(s.minor.start, s.minor.prec), {font: taFont}).w;
170                                                 d = gtb(this._getLabel(s.minor.start + s.minor.count * s.minor.tick, s.minor.prec), {font: taFont}).w;
171                                                 labelWidth = Math.max(a, b, c, d);
172                                         }
173                                         offsets.l = offsets.r = labelWidth / 2;
174                                 }
175                         }
176                         return offsets;
177                 },
178                 render: function(dim, offsets){
179                         if(!this.dirty){ return this; }
180                         // prepare variable
181                         var start, stop, axisVector, tickVector, labelOffset, labelAlign,
182                                 ta = this.chart.theme.axis, 
183                                 taStroke = "stroke" in this.opt ? this.opt.stroke : ta.stroke,
184                                 taMajorTick = "majorTick" in this.opt ? this.opt.majorTick : ta.majorTick,
185                                 taMinorTick = "minorTick" in this.opt ? this.opt.minorTick : ta.minorTick,
186                                 taFont = "font" in this.opt ? this.opt.font : ta.font,
187                                 taFontColor = "fontColor" in this.opt ? this.opt.fontColor : ta.fontColor,
188                                 tickSize = Math.max(taMajorTick.length, taMinorTick.length),
189                                 size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0;
190                         if(this.vertical){
191                                 start = {y: dim.height - offsets.b};
192                                 stop  = {y: offsets.t};
193                                 axisVector = {x: 0, y: -1};
194                                 if(this.opt.leftBottom){
195                                         start.x = stop.x = offsets.l;
196                                         tickVector = {x: -1, y: 0};
197                                         labelAlign = "end";
198                                 }else{
199                                         start.x = stop.x = dim.width - offsets.r;
200                                         tickVector = {x: 1, y: 0};
201                                         labelAlign = "start";
202                                 }
203                                 labelOffset = {x: tickVector.x * (tickSize + labelGap), y: size * 0.4};
204                         }else{
205                                 start = {x: offsets.l};
206                                 stop  = {x: dim.width - offsets.r};
207                                 axisVector = {x: 1, y: 0};
208                                 labelAlign = "middle";
209                                 if(this.opt.leftBottom){
210                                         start.y = stop.y = dim.height - offsets.b;
211                                         tickVector = {x: 0, y: 1};
212                                         labelOffset = {y: tickSize + labelGap + size};
213                                 }else{
214                                         start.y = stop.y = offsets.t;
215                                         tickVector = {x: 0, y: -1};
216                                         labelOffset = {y: -tickSize - labelGap};
217                                 }
218                                 labelOffset.x = 0;
219                         }
220                         
221                         // render shapes
222                         this.cleanGroup();
223                         var s = this.group, c = this.scaler, step, next,
224                                 nextMajor = c.major.start, nextMinor = c.minor.start, nextMicro = c.micro.start;
225                         s.createLine({x1: start.x, y1: start.y, x2: stop.x, y2: stop.y}).setStroke(taStroke);
226                         if(this.opt.microTicks && c.micro.tick){
227                                 step = c.micro.tick, next = nextMicro;
228                         }else if(this.opt.minorTicks && c.minor.tick){
229                                 step = c.minor.tick, next = nextMinor;
230                         }else if(c.major.tick){
231                                 step = c.major.tick, next = nextMajor;
232                         }else{
233                                 // don't draw anything
234                                 return this;
235                         }
236                         while(next <= c.bounds.upper + 1/c.scale){
237                                 var offset = (next - c.bounds.lower) * c.scale,
238                                         x = start.x + axisVector.x * offset,
239                                         y = start.y + axisVector.y * offset, elem;
240                                 if(Math.abs(nextMajor - next) < step / 2){
241                                         // major tick
242                                         s.createLine({
243                                                 x1: x, y1: y,
244                                                 x2: x + tickVector.x * taMajorTick.length,
245                                                 y2: y + tickVector.y * taMajorTick.length
246                                         }).setStroke(taMajorTick);
247                                         if(this.opt.majorLabels){
248                                                 elem = dc.axis2d.common.createText[this.opt.htmlLabels && dojox.gfx.renderer != "vml" ? "html" : "gfx"]
249                                                                                 (this.chart, s, x + labelOffset.x, y + labelOffset.y, labelAlign,
250                                                                                         this._getLabel(nextMajor, c.major.prec), taFont, taFontColor);
251                                                 if(this.opt.htmlLabels){ this.htmlElements.push(elem); }
252                                         }
253                                         nextMajor += c.major.tick;
254                                         nextMinor += c.minor.tick;
255                                         nextMicro += c.micro.tick;
256                                 }else if(Math.abs(nextMinor - next) < step / 2){
257                                         // minor tick
258                                         if(this.opt.minorTicks){
259                                                 s.createLine({
260                                                         x1: x, y1: y,
261                                                         x2: x + tickVector.x * taMinorTick.length,
262                                                         y2: y + tickVector.y * taMinorTick.length
263                                                 }).setStroke(taMinorTick);
264                                                 if(this.opt.minorLabels && (c.minMinorStep <= c.minor.tick * c.scale)){
265                                                         elem = dc.axis2d.common.createText[this.opt.htmlLabels && dojox.gfx.renderer != "vml" ? "html" : "gfx"]
266                                                                                         (this.chart, s, x + labelOffset.x, y + labelOffset.y, labelAlign,
267                                                                                                 this._getLabel(nextMinor, c.minor.prec), taFont, taFontColor);
268                                                         if(this.opt.htmlLabels){ this.htmlElements.push(elem); }
269                                                 }
270                                         }
271                                         nextMinor += c.minor.tick;
272                                         nextMicro += c.micro.tick;
273                                 }else{
274                                         // micro tick
275                                         if(this.opt.microTicks){
276                                                 s.createLine({
277                                                         x1: x, y1: y,
278                                                         // use minor ticks for now
279                                                         x2: x + tickVector.x * taMinorTick.length,
280                                                         y2: y + tickVector.y * taMinorTick.length
281                                                 }).setStroke(taMinorTick);
282                                         }
283                                         nextMicro += c.micro.tick;
284                                 }
285                                 next += step;
286                         }
287                         this.dirty = false;
288                         return this;
289                 },
290                 
291                 // utilities
292                 _getLabel: function(number, precision){
293                         if(this.opt.labels){
294                                 // classic binary search
295                                 var l = this.opt.labels, lo = 0, hi = l.length;
296                                 while(lo < hi){
297                                         var mid = Math.floor((lo + hi) / 2), val = l[mid].value;
298                                         if(val < number){
299                                                 lo = mid + 1;
300                                         }else{
301                                                 hi = mid;
302                                         }
303                                 }
304                                 // lets take into account FP errors
305                                 if(lo < l.length && eq(l[lo].value, number)){
306                                         return l[lo].text;
307                                 }
308                                 --lo;
309                                 if(lo < l.length && eq(l[lo].value, number)){
310                                         return l[lo].text;
311                                 }
312                                 lo += 2;
313                                 if(lo < l.length && eq(l[lo].value, number)){
314                                         return l[lo].text;
315                                 }
316                                 // otherwise we will produce a number
317                         }
318                         return this.opt.fixed ? number.toFixed(precision < 0 ? -precision : 0) : number.toString();
319                 }
320         });
321 })();
322
323 }