1 | define(["dojo/_base/lang", "dojo/_base/array","dojo/_base/sniff", "dojo/_base/declare", |
---|
2 | "dojo/_base/connect", "dojo/_base/html", "dojo/dom-geometry", "./Invisible", |
---|
3 | "../scaler/common", "../scaler/linear", "./common", "dojox/gfx", "dojox/lang/utils"], |
---|
4 | function(lang, arr, has, declare, connect, html, domGeom, Invisible, scommon, |
---|
5 | lin, acommon, g, du){ |
---|
6 | |
---|
7 | /*===== |
---|
8 | dojox.charting.axis2d.__AxisCtorArgs = function( |
---|
9 | vertical, fixUpper, fixLower, natural, leftBottom, |
---|
10 | includeZero, fixed, majorLabels, minorTicks, minorLabels, microTicks, htmlLabels, |
---|
11 | min, max, from, to, majorTickStep, minorTickStep, microTickStep, |
---|
12 | labels, labelFunc, maxLabelSize, |
---|
13 | stroke, majorTick, minorTick, microTick, tick, |
---|
14 | font, fontColor |
---|
15 | ){ |
---|
16 | // summary: |
---|
17 | // Optional arguments used in the definition of an axis. |
---|
18 | // |
---|
19 | // vertical: Boolean? |
---|
20 | // A flag that says whether an axis is vertical (i.e. y axis) or horizontal. Default is false (horizontal). |
---|
21 | // fixUpper: String? |
---|
22 | // Align the greatest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none". |
---|
23 | // fixLower: String? |
---|
24 | // Align the smallest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none". |
---|
25 | // natural: Boolean? |
---|
26 | // Ensure tick marks are made on "natural" numbers. Defaults to false. |
---|
27 | // leftBottom: Boolean? |
---|
28 | // The position of a vertical axis; if true, will be placed against the left-bottom corner of the chart. Defaults to true. |
---|
29 | // includeZero: Boolean? |
---|
30 | // Include 0 on the axis rendering. Default is false. |
---|
31 | // fixed: Boolean? |
---|
32 | // Force all axis labels to be fixed numbers. Default is true. |
---|
33 | // majorLabels: Boolean? |
---|
34 | // Flag to draw all labels at major ticks. Default is true. |
---|
35 | // minorTicks: Boolean? |
---|
36 | // Flag to draw minor ticks on an axis. Default is true. |
---|
37 | // minorLabels: Boolean? |
---|
38 | // Flag to draw labels on minor ticks. Default is true. |
---|
39 | // microTicks: Boolean? |
---|
40 | // Flag to draw micro ticks on an axis. Default is false. |
---|
41 | // htmlLabels: Boolean? |
---|
42 | // Flag to use HTML (as opposed to the native vector graphics engine) to draw labels. Default is true. |
---|
43 | // min: Number? |
---|
44 | // The smallest value on an axis. Default is 0. |
---|
45 | // max: Number? |
---|
46 | // The largest value on an axis. Default is 1. |
---|
47 | // from: Number? |
---|
48 | // Force the chart to render data visible from this value. Default is 0. |
---|
49 | // to: Number? |
---|
50 | // Force the chart to render data visible to this value. Default is 1. |
---|
51 | // majorTickStep: Number? |
---|
52 | // The amount to skip before a major tick is drawn. Default is 4. |
---|
53 | // minorTickStep: Number? |
---|
54 | // The amount to skip before a minor tick is drawn. Default is 2. |
---|
55 | // microTickStep: Number? |
---|
56 | // The amount to skip before a micro tick is drawn. Default is 1. |
---|
57 | // labels: Object[]? |
---|
58 | // An array of labels for major ticks, with corresponding numeric values, ordered by value. |
---|
59 | // labelFunc: Function? |
---|
60 | // An optional function used to compute label values. |
---|
61 | // maxLabelSize: Number? |
---|
62 | // The maximum size, in pixels, for a label. To be used with the optional label function. |
---|
63 | // stroke: dojox.gfx.Stroke? |
---|
64 | // An optional stroke to be used for drawing an axis. |
---|
65 | // majorTick: Object? |
---|
66 | // An object containing a dojox.gfx.Stroke, and a length (number) for a major tick. |
---|
67 | // minorTick: Object? |
---|
68 | // An object containing a dojox.gfx.Stroke, and a length (number) for a minor tick. |
---|
69 | // microTick: Object? |
---|
70 | // An object containing a dojox.gfx.Stroke, and a length (number) for a micro tick. |
---|
71 | // tick: Object? |
---|
72 | // An object containing a dojox.gfx.Stroke, and a length (number) for a tick. |
---|
73 | // font: String? |
---|
74 | // An optional font definition (as used in the CSS font property) for labels. |
---|
75 | // fontColor: String|dojo.Color? |
---|
76 | // An optional color to be used in drawing labels. |
---|
77 | // enableCache: Boolean? |
---|
78 | // Whether the ticks and labels are cached from one rendering to another. This improves the rendering performance of |
---|
79 | // successive rendering but penalize the first rendering. For labels it is only working with gfx labels |
---|
80 | // not html ones. Default false. |
---|
81 | |
---|
82 | this.vertical = vertical; |
---|
83 | this.fixUpper = fixUpper; |
---|
84 | this.fixLower = fixLower; |
---|
85 | this.natural = natural; |
---|
86 | this.leftBottom = leftBottom; |
---|
87 | this.includeZero = includeZero; |
---|
88 | this.fixed = fixed; |
---|
89 | this.majorLabels = majorLabels; |
---|
90 | this.minorTicks = minorTicks; |
---|
91 | this.minorLabels = minorLabels; |
---|
92 | this.microTicks = microTicks; |
---|
93 | this.htmlLabels = htmlLabels; |
---|
94 | this.min = min; |
---|
95 | this.max = max; |
---|
96 | this.from = from; |
---|
97 | this.to = to; |
---|
98 | this.majorTickStep = majorTickStep; |
---|
99 | this.minorTickStep = minorTickStep; |
---|
100 | this.microTickStep = microTickStep; |
---|
101 | this.labels = labels; |
---|
102 | this.labelFunc = labelFunc; |
---|
103 | this.maxLabelSize = maxLabelSize; |
---|
104 | this.stroke = stroke; |
---|
105 | this.majorTick = majorTick; |
---|
106 | this.minorTick = minorTick; |
---|
107 | this.microTick = microTick; |
---|
108 | this.tick = tick; |
---|
109 | this.font = font; |
---|
110 | this.fontColor = fontColor; |
---|
111 | this.enableCache = enableCache; |
---|
112 | } |
---|
113 | var Invisible = dojox.charting.axis2d.Invisible |
---|
114 | =====*/ |
---|
115 | |
---|
116 | var labelGap = 4, // in pixels |
---|
117 | centerAnchorLimit = 45; // in degrees |
---|
118 | |
---|
119 | return declare("dojox.charting.axis2d.Default", Invisible, { |
---|
120 | // summary: |
---|
121 | // The default axis object used in dojox.charting. See dojox.charting.Chart.addAxis for details. |
---|
122 | // |
---|
123 | // defaultParams: Object |
---|
124 | // The default parameters used to define any axis. |
---|
125 | // optionalParams: Object |
---|
126 | // Any optional parameters needed to define an axis. |
---|
127 | |
---|
128 | /* |
---|
129 | // TODO: the documentation tools need these to be pre-defined in order to pick them up |
---|
130 | // correctly, but the code here is partially predicated on whether or not the properties |
---|
131 | // actually exist. For now, we will leave these undocumented but in the code for later. -- TRT |
---|
132 | |
---|
133 | // opt: Object |
---|
134 | // The actual options used to define this axis, created at initialization. |
---|
135 | // scalar: Object |
---|
136 | // The calculated helper object to tell charts how to draw an axis and any data. |
---|
137 | // ticks: Object |
---|
138 | // The calculated tick object that helps a chart draw the scaling on an axis. |
---|
139 | // dirty: Boolean |
---|
140 | // The state of the axis (whether it needs to be redrawn or not) |
---|
141 | // scale: Number |
---|
142 | // The current scale of the axis. |
---|
143 | // offset: Number |
---|
144 | // The current offset of the axis. |
---|
145 | |
---|
146 | opt: null, |
---|
147 | scalar: null, |
---|
148 | ticks: null, |
---|
149 | dirty: true, |
---|
150 | scale: 1, |
---|
151 | offset: 0, |
---|
152 | */ |
---|
153 | defaultParams: { |
---|
154 | vertical: false, // true for vertical axis |
---|
155 | fixUpper: "none", // align the upper on ticks: "major", "minor", "micro", "none" |
---|
156 | fixLower: "none", // align the lower on ticks: "major", "minor", "micro", "none" |
---|
157 | natural: false, // all tick marks should be made on natural numbers |
---|
158 | leftBottom: true, // position of the axis, used with "vertical" |
---|
159 | includeZero: false, // 0 should be included |
---|
160 | fixed: true, // all labels are fixed numbers |
---|
161 | majorLabels: true, // draw major labels |
---|
162 | minorTicks: true, // draw minor ticks |
---|
163 | minorLabels: true, // draw minor labels |
---|
164 | microTicks: false, // draw micro ticks |
---|
165 | rotation: 0, // label rotation angle in degrees |
---|
166 | htmlLabels: true, // use HTML to draw labels |
---|
167 | enableCache: false // whether we cache or not |
---|
168 | }, |
---|
169 | optionalParams: { |
---|
170 | min: 0, // minimal value on this axis |
---|
171 | max: 1, // maximal value on this axis |
---|
172 | from: 0, // visible from this value |
---|
173 | to: 1, // visible to this value |
---|
174 | majorTickStep: 4, // major tick step |
---|
175 | minorTickStep: 2, // minor tick step |
---|
176 | microTickStep: 1, // micro tick step |
---|
177 | labels: [], // array of labels for major ticks |
---|
178 | // with corresponding numeric values |
---|
179 | // ordered by values |
---|
180 | labelFunc: null, // function to compute label values |
---|
181 | maxLabelSize: 0, // size in px. For use with labelFunc |
---|
182 | maxLabelCharCount: 0, // size in word count. |
---|
183 | trailingSymbol: null, |
---|
184 | |
---|
185 | // TODO: add support for minRange! |
---|
186 | // minRange: 1, // smallest distance from min allowed on the axis |
---|
187 | |
---|
188 | // theme components |
---|
189 | stroke: {}, // stroke for an axis |
---|
190 | majorTick: {}, // stroke + length for a tick |
---|
191 | minorTick: {}, // stroke + length for a tick |
---|
192 | microTick: {}, // stroke + length for a tick |
---|
193 | tick: {}, // stroke + length for a tick |
---|
194 | font: "", // font for labels |
---|
195 | fontColor: "", // color for labels as a string |
---|
196 | title: "", // axis title |
---|
197 | titleGap: 0, // gap between axis title and axis label |
---|
198 | titleFont: "", // axis title font |
---|
199 | titleFontColor: "", // axis title font color |
---|
200 | titleOrientation: "" // "axis" means the title facing the axis, "away" means facing away |
---|
201 | }, |
---|
202 | |
---|
203 | constructor: function(chart, kwArgs){ |
---|
204 | // summary: |
---|
205 | // The constructor for an axis. |
---|
206 | // chart: dojox.charting.Chart |
---|
207 | // The chart the axis belongs to. |
---|
208 | // kwArgs: dojox.charting.axis2d.__AxisCtorArgs? |
---|
209 | // Any optional keyword arguments to be used to define this axis. |
---|
210 | this.opt = lang.clone(this.defaultParams); |
---|
211 | du.updateWithObject(this.opt, kwArgs); |
---|
212 | du.updateWithPattern(this.opt, kwArgs, this.optionalParams); |
---|
213 | if(this.opt.enableCache){ |
---|
214 | this._textFreePool = []; |
---|
215 | this._lineFreePool = []; |
---|
216 | this._textUsePool = []; |
---|
217 | this._lineUsePool = []; |
---|
218 | } |
---|
219 | }, |
---|
220 | getOffsets: function(){ |
---|
221 | // summary: |
---|
222 | // Get the physical offset values for this axis (used in drawing data series). |
---|
223 | // returns: Object |
---|
224 | // The calculated offsets in the form of { l, r, t, b } (left, right, top, bottom). |
---|
225 | var s = this.scaler, offsets = { l: 0, r: 0, t: 0, b: 0 }; |
---|
226 | if(!s){ |
---|
227 | return offsets; |
---|
228 | } |
---|
229 | var o = this.opt, labelWidth = 0, a, b, c, d, |
---|
230 | gl = scommon.getNumericLabel, |
---|
231 | offset = 0, ma = s.major, mi = s.minor, |
---|
232 | ta = this.chart.theme.axis, |
---|
233 | // TODO: we use one font --- of major tick, we need to use major and minor fonts |
---|
234 | taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), |
---|
235 | taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont), |
---|
236 | taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15, |
---|
237 | taMajorTick = this.chart.theme.getTick("major", o), |
---|
238 | taMinorTick = this.chart.theme.getTick("minor", o), |
---|
239 | size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, |
---|
240 | tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0, |
---|
241 | rotation = o.rotation % 360, leftBottom = o.leftBottom, |
---|
242 | cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), |
---|
243 | sinr = Math.abs(Math.sin(rotation * Math.PI / 180)); |
---|
244 | this.trailingSymbol = (o.trailingSymbol === undefined || o.trailingSymbol === null) ? this.trailingSymbol : o.trailingSymbol; |
---|
245 | if(rotation < 0){ |
---|
246 | rotation += 360; |
---|
247 | } |
---|
248 | |
---|
249 | if(size){ |
---|
250 | // we need width of all labels |
---|
251 | if(this.labels){ |
---|
252 | labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount); |
---|
253 | }else{ |
---|
254 | labelWidth = this._groupLabelWidth([ |
---|
255 | gl(ma.start, ma.prec, o), |
---|
256 | gl(ma.start + ma.count * ma.tick, ma.prec, o), |
---|
257 | gl(mi.start, mi.prec, o), |
---|
258 | gl(mi.start + mi.count * mi.tick, mi.prec, o) |
---|
259 | ], taFont, o.maxLabelCharCount); |
---|
260 | } |
---|
261 | labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth; |
---|
262 | if(this.vertical){ |
---|
263 | var side = leftBottom ? "l" : "r"; |
---|
264 | switch(rotation){ |
---|
265 | case 0: |
---|
266 | case 180: |
---|
267 | offsets[side] = labelWidth; |
---|
268 | offsets.t = offsets.b = size / 2; |
---|
269 | break; |
---|
270 | case 90: |
---|
271 | case 270: |
---|
272 | offsets[side] = size; |
---|
273 | offsets.t = offsets.b = labelWidth / 2; |
---|
274 | break; |
---|
275 | default: |
---|
276 | if(rotation <= centerAnchorLimit || (180 < rotation && rotation <= (180 + centerAnchorLimit))){ |
---|
277 | offsets[side] = size * sinr / 2 + labelWidth * cosr; |
---|
278 | offsets[leftBottom ? "t" : "b"] = size * cosr / 2 + labelWidth * sinr; |
---|
279 | offsets[leftBottom ? "b" : "t"] = size * cosr / 2; |
---|
280 | }else if(rotation > (360 - centerAnchorLimit) || (180 > rotation && rotation > (180 - centerAnchorLimit))){ |
---|
281 | offsets[side] = size * sinr / 2 + labelWidth * cosr; |
---|
282 | offsets[leftBottom ? "b" : "t"] = size * cosr / 2 + labelWidth * sinr; |
---|
283 | offsets[leftBottom ? "t" : "b"] = size * cosr / 2; |
---|
284 | }else if(rotation < 90 || (180 < rotation && rotation < 270)){ |
---|
285 | offsets[side] = size * sinr + labelWidth * cosr; |
---|
286 | offsets[leftBottom ? "t" : "b"] = size * cosr + labelWidth * sinr; |
---|
287 | }else{ |
---|
288 | offsets[side] = size * sinr + labelWidth * cosr; |
---|
289 | offsets[leftBottom ? "b" : "t"] = size * cosr + labelWidth * sinr; |
---|
290 | } |
---|
291 | break; |
---|
292 | } |
---|
293 | offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0); |
---|
294 | }else{ |
---|
295 | var side = leftBottom ? "b" : "t"; |
---|
296 | switch(rotation){ |
---|
297 | case 0: |
---|
298 | case 180: |
---|
299 | offsets[side] = size; |
---|
300 | offsets.l = offsets.r = labelWidth / 2; |
---|
301 | break; |
---|
302 | case 90: |
---|
303 | case 270: |
---|
304 | offsets[side] = labelWidth; |
---|
305 | offsets.l = offsets.r = size / 2; |
---|
306 | break; |
---|
307 | default: |
---|
308 | if((90 - centerAnchorLimit) <= rotation && rotation <= 90 || (270 - centerAnchorLimit) <= rotation && rotation <= 270){ |
---|
309 | offsets[side] = size * sinr / 2 + labelWidth * cosr; |
---|
310 | offsets[leftBottom ? "r" : "l"] = size * cosr / 2 + labelWidth * sinr; |
---|
311 | offsets[leftBottom ? "l" : "r"] = size * cosr / 2; |
---|
312 | }else if(90 <= rotation && rotation <= (90 + centerAnchorLimit) || 270 <= rotation && rotation <= (270 + centerAnchorLimit)){ |
---|
313 | offsets[side] = size * sinr / 2 + labelWidth * cosr; |
---|
314 | offsets[leftBottom ? "l" : "r"] = size * cosr / 2 + labelWidth * sinr; |
---|
315 | offsets[leftBottom ? "r" : "l"] = size * cosr / 2; |
---|
316 | }else if(rotation < centerAnchorLimit || (180 < rotation && rotation < (180 - centerAnchorLimit))){ |
---|
317 | offsets[side] = size * sinr + labelWidth * cosr; |
---|
318 | offsets[leftBottom ? "r" : "l"] = size * cosr + labelWidth * sinr; |
---|
319 | }else{ |
---|
320 | offsets[side] = size * sinr + labelWidth * cosr; |
---|
321 | offsets[leftBottom ? "l" : "r"] = size * cosr + labelWidth * sinr; |
---|
322 | } |
---|
323 | break; |
---|
324 | } |
---|
325 | offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0); |
---|
326 | } |
---|
327 | } |
---|
328 | if(labelWidth){ |
---|
329 | this._cachedLabelWidth = labelWidth; |
---|
330 | } |
---|
331 | return offsets; // Object |
---|
332 | }, |
---|
333 | cleanGroup: function(creator){ |
---|
334 | if(this.opt.enableCache && this.group){ |
---|
335 | this._lineFreePool = this._lineFreePool.concat(this._lineUsePool); |
---|
336 | this._lineUsePool = []; |
---|
337 | this._textFreePool = this._textFreePool.concat(this._textUsePool); |
---|
338 | this._textUsePool = []; |
---|
339 | } |
---|
340 | this.inherited(arguments); |
---|
341 | }, |
---|
342 | createText: function(labelType, creator, x, y, align, textContent, font, fontColor, labelWidth){ |
---|
343 | if(!this.opt.enableCache || labelType=="html"){ |
---|
344 | return acommon.createText[labelType]( |
---|
345 | this.chart, |
---|
346 | creator, |
---|
347 | x, |
---|
348 | y, |
---|
349 | align, |
---|
350 | textContent, |
---|
351 | font, |
---|
352 | fontColor, |
---|
353 | labelWidth |
---|
354 | ); |
---|
355 | } |
---|
356 | var text; |
---|
357 | if (this._textFreePool.length > 0){ |
---|
358 | text = this._textFreePool.pop(); |
---|
359 | text.setShape({x: x, y: y, text: textContent, align: align}); |
---|
360 | // For now all items share the same font, no need to re-set it |
---|
361 | //.setFont(font).setFill(fontColor); |
---|
362 | // was cleared, add it back |
---|
363 | creator.add(text); |
---|
364 | }else{ |
---|
365 | text = acommon.createText[labelType]( |
---|
366 | this.chart, |
---|
367 | creator, |
---|
368 | x, |
---|
369 | y, |
---|
370 | align, |
---|
371 | textContent, |
---|
372 | font, |
---|
373 | fontColor, |
---|
374 | labelWidth |
---|
375 | ); } |
---|
376 | this._textUsePool.push(text); |
---|
377 | return text; |
---|
378 | }, |
---|
379 | createLine: function(creator, params){ |
---|
380 | var line; |
---|
381 | if(this.opt.enableCache && this._lineFreePool.length > 0){ |
---|
382 | line = this._lineFreePool.pop(); |
---|
383 | line.setShape(params); |
---|
384 | // was cleared, add it back |
---|
385 | creator.add(line); |
---|
386 | }else{ |
---|
387 | line = creator.createLine(params); |
---|
388 | } |
---|
389 | if(this.opt.enableCache){ |
---|
390 | this._lineUsePool.push(line); |
---|
391 | } |
---|
392 | return line; |
---|
393 | }, |
---|
394 | render: function(dim, offsets){ |
---|
395 | // summary: |
---|
396 | // Render/draw the axis. |
---|
397 | // dim: Object |
---|
398 | // An object of the form { width, height}. |
---|
399 | // offsets: Object |
---|
400 | // An object of the form { l, r, t, b }. |
---|
401 | // returns: dojox.charting.axis2d.Default |
---|
402 | // The reference to the axis for functional chaining. |
---|
403 | if(!this.dirty){ |
---|
404 | return this; // dojox.charting.axis2d.Default |
---|
405 | } |
---|
406 | // prepare variable |
---|
407 | var o = this.opt, ta = this.chart.theme.axis, leftBottom = o.leftBottom, rotation = o.rotation % 360, |
---|
408 | start, stop, titlePos, titleRotation=0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, |
---|
409 | |
---|
410 | // TODO: we use one font --- of major tick, we need to use major and minor fonts |
---|
411 | taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), |
---|
412 | taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont), |
---|
413 | // TODO: we use one font color --- we need to use different colors |
---|
414 | taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", |
---|
415 | taTitleFontColor = o.titleFontColor || (ta.tick && ta.tick.titleFontColor) || "black", |
---|
416 | taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15, |
---|
417 | taTitleOrientation = o.titleOrientation || (ta.tick && ta.tick.titleOrientation) || "axis", |
---|
418 | taMajorTick = this.chart.theme.getTick("major", o), |
---|
419 | taMinorTick = this.chart.theme.getTick("minor", o), |
---|
420 | taMicroTick = this.chart.theme.getTick("micro", o), |
---|
421 | |
---|
422 | tickSize = Math.max(taMajorTick.length, taMinorTick.length, taMicroTick.length), |
---|
423 | taStroke = "stroke" in o ? o.stroke : ta.stroke, |
---|
424 | size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, |
---|
425 | cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), |
---|
426 | sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), |
---|
427 | tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; |
---|
428 | if(rotation < 0){ |
---|
429 | rotation += 360; |
---|
430 | } |
---|
431 | if(this.vertical){ |
---|
432 | start = {y: dim.height - offsets.b}; |
---|
433 | stop = {y: offsets.t}; |
---|
434 | titlePos = {y: (dim.height - offsets.b + offsets.t)/2}; |
---|
435 | titleOffset = size * sinr + (this._cachedLabelWidth || 0) * cosr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap; |
---|
436 | axisVector = {x: 0, y: -1}; |
---|
437 | labelOffset = {x: 0, y: 0}; |
---|
438 | tickVector = {x: 1, y: 0}; |
---|
439 | anchorOffset = {x: labelGap, y: 0}; |
---|
440 | switch(rotation){ |
---|
441 | case 0: |
---|
442 | labelAlign = "end"; |
---|
443 | labelOffset.y = size * 0.4; |
---|
444 | break; |
---|
445 | case 90: |
---|
446 | labelAlign = "middle"; |
---|
447 | labelOffset.x = -size; |
---|
448 | break; |
---|
449 | case 180: |
---|
450 | labelAlign = "start"; |
---|
451 | labelOffset.y = -size * 0.4; |
---|
452 | break; |
---|
453 | case 270: |
---|
454 | labelAlign = "middle"; |
---|
455 | break; |
---|
456 | default: |
---|
457 | if(rotation < centerAnchorLimit){ |
---|
458 | labelAlign = "end"; |
---|
459 | labelOffset.y = size * 0.4; |
---|
460 | }else if(rotation < 90){ |
---|
461 | labelAlign = "end"; |
---|
462 | labelOffset.y = size * 0.4; |
---|
463 | }else if(rotation < (180 - centerAnchorLimit)){ |
---|
464 | labelAlign = "start"; |
---|
465 | }else if(rotation < (180 + centerAnchorLimit)){ |
---|
466 | labelAlign = "start"; |
---|
467 | labelOffset.y = -size * 0.4; |
---|
468 | }else if(rotation < 270){ |
---|
469 | labelAlign = "start"; |
---|
470 | labelOffset.x = leftBottom ? 0 : size * 0.4; |
---|
471 | }else if(rotation < (360 - centerAnchorLimit)){ |
---|
472 | labelAlign = "end"; |
---|
473 | labelOffset.x = leftBottom ? 0 : size * 0.4; |
---|
474 | }else{ |
---|
475 | labelAlign = "end"; |
---|
476 | labelOffset.y = size * 0.4; |
---|
477 | } |
---|
478 | } |
---|
479 | if(leftBottom){ |
---|
480 | start.x = stop.x = offsets.l; |
---|
481 | titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 90 : 270; |
---|
482 | titlePos.x = offsets.l - titleOffset + (titleRotation == 270 ? tsize : 0); |
---|
483 | tickVector.x = -1; |
---|
484 | anchorOffset.x = -anchorOffset.x; |
---|
485 | }else{ |
---|
486 | start.x = stop.x = dim.width - offsets.r; |
---|
487 | titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 90 : 270; |
---|
488 | titlePos.x = dim.width - offsets.r + titleOffset - (titleRotation == 270 ? 0 : tsize); |
---|
489 | switch(labelAlign){ |
---|
490 | case "start": |
---|
491 | labelAlign = "end"; |
---|
492 | break; |
---|
493 | case "end": |
---|
494 | labelAlign = "start"; |
---|
495 | break; |
---|
496 | case "middle": |
---|
497 | labelOffset.x += size; |
---|
498 | break; |
---|
499 | } |
---|
500 | } |
---|
501 | }else{ |
---|
502 | start = {x: offsets.l}; |
---|
503 | stop = {x: dim.width - offsets.r}; |
---|
504 | titlePos = {x: (dim.width - offsets.r + offsets.l)/2}; |
---|
505 | titleOffset = size * cosr + (this._cachedLabelWidth || 0) * sinr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap; |
---|
506 | axisVector = {x: 1, y: 0}; |
---|
507 | labelOffset = {x: 0, y: 0}; |
---|
508 | tickVector = {x: 0, y: 1}; |
---|
509 | anchorOffset = {x: 0, y: labelGap}; |
---|
510 | switch(rotation){ |
---|
511 | case 0: |
---|
512 | labelAlign = "middle"; |
---|
513 | labelOffset.y = size; |
---|
514 | break; |
---|
515 | case 90: |
---|
516 | labelAlign = "start"; |
---|
517 | labelOffset.x = -size * 0.4; |
---|
518 | break; |
---|
519 | case 180: |
---|
520 | labelAlign = "middle"; |
---|
521 | break; |
---|
522 | case 270: |
---|
523 | labelAlign = "end"; |
---|
524 | labelOffset.x = size * 0.4; |
---|
525 | break; |
---|
526 | default: |
---|
527 | if(rotation < (90 - centerAnchorLimit)){ |
---|
528 | labelAlign = "start"; |
---|
529 | labelOffset.y = leftBottom ? size : 0; |
---|
530 | }else if(rotation < (90 + centerAnchorLimit)){ |
---|
531 | labelAlign = "start"; |
---|
532 | labelOffset.x = -size * 0.4; |
---|
533 | }else if(rotation < 180){ |
---|
534 | labelAlign = "start"; |
---|
535 | labelOffset.y = leftBottom ? 0 : -size; |
---|
536 | }else if(rotation < (270 - centerAnchorLimit)){ |
---|
537 | labelAlign = "end"; |
---|
538 | labelOffset.y = leftBottom ? 0 : -size; |
---|
539 | }else if(rotation < (270 + centerAnchorLimit)){ |
---|
540 | labelAlign = "end"; |
---|
541 | labelOffset.y = leftBottom ? size * 0.4 : 0; |
---|
542 | }else{ |
---|
543 | labelAlign = "end"; |
---|
544 | labelOffset.y = leftBottom ? size : 0; |
---|
545 | } |
---|
546 | } |
---|
547 | if(leftBottom){ |
---|
548 | start.y = stop.y = dim.height - offsets.b; |
---|
549 | titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 180 : 0; |
---|
550 | titlePos.y = dim.height - offsets.b + titleOffset - (titleRotation ? tsize : 0); |
---|
551 | }else{ |
---|
552 | start.y = stop.y = offsets.t; |
---|
553 | titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; |
---|
554 | titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); |
---|
555 | tickVector.y = -1; |
---|
556 | anchorOffset.y = -anchorOffset.y; |
---|
557 | switch(labelAlign){ |
---|
558 | case "start": |
---|
559 | labelAlign = "end"; |
---|
560 | break; |
---|
561 | case "end": |
---|
562 | labelAlign = "start"; |
---|
563 | break; |
---|
564 | case "middle": |
---|
565 | labelOffset.y -= size; |
---|
566 | break; |
---|
567 | } |
---|
568 | } |
---|
569 | } |
---|
570 | |
---|
571 | // render shapes |
---|
572 | |
---|
573 | this.cleanGroup(); |
---|
574 | |
---|
575 | try{ |
---|
576 | var s = this.group, |
---|
577 | c = this.scaler, |
---|
578 | t = this.ticks, |
---|
579 | canLabel, |
---|
580 | f = lin.getTransformerFromModel(this.scaler), |
---|
581 | // GFX Canvas now supports labels, so let's _not_ fallback to HTML anymore on canvas, just use |
---|
582 | // HTML labels if explicitly asked + no rotation + no IE + no Opera |
---|
583 | labelType = (!o.title || !titleRotation) && !rotation && this.opt.htmlLabels && !has("ie") && !has("opera") ? "html" : "gfx", |
---|
584 | dx = tickVector.x * taMajorTick.length, |
---|
585 | dy = tickVector.y * taMajorTick.length; |
---|
586 | |
---|
587 | s.createLine({ |
---|
588 | x1: start.x, |
---|
589 | y1: start.y, |
---|
590 | x2: stop.x, |
---|
591 | y2: stop.y |
---|
592 | }).setStroke(taStroke); |
---|
593 | |
---|
594 | //create axis title |
---|
595 | if(o.title){ |
---|
596 | var axisTitle = acommon.createText[labelType]( |
---|
597 | this.chart, |
---|
598 | s, |
---|
599 | titlePos.x, |
---|
600 | titlePos.y, |
---|
601 | "middle", |
---|
602 | o.title, |
---|
603 | taTitleFont, |
---|
604 | taTitleFontColor |
---|
605 | ); |
---|
606 | if(labelType == "html"){ |
---|
607 | this.htmlElements.push(axisTitle); |
---|
608 | }else{ |
---|
609 | //as soon as rotation is provided, labelType won't be "html" |
---|
610 | //rotate gfx labels |
---|
611 | axisTitle.setTransform(g.matrix.rotategAt(titleRotation, titlePos.x, titlePos.y)); |
---|
612 | } |
---|
613 | } |
---|
614 | |
---|
615 | // go out nicely instead of try/catch |
---|
616 | if(t==null){ |
---|
617 | this.dirty = false; |
---|
618 | return this; |
---|
619 | } |
---|
620 | |
---|
621 | arr.forEach(t.major, function(tick){ |
---|
622 | var offset = f(tick.value), elem, |
---|
623 | x = start.x + axisVector.x * offset, |
---|
624 | y = start.y + axisVector.y * offset; |
---|
625 | this.createLine(s, { |
---|
626 | x1: x, y1: y, |
---|
627 | x2: x + dx, |
---|
628 | y2: y + dy |
---|
629 | }).setStroke(taMajorTick); |
---|
630 | if(tick.label){ |
---|
631 | var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : { |
---|
632 | text: tick.label, |
---|
633 | truncated: false |
---|
634 | }; |
---|
635 | label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label; |
---|
636 | elem = this.createText(labelType, |
---|
637 | s, |
---|
638 | x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x), |
---|
639 | y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y), |
---|
640 | labelAlign, |
---|
641 | label.text, |
---|
642 | taFont, |
---|
643 | taFontColor |
---|
644 | //this._cachedLabelWidth |
---|
645 | ); |
---|
646 | |
---|
647 | // if bidi support was required, the textDir is "auto" and truncation |
---|
648 | // took place, we need to update the dir of the element for cases as: |
---|
649 | // Fool label: 111111W (W for bidi character) |
---|
650 | // truncated label: 11... |
---|
651 | // in this case for auto textDir the dir will be "ltr" which is wrong. |
---|
652 | if(this.chart.truncateBidi && label.truncated){ |
---|
653 | this.chart.truncateBidi(elem, tick.label, labelType); |
---|
654 | } |
---|
655 | label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType); |
---|
656 | if(labelType == "html"){ |
---|
657 | this.htmlElements.push(elem); |
---|
658 | }else if(rotation){ |
---|
659 | elem.setTransform([ |
---|
660 | {dx: labelOffset.x, dy: labelOffset.y}, |
---|
661 | g.matrix.rotategAt( |
---|
662 | rotation, |
---|
663 | x + dx + anchorOffset.x, |
---|
664 | y + dy + anchorOffset.y |
---|
665 | ) |
---|
666 | ]); |
---|
667 | } |
---|
668 | } |
---|
669 | }, this); |
---|
670 | |
---|
671 | dx = tickVector.x * taMinorTick.length; |
---|
672 | dy = tickVector.y * taMinorTick.length; |
---|
673 | canLabel = c.minMinorStep <= c.minor.tick * c.bounds.scale; |
---|
674 | arr.forEach(t.minor, function(tick){ |
---|
675 | var offset = f(tick.value), elem, |
---|
676 | x = start.x + axisVector.x * offset, |
---|
677 | y = start.y + axisVector.y * offset; |
---|
678 | this.createLine(s, { |
---|
679 | x1: x, y1: y, |
---|
680 | x2: x + dx, |
---|
681 | y2: y + dy |
---|
682 | }).setStroke(taMinorTick); |
---|
683 | if(canLabel && tick.label){ |
---|
684 | var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : { |
---|
685 | text: tick.label, |
---|
686 | truncated: false |
---|
687 | }; |
---|
688 | label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label; |
---|
689 | elem = this.createText(labelType, |
---|
690 | s, |
---|
691 | x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x), |
---|
692 | y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y), |
---|
693 | labelAlign, |
---|
694 | label.text, |
---|
695 | taFont, |
---|
696 | taFontColor |
---|
697 | //this._cachedLabelWidth |
---|
698 | ); |
---|
699 | // if bidi support was required, the textDir is "auto" and truncation |
---|
700 | // took place, we need to update the dir of the element for cases as: |
---|
701 | // Fool label: 111111W (W for bidi character) |
---|
702 | // truncated label: 11... |
---|
703 | // in this case for auto textDir the dir will be "ltr" which is wrong. |
---|
704 | if(this.chart.getTextDir && label.truncated){ |
---|
705 | this.chart.truncateBidi(elem, tick.label, labelType); |
---|
706 | } |
---|
707 | label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType); |
---|
708 | if(labelType == "html"){ |
---|
709 | this.htmlElements.push(elem); |
---|
710 | }else if(rotation){ |
---|
711 | elem.setTransform([ |
---|
712 | {dx: labelOffset.x, dy: labelOffset.y}, |
---|
713 | g.matrix.rotategAt( |
---|
714 | rotation, |
---|
715 | x + dx + anchorOffset.x, |
---|
716 | y + dy + anchorOffset.y |
---|
717 | ) |
---|
718 | ]); |
---|
719 | } |
---|
720 | } |
---|
721 | }, this); |
---|
722 | |
---|
723 | dx = tickVector.x * taMicroTick.length; |
---|
724 | dy = tickVector.y * taMicroTick.length; |
---|
725 | arr.forEach(t.micro, function(tick){ |
---|
726 | var offset = f(tick.value), elem, |
---|
727 | x = start.x + axisVector.x * offset, |
---|
728 | y = start.y + axisVector.y * offset; |
---|
729 | this.createLine(s, { |
---|
730 | x1: x, y1: y, |
---|
731 | x2: x + dx, |
---|
732 | y2: y + dy |
---|
733 | }).setStroke(taMicroTick); |
---|
734 | }, this); |
---|
735 | }catch(e){ |
---|
736 | // squelch |
---|
737 | } |
---|
738 | |
---|
739 | this.dirty = false; |
---|
740 | return this; // dojox.charting.axis2d.Default |
---|
741 | }, |
---|
742 | labelTooltip: function(elem, chart, label, truncatedLabel, font, elemType){ |
---|
743 | var modules = ["dijit/Tooltip"]; |
---|
744 | var aroundRect = {type: "rect"}, position = ["above", "below"], |
---|
745 | fontWidth = g._base._getTextBox(truncatedLabel, {font: font}).w || 0, |
---|
746 | fontHeight = font ? g.normalizedLength(g.splitFontString(font).size) : 0; |
---|
747 | if(elemType == "html"){ |
---|
748 | lang.mixin(aroundRect, html.coords(elem.firstChild, true)); |
---|
749 | aroundRect.width = Math.ceil(fontWidth); |
---|
750 | aroundRect.height = Math.ceil(fontHeight); |
---|
751 | this._events.push({ |
---|
752 | shape: dojo, |
---|
753 | handle: connect.connect(elem.firstChild, "onmouseover", this, function(e){ |
---|
754 | require(modules, function(Tooltip){ |
---|
755 | Tooltip.show(label, aroundRect, position); |
---|
756 | }); |
---|
757 | }) |
---|
758 | }); |
---|
759 | this._events.push({ |
---|
760 | shape: dojo, |
---|
761 | handle: connect.connect(elem.firstChild, "onmouseout", this, function(e){ |
---|
762 | require(modules, function(Tooltip){ |
---|
763 | Tooltip.hide(aroundRect); |
---|
764 | }); |
---|
765 | }) |
---|
766 | }); |
---|
767 | }else{ |
---|
768 | var shp = elem.getShape(), |
---|
769 | lt = html.coords(chart.node, true); |
---|
770 | aroundRect = lang.mixin(aroundRect, { |
---|
771 | x: shp.x - fontWidth / 2, |
---|
772 | y: shp.y |
---|
773 | }); |
---|
774 | aroundRect.x += lt.x; |
---|
775 | aroundRect.y += lt.y; |
---|
776 | aroundRect.x = Math.round(aroundRect.x); |
---|
777 | aroundRect.y = Math.round(aroundRect.y); |
---|
778 | aroundRect.width = Math.ceil(fontWidth); |
---|
779 | aroundRect.height = Math.ceil(fontHeight); |
---|
780 | this._events.push({ |
---|
781 | shape: elem, |
---|
782 | handle: elem.connect("onmouseenter", this, function(e){ |
---|
783 | require(modules, function(Tooltip){ |
---|
784 | Tooltip.show(label, aroundRect, position); |
---|
785 | }); |
---|
786 | }) |
---|
787 | }); |
---|
788 | this._events.push({ |
---|
789 | shape: elem, |
---|
790 | handle: elem.connect("onmouseleave", this, function(e){ |
---|
791 | require(modules, function(Tooltip){ |
---|
792 | Tooltip.hide(aroundRect); |
---|
793 | }); |
---|
794 | }) |
---|
795 | }); |
---|
796 | } |
---|
797 | } |
---|
798 | }); |
---|
799 | }); |
---|