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");
5 dojo.require("dojox.charting.scaler");
6 dojo.require("dojox.charting.axis2d.common");
7 dojo.require("dojox.charting.axis2d.Base");
9 dojo.require("dojo.colors");
10 dojo.require("dojox.gfx");
11 dojo.require("dojox.lang.functional");
12 dojo.require("dojox.lang.utils");
15 var dc = dojox.charting,
16 df = dojox.lang.functional,
17 du = dojox.lang.utils,
19 labelGap = 4; // in pixels
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
26 dojo.declare("dojox.charting.axis2d.Default", dojox.charting.axis2d.Base, {
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
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
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
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);
63 dependOnData: function(){
64 return !("min" in this.opt) || !("max" in this.opt);
71 initialized: function(){
72 return "scaler" in this;
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; }
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;
88 minMinorStep = size + labelGap;
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);
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);
104 for(i = 0; i < precision; ++i){ t.push("9"); }
106 labelWidth = dojox.gfx._base._getTextBox(t.join(""), {font: taFont}).w;
108 minMinorStep = labelWidth + labelGap;
112 fixUpper: this.opt.fixUpper,
113 fixLower: this.opt.fixLower,
114 natural: this.opt.natural
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;
123 getScaler: function(){
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;
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);
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);
148 offset = labelWidth + labelGap;
150 offset += labelGap + Math.max(taMajorTick.length, taMinorTick.length);
151 offsets[this.opt.leftBottom ? "l" : "r"] = offset;
152 offsets.t = offsets.b = size / 2;
155 offset = size + labelGap;
157 offset += labelGap + Math.max(taMajorTick.length, taMinorTick.length);
158 offsets[this.opt.leftBottom ? "b" : "t"] = offset;
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);
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);
173 offsets.l = offsets.r = labelWidth / 2;
178 render: function(dim, offsets){
179 if(!this.dirty){ return this; }
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;
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};
199 start.x = stop.x = dim.width - offsets.r;
200 tickVector = {x: 1, y: 0};
201 labelAlign = "start";
203 labelOffset = {x: tickVector.x * (tickSize + labelGap), y: size * 0.4};
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};
214 start.y = stop.y = offsets.t;
215 tickVector = {x: 0, y: -1};
216 labelOffset = {y: -tickSize - labelGap};
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;
233 // don't draw anything
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){
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); }
253 nextMajor += c.major.tick;
254 nextMinor += c.minor.tick;
255 nextMicro += c.micro.tick;
256 }else if(Math.abs(nextMinor - next) < step / 2){
258 if(this.opt.minorTicks){
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); }
271 nextMinor += c.minor.tick;
272 nextMicro += c.micro.tick;
275 if(this.opt.microTicks){
278 // use minor ticks for now
279 x2: x + tickVector.x * taMinorTick.length,
280 y2: y + tickVector.y * taMinorTick.length
281 }).setStroke(taMinorTick);
283 nextMicro += c.micro.tick;
292 _getLabel: function(number, precision){
294 // classic binary search
295 var l = this.opt.labels, lo = 0, hi = l.length;
297 var mid = Math.floor((lo + hi) / 2), val = l[mid].value;
304 // lets take into account FP errors
305 if(lo < l.length && eq(l[lo].value, number)){
309 if(lo < l.length && eq(l[lo].value, number)){
313 if(lo < l.length && eq(l[lo].value, number)){
316 // otherwise we will produce a number
318 return this.opt.fixed ? number.toFixed(precision < 0 ? -precision : 0) : number.toString();