|
1 YUI.add('axis', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 * Provides base functionality for drawing chart axes. |
|
5 * |
|
6 * @module charts |
|
7 * @submodule axis |
|
8 */ |
|
9 var CONFIG = Y.config, |
|
10 DOCUMENT = CONFIG.doc, |
|
11 Y_Lang = Y.Lang, |
|
12 IS_STRING = Y_Lang.isString, |
|
13 Y_DOM = Y.DOM, |
|
14 LeftAxisLayout, |
|
15 RightAxisLayout, |
|
16 BottomAxisLayout, |
|
17 TopAxisLayout; |
|
18 /** |
|
19 * Algorithmic strategy for rendering a left axis. |
|
20 * |
|
21 * @class LeftAxisLayout |
|
22 * @constructor |
|
23 * @submodule axis |
|
24 */ |
|
25 LeftAxisLayout = function() {}; |
|
26 |
|
27 LeftAxisLayout.prototype = { |
|
28 /** |
|
29 * Default margins for text fields. |
|
30 * |
|
31 * @private |
|
32 * @method _getDefaultMargins |
|
33 * @return Object |
|
34 */ |
|
35 _getDefaultMargins: function() |
|
36 { |
|
37 return { |
|
38 top: 0, |
|
39 left: 0, |
|
40 right: 4, |
|
41 bottom: 0 |
|
42 }; |
|
43 }, |
|
44 |
|
45 /** |
|
46 * Sets the length of the tick on either side of the axis line. |
|
47 * |
|
48 * @method setTickOffset |
|
49 * @protected |
|
50 */ |
|
51 setTickOffsets: function() |
|
52 { |
|
53 var host = this, |
|
54 majorTicks = host.get("styles").majorTicks, |
|
55 tickLength = majorTicks.length, |
|
56 halfTick = tickLength * 0.5, |
|
57 display = majorTicks.display; |
|
58 host.set("topTickOffset", 0); |
|
59 host.set("bottomTickOffset", 0); |
|
60 |
|
61 switch(display) |
|
62 { |
|
63 case "inside" : |
|
64 host.set("rightTickOffset", tickLength); |
|
65 host.set("leftTickOffset", 0); |
|
66 break; |
|
67 case "outside" : |
|
68 host.set("rightTickOffset", 0); |
|
69 host.set("leftTickOffset", tickLength); |
|
70 break; |
|
71 case "cross": |
|
72 host.set("rightTickOffset", halfTick); |
|
73 host.set("leftTickOffset", halfTick); |
|
74 break; |
|
75 default: |
|
76 host.set("rightTickOffset", 0); |
|
77 host.set("leftTickOffset", 0); |
|
78 break; |
|
79 } |
|
80 }, |
|
81 |
|
82 /** |
|
83 * Draws a tick |
|
84 * |
|
85 * @method drawTick |
|
86 * @param {Path} path reference to the path `Path` element in which to draw the tick. |
|
87 * @param {Object} pt Point on the axis in which the tick will intersect. |
|
88 * @param {Object} tickStyle Hash of properties to apply to the tick. |
|
89 * @protected |
|
90 */ |
|
91 drawTick: function(path, pt, tickStyles) |
|
92 { |
|
93 var host = this, |
|
94 style = host.get("styles"), |
|
95 padding = style.padding, |
|
96 tickLength = tickStyles.length, |
|
97 start = {x:padding.left, y:pt.y}, |
|
98 end = {x:tickLength + padding.left, y:pt.y}; |
|
99 host.drawLine(path, start, end); |
|
100 }, |
|
101 |
|
102 /** |
|
103 * Calculates the coordinates for the first point on an axis. |
|
104 * |
|
105 * @method getLineStart |
|
106 * @return {Object} |
|
107 * @protected |
|
108 */ |
|
109 getLineStart: function() |
|
110 { |
|
111 var style = this.get("styles"), |
|
112 padding = style.padding, |
|
113 majorTicks = style.majorTicks, |
|
114 tickLength = majorTicks.length, |
|
115 display = majorTicks.display, |
|
116 pt = {x:padding.left, y:0}; |
|
117 if(display === "outside") |
|
118 { |
|
119 pt.x += tickLength; |
|
120 } |
|
121 else if(display === "cross") |
|
122 { |
|
123 pt.x += tickLength/2; |
|
124 } |
|
125 return pt; |
|
126 }, |
|
127 |
|
128 /** |
|
129 * Calculates the point for a label. |
|
130 * |
|
131 * @method getLabelPoint |
|
132 * @param {Object} point Point on the axis in which the tick will intersect. |
|
133 * @return {Object} |
|
134 * @protected |
|
135 */ |
|
136 getLabelPoint: function(point) |
|
137 { |
|
138 return {x:point.x - this.get("leftTickOffset"), y:point.y}; |
|
139 }, |
|
140 |
|
141 /** |
|
142 * Updates the value for the `maxLabelSize` for use in calculating total size. |
|
143 * |
|
144 * @method updateMaxLabelSize |
|
145 * @param {HTMLElement} label to measure |
|
146 * @protected |
|
147 */ |
|
148 updateMaxLabelSize: function(labelWidth, labelHeight) |
|
149 { |
|
150 var host = this, |
|
151 props = this._labelRotationProps, |
|
152 rot = props.rot, |
|
153 absRot = props.absRot, |
|
154 sinRadians = props.sinRadians, |
|
155 cosRadians = props.cosRadians, |
|
156 max; |
|
157 if(rot === 0) |
|
158 { |
|
159 max = labelWidth; |
|
160 } |
|
161 else if(absRot === 90) |
|
162 { |
|
163 max = labelHeight; |
|
164 } |
|
165 else |
|
166 { |
|
167 max = (cosRadians * labelWidth) + (sinRadians * labelHeight); |
|
168 } |
|
169 host._maxLabelSize = Math.max(host._maxLabelSize, max); |
|
170 }, |
|
171 |
|
172 /** |
|
173 * Determines the available label width when the axis width has been explicitly set. |
|
174 * |
|
175 * @method getExplicitlySized |
|
176 * @return Boolean |
|
177 * @protected |
|
178 */ |
|
179 getExplicitlySized: function(styles) |
|
180 { |
|
181 if(this._explicitWidth) |
|
182 { |
|
183 var host = this, |
|
184 w = host._explicitWidth, |
|
185 totalTitleSize = host._totalTitleSize, |
|
186 leftTickOffset = host.get("leftTickOffset"), |
|
187 margin = styles.label.margin.right; |
|
188 host._maxLabelSize = w - (leftTickOffset + margin + totalTitleSize); |
|
189 return true; |
|
190 } |
|
191 return false; |
|
192 }, |
|
193 |
|
194 /** |
|
195 * Rotate and position title. |
|
196 * |
|
197 * @method positionTitle |
|
198 * @param {HTMLElement} label to rotate position |
|
199 * @protected |
|
200 */ |
|
201 positionTitle: function(label) |
|
202 { |
|
203 var host = this, |
|
204 bounds = host._titleBounds, |
|
205 margin = host.get("styles").title.margin, |
|
206 props = host._titleRotationProps, |
|
207 w = bounds.right - bounds.left, |
|
208 labelWidth = label.offsetWidth, |
|
209 labelHeight = label.offsetHeight, |
|
210 x = (labelWidth * -0.5) + (w * 0.5), |
|
211 y = (host.get("height") * 0.5) - (labelHeight * 0.5); |
|
212 props.labelWidth = labelWidth; |
|
213 props.labelHeight = labelHeight; |
|
214 if(margin && margin.left) |
|
215 { |
|
216 x += margin.left; |
|
217 } |
|
218 props.x = x; |
|
219 props.y = y; |
|
220 props.transformOrigin = [0.5, 0.5]; |
|
221 host._rotate(label, props); |
|
222 }, |
|
223 |
|
224 /** |
|
225 * Rotate and position labels. |
|
226 * |
|
227 * @method positionLabel |
|
228 * @param {HTMLElement} label to rotate position |
|
229 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned |
|
230 * against. |
|
231 * @protected |
|
232 */ |
|
233 positionLabel: function(label, pt, styles, i) |
|
234 { |
|
235 var host = this, |
|
236 offset = parseFloat(styles.label.offset), |
|
237 tickOffset = host.get("leftTickOffset"), |
|
238 totalTitleSize = this._totalTitleSize, |
|
239 leftOffset = pt.x + totalTitleSize - tickOffset, |
|
240 topOffset = pt.y, |
|
241 props = this._labelRotationProps, |
|
242 rot = props.rot, |
|
243 absRot = props.absRot, |
|
244 maxLabelSize = host._maxLabelSize, |
|
245 labelWidth = this._labelWidths[i], |
|
246 labelHeight = this._labelHeights[i]; |
|
247 if(rot === 0) |
|
248 { |
|
249 leftOffset -= labelWidth; |
|
250 topOffset -= labelHeight * offset; |
|
251 } |
|
252 else if(rot === 90) |
|
253 { |
|
254 leftOffset -= labelWidth * 0.5; |
|
255 topOffset = topOffset + labelWidth/2 - (labelWidth * offset); |
|
256 } |
|
257 else if(rot === -90) |
|
258 { |
|
259 leftOffset -= labelWidth * 0.5; |
|
260 topOffset = topOffset - labelHeight + labelWidth/2 - (labelWidth * offset); |
|
261 } |
|
262 else |
|
263 { |
|
264 leftOffset -= labelWidth + (labelHeight * absRot/360); |
|
265 topOffset -= labelHeight * offset; |
|
266 } |
|
267 props.labelWidth = labelWidth; |
|
268 props.labelHeight = labelHeight; |
|
269 props.x = Math.round(maxLabelSize + leftOffset); |
|
270 props.y = Math.round(topOffset); |
|
271 this._rotate(label, props); |
|
272 }, |
|
273 |
|
274 /** |
|
275 * Adjusts the coordinates of an axis label based on the rotation. |
|
276 * |
|
277 * @method _setRotationCoords |
|
278 * @param {Object} props Coordinates, dimension and rotation properties of the label. |
|
279 * @protected |
|
280 */ |
|
281 _setRotationCoords: function(props) |
|
282 { |
|
283 var rot = props.rot, |
|
284 absRot = props.absRot, |
|
285 leftOffset, |
|
286 topOffset, |
|
287 labelWidth = props.labelWidth, |
|
288 labelHeight = props.labelHeight; |
|
289 if(rot === 0) |
|
290 { |
|
291 leftOffset = labelWidth; |
|
292 topOffset = labelHeight * 0.5; |
|
293 } |
|
294 else if(rot === 90) |
|
295 { |
|
296 topOffset = 0; |
|
297 leftOffset = labelWidth * 0.5; |
|
298 } |
|
299 else if(rot === -90) |
|
300 { |
|
301 leftOffset = labelWidth * 0.5; |
|
302 topOffset = labelHeight; |
|
303 } |
|
304 else |
|
305 { |
|
306 leftOffset = labelWidth + (labelHeight * absRot/360); |
|
307 topOffset = labelHeight * 0.5; |
|
308 } |
|
309 props.x -= leftOffset; |
|
310 props.y -= topOffset; |
|
311 }, |
|
312 |
|
313 /** |
|
314 * Returns the transformOrigin to use for an axis label based on the position of the axis |
|
315 * and the rotation of the label. |
|
316 * |
|
317 * @method _getTransformOrigin |
|
318 * @param {Number} rot The rotation (in degrees) of the label. |
|
319 * @return Array |
|
320 * @protected |
|
321 */ |
|
322 _getTransformOrigin: function(rot) |
|
323 { |
|
324 var transformOrigin; |
|
325 if(rot === 0) |
|
326 { |
|
327 transformOrigin = [0, 0]; |
|
328 } |
|
329 else if(rot === 90) |
|
330 { |
|
331 transformOrigin = [0.5, 0]; |
|
332 } |
|
333 else if(rot === -90) |
|
334 { |
|
335 transformOrigin = [0.5, 1]; |
|
336 } |
|
337 else |
|
338 { |
|
339 transformOrigin = [1, 0.5]; |
|
340 } |
|
341 return transformOrigin; |
|
342 }, |
|
343 |
|
344 /** |
|
345 * Adjust the position of the Axis widget's content box for internal axes. |
|
346 * |
|
347 * @method offsetNodeForTick |
|
348 * @param {Node} cb contentBox of the axis |
|
349 * @protected |
|
350 */ |
|
351 offsetNodeForTick: function() |
|
352 { |
|
353 }, |
|
354 |
|
355 /** |
|
356 * Sets the width of the axis based on its contents. |
|
357 * |
|
358 * @method setCalculatedSize |
|
359 * @protected |
|
360 */ |
|
361 setCalculatedSize: function() |
|
362 { |
|
363 var host = this, |
|
364 graphic = this.get("graphic"), |
|
365 style = host.get("styles"), |
|
366 label = style.label, |
|
367 tickOffset = host.get("leftTickOffset"), |
|
368 max = host._maxLabelSize, |
|
369 totalTitleSize = this._totalTitleSize, |
|
370 ttl = Math.round(totalTitleSize + tickOffset + max + label.margin.right); |
|
371 if(this._explicitWidth) |
|
372 { |
|
373 ttl = this._explicitWidth; |
|
374 } |
|
375 this.set("calculatedWidth", ttl); |
|
376 graphic.set("x", ttl - tickOffset); |
|
377 } |
|
378 }; |
|
379 |
|
380 Y.LeftAxisLayout = LeftAxisLayout; |
|
381 /** |
|
382 * RightAxisLayout contains algorithms for rendering a right axis. |
|
383 * |
|
384 * @class RightAxisLayout |
|
385 * @constructor |
|
386 * @submodule axis |
|
387 */ |
|
388 RightAxisLayout = function(){}; |
|
389 |
|
390 RightAxisLayout.prototype = { |
|
391 /** |
|
392 * Default margins for text fields. |
|
393 * |
|
394 * @private |
|
395 * @method _getDefaultMargins |
|
396 * @return Object |
|
397 */ |
|
398 _getDefaultMargins: function() |
|
399 { |
|
400 return { |
|
401 top: 0, |
|
402 left: 4, |
|
403 right: 0, |
|
404 bottom: 0 |
|
405 }; |
|
406 }, |
|
407 |
|
408 /** |
|
409 * Sets the length of the tick on either side of the axis line. |
|
410 * |
|
411 * @method setTickOffset |
|
412 * @protected |
|
413 */ |
|
414 setTickOffsets: function() |
|
415 { |
|
416 var host = this, |
|
417 majorTicks = host.get("styles").majorTicks, |
|
418 tickLength = majorTicks.length, |
|
419 halfTick = tickLength * 0.5, |
|
420 display = majorTicks.display; |
|
421 host.set("topTickOffset", 0); |
|
422 host.set("bottomTickOffset", 0); |
|
423 |
|
424 switch(display) |
|
425 { |
|
426 case "inside" : |
|
427 host.set("leftTickOffset", tickLength); |
|
428 host.set("rightTickOffset", 0); |
|
429 break; |
|
430 case "outside" : |
|
431 host.set("leftTickOffset", 0); |
|
432 host.set("rightTickOffset", tickLength); |
|
433 break; |
|
434 case "cross" : |
|
435 host.set("rightTickOffset", halfTick); |
|
436 host.set("leftTickOffset", halfTick); |
|
437 break; |
|
438 default: |
|
439 host.set("leftTickOffset", 0); |
|
440 host.set("rightTickOffset", 0); |
|
441 break; |
|
442 } |
|
443 }, |
|
444 |
|
445 /** |
|
446 * Draws a tick |
|
447 * |
|
448 * @method drawTick |
|
449 * @param {Path} path reference to the path `Path` element in which to draw the tick. |
|
450 * @param {Object} pt Point on the axis in which the tick will intersect. |
|
451 * @param {Object} tickStyle Hash of properties to apply to the tick. |
|
452 * @protected |
|
453 */ |
|
454 drawTick: function(path, pt, tickStyles) |
|
455 { |
|
456 var host = this, |
|
457 style = host.get("styles"), |
|
458 padding = style.padding, |
|
459 tickLength = tickStyles.length, |
|
460 start = {x:padding.left, y:pt.y}, |
|
461 end = {x:padding.left + tickLength, y:pt.y}; |
|
462 host.drawLine(path, start, end); |
|
463 }, |
|
464 |
|
465 /** |
|
466 * Calculates the coordinates for the first point on an axis. |
|
467 * |
|
468 * @method getLineStart |
|
469 * @return {Object} |
|
470 * @protected |
|
471 */ |
|
472 getLineStart: function() |
|
473 { |
|
474 var host = this, |
|
475 style = host.get("styles"), |
|
476 padding = style.padding, |
|
477 majorTicks = style.majorTicks, |
|
478 tickLength = majorTicks.length, |
|
479 display = majorTicks.display, |
|
480 pt = {x:padding.left, y:padding.top}; |
|
481 if(display === "inside") |
|
482 { |
|
483 pt.x += tickLength; |
|
484 } |
|
485 else if(display === "cross") |
|
486 { |
|
487 pt.x += tickLength/2; |
|
488 } |
|
489 return pt; |
|
490 }, |
|
491 |
|
492 /** |
|
493 * Calculates the point for a label. |
|
494 * |
|
495 * @method getLabelPoint |
|
496 * @param {Object} point Point on the axis in which the tick will intersect. |
|
497 * @return {Object} |
|
498 * @protected |
|
499 */ |
|
500 getLabelPoint: function(point) |
|
501 { |
|
502 return {x:point.x + this.get("rightTickOffset"), y:point.y}; |
|
503 }, |
|
504 |
|
505 /** |
|
506 * Updates the value for the `maxLabelSize` for use in calculating total size. |
|
507 * |
|
508 * @method updateMaxLabelSize |
|
509 * @param {HTMLElement} label to measure |
|
510 * @protected |
|
511 */ |
|
512 updateMaxLabelSize: function(labelWidth, labelHeight) |
|
513 { |
|
514 var host = this, |
|
515 props = this._labelRotationProps, |
|
516 rot = props.rot, |
|
517 absRot = props.absRot, |
|
518 sinRadians = props.sinRadians, |
|
519 cosRadians = props.cosRadians, |
|
520 max; |
|
521 if(rot === 0) |
|
522 { |
|
523 max = labelWidth; |
|
524 } |
|
525 else if(absRot === 90) |
|
526 { |
|
527 max = labelHeight; |
|
528 } |
|
529 else |
|
530 { |
|
531 max = (cosRadians * labelWidth) + (sinRadians * labelHeight); |
|
532 } |
|
533 host._maxLabelSize = Math.max(host._maxLabelSize, max); |
|
534 }, |
|
535 |
|
536 /** |
|
537 * Determines the available label width when the axis width has been explicitly set. |
|
538 * |
|
539 * @method getExplicitlySized |
|
540 * @return Boolean |
|
541 * @protected |
|
542 */ |
|
543 getExplicitlySized: function(styles) |
|
544 { |
|
545 if(this._explicitWidth) |
|
546 { |
|
547 var host = this, |
|
548 w = host._explicitWidth, |
|
549 totalTitleSize = this._totalTitleSize, |
|
550 rightTickOffset = host.get("rightTickOffset"), |
|
551 margin = styles.label.margin.right; |
|
552 host._maxLabelSize = w - (rightTickOffset + margin + totalTitleSize); |
|
553 return true; |
|
554 } |
|
555 return false; |
|
556 }, |
|
557 |
|
558 /** |
|
559 * Rotate and position title. |
|
560 * |
|
561 * @method positionTitle |
|
562 * @param {HTMLElement} label to rotate position |
|
563 * @protected |
|
564 */ |
|
565 positionTitle: function(label) |
|
566 { |
|
567 var host = this, |
|
568 bounds = host._titleBounds, |
|
569 margin = host.get("styles").title.margin, |
|
570 props = host._titleRotationProps, |
|
571 labelWidth = label.offsetWidth, |
|
572 labelHeight = label.offsetHeight, |
|
573 w = bounds.right - bounds.left, |
|
574 x = this.get("width") - (labelWidth * 0.5) - (w * 0.5), |
|
575 y = (host.get("height") * 0.5) - (labelHeight * 0.5); |
|
576 props.labelWidth = labelWidth; |
|
577 props.labelHeight = labelHeight; |
|
578 if(margin && margin.right) |
|
579 { |
|
580 x -= margin.left; |
|
581 } |
|
582 props.x = x; |
|
583 props.y = y; |
|
584 props.transformOrigin = [0.5, 0.5]; |
|
585 host._rotate(label, props); |
|
586 }, |
|
587 |
|
588 /** |
|
589 * Rotate and position labels. |
|
590 * |
|
591 * @method positionLabel |
|
592 * @param {HTMLElement} label to rotate position |
|
593 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned |
|
594 * against. |
|
595 * @protected |
|
596 */ |
|
597 positionLabel: function(label, pt, styles, i) |
|
598 { |
|
599 var host = this, |
|
600 offset = parseFloat(styles.label.offset), |
|
601 tickOffset = host.get("rightTickOffset"), |
|
602 labelStyles = styles.label, |
|
603 margin = 0, |
|
604 leftOffset = pt.x, |
|
605 topOffset = pt.y, |
|
606 props = this._labelRotationProps, |
|
607 rot = props.rot, |
|
608 absRot = props.absRot, |
|
609 labelWidth = this._labelWidths[i], |
|
610 labelHeight = this._labelHeights[i]; |
|
611 if(labelStyles.margin && labelStyles.margin.left) |
|
612 { |
|
613 margin = labelStyles.margin.left; |
|
614 } |
|
615 if(rot === 0) |
|
616 { |
|
617 topOffset -= labelHeight * offset; |
|
618 } |
|
619 else if(rot === 90) |
|
620 { |
|
621 leftOffset -= labelWidth * 0.5; |
|
622 topOffset = topOffset - labelHeight + labelWidth/2 - (labelWidth * offset); |
|
623 } |
|
624 else if(rot === -90) |
|
625 { |
|
626 topOffset = topOffset + labelWidth/2 - (labelWidth * offset); |
|
627 leftOffset -= labelWidth * 0.5; |
|
628 } |
|
629 else |
|
630 { |
|
631 topOffset -= labelHeight * offset; |
|
632 leftOffset += labelHeight/2 * absRot/90; |
|
633 } |
|
634 leftOffset += margin; |
|
635 leftOffset += tickOffset; |
|
636 props.labelWidth = labelWidth; |
|
637 props.labelHeight = labelHeight; |
|
638 props.x = Math.round(leftOffset); |
|
639 props.y = Math.round(topOffset); |
|
640 this._rotate(label, props); |
|
641 }, |
|
642 |
|
643 /** |
|
644 * Adjusts the coordinates of an axis label based on the rotation. |
|
645 * |
|
646 * @method _setRotationCoords |
|
647 * @param {Object} props Coordinates, dimension and rotation properties of the label. |
|
648 * @protected |
|
649 */ |
|
650 _setRotationCoords: function(props) |
|
651 { |
|
652 var rot = props.rot, |
|
653 absRot = props.absRot, |
|
654 leftOffset = 0, |
|
655 topOffset = 0, |
|
656 labelWidth = props.labelWidth, |
|
657 labelHeight = props.labelHeight; |
|
658 if(rot === 0) |
|
659 { |
|
660 topOffset = labelHeight * 0.5; |
|
661 } |
|
662 else if(rot === 90) |
|
663 { |
|
664 leftOffset = labelWidth * 0.5; |
|
665 topOffset = labelHeight; |
|
666 } |
|
667 else if(rot === -90) |
|
668 { |
|
669 leftOffset = labelWidth * 0.5; |
|
670 } |
|
671 else |
|
672 { |
|
673 topOffset = labelHeight * 0.5; |
|
674 leftOffset = labelHeight/2 * absRot/90; |
|
675 } |
|
676 props.x -= leftOffset; |
|
677 props.y -= topOffset; |
|
678 }, |
|
679 |
|
680 /** |
|
681 * Returns the transformOrigin to use for an axis label based on the position of the axis |
|
682 * and the rotation of the label. |
|
683 * |
|
684 * @method _getTransformOrigin |
|
685 * @param {Number} rot The rotation (in degrees) of the label. |
|
686 * @return Array |
|
687 * @protected |
|
688 */ |
|
689 _getTransformOrigin: function(rot) |
|
690 { |
|
691 var transformOrigin; |
|
692 if(rot === 0) |
|
693 { |
|
694 transformOrigin = [0, 0]; |
|
695 } |
|
696 else if(rot === 90) |
|
697 { |
|
698 transformOrigin = [0.5, 1]; |
|
699 } |
|
700 else if(rot === -90) |
|
701 { |
|
702 transformOrigin = [0.5, 0]; |
|
703 } |
|
704 else |
|
705 { |
|
706 transformOrigin = [0, 0.5]; |
|
707 } |
|
708 return transformOrigin; |
|
709 }, |
|
710 |
|
711 /** |
|
712 * Adjusts position for inner ticks. |
|
713 * |
|
714 * @method offsetNodeForTick |
|
715 * @param {Node} cb contentBox of the axis |
|
716 * @protected |
|
717 */ |
|
718 offsetNodeForTick: function(cb) |
|
719 { |
|
720 var host = this, |
|
721 tickOffset = host.get("leftTickOffset"), |
|
722 offset = 0 - tickOffset; |
|
723 cb.setStyle("left", offset); |
|
724 }, |
|
725 |
|
726 /** |
|
727 * Assigns a height based on the size of the contents. |
|
728 * |
|
729 * @method setCalculatedSize |
|
730 * @protected |
|
731 */ |
|
732 setCalculatedSize: function() |
|
733 { |
|
734 var host = this, |
|
735 styles = host.get("styles"), |
|
736 labelStyle = styles.label, |
|
737 totalTitleSize = this._totalTitleSize, |
|
738 ttl = Math.round(host.get("rightTickOffset") + host._maxLabelSize + totalTitleSize + labelStyle.margin.left); |
|
739 if(this._explicitWidth) |
|
740 { |
|
741 ttl = this._explicitWidth; |
|
742 } |
|
743 host.set("calculatedWidth", ttl); |
|
744 host.get("contentBox").setStyle("width", ttl); |
|
745 } |
|
746 }; |
|
747 |
|
748 Y.RightAxisLayout = RightAxisLayout; |
|
749 /** |
|
750 * Contains algorithms for rendering a bottom axis. |
|
751 * |
|
752 * @class BottomAxisLayout |
|
753 * @Constructor |
|
754 * @submodule axis |
|
755 */ |
|
756 BottomAxisLayout = function(){}; |
|
757 |
|
758 BottomAxisLayout.prototype = { |
|
759 /** |
|
760 * Default margins for text fields. |
|
761 * |
|
762 * @private |
|
763 * @method _getDefaultMargins |
|
764 * @return Object |
|
765 */ |
|
766 _getDefaultMargins: function() |
|
767 { |
|
768 return { |
|
769 top: 4, |
|
770 left: 0, |
|
771 right: 0, |
|
772 bottom: 0 |
|
773 }; |
|
774 }, |
|
775 |
|
776 /** |
|
777 * Sets the length of the tick on either side of the axis line. |
|
778 * |
|
779 * @method setTickOffsets |
|
780 * @protected |
|
781 */ |
|
782 setTickOffsets: function() |
|
783 { |
|
784 var host = this, |
|
785 majorTicks = host.get("styles").majorTicks, |
|
786 tickLength = majorTicks.length, |
|
787 halfTick = tickLength * 0.5, |
|
788 display = majorTicks.display; |
|
789 host.set("leftTickOffset", 0); |
|
790 host.set("rightTickOffset", 0); |
|
791 |
|
792 switch(display) |
|
793 { |
|
794 case "inside" : |
|
795 host.set("topTickOffset", tickLength); |
|
796 host.set("bottomTickOffset", 0); |
|
797 break; |
|
798 case "outside" : |
|
799 host.set("topTickOffset", 0); |
|
800 host.set("bottomTickOffset", tickLength); |
|
801 break; |
|
802 case "cross": |
|
803 host.set("topTickOffset", halfTick); |
|
804 host.set("bottomTickOffset", halfTick); |
|
805 break; |
|
806 default: |
|
807 host.set("topTickOffset", 0); |
|
808 host.set("bottomTickOffset", 0); |
|
809 break; |
|
810 } |
|
811 }, |
|
812 |
|
813 /** |
|
814 * Calculates the coordinates for the first point on an axis. |
|
815 * |
|
816 * @method getLineStart |
|
817 * @protected |
|
818 */ |
|
819 getLineStart: function() |
|
820 { |
|
821 var style = this.get("styles"), |
|
822 padding = style.padding, |
|
823 majorTicks = style.majorTicks, |
|
824 tickLength = majorTicks.length, |
|
825 display = majorTicks.display, |
|
826 pt = {x:0, y:padding.top}; |
|
827 if(display === "inside") |
|
828 { |
|
829 pt.y += tickLength; |
|
830 } |
|
831 else if(display === "cross") |
|
832 { |
|
833 pt.y += tickLength/2; |
|
834 } |
|
835 return pt; |
|
836 }, |
|
837 |
|
838 /** |
|
839 * Draws a tick |
|
840 * |
|
841 * @method drawTick |
|
842 * @param {Path} path reference to the path `Path` element in which to draw the tick. |
|
843 * @param {Object} pt hash containing x and y coordinates |
|
844 * @param {Object} tickStyles hash of properties used to draw the tick |
|
845 * @protected |
|
846 */ |
|
847 drawTick: function(path, pt, tickStyles) |
|
848 { |
|
849 var host = this, |
|
850 style = host.get("styles"), |
|
851 padding = style.padding, |
|
852 tickLength = tickStyles.length, |
|
853 start = {x:pt.x, y:padding.top}, |
|
854 end = {x:pt.x, y:tickLength + padding.top}; |
|
855 host.drawLine(path, start, end); |
|
856 }, |
|
857 |
|
858 /** |
|
859 * Calculates the point for a label. |
|
860 * |
|
861 * @method getLabelPoint |
|
862 * @param {Object} pt Object containing x and y coordinates |
|
863 * @return Object |
|
864 * @protected |
|
865 */ |
|
866 getLabelPoint: function(point) |
|
867 { |
|
868 return {x:point.x, y:point.y + this.get("bottomTickOffset")}; |
|
869 }, |
|
870 |
|
871 /** |
|
872 * Updates the value for the `maxLabelSize` for use in calculating total size. |
|
873 * |
|
874 * @method updateMaxLabelSize |
|
875 * @param {HTMLElement} label to measure |
|
876 * @protected |
|
877 */ |
|
878 updateMaxLabelSize: function(labelWidth, labelHeight) |
|
879 { |
|
880 var host = this, |
|
881 props = this._labelRotationProps, |
|
882 rot = props.rot, |
|
883 absRot = props.absRot, |
|
884 sinRadians = props.sinRadians, |
|
885 cosRadians = props.cosRadians, |
|
886 max; |
|
887 if(rot === 0) |
|
888 { |
|
889 max = labelHeight; |
|
890 } |
|
891 else if(absRot === 90) |
|
892 { |
|
893 max = labelWidth; |
|
894 } |
|
895 else |
|
896 { |
|
897 max = (sinRadians * labelWidth) + (cosRadians * labelHeight); |
|
898 } |
|
899 host._maxLabelSize = Math.max(host._maxLabelSize, max); |
|
900 }, |
|
901 |
|
902 /** |
|
903 * Determines the available label height when the axis width has been explicitly set. |
|
904 * |
|
905 * @method getExplicitlySized |
|
906 * @return Boolean |
|
907 * @protected |
|
908 */ |
|
909 getExplicitlySized: function(styles) |
|
910 { |
|
911 if(this._explicitHeight) |
|
912 { |
|
913 var host = this, |
|
914 h = host._explicitHeight, |
|
915 totalTitleSize = host._totalTitleSize, |
|
916 bottomTickOffset = host.get("bottomTickOffset"), |
|
917 margin = styles.label.margin.right; |
|
918 host._maxLabelSize = h - (bottomTickOffset + margin + totalTitleSize); |
|
919 return true; |
|
920 } |
|
921 return false; |
|
922 }, |
|
923 |
|
924 /** |
|
925 * Rotate and position title. |
|
926 * |
|
927 * @method positionTitle |
|
928 * @param {HTMLElement} label to rotate position |
|
929 * @protected |
|
930 */ |
|
931 positionTitle: function(label) |
|
932 { |
|
933 var host = this, |
|
934 bounds = host._titleBounds, |
|
935 margin = host.get("styles").title.margin, |
|
936 props = host._titleRotationProps, |
|
937 h = bounds.bottom - bounds.top, |
|
938 labelWidth = label.offsetWidth, |
|
939 labelHeight = label.offsetHeight, |
|
940 x = (host.get("width") * 0.5) - (labelWidth * 0.5), |
|
941 y = host.get("height") - labelHeight/2 - h/2; |
|
942 props.labelWidth = labelWidth; |
|
943 props.labelHeight = labelHeight; |
|
944 if(margin && margin.bottom) |
|
945 { |
|
946 y -= margin.bottom; |
|
947 } |
|
948 props.x = x; |
|
949 props.y = y; |
|
950 props.transformOrigin = [0.5, 0.5]; |
|
951 host._rotate(label, props); |
|
952 }, |
|
953 |
|
954 /** |
|
955 * Rotate and position labels. |
|
956 * |
|
957 * @method positionLabel |
|
958 * @param {HTMLElement} label to rotate position |
|
959 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned |
|
960 * against. |
|
961 * @protected |
|
962 */ |
|
963 positionLabel: function(label, pt, styles, i) |
|
964 { |
|
965 var host = this, |
|
966 offset = parseFloat(styles.label.offset), |
|
967 tickOffset = host.get("bottomTickOffset"), |
|
968 labelStyles = styles.label, |
|
969 margin = 0, |
|
970 props = host._labelRotationProps, |
|
971 rot = props.rot, |
|
972 absRot = props.absRot, |
|
973 leftOffset = Math.round(pt.x), |
|
974 topOffset = Math.round(pt.y), |
|
975 labelWidth = host._labelWidths[i], |
|
976 labelHeight = host._labelHeights[i]; |
|
977 if(labelStyles.margin && labelStyles.margin.top) |
|
978 { |
|
979 margin = labelStyles.margin.top; |
|
980 } |
|
981 if(rot === 90) |
|
982 { |
|
983 topOffset -= labelHeight/2 * rot/90; |
|
984 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); |
|
985 } |
|
986 else if(rot === -90) |
|
987 { |
|
988 topOffset -= labelHeight/2 * absRot/90; |
|
989 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); |
|
990 } |
|
991 else if(rot > 0) |
|
992 { |
|
993 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); |
|
994 topOffset -= labelHeight/2 * rot/90; |
|
995 } |
|
996 else if(rot < 0) |
|
997 { |
|
998 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); |
|
999 topOffset -= labelHeight/2 * absRot/90; |
|
1000 } |
|
1001 else |
|
1002 { |
|
1003 leftOffset -= labelWidth * offset; |
|
1004 } |
|
1005 topOffset += margin; |
|
1006 topOffset += tickOffset; |
|
1007 props.labelWidth = labelWidth; |
|
1008 props.labelHeight = labelHeight; |
|
1009 props.x = leftOffset; |
|
1010 props.y = topOffset; |
|
1011 host._rotate(label, props); |
|
1012 }, |
|
1013 |
|
1014 /** |
|
1015 * Adjusts the coordinates of an axis label based on the rotation. |
|
1016 * |
|
1017 * @method _setRotationCoords |
|
1018 * @param {Object} props Coordinates, dimension and rotation properties of the label. |
|
1019 * @protected |
|
1020 */ |
|
1021 _setRotationCoords: function(props) |
|
1022 { |
|
1023 var rot = props.rot, |
|
1024 absRot = props.absRot, |
|
1025 labelWidth = props.labelWidth, |
|
1026 labelHeight = props.labelHeight, |
|
1027 leftOffset, |
|
1028 topOffset; |
|
1029 |
|
1030 if(rot > 0) |
|
1031 { |
|
1032 leftOffset = 0; |
|
1033 topOffset = labelHeight/2 * rot/90; |
|
1034 } |
|
1035 else if(rot < 0) |
|
1036 { |
|
1037 leftOffset = labelWidth; |
|
1038 topOffset = labelHeight/2 * absRot/90; |
|
1039 } |
|
1040 else |
|
1041 { |
|
1042 leftOffset = labelWidth * 0.5; |
|
1043 topOffset = 0; |
|
1044 } |
|
1045 props.x -= leftOffset; |
|
1046 props.y -= topOffset; |
|
1047 }, |
|
1048 |
|
1049 /** |
|
1050 * Returns the transformOrigin to use for an axis label based on the position of the axis |
|
1051 * and the rotation of the label. |
|
1052 * |
|
1053 * @method _getTransformOrigin |
|
1054 * @param {Number} rot The rotation (in degrees) of the label. |
|
1055 * @return Array |
|
1056 * @protected |
|
1057 */ |
|
1058 _getTransformOrigin: function(rot) |
|
1059 { |
|
1060 var transformOrigin; |
|
1061 if(rot > 0) |
|
1062 { |
|
1063 transformOrigin = [0, 0.5]; |
|
1064 } |
|
1065 else if(rot < 0) |
|
1066 { |
|
1067 transformOrigin = [1, 0.5]; |
|
1068 } |
|
1069 else |
|
1070 { |
|
1071 transformOrigin = [0, 0]; |
|
1072 } |
|
1073 return transformOrigin; |
|
1074 }, |
|
1075 |
|
1076 /** |
|
1077 * Adjusts position for inner ticks. |
|
1078 * |
|
1079 * @method offsetNodeForTick |
|
1080 * @param {Node} cb contentBox of the axis |
|
1081 * @protected |
|
1082 */ |
|
1083 offsetNodeForTick: function(cb) |
|
1084 { |
|
1085 var host = this; |
|
1086 cb.setStyle("top", 0 - host.get("topTickOffset")); |
|
1087 }, |
|
1088 |
|
1089 /** |
|
1090 * Assigns a height based on the size of the contents. |
|
1091 * |
|
1092 * @method setCalculatedSize |
|
1093 * @protected |
|
1094 */ |
|
1095 setCalculatedSize: function() |
|
1096 { |
|
1097 var host = this, |
|
1098 styles = host.get("styles"), |
|
1099 labelStyle = styles.label, |
|
1100 totalTitleSize = host._totalTitleSize, |
|
1101 ttl = Math.round(host.get("bottomTickOffset") + host._maxLabelSize + labelStyle.margin.top + totalTitleSize); |
|
1102 if(host._explicitHeight) |
|
1103 { |
|
1104 ttl = host._explicitHeight; |
|
1105 } |
|
1106 host.set("calculatedHeight", ttl); |
|
1107 } |
|
1108 }; |
|
1109 Y.BottomAxisLayout = BottomAxisLayout; |
|
1110 /** |
|
1111 * Contains algorithms for rendering a top axis. |
|
1112 * |
|
1113 * @class TopAxisLayout |
|
1114 * @constructor |
|
1115 * @submodule axis |
|
1116 */ |
|
1117 TopAxisLayout = function(){}; |
|
1118 |
|
1119 TopAxisLayout.prototype = { |
|
1120 /** |
|
1121 * Default margins for text fields. |
|
1122 * |
|
1123 * @private |
|
1124 * @method _getDefaultMargins |
|
1125 * @return Object |
|
1126 */ |
|
1127 _getDefaultMargins: function() |
|
1128 { |
|
1129 return { |
|
1130 top: 0, |
|
1131 left: 0, |
|
1132 right: 0, |
|
1133 bottom: 4 |
|
1134 }; |
|
1135 }, |
|
1136 |
|
1137 /** |
|
1138 * Sets the length of the tick on either side of the axis line. |
|
1139 * |
|
1140 * @method setTickOffsets |
|
1141 * @protected |
|
1142 */ |
|
1143 setTickOffsets: function() |
|
1144 { |
|
1145 var host = this, |
|
1146 majorTicks = host.get("styles").majorTicks, |
|
1147 tickLength = majorTicks.length, |
|
1148 halfTick = tickLength * 0.5, |
|
1149 display = majorTicks.display; |
|
1150 host.set("leftTickOffset", 0); |
|
1151 host.set("rightTickOffset", 0); |
|
1152 switch(display) |
|
1153 { |
|
1154 case "inside" : |
|
1155 host.set("bottomTickOffset", tickLength); |
|
1156 host.set("topTickOffset", 0); |
|
1157 break; |
|
1158 case "outside" : |
|
1159 host.set("bottomTickOffset", 0); |
|
1160 host.set("topTickOffset", tickLength); |
|
1161 break; |
|
1162 case "cross" : |
|
1163 host.set("topTickOffset", halfTick); |
|
1164 host.set("bottomTickOffset", halfTick); |
|
1165 break; |
|
1166 default: |
|
1167 host.set("topTickOffset", 0); |
|
1168 host.set("bottomTickOffset", 0); |
|
1169 break; |
|
1170 } |
|
1171 }, |
|
1172 |
|
1173 /** |
|
1174 * Calculates the coordinates for the first point on an axis. |
|
1175 * |
|
1176 * @method getLineStart |
|
1177 * @protected |
|
1178 */ |
|
1179 getLineStart: function() |
|
1180 { |
|
1181 var host = this, |
|
1182 style = host.get("styles"), |
|
1183 padding = style.padding, |
|
1184 majorTicks = style.majorTicks, |
|
1185 tickLength = majorTicks.length, |
|
1186 display = majorTicks.display, |
|
1187 pt = {x:0, y:padding.top}; |
|
1188 if(display === "outside") |
|
1189 { |
|
1190 pt.y += tickLength; |
|
1191 } |
|
1192 else if(display === "cross") |
|
1193 { |
|
1194 pt.y += tickLength/2; |
|
1195 } |
|
1196 return pt; |
|
1197 }, |
|
1198 |
|
1199 /** |
|
1200 * Draws a tick |
|
1201 * |
|
1202 * @method drawTick |
|
1203 * @param {Path} path reference to the path `Path` element in which to draw the tick. |
|
1204 * @param {Object} pt hash containing x and y coordinates |
|
1205 * @param {Object} tickStyles hash of properties used to draw the tick |
|
1206 * @protected |
|
1207 */ |
|
1208 drawTick: function(path, pt, tickStyles) |
|
1209 { |
|
1210 var host = this, |
|
1211 style = host.get("styles"), |
|
1212 padding = style.padding, |
|
1213 tickLength = tickStyles.length, |
|
1214 start = {x:pt.x, y:padding.top}, |
|
1215 end = {x:pt.x, y:tickLength + padding.top}; |
|
1216 host.drawLine(path, start, end); |
|
1217 }, |
|
1218 |
|
1219 /** |
|
1220 * Calculates the point for a label. |
|
1221 * |
|
1222 * @method getLabelPoint |
|
1223 * @param {Object} pt hash containing x and y coordinates |
|
1224 * @return Object |
|
1225 * @protected |
|
1226 */ |
|
1227 getLabelPoint: function(pt) |
|
1228 { |
|
1229 return {x:pt.x, y:pt.y - this.get("topTickOffset")}; |
|
1230 }, |
|
1231 |
|
1232 /** |
|
1233 * Updates the value for the `maxLabelSize` for use in calculating total size. |
|
1234 * |
|
1235 * @method updateMaxLabelSize |
|
1236 * @param {HTMLElement} label to measure |
|
1237 * @protected |
|
1238 */ |
|
1239 updateMaxLabelSize: function(labelWidth, labelHeight) |
|
1240 { |
|
1241 var host = this, |
|
1242 props = this._labelRotationProps, |
|
1243 rot = props.rot, |
|
1244 absRot = props.absRot, |
|
1245 sinRadians = props.sinRadians, |
|
1246 cosRadians = props.cosRadians, |
|
1247 max; |
|
1248 if(rot === 0) |
|
1249 { |
|
1250 max = labelHeight; |
|
1251 } |
|
1252 else if(absRot === 90) |
|
1253 { |
|
1254 max = labelWidth; |
|
1255 } |
|
1256 else |
|
1257 { |
|
1258 max = (sinRadians * labelWidth) + (cosRadians * labelHeight); |
|
1259 } |
|
1260 host._maxLabelSize = Math.max(host._maxLabelSize, max); |
|
1261 }, |
|
1262 |
|
1263 /** |
|
1264 * Determines the available label height when the axis width has been explicitly set. |
|
1265 * |
|
1266 * @method getExplicitlySized |
|
1267 * @return Boolean |
|
1268 * @protected |
|
1269 */ |
|
1270 getExplicitlySized: function(styles) |
|
1271 { |
|
1272 if(this._explicitHeight) |
|
1273 { |
|
1274 var host = this, |
|
1275 h = host._explicitHeight, |
|
1276 totalTitleSize = host._totalTitleSize, |
|
1277 topTickOffset = host.get("topTickOffset"), |
|
1278 margin = styles.label.margin.right; |
|
1279 host._maxLabelSize = h - (topTickOffset + margin + totalTitleSize); |
|
1280 return true; |
|
1281 } |
|
1282 return false; |
|
1283 }, |
|
1284 |
|
1285 /** |
|
1286 * Rotate and position title. |
|
1287 * |
|
1288 * @method positionTitle |
|
1289 * @param {HTMLElement} label to rotate position |
|
1290 * @protected |
|
1291 */ |
|
1292 positionTitle: function(label) |
|
1293 { |
|
1294 var host = this, |
|
1295 bounds = host._titleBounds, |
|
1296 margin = host.get("styles").title.margin, |
|
1297 props = host._titleRotationProps, |
|
1298 labelWidth = label.offsetWidth, |
|
1299 labelHeight = label.offsetHeight, |
|
1300 h = bounds.bottom - bounds.top, |
|
1301 x = (host.get("width") * 0.5) - (labelWidth * 0.5), |
|
1302 y = h/2 - labelHeight/2; |
|
1303 props.labelWidth = labelWidth; |
|
1304 props.labelHeight = labelHeight; |
|
1305 if(margin && margin.top) |
|
1306 { |
|
1307 y += margin.top; |
|
1308 } |
|
1309 props.x = x; |
|
1310 props.y = y; |
|
1311 props.transformOrigin = [0.5, 0.5]; |
|
1312 host._rotate(label, props); |
|
1313 }, |
|
1314 |
|
1315 /** |
|
1316 * Rotate and position labels. |
|
1317 * |
|
1318 * @method positionLabel |
|
1319 * @param {HTMLElement} label to rotate position |
|
1320 * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned |
|
1321 * against. |
|
1322 * @protected |
|
1323 */ |
|
1324 positionLabel: function(label, pt, styles, i) |
|
1325 { |
|
1326 var host = this, |
|
1327 offset = parseFloat(styles.label.offset), |
|
1328 totalTitleSize = this._totalTitleSize, |
|
1329 maxLabelSize = host._maxLabelSize, |
|
1330 leftOffset = pt.x, |
|
1331 topOffset = pt.y + totalTitleSize + maxLabelSize, |
|
1332 props = this._labelRotationProps, |
|
1333 rot = props.rot, |
|
1334 absRot = props.absRot, |
|
1335 labelWidth = this._labelWidths[i], |
|
1336 labelHeight = this._labelHeights[i]; |
|
1337 if(rot === 0) |
|
1338 { |
|
1339 leftOffset -= labelWidth * offset; |
|
1340 topOffset -= labelHeight; |
|
1341 } |
|
1342 else |
|
1343 { |
|
1344 if(rot === 90) |
|
1345 { |
|
1346 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); |
|
1347 topOffset -= (labelHeight * 0.5); |
|
1348 } |
|
1349 else if (rot === -90) |
|
1350 { |
|
1351 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); |
|
1352 topOffset -= (labelHeight * 0.5); |
|
1353 } |
|
1354 else if(rot > 0) |
|
1355 { |
|
1356 leftOffset = leftOffset - labelWidth + labelHeight/2 - (labelHeight * offset); |
|
1357 topOffset -= labelHeight - (labelHeight * rot/180); |
|
1358 } |
|
1359 else |
|
1360 { |
|
1361 leftOffset = leftOffset + labelHeight/2 - (labelHeight * offset); |
|
1362 topOffset -= labelHeight - (labelHeight * absRot/180); |
|
1363 } |
|
1364 } |
|
1365 props.x = Math.round(leftOffset); |
|
1366 props.y = Math.round(topOffset); |
|
1367 props.labelWidth = labelWidth; |
|
1368 props.labelHeight = labelHeight; |
|
1369 this._rotate(label, props); |
|
1370 }, |
|
1371 |
|
1372 /** |
|
1373 * Adjusts the coordinates of an axis label based on the rotation. |
|
1374 * |
|
1375 * @method _setRotationCoords |
|
1376 * @param {Object} props Coordinates, dimension and rotation properties of the label. |
|
1377 * @protected |
|
1378 */ |
|
1379 _setRotationCoords: function(props) |
|
1380 { |
|
1381 var rot = props.rot, |
|
1382 absRot = props.absRot, |
|
1383 labelWidth = props.labelWidth, |
|
1384 labelHeight = props.labelHeight, |
|
1385 leftOffset, |
|
1386 topOffset; |
|
1387 if(rot === 0) |
|
1388 { |
|
1389 leftOffset = labelWidth * 0.5; |
|
1390 topOffset = labelHeight; |
|
1391 } |
|
1392 else |
|
1393 { |
|
1394 if(rot === 90) |
|
1395 { |
|
1396 leftOffset = labelWidth; |
|
1397 topOffset = (labelHeight * 0.5); |
|
1398 } |
|
1399 else if (rot === -90) |
|
1400 { |
|
1401 topOffset = (labelHeight * 0.5); |
|
1402 } |
|
1403 else if(rot > 0) |
|
1404 { |
|
1405 leftOffset = labelWidth; |
|
1406 topOffset = labelHeight - (labelHeight * rot/180); |
|
1407 } |
|
1408 else |
|
1409 { |
|
1410 topOffset = labelHeight - (labelHeight * absRot/180); |
|
1411 } |
|
1412 } |
|
1413 props.x -= leftOffset; |
|
1414 props.y -= topOffset; |
|
1415 }, |
|
1416 |
|
1417 /** |
|
1418 * Returns the transformOrigin to use for an axis label based on the position of the axis |
|
1419 * and the rotation of the label. |
|
1420 * |
|
1421 * @method _getTransformOrigin |
|
1422 * @param {Number} rot The rotation (in degrees) of the label. |
|
1423 * @return Array |
|
1424 * @protected |
|
1425 */ |
|
1426 _getTransformOrigin: function(rot) |
|
1427 { |
|
1428 var transformOrigin; |
|
1429 if(rot === 0) |
|
1430 { |
|
1431 transformOrigin = [0, 0]; |
|
1432 } |
|
1433 else |
|
1434 { |
|
1435 if(rot === 90) |
|
1436 { |
|
1437 transformOrigin = [1, 0.5]; |
|
1438 } |
|
1439 else if (rot === -90) |
|
1440 { |
|
1441 transformOrigin = [0, 0.5]; |
|
1442 } |
|
1443 else if(rot > 0) |
|
1444 { |
|
1445 transformOrigin = [1, 0.5]; |
|
1446 } |
|
1447 else |
|
1448 { |
|
1449 transformOrigin = [0, 0.5]; |
|
1450 } |
|
1451 } |
|
1452 return transformOrigin; |
|
1453 }, |
|
1454 |
|
1455 /** |
|
1456 * Adjusts position for inner ticks. |
|
1457 * |
|
1458 * @method offsetNodeForTick |
|
1459 * @param {Node} cb contentBox of the axis |
|
1460 * @protected |
|
1461 */ |
|
1462 offsetNodeForTick: function() |
|
1463 { |
|
1464 }, |
|
1465 |
|
1466 /** |
|
1467 * Assigns a height based on the size of the contents. |
|
1468 * |
|
1469 * @method setCalculatedSize |
|
1470 * @protected |
|
1471 */ |
|
1472 setCalculatedSize: function() |
|
1473 { |
|
1474 var host = this, |
|
1475 graphic = host.get("graphic"), |
|
1476 styles = host.get("styles"), |
|
1477 labelMargin = styles.label.margin, |
|
1478 totalLabelSize = labelMargin.bottom + host._maxLabelSize, |
|
1479 totalTitleSize = host._totalTitleSize, |
|
1480 topTickOffset = this.get("topTickOffset"), |
|
1481 ttl = Math.round(topTickOffset + totalLabelSize + totalTitleSize); |
|
1482 if(this._explicitHeight) |
|
1483 { |
|
1484 ttl = this._explicitHeight; |
|
1485 } |
|
1486 host.set("calculatedHeight", ttl); |
|
1487 graphic.set("y", ttl - topTickOffset); |
|
1488 } |
|
1489 }; |
|
1490 Y.TopAxisLayout = TopAxisLayout; |
|
1491 |
|
1492 /** |
|
1493 * An abstract class that provides the core functionality for draw a chart axis. Axis is used by the following classes: |
|
1494 * <ul> |
|
1495 * <li>{{#crossLink "CategoryAxis"}}{{/crossLink}}</li> |
|
1496 * <li>{{#crossLink "NumericAxis"}}{{/crossLink}}</li> |
|
1497 * <li>{{#crossLink "StackedAxis"}}{{/crossLink}}</li> |
|
1498 * <li>{{#crossLink "TimeAxis"}}{{/crossLink}}</li> |
|
1499 * </ul> |
|
1500 * |
|
1501 * @class Axis |
|
1502 * @extends Widget |
|
1503 * @uses AxisBase |
|
1504 * @uses TopAxisLayout |
|
1505 * @uses RightAxisLayout |
|
1506 * @uses BottomAxisLayout |
|
1507 * @uses LeftAxisLayout |
|
1508 * @constructor |
|
1509 * @param {Object} config (optional) Configuration parameters. |
|
1510 * @submodule axis |
|
1511 */ |
|
1512 Y.Axis = Y.Base.create("axis", Y.Widget, [Y.AxisBase], { |
|
1513 /** |
|
1514 * Calculates and returns a value based on the number of labels and the index of |
|
1515 * the current label. |
|
1516 * |
|
1517 * @method getLabelByIndex |
|
1518 * @param {Number} i Index of the label. |
|
1519 * @param {Number} l Total number of labels. |
|
1520 * @return String |
|
1521 */ |
|
1522 getLabelByIndex: function(i, l) |
|
1523 { |
|
1524 var position = this.get("position"), |
|
1525 direction = position === "left" || position === "right" ? "vertical" : "horizontal"; |
|
1526 return this._getLabelByIndex(i, l, direction); |
|
1527 }, |
|
1528 |
|
1529 /** |
|
1530 * @method bindUI |
|
1531 * @private |
|
1532 */ |
|
1533 bindUI: function() |
|
1534 { |
|
1535 this.after("dataReady", Y.bind(this._dataChangeHandler, this)); |
|
1536 this.after("dataUpdate", Y.bind(this._dataChangeHandler, this)); |
|
1537 this.after("stylesChange", this._updateHandler); |
|
1538 this.after("overlapGraphChange", this._updateHandler); |
|
1539 this.after("positionChange", this._positionChangeHandler); |
|
1540 this.after("widthChange", this._handleSizeChange); |
|
1541 this.after("heightChange", this._handleSizeChange); |
|
1542 this.after("calculatedWidthChange", this._handleSizeChange); |
|
1543 this.after("calculatedHeightChange", this._handleSizeChange); |
|
1544 }, |
|
1545 /** |
|
1546 * Storage for calculatedWidth value. |
|
1547 * |
|
1548 * @property _calculatedWidth |
|
1549 * @type Number |
|
1550 * @private |
|
1551 */ |
|
1552 _calculatedWidth: 0, |
|
1553 |
|
1554 /** |
|
1555 * Storage for calculatedHeight value. |
|
1556 * |
|
1557 * @property _calculatedHeight |
|
1558 * @type Number |
|
1559 * @private |
|
1560 */ |
|
1561 _calculatedHeight: 0, |
|
1562 |
|
1563 /** |
|
1564 * Handles change to the dataProvider |
|
1565 * |
|
1566 * @method _dataChangeHandler |
|
1567 * @param {Object} e Event object |
|
1568 * @private |
|
1569 */ |
|
1570 _dataChangeHandler: function() |
|
1571 { |
|
1572 if(this.get("rendered")) |
|
1573 { |
|
1574 this._drawAxis(); |
|
1575 } |
|
1576 }, |
|
1577 |
|
1578 /** |
|
1579 * Handles change to the position attribute |
|
1580 * |
|
1581 * @method _positionChangeHandler |
|
1582 * @param {Object} e Event object |
|
1583 * @private |
|
1584 */ |
|
1585 _positionChangeHandler: function(e) |
|
1586 { |
|
1587 this._updateGraphic(e.newVal); |
|
1588 this._updateHandler(); |
|
1589 }, |
|
1590 |
|
1591 /** |
|
1592 * Updates the the Graphic instance |
|
1593 * |
|
1594 * @method _updateGraphic |
|
1595 * @param {String} position Position of axis |
|
1596 * @private |
|
1597 */ |
|
1598 _updateGraphic: function(position) |
|
1599 { |
|
1600 var graphic = this.get("graphic"); |
|
1601 if(position === "none") |
|
1602 { |
|
1603 if(graphic) |
|
1604 { |
|
1605 graphic.destroy(); |
|
1606 } |
|
1607 } |
|
1608 else |
|
1609 { |
|
1610 if(!graphic) |
|
1611 { |
|
1612 this._setCanvas(); |
|
1613 } |
|
1614 } |
|
1615 }, |
|
1616 |
|
1617 /** |
|
1618 * Handles changes to axis. |
|
1619 * |
|
1620 * @method _updateHandler |
|
1621 * @param {Object} e Event object |
|
1622 * @private |
|
1623 */ |
|
1624 _updateHandler: function() |
|
1625 { |
|
1626 if(this.get("rendered")) |
|
1627 { |
|
1628 this._drawAxis(); |
|
1629 } |
|
1630 }, |
|
1631 |
|
1632 /** |
|
1633 * @method renderUI |
|
1634 * @private |
|
1635 */ |
|
1636 renderUI: function() |
|
1637 { |
|
1638 this._updateGraphic(this.get("position")); |
|
1639 }, |
|
1640 |
|
1641 /** |
|
1642 * @method syncUI |
|
1643 * @private |
|
1644 */ |
|
1645 syncUI: function() |
|
1646 { |
|
1647 var layout = this._layout, |
|
1648 defaultMargins, |
|
1649 styles, |
|
1650 label, |
|
1651 title, |
|
1652 i; |
|
1653 if(layout) |
|
1654 { |
|
1655 defaultMargins = layout._getDefaultMargins(); |
|
1656 styles = this.get("styles"); |
|
1657 label = styles.label.margin; |
|
1658 title =styles.title.margin; |
|
1659 //need to defaultMargins method to the layout classes. |
|
1660 for(i in defaultMargins) |
|
1661 { |
|
1662 if(defaultMargins.hasOwnProperty(i)) |
|
1663 { |
|
1664 label[i] = label[i] === undefined ? defaultMargins[i] : label[i]; |
|
1665 title[i] = title[i] === undefined ? defaultMargins[i] : title[i]; |
|
1666 } |
|
1667 } |
|
1668 } |
|
1669 this._drawAxis(); |
|
1670 }, |
|
1671 |
|
1672 /** |
|
1673 * Creates a graphic instance to be used for the axis line and ticks. |
|
1674 * |
|
1675 * @method _setCanvas |
|
1676 * @private |
|
1677 */ |
|
1678 _setCanvas: function() |
|
1679 { |
|
1680 var cb = this.get("contentBox"), |
|
1681 bb = this.get("boundingBox"), |
|
1682 p = this.get("position"), |
|
1683 pn = this._parentNode, |
|
1684 w = this.get("width"), |
|
1685 h = this.get("height"); |
|
1686 bb.setStyle("position", "absolute"); |
|
1687 bb.setStyle("zIndex", 2); |
|
1688 w = w ? w + "px" : pn.getStyle("width"); |
|
1689 h = h ? h + "px" : pn.getStyle("height"); |
|
1690 if(p === "top" || p === "bottom") |
|
1691 { |
|
1692 cb.setStyle("width", w); |
|
1693 } |
|
1694 else |
|
1695 { |
|
1696 cb.setStyle("height", h); |
|
1697 } |
|
1698 cb.setStyle("position", "relative"); |
|
1699 cb.setStyle("left", "0px"); |
|
1700 cb.setStyle("top", "0px"); |
|
1701 this.set("graphic", new Y.Graphic()); |
|
1702 this.get("graphic").render(cb); |
|
1703 }, |
|
1704 |
|
1705 /** |
|
1706 * Gets the default value for the `styles` attribute. Overrides |
|
1707 * base implementation. |
|
1708 * |
|
1709 * @method _getDefaultStyles |
|
1710 * @return Object |
|
1711 * @protected |
|
1712 */ |
|
1713 _getDefaultStyles: function() |
|
1714 { |
|
1715 var axisstyles = { |
|
1716 majorTicks: { |
|
1717 display:"inside", |
|
1718 length:4, |
|
1719 color:"#dad8c9", |
|
1720 weight:1, |
|
1721 alpha:1 |
|
1722 }, |
|
1723 minorTicks: { |
|
1724 display:"none", |
|
1725 length:2, |
|
1726 color:"#dad8c9", |
|
1727 weight:1 |
|
1728 }, |
|
1729 line: { |
|
1730 weight:1, |
|
1731 color:"#dad8c9", |
|
1732 alpha:1 |
|
1733 }, |
|
1734 majorUnit: { |
|
1735 determinant:"count", |
|
1736 count:11, |
|
1737 distance:75 |
|
1738 }, |
|
1739 top: "0px", |
|
1740 left: "0px", |
|
1741 width: "100px", |
|
1742 height: "100px", |
|
1743 label: { |
|
1744 color:"#808080", |
|
1745 alpha: 1, |
|
1746 fontSize:"85%", |
|
1747 rotation: 0, |
|
1748 offset: 0.5, |
|
1749 margin: { |
|
1750 top: undefined, |
|
1751 right: undefined, |
|
1752 bottom: undefined, |
|
1753 left: undefined |
|
1754 } |
|
1755 }, |
|
1756 title: { |
|
1757 color:"#808080", |
|
1758 alpha: 1, |
|
1759 fontSize:"85%", |
|
1760 rotation: undefined, |
|
1761 margin: { |
|
1762 top: undefined, |
|
1763 right: undefined, |
|
1764 bottom: undefined, |
|
1765 left: undefined |
|
1766 } |
|
1767 }, |
|
1768 hideOverlappingLabelTicks: false |
|
1769 }; |
|
1770 |
|
1771 return Y.merge(Y.Renderer.prototype._getDefaultStyles(), axisstyles); |
|
1772 }, |
|
1773 |
|
1774 /** |
|
1775 * Updates the axis when the size changes. |
|
1776 * |
|
1777 * @method _handleSizeChange |
|
1778 * @param {Object} e Event object. |
|
1779 * @private |
|
1780 */ |
|
1781 _handleSizeChange: function(e) |
|
1782 { |
|
1783 var attrName = e.attrName, |
|
1784 pos = this.get("position"), |
|
1785 vert = pos === "left" || pos === "right", |
|
1786 cb = this.get("contentBox"), |
|
1787 hor = pos === "bottom" || pos === "top"; |
|
1788 cb.setStyle("width", this.get("width")); |
|
1789 cb.setStyle("height", this.get("height")); |
|
1790 if((hor && attrName === "width") || (vert && attrName === "height")) |
|
1791 { |
|
1792 this._drawAxis(); |
|
1793 } |
|
1794 }, |
|
1795 |
|
1796 /** |
|
1797 * Maps key values to classes containing layout algorithms |
|
1798 * |
|
1799 * @property _layoutClasses |
|
1800 * @type Object |
|
1801 * @private |
|
1802 */ |
|
1803 _layoutClasses: |
|
1804 { |
|
1805 top : TopAxisLayout, |
|
1806 bottom: BottomAxisLayout, |
|
1807 left: LeftAxisLayout, |
|
1808 right : RightAxisLayout |
|
1809 }, |
|
1810 |
|
1811 /** |
|
1812 * Draws a line segment between 2 points |
|
1813 * |
|
1814 * @method drawLine |
|
1815 * @param {Object} startPoint x and y coordinates for the start point of the line segment |
|
1816 * @param {Object} endPoint x and y coordinates for the for the end point of the line segment |
|
1817 * @param {Object} line styles (weight, color and alpha to be applied to the line segment) |
|
1818 * @private |
|
1819 */ |
|
1820 drawLine: function(path, startPoint, endPoint) |
|
1821 { |
|
1822 path.moveTo(startPoint.x, startPoint.y); |
|
1823 path.lineTo(endPoint.x, endPoint.y); |
|
1824 }, |
|
1825 |
|
1826 /** |
|
1827 * Generates the properties necessary for rotating and positioning a text field. |
|
1828 * |
|
1829 * @method _getTextRotationProps |
|
1830 * @param {Object} styles properties for the text field |
|
1831 * @return Object |
|
1832 * @private |
|
1833 */ |
|
1834 _getTextRotationProps: function(styles) |
|
1835 { |
|
1836 if(styles.rotation === undefined) |
|
1837 { |
|
1838 switch(this.get("position")) |
|
1839 { |
|
1840 case "left" : |
|
1841 styles.rotation = -90; |
|
1842 break; |
|
1843 case "right" : |
|
1844 styles.rotation = 90; |
|
1845 break; |
|
1846 default : |
|
1847 styles.rotation = 0; |
|
1848 break; |
|
1849 } |
|
1850 } |
|
1851 var rot = Math.min(90, Math.max(-90, styles.rotation)), |
|
1852 absRot = Math.abs(rot), |
|
1853 radCon = Math.PI/180, |
|
1854 sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)), |
|
1855 cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)); |
|
1856 return { |
|
1857 rot: rot, |
|
1858 absRot: absRot, |
|
1859 radCon: radCon, |
|
1860 sinRadians: sinRadians, |
|
1861 cosRadians: cosRadians, |
|
1862 textAlpha: styles.alpha |
|
1863 }; |
|
1864 }, |
|
1865 |
|
1866 /** |
|
1867 * Draws an axis. |
|
1868 * |
|
1869 * @method _drawAxis |
|
1870 * @private |
|
1871 */ |
|
1872 _drawAxis: function () |
|
1873 { |
|
1874 if(this._drawing) |
|
1875 { |
|
1876 this._callLater = true; |
|
1877 return; |
|
1878 } |
|
1879 this._drawing = true; |
|
1880 this._callLater = false; |
|
1881 if(this._layout) |
|
1882 { |
|
1883 var styles = this.get("styles"), |
|
1884 line = styles.line, |
|
1885 labelStyles = styles.label, |
|
1886 majorTickStyles = styles.majorTicks, |
|
1887 drawTicks = majorTickStyles.display !== "none", |
|
1888 len, |
|
1889 i = 0, |
|
1890 layout = this._layout, |
|
1891 layoutLength, |
|
1892 lineStart, |
|
1893 label, |
|
1894 labelWidth, |
|
1895 labelHeight, |
|
1896 labelFunction = this.get("labelFunction"), |
|
1897 labelFunctionScope = this.get("labelFunctionScope"), |
|
1898 labelFormat = this.get("labelFormat"), |
|
1899 graphic = this.get("graphic"), |
|
1900 path = this.get("path"), |
|
1901 tickPath, |
|
1902 explicitlySized, |
|
1903 position = this.get("position"), |
|
1904 labelData, |
|
1905 labelValues, |
|
1906 point, |
|
1907 points, |
|
1908 firstPoint, |
|
1909 lastPoint, |
|
1910 firstLabel, |
|
1911 lastLabel, |
|
1912 staticCoord, |
|
1913 dynamicCoord, |
|
1914 edgeOffset, |
|
1915 explicitLabels = this._labelValuesExplicitlySet ? this.get("labelValues") : null, |
|
1916 direction = (position === "left" || position === "right") ? "vertical" : "horizontal"; |
|
1917 this._labelWidths = []; |
|
1918 this._labelHeights = []; |
|
1919 graphic.set("autoDraw", false); |
|
1920 path.clear(); |
|
1921 path.set("stroke", { |
|
1922 weight: line.weight, |
|
1923 color: line.color, |
|
1924 opacity: line.alpha |
|
1925 }); |
|
1926 this._labelRotationProps = this._getTextRotationProps(labelStyles); |
|
1927 this._labelRotationProps.transformOrigin = layout._getTransformOrigin(this._labelRotationProps.rot); |
|
1928 layout.setTickOffsets.apply(this); |
|
1929 layoutLength = this.getLength(); |
|
1930 |
|
1931 len = this.getTotalMajorUnits(); |
|
1932 edgeOffset = this.getEdgeOffset(len, layoutLength); |
|
1933 this.set("edgeOffset", edgeOffset); |
|
1934 lineStart = layout.getLineStart.apply(this); |
|
1935 |
|
1936 if(direction === "vertical") |
|
1937 { |
|
1938 staticCoord = "x"; |
|
1939 dynamicCoord = "y"; |
|
1940 } |
|
1941 else |
|
1942 { |
|
1943 staticCoord = "y"; |
|
1944 dynamicCoord = "x"; |
|
1945 } |
|
1946 |
|
1947 labelData = this._getLabelData( |
|
1948 lineStart[staticCoord], |
|
1949 staticCoord, |
|
1950 dynamicCoord, |
|
1951 this.get("minimum"), |
|
1952 this.get("maximum"), |
|
1953 edgeOffset, |
|
1954 layoutLength - edgeOffset - edgeOffset, |
|
1955 len, |
|
1956 explicitLabels |
|
1957 ); |
|
1958 |
|
1959 points = labelData.points; |
|
1960 labelValues = labelData.values; |
|
1961 len = points.length; |
|
1962 if(!this._labelValuesExplicitlySet) |
|
1963 { |
|
1964 this.set("labelValues", labelValues, {src: "internal"}); |
|
1965 } |
|
1966 |
|
1967 //Don't create the last label or tick. |
|
1968 if(this.get("hideFirstMajorUnit")) |
|
1969 { |
|
1970 firstPoint = points.shift(); |
|
1971 firstLabel = labelValues.shift(); |
|
1972 len = len - 1; |
|
1973 } |
|
1974 |
|
1975 //Don't create the last label or tick. |
|
1976 if(this.get("hideLastMajorUnit")) |
|
1977 { |
|
1978 lastPoint = points.pop(); |
|
1979 lastLabel = labelValues.pop(); |
|
1980 len = len - 1; |
|
1981 } |
|
1982 |
|
1983 if(len < 1) |
|
1984 { |
|
1985 this._clearLabelCache(); |
|
1986 } |
|
1987 else |
|
1988 { |
|
1989 this.drawLine(path, lineStart, this.getLineEnd(lineStart)); |
|
1990 if(drawTicks) |
|
1991 { |
|
1992 tickPath = this.get("tickPath"); |
|
1993 tickPath.clear(); |
|
1994 tickPath.set("stroke", { |
|
1995 weight: majorTickStyles.weight, |
|
1996 color: majorTickStyles.color, |
|
1997 opacity: majorTickStyles.alpha |
|
1998 }); |
|
1999 for(i = 0; i < len; i = i + 1) |
|
2000 { |
|
2001 point = points[i]; |
|
2002 if(point) |
|
2003 { |
|
2004 layout.drawTick.apply(this, [tickPath, points[i], majorTickStyles]); |
|
2005 } |
|
2006 } |
|
2007 } |
|
2008 this._createLabelCache(); |
|
2009 this._maxLabelSize = 0; |
|
2010 this._totalTitleSize = 0; |
|
2011 this._titleSize = 0; |
|
2012 this._setTitle(); |
|
2013 explicitlySized = layout.getExplicitlySized.apply(this, [styles]); |
|
2014 for(i = 0; i < len; i = i + 1) |
|
2015 { |
|
2016 point = points[i]; |
|
2017 if(point) |
|
2018 { |
|
2019 label = this.getLabel(labelStyles); |
|
2020 this._labels.push(label); |
|
2021 this.get("appendLabelFunction")(label, labelFunction.apply(labelFunctionScope, [labelValues[i], labelFormat])); |
|
2022 labelWidth = Math.round(label.offsetWidth); |
|
2023 labelHeight = Math.round(label.offsetHeight); |
|
2024 if(!explicitlySized) |
|
2025 { |
|
2026 this._layout.updateMaxLabelSize.apply(this, [labelWidth, labelHeight]); |
|
2027 } |
|
2028 this._labelWidths.push(labelWidth); |
|
2029 this._labelHeights.push(labelHeight); |
|
2030 } |
|
2031 } |
|
2032 this._clearLabelCache(); |
|
2033 if(this.get("overlapGraph")) |
|
2034 { |
|
2035 layout.offsetNodeForTick.apply(this, [this.get("contentBox")]); |
|
2036 } |
|
2037 layout.setCalculatedSize.apply(this); |
|
2038 if(this._titleTextField) |
|
2039 { |
|
2040 this._layout.positionTitle.apply(this, [this._titleTextField]); |
|
2041 } |
|
2042 len = this._labels.length; |
|
2043 for(i = 0; i < len; ++i) |
|
2044 { |
|
2045 layout.positionLabel.apply(this, [this.get("labels")[i], points[i], styles, i]); |
|
2046 } |
|
2047 if(firstPoint) |
|
2048 { |
|
2049 points.unshift(firstPoint); |
|
2050 } |
|
2051 if(lastPoint) |
|
2052 { |
|
2053 points.push(lastPoint); |
|
2054 } |
|
2055 if(firstLabel) |
|
2056 { |
|
2057 labelValues.unshift(firstLabel); |
|
2058 } |
|
2059 if(lastLabel) |
|
2060 { |
|
2061 labelValues.push(lastLabel); |
|
2062 } |
|
2063 this._tickPoints = points; |
|
2064 } |
|
2065 } |
|
2066 this._drawing = false; |
|
2067 if(this._callLater) |
|
2068 { |
|
2069 this._drawAxis(); |
|
2070 } |
|
2071 else |
|
2072 { |
|
2073 this._updatePathElement(); |
|
2074 this.fire("axisRendered"); |
|
2075 } |
|
2076 }, |
|
2077 |
|
2078 /** |
|
2079 * Calculates and sets the total size of a title. |
|
2080 * |
|
2081 * @method _setTotalTitleSize |
|
2082 * @param {Object} styles Properties for the title field. |
|
2083 * @private |
|
2084 */ |
|
2085 _setTotalTitleSize: function(styles) |
|
2086 { |
|
2087 var title = this._titleTextField, |
|
2088 w = title.offsetWidth, |
|
2089 h = title.offsetHeight, |
|
2090 rot = this._titleRotationProps.rot, |
|
2091 bounds, |
|
2092 size, |
|
2093 margin = styles.margin, |
|
2094 position = this.get("position"), |
|
2095 matrix = new Y.Matrix(); |
|
2096 matrix.rotate(rot); |
|
2097 bounds = matrix.getContentRect(w, h); |
|
2098 if(position === "left" || position === "right") |
|
2099 { |
|
2100 size = bounds.right - bounds.left; |
|
2101 if(margin) |
|
2102 { |
|
2103 size += margin.left + margin.right; |
|
2104 } |
|
2105 } |
|
2106 else |
|
2107 { |
|
2108 size = bounds.bottom - bounds.top; |
|
2109 if(margin) |
|
2110 { |
|
2111 size += margin.top + margin.bottom; |
|
2112 } |
|
2113 } |
|
2114 this._titleBounds = bounds; |
|
2115 this._totalTitleSize = size; |
|
2116 }, |
|
2117 |
|
2118 /** |
|
2119 * Updates path. |
|
2120 * |
|
2121 * @method _updatePathElement |
|
2122 * @private |
|
2123 */ |
|
2124 _updatePathElement: function() |
|
2125 { |
|
2126 var path = this._path, |
|
2127 tickPath = this._tickPath, |
|
2128 redrawGraphic = false, |
|
2129 graphic = this.get("graphic"); |
|
2130 if(path) |
|
2131 { |
|
2132 redrawGraphic = true; |
|
2133 path.end(); |
|
2134 } |
|
2135 if(tickPath) |
|
2136 { |
|
2137 redrawGraphic = true; |
|
2138 tickPath.end(); |
|
2139 } |
|
2140 if(redrawGraphic) |
|
2141 { |
|
2142 graphic._redraw(); |
|
2143 } |
|
2144 }, |
|
2145 |
|
2146 /** |
|
2147 * Updates the content and style properties for a title field. |
|
2148 * |
|
2149 * @method _updateTitle |
|
2150 * @private |
|
2151 */ |
|
2152 _setTitle: function() |
|
2153 { |
|
2154 var i, |
|
2155 styles, |
|
2156 customStyles, |
|
2157 title = this.get("title"), |
|
2158 titleTextField = this._titleTextField, |
|
2159 parentNode; |
|
2160 if(title !== null && title !== undefined) |
|
2161 { |
|
2162 customStyles = { |
|
2163 rotation: "rotation", |
|
2164 margin: "margin", |
|
2165 alpha: "alpha" |
|
2166 }; |
|
2167 styles = this.get("styles").title; |
|
2168 if(!titleTextField) |
|
2169 { |
|
2170 titleTextField = DOCUMENT.createElement('span'); |
|
2171 titleTextField.style.display = "block"; |
|
2172 titleTextField.style.whiteSpace = "nowrap"; |
|
2173 titleTextField.setAttribute("class", "axisTitle"); |
|
2174 this.get("contentBox").append(titleTextField); |
|
2175 } |
|
2176 else if(!DOCUMENT.createElementNS) |
|
2177 { |
|
2178 if(titleTextField.style.filter) |
|
2179 { |
|
2180 titleTextField.style.filter = null; |
|
2181 } |
|
2182 } |
|
2183 titleTextField.style.position = "absolute"; |
|
2184 for(i in styles) |
|
2185 { |
|
2186 if(styles.hasOwnProperty(i) && !customStyles.hasOwnProperty(i)) |
|
2187 { |
|
2188 titleTextField.style[i] = styles[i]; |
|
2189 } |
|
2190 } |
|
2191 this.get("appendTitleFunction")(titleTextField, title); |
|
2192 this._titleTextField = titleTextField; |
|
2193 this._titleRotationProps = this._getTextRotationProps(styles); |
|
2194 this._setTotalTitleSize(styles); |
|
2195 } |
|
2196 else if(titleTextField) |
|
2197 { |
|
2198 parentNode = titleTextField.parentNode; |
|
2199 if(parentNode) |
|
2200 { |
|
2201 parentNode.removeChild(titleTextField); |
|
2202 } |
|
2203 this._titleTextField = null; |
|
2204 this._totalTitleSize = 0; |
|
2205 } |
|
2206 }, |
|
2207 |
|
2208 /** |
|
2209 * Creates or updates an axis label. |
|
2210 * |
|
2211 * @method getLabel |
|
2212 * @param {Object} styles styles applied to label |
|
2213 * @return HTMLElement |
|
2214 * @private |
|
2215 */ |
|
2216 getLabel: function(styles) |
|
2217 { |
|
2218 var i, |
|
2219 label, |
|
2220 labelCache = this._labelCache, |
|
2221 customStyles = { |
|
2222 rotation: "rotation", |
|
2223 margin: "margin", |
|
2224 alpha: "alpha" |
|
2225 }; |
|
2226 if(labelCache && labelCache.length > 0) |
|
2227 { |
|
2228 label = labelCache.shift(); |
|
2229 } |
|
2230 else |
|
2231 { |
|
2232 label = DOCUMENT.createElement("span"); |
|
2233 label.className = Y.Lang.trim([label.className, "axisLabel"].join(' ')); |
|
2234 this.get("contentBox").append(label); |
|
2235 } |
|
2236 if(!DOCUMENT.createElementNS) |
|
2237 { |
|
2238 if(label.style.filter) |
|
2239 { |
|
2240 label.style.filter = null; |
|
2241 } |
|
2242 } |
|
2243 label.style.display = "block"; |
|
2244 label.style.whiteSpace = "nowrap"; |
|
2245 label.style.position = "absolute"; |
|
2246 for(i in styles) |
|
2247 { |
|
2248 if(styles.hasOwnProperty(i) && !customStyles.hasOwnProperty(i)) |
|
2249 { |
|
2250 label.style[i] = styles[i]; |
|
2251 } |
|
2252 } |
|
2253 return label; |
|
2254 }, |
|
2255 |
|
2256 /** |
|
2257 * Creates a cache of labels that can be re-used when the axis redraws. |
|
2258 * |
|
2259 * @method _createLabelCache |
|
2260 * @private |
|
2261 */ |
|
2262 _createLabelCache: function() |
|
2263 { |
|
2264 if(this._labels) |
|
2265 { |
|
2266 while(this._labels.length > 0) |
|
2267 { |
|
2268 this._labelCache.push(this._labels.shift()); |
|
2269 } |
|
2270 } |
|
2271 else |
|
2272 { |
|
2273 this._clearLabelCache(); |
|
2274 } |
|
2275 this._labels = []; |
|
2276 }, |
|
2277 |
|
2278 /** |
|
2279 * Removes axis labels from the dom and clears the label cache. |
|
2280 * |
|
2281 * @method _clearLabelCache |
|
2282 * @private |
|
2283 */ |
|
2284 _clearLabelCache: function() |
|
2285 { |
|
2286 if(this._labelCache) |
|
2287 { |
|
2288 var len = this._labelCache.length, |
|
2289 i = 0, |
|
2290 label; |
|
2291 for(; i < len; ++i) |
|
2292 { |
|
2293 label = this._labelCache[i]; |
|
2294 this._removeChildren(label); |
|
2295 Y.Event.purgeElement(label, true); |
|
2296 label.parentNode.removeChild(label); |
|
2297 } |
|
2298 } |
|
2299 this._labelCache = []; |
|
2300 }, |
|
2301 |
|
2302 /** |
|
2303 * Gets the end point of an axis. |
|
2304 * |
|
2305 * @method getLineEnd |
|
2306 * @return Object |
|
2307 * @private |
|
2308 */ |
|
2309 getLineEnd: function(pt) |
|
2310 { |
|
2311 var w = this.get("width"), |
|
2312 h = this.get("height"), |
|
2313 pos = this.get("position"); |
|
2314 if(pos === "top" || pos === "bottom") |
|
2315 { |
|
2316 return {x:w, y:pt.y}; |
|
2317 } |
|
2318 else |
|
2319 { |
|
2320 return {x:pt.x, y:h}; |
|
2321 } |
|
2322 }, |
|
2323 |
|
2324 /** |
|
2325 * Calcuates the width or height of an axis depending on its direction. |
|
2326 * |
|
2327 * @method getLength |
|
2328 * @return Number |
|
2329 * @private |
|
2330 */ |
|
2331 getLength: function() |
|
2332 { |
|
2333 var l, |
|
2334 style = this.get("styles"), |
|
2335 padding = style.padding, |
|
2336 w = this.get("width"), |
|
2337 h = this.get("height"), |
|
2338 pos = this.get("position"); |
|
2339 if(pos === "top" || pos === "bottom") |
|
2340 { |
|
2341 l = w - (padding.left + padding.right); |
|
2342 } |
|
2343 else |
|
2344 { |
|
2345 l = h - (padding.top + padding.bottom); |
|
2346 } |
|
2347 return l; |
|
2348 }, |
|
2349 |
|
2350 /** |
|
2351 * Gets the position of the first point on an axis. |
|
2352 * |
|
2353 * @method getFirstPoint |
|
2354 * @param {Object} pt Object containing x and y coordinates. |
|
2355 * @return Object |
|
2356 * @private |
|
2357 */ |
|
2358 getFirstPoint:function(pt) |
|
2359 { |
|
2360 var style = this.get("styles"), |
|
2361 pos = this.get("position"), |
|
2362 padding = style.padding, |
|
2363 np = {x:pt.x, y:pt.y}; |
|
2364 if(pos === "top" || pos === "bottom") |
|
2365 { |
|
2366 np.x += padding.left + this.get("edgeOffset"); |
|
2367 } |
|
2368 else |
|
2369 { |
|
2370 np.y += this.get("height") - (padding.top + this.get("edgeOffset")); |
|
2371 } |
|
2372 return np; |
|
2373 }, |
|
2374 |
|
2375 /** |
|
2376 * Rotates and positions a text field. |
|
2377 * |
|
2378 * @method _rotate |
|
2379 * @param {HTMLElement} label text field to rotate and position |
|
2380 * @param {Object} props properties to be applied to the text field. |
|
2381 * @private |
|
2382 */ |
|
2383 _rotate: function(label, props) |
|
2384 { |
|
2385 var rot = props.rot, |
|
2386 x = props.x, |
|
2387 y = props.y, |
|
2388 filterString, |
|
2389 textAlpha, |
|
2390 matrix = new Y.Matrix(), |
|
2391 transformOrigin = props.transformOrigin || [0, 0], |
|
2392 offsetRect; |
|
2393 if(DOCUMENT.createElementNS) |
|
2394 { |
|
2395 matrix.translate(x, y); |
|
2396 matrix.rotate(rot); |
|
2397 Y_DOM.setStyle(label, "transformOrigin", (transformOrigin[0] * 100) + "% " + (transformOrigin[1] * 100) + "%"); |
|
2398 Y_DOM.setStyle(label, "transform", matrix.toCSSText()); |
|
2399 } |
|
2400 else |
|
2401 { |
|
2402 textAlpha = props.textAlpha; |
|
2403 if(Y_Lang.isNumber(textAlpha) && textAlpha < 1 && textAlpha > -1 && !isNaN(textAlpha)) |
|
2404 { |
|
2405 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(textAlpha * 100) + ")"; |
|
2406 } |
|
2407 if(rot !== 0) |
|
2408 { |
|
2409 //ms filters kind of, sort of uses a transformOrigin of 0, 0. |
|
2410 //we'll translate the difference to create a true 0, 0 origin. |
|
2411 matrix.rotate(rot); |
|
2412 offsetRect = matrix.getContentRect(props.labelWidth, props.labelHeight); |
|
2413 matrix.init(); |
|
2414 matrix.translate(offsetRect.left, offsetRect.top); |
|
2415 matrix.translate(x, y); |
|
2416 this._simulateRotateWithTransformOrigin(matrix, rot, transformOrigin, props.labelWidth, props.labelHeight); |
|
2417 if(filterString) |
|
2418 { |
|
2419 filterString += " "; |
|
2420 } |
|
2421 else |
|
2422 { |
|
2423 filterString = ""; |
|
2424 } |
|
2425 filterString += matrix.toFilterText(); |
|
2426 label.style.left = matrix.dx + "px"; |
|
2427 label.style.top = matrix.dy + "px"; |
|
2428 } |
|
2429 else |
|
2430 { |
|
2431 label.style.left = x + "px"; |
|
2432 label.style.top = y + "px"; |
|
2433 } |
|
2434 if(filterString) |
|
2435 { |
|
2436 label.style.filter = filterString; |
|
2437 } |
|
2438 } |
|
2439 }, |
|
2440 |
|
2441 /** |
|
2442 * Simulates a rotation with a specified transformOrigin. |
|
2443 * |
|
2444 * @method _simulateTransformOrigin |
|
2445 * @param {Matrix} matrix Reference to a `Matrix` instance. |
|
2446 * @param {Number} rot The rotation (in degrees) that will be performed on a matrix. |
|
2447 * @param {Array} transformOrigin An array represeniting the origin in which to perform the transform. The first |
|
2448 * index represents the x origin and the second index represents the y origin. |
|
2449 * @param {Number} w The width of the object that will be transformed. |
|
2450 * @param {Number} h The height of the object that will be transformed. |
|
2451 * @private |
|
2452 */ |
|
2453 _simulateRotateWithTransformOrigin: function(matrix, rot, transformOrigin, w, h) |
|
2454 { |
|
2455 var transformX = transformOrigin[0] * w, |
|
2456 transformY = transformOrigin[1] * h; |
|
2457 transformX = !isNaN(transformX) ? transformX : 0; |
|
2458 transformY = !isNaN(transformY) ? transformY : 0; |
|
2459 matrix.translate(transformX, transformY); |
|
2460 matrix.rotate(rot); |
|
2461 matrix.translate(-transformX, -transformY); |
|
2462 }, |
|
2463 |
|
2464 /** |
|
2465 * Returns the coordinates (top, right, bottom, left) for the bounding box of the last label. |
|
2466 * |
|
2467 * @method getMaxLabelBounds |
|
2468 * @return Object |
|
2469 */ |
|
2470 getMaxLabelBounds: function() |
|
2471 { |
|
2472 return this._getLabelBounds(this.getMaximumValue()); |
|
2473 }, |
|
2474 |
|
2475 /** |
|
2476 * Returns the coordinates (top, right, bottom, left) for the bounding box of the first label. |
|
2477 * |
|
2478 * @method getMinLabelBounds |
|
2479 * @return Object |
|
2480 */ |
|
2481 getMinLabelBounds: function() |
|
2482 { |
|
2483 return this._getLabelBounds(this.getMinimumValue()); |
|
2484 }, |
|
2485 |
|
2486 /** |
|
2487 * Returns the coordinates (top, right, bottom, left) for the bounding box of a label. |
|
2488 * |
|
2489 * @method _getLabelBounds |
|
2490 * @param {String} Value of the label |
|
2491 * @return Object |
|
2492 * @private |
|
2493 */ |
|
2494 _getLabelBounds: function(val) |
|
2495 { |
|
2496 var layout = this._layout, |
|
2497 labelStyles = this.get("styles").label, |
|
2498 matrix = new Y.Matrix(), |
|
2499 label, |
|
2500 props = this._getTextRotationProps(labelStyles); |
|
2501 props.transformOrigin = layout._getTransformOrigin(props.rot); |
|
2502 label = this.getLabel(labelStyles); |
|
2503 this.get("appendLabelFunction")(label, this.get("labelFunction").apply(this, [val, this.get("labelFormat")])); |
|
2504 props.labelWidth = label.offsetWidth; |
|
2505 props.labelHeight = label.offsetHeight; |
|
2506 this._removeChildren(label); |
|
2507 Y.Event.purgeElement(label, true); |
|
2508 label.parentNode.removeChild(label); |
|
2509 props.x = 0; |
|
2510 props.y = 0; |
|
2511 layout._setRotationCoords(props); |
|
2512 matrix.translate(props.x, props.y); |
|
2513 this._simulateRotateWithTransformOrigin(matrix, props.rot, props.transformOrigin, props.labelWidth, props.labelHeight); |
|
2514 return matrix.getContentRect(props.labelWidth, props.labelHeight); |
|
2515 }, |
|
2516 |
|
2517 /** |
|
2518 * Removes all DOM elements from an HTML element. Used to clear out labels during detruction |
|
2519 * phase. |
|
2520 * |
|
2521 * @method _removeChildren |
|
2522 * @private |
|
2523 */ |
|
2524 _removeChildren: function(node) |
|
2525 { |
|
2526 if(node.hasChildNodes()) |
|
2527 { |
|
2528 var child; |
|
2529 while(node.firstChild) |
|
2530 { |
|
2531 child = node.firstChild; |
|
2532 this._removeChildren(child); |
|
2533 node.removeChild(child); |
|
2534 } |
|
2535 } |
|
2536 }, |
|
2537 |
|
2538 /** |
|
2539 * Destructor implementation Axis class. Removes all labels and the Graphic instance from the widget. |
|
2540 * |
|
2541 * @method destructor |
|
2542 * @protected |
|
2543 */ |
|
2544 destructor: function() |
|
2545 { |
|
2546 var cb = this.get("contentBox").getDOMNode(), |
|
2547 labels = this.get("labels"), |
|
2548 graphic = this.get("graphic"), |
|
2549 label, |
|
2550 len = labels ? labels.length : 0; |
|
2551 if(len > 0) |
|
2552 { |
|
2553 while(labels.length > 0) |
|
2554 { |
|
2555 label = labels.shift(); |
|
2556 this._removeChildren(label); |
|
2557 cb.removeChild(label); |
|
2558 label = null; |
|
2559 } |
|
2560 } |
|
2561 if(graphic) |
|
2562 { |
|
2563 graphic.destroy(); |
|
2564 } |
|
2565 }, |
|
2566 |
|
2567 /** |
|
2568 * Length in pixels of largest text bounding box. Used to calculate the height of the axis. |
|
2569 * |
|
2570 * @property maxLabelSize |
|
2571 * @type Number |
|
2572 * @protected |
|
2573 */ |
|
2574 _maxLabelSize: 0, |
|
2575 |
|
2576 /** |
|
2577 * Updates the content of text field. This method writes a value into a text field using |
|
2578 * `appendChild`. If the value is a `String`, it is converted to a `TextNode` first. |
|
2579 * |
|
2580 * @method _setText |
|
2581 * @param label {HTMLElement} label to be updated |
|
2582 * @param val {String} value with which to update the label |
|
2583 * @private |
|
2584 */ |
|
2585 _setText: function(textField, val) |
|
2586 { |
|
2587 textField.innerHTML = ""; |
|
2588 if(Y_Lang.isNumber(val)) |
|
2589 { |
|
2590 val = val + ""; |
|
2591 } |
|
2592 else if(!val) |
|
2593 { |
|
2594 val = ""; |
|
2595 } |
|
2596 if(IS_STRING(val)) |
|
2597 { |
|
2598 val = DOCUMENT.createTextNode(val); |
|
2599 } |
|
2600 textField.appendChild(val); |
|
2601 }, |
|
2602 |
|
2603 /** |
|
2604 * Returns the total number of majorUnits that will appear on an axis. |
|
2605 * |
|
2606 * @method getTotalMajorUnits |
|
2607 * @return Number |
|
2608 */ |
|
2609 getTotalMajorUnits: function() |
|
2610 { |
|
2611 var units, |
|
2612 majorUnit = this.get("styles").majorUnit, |
|
2613 len; |
|
2614 if(majorUnit.determinant === "count") |
|
2615 { |
|
2616 units = majorUnit.count; |
|
2617 } |
|
2618 else if(majorUnit.determinant === "distance") |
|
2619 { |
|
2620 len = this.getLength(); |
|
2621 units = (len/majorUnit.distance) + 1; |
|
2622 } |
|
2623 return units; |
|
2624 }, |
|
2625 |
|
2626 /** |
|
2627 * Returns the distance between major units on an axis. |
|
2628 * |
|
2629 * @method getMajorUnitDistance |
|
2630 * @param {Number} len Number of ticks |
|
2631 * @param {Number} uiLen Size of the axis. |
|
2632 * @param {Object} majorUnit Hash of properties used to determine the majorUnit |
|
2633 * @return Number |
|
2634 */ |
|
2635 getMajorUnitDistance: function(len, uiLen, majorUnit) |
|
2636 { |
|
2637 var dist; |
|
2638 if(majorUnit.determinant === "count") |
|
2639 { |
|
2640 if(!this.get("calculateEdgeOffset")) |
|
2641 { |
|
2642 len = len - 1; |
|
2643 } |
|
2644 dist = uiLen/len; |
|
2645 } |
|
2646 else if(majorUnit.determinant === "distance") |
|
2647 { |
|
2648 dist = majorUnit.distance; |
|
2649 } |
|
2650 return dist; |
|
2651 }, |
|
2652 |
|
2653 /** |
|
2654 * Checks to see if data extends beyond the range of the axis. If so, |
|
2655 * that data will need to be hidden. This method is internal, temporary and subject |
|
2656 * to removal in the future. |
|
2657 * |
|
2658 * @method _hasDataOverflow |
|
2659 * @protected |
|
2660 * @return Boolean |
|
2661 */ |
|
2662 _hasDataOverflow: function() |
|
2663 { |
|
2664 if(this.get("setMin") || this.get("setMax")) |
|
2665 { |
|
2666 return true; |
|
2667 } |
|
2668 return false; |
|
2669 }, |
|
2670 |
|
2671 /** |
|
2672 * Returns a string corresponding to the first label on an |
|
2673 * axis. |
|
2674 * |
|
2675 * @method getMinimumValue |
|
2676 * @return String |
|
2677 */ |
|
2678 getMinimumValue: function() |
|
2679 { |
|
2680 return this.get("minimum"); |
|
2681 }, |
|
2682 |
|
2683 /** |
|
2684 * Returns a string corresponding to the last label on an |
|
2685 * axis. |
|
2686 * |
|
2687 * @method getMaximumValue |
|
2688 * @return String |
|
2689 */ |
|
2690 getMaximumValue: function() |
|
2691 { |
|
2692 return this.get("maximum"); |
|
2693 } |
|
2694 }, { |
|
2695 ATTRS: |
|
2696 { |
|
2697 /** |
|
2698 * When set, defines the width of a vertical axis instance. By default, vertical axes automatically size based |
|
2699 * on their contents. When the width attribute is set, the axis will not calculate its width. When the width |
|
2700 * attribute is explicitly set, axis labels will postion themselves off of the the inner edge of the axis and the |
|
2701 * title, if present, will position itself off of the outer edge. If a specified width is less than the sum of |
|
2702 * the axis' contents, excess content will overflow. |
|
2703 * |
|
2704 * @attribute width |
|
2705 * @type Number |
|
2706 */ |
|
2707 width: { |
|
2708 lazyAdd: false, |
|
2709 |
|
2710 getter: function() |
|
2711 { |
|
2712 if(this._explicitWidth) |
|
2713 { |
|
2714 return this._explicitWidth; |
|
2715 } |
|
2716 return this._calculatedWidth; |
|
2717 }, |
|
2718 |
|
2719 setter: function(val) |
|
2720 { |
|
2721 this._explicitWidth = val; |
|
2722 return val; |
|
2723 } |
|
2724 }, |
|
2725 |
|
2726 /** |
|
2727 * When set, defines the height of a horizontal axis instance. By default, horizontal axes automatically size based |
|
2728 * on their contents. When the height attribute is set, the axis will not calculate its height. When the height |
|
2729 * attribute is explicitly set, axis labels will postion themselves off of the the inner edge of the axis and the |
|
2730 * title, if present, will position itself off of the outer edge. If a specified height is less than the sum of |
|
2731 * the axis' contents, excess content will overflow. |
|
2732 * |
|
2733 * @attribute height |
|
2734 * @type Number |
|
2735 */ |
|
2736 height: { |
|
2737 lazyAdd: false, |
|
2738 |
|
2739 getter: function() |
|
2740 { |
|
2741 if(this._explicitHeight) |
|
2742 { |
|
2743 return this._explicitHeight; |
|
2744 } |
|
2745 return this._calculatedHeight; |
|
2746 }, |
|
2747 |
|
2748 setter: function(val) |
|
2749 { |
|
2750 this._explicitHeight = val; |
|
2751 return val; |
|
2752 } |
|
2753 }, |
|
2754 |
|
2755 /** |
|
2756 * Calculated value of an axis' width. By default, the value is used internally for vertical axes. If the `width` |
|
2757 * attribute is explicitly set, this value will be ignored. |
|
2758 * |
|
2759 * @attribute calculatedWidth |
|
2760 * @type Number |
|
2761 * @private |
|
2762 */ |
|
2763 calculatedWidth: { |
|
2764 getter: function() |
|
2765 { |
|
2766 return this._calculatedWidth; |
|
2767 }, |
|
2768 |
|
2769 setter: function(val) |
|
2770 { |
|
2771 this._calculatedWidth = val; |
|
2772 return val; |
|
2773 } |
|
2774 }, |
|
2775 |
|
2776 /** |
|
2777 * Calculated value of an axis' height. By default, the value is used internally for horizontal axes. If the `height` |
|
2778 * attribute is explicitly set, this value will be ignored. |
|
2779 * |
|
2780 * @attribute calculatedHeight |
|
2781 * @type Number |
|
2782 * @private |
|
2783 */ |
|
2784 calculatedHeight: { |
|
2785 getter: function() |
|
2786 { |
|
2787 return this._calculatedHeight; |
|
2788 }, |
|
2789 |
|
2790 setter: function(val) |
|
2791 { |
|
2792 this._calculatedHeight = val; |
|
2793 return val; |
|
2794 } |
|
2795 }, |
|
2796 |
|
2797 /** |
|
2798 * Difference between the first/last tick and edge of axis. |
|
2799 * |
|
2800 * @attribute edgeOffset |
|
2801 * @type Number |
|
2802 * @protected |
|
2803 */ |
|
2804 edgeOffset: |
|
2805 { |
|
2806 value: 0 |
|
2807 }, |
|
2808 |
|
2809 /** |
|
2810 * The graphic in which the axis line and ticks will be rendered. |
|
2811 * |
|
2812 * @attribute graphic |
|
2813 * @type Graphic |
|
2814 */ |
|
2815 graphic: {}, |
|
2816 |
|
2817 /** |
|
2818 * @attribute path |
|
2819 * @type Shape |
|
2820 * @readOnly |
|
2821 * @private |
|
2822 */ |
|
2823 path: { |
|
2824 readOnly: true, |
|
2825 |
|
2826 getter: function() |
|
2827 { |
|
2828 if(!this._path) |
|
2829 { |
|
2830 var graphic = this.get("graphic"); |
|
2831 if(graphic) |
|
2832 { |
|
2833 this._path = graphic.addShape({type:"path"}); |
|
2834 } |
|
2835 } |
|
2836 return this._path; |
|
2837 } |
|
2838 }, |
|
2839 |
|
2840 /** |
|
2841 * @attribute tickPath |
|
2842 * @type Shape |
|
2843 * @readOnly |
|
2844 * @private |
|
2845 */ |
|
2846 tickPath: { |
|
2847 readOnly: true, |
|
2848 |
|
2849 getter: function() |
|
2850 { |
|
2851 if(!this._tickPath) |
|
2852 { |
|
2853 var graphic = this.get("graphic"); |
|
2854 if(graphic) |
|
2855 { |
|
2856 this._tickPath = graphic.addShape({type:"path"}); |
|
2857 } |
|
2858 } |
|
2859 return this._tickPath; |
|
2860 } |
|
2861 }, |
|
2862 |
|
2863 /** |
|
2864 * Contains the contents of the axis. |
|
2865 * |
|
2866 * @attribute node |
|
2867 * @type HTMLElement |
|
2868 */ |
|
2869 node: {}, |
|
2870 |
|
2871 /** |
|
2872 * Direction of the axis. |
|
2873 * |
|
2874 * @attribute position |
|
2875 * @type String |
|
2876 */ |
|
2877 position: { |
|
2878 lazyAdd: false, |
|
2879 |
|
2880 setter: function(val) |
|
2881 { |
|
2882 var LayoutClass = this._layoutClasses[val]; |
|
2883 if(val && val !== "none") |
|
2884 { |
|
2885 this._layout = new LayoutClass(); |
|
2886 } |
|
2887 return val; |
|
2888 } |
|
2889 }, |
|
2890 |
|
2891 /** |
|
2892 * Distance determined by the tick styles used to calculate the distance between the axis |
|
2893 * line in relation to the top of the axis. |
|
2894 * |
|
2895 * @attribute topTickOffset |
|
2896 * @type Number |
|
2897 */ |
|
2898 topTickOffset: { |
|
2899 value: 0 |
|
2900 }, |
|
2901 |
|
2902 /** |
|
2903 * Distance determined by the tick styles used to calculate the distance between the axis |
|
2904 * line in relation to the bottom of the axis. |
|
2905 * |
|
2906 * @attribute bottomTickOffset |
|
2907 * @type Number |
|
2908 */ |
|
2909 bottomTickOffset: { |
|
2910 value: 0 |
|
2911 }, |
|
2912 |
|
2913 /** |
|
2914 * Distance determined by the tick styles used to calculate the distance between the axis |
|
2915 * line in relation to the left of the axis. |
|
2916 * |
|
2917 * @attribute leftTickOffset |
|
2918 * @type Number |
|
2919 */ |
|
2920 leftTickOffset: { |
|
2921 value: 0 |
|
2922 }, |
|
2923 |
|
2924 /** |
|
2925 * Distance determined by the tick styles used to calculate the distance between the axis |
|
2926 * line in relation to the right side of the axis. |
|
2927 * |
|
2928 * @attribute rightTickOffset |
|
2929 * @type Number |
|
2930 */ |
|
2931 rightTickOffset: { |
|
2932 value: 0 |
|
2933 }, |
|
2934 |
|
2935 /** |
|
2936 * Collection of labels used to render the axis. |
|
2937 * |
|
2938 * @attribute labels |
|
2939 * @type Array |
|
2940 */ |
|
2941 labels: { |
|
2942 readOnly: true, |
|
2943 getter: function() |
|
2944 { |
|
2945 return this._labels; |
|
2946 } |
|
2947 }, |
|
2948 |
|
2949 /** |
|
2950 * Collection of points used for placement of labels and ticks along the axis. |
|
2951 * |
|
2952 * @attribute tickPoints |
|
2953 * @type Array |
|
2954 */ |
|
2955 tickPoints: { |
|
2956 readOnly: true, |
|
2957 |
|
2958 getter: function() |
|
2959 { |
|
2960 if(this.get("position") === "none") |
|
2961 { |
|
2962 return this.get("styles").majorUnit.count; |
|
2963 } |
|
2964 return this._tickPoints; |
|
2965 } |
|
2966 }, |
|
2967 |
|
2968 /** |
|
2969 * Indicates whether the axis overlaps the graph. If an axis is the inner most axis on a given |
|
2970 * position and the tick position is inside or cross, the axis will need to overlap the graph. |
|
2971 * |
|
2972 * @attribute overlapGraph |
|
2973 * @type Boolean |
|
2974 */ |
|
2975 overlapGraph: { |
|
2976 value:true, |
|
2977 |
|
2978 validator: function(val) |
|
2979 { |
|
2980 return Y_Lang.isBoolean(val); |
|
2981 } |
|
2982 }, |
|
2983 |
|
2984 /** |
|
2985 * Length in pixels of largest text bounding box. Used to calculate the height of the axis. |
|
2986 * |
|
2987 * @attribute maxLabelSize |
|
2988 * @type Number |
|
2989 * @protected |
|
2990 */ |
|
2991 maxLabelSize: { |
|
2992 getter: function() |
|
2993 { |
|
2994 return this._maxLabelSize; |
|
2995 }, |
|
2996 |
|
2997 setter: function(val) |
|
2998 { |
|
2999 this._maxLabelSize = val; |
|
3000 return val; |
|
3001 } |
|
3002 }, |
|
3003 |
|
3004 /** |
|
3005 * Title for the axis. When specified, the title will display. The position of the title is determined by the axis position. |
|
3006 * <dl> |
|
3007 * <dt>top</dt><dd>Appears above the axis and it labels. The default rotation is 0.</dd> |
|
3008 * <dt>right</dt><dd>Appears to the right of the axis and its labels. The default rotation is 90.</dd> |
|
3009 * <dt>bottom</dt><dd>Appears below the axis and its labels. The default rotation is 0.</dd> |
|
3010 * <dt>left</dt><dd>Appears to the left of the axis and its labels. The default rotation is -90.</dd> |
|
3011 * </dl> |
|
3012 * |
|
3013 * @attribute title |
|
3014 * @type String |
|
3015 */ |
|
3016 title: { |
|
3017 value: null |
|
3018 }, |
|
3019 |
|
3020 /** |
|
3021 * Function used to append an axis value to an axis label. This function has the following signature: |
|
3022 * <dl> |
|
3023 * <dt>textField</dt><dd>The axis label to be appended. (`HTMLElement`)</dd> |
|
3024 * <dt>val</dt><dd>The value to attach to the text field. This method will accept an `HTMLELement` |
|
3025 * or a `String`. This method does not use (`HTMLElement` | `String`)</dd> |
|
3026 * </dl> |
|
3027 * The default method appends a value to the `HTMLElement` using the `appendChild` method. If the given |
|
3028 * value is a `String`, the method will convert the the value to a `textNode` before appending to the |
|
3029 * `HTMLElement`. This method will not convert an `HTMLString` to an `HTMLElement`. |
|
3030 * |
|
3031 * @attribute appendLabelFunction |
|
3032 * @type Function |
|
3033 */ |
|
3034 appendLabelFunction: { |
|
3035 valueFn: function() |
|
3036 { |
|
3037 return this._setText; |
|
3038 } |
|
3039 }, |
|
3040 |
|
3041 /** |
|
3042 * Function used to append a title value to the title object. This function has the following signature: |
|
3043 * <dl> |
|
3044 * <dt>textField</dt><dd>The title text field to be appended. (`HTMLElement`)</dd> |
|
3045 * <dt>val</dt><dd>The value to attach to the text field. This method will accept an `HTMLELement` |
|
3046 * or a `String`. This method does not use (`HTMLElement` | `String`)</dd> |
|
3047 * </dl> |
|
3048 * The default method appends a value to the `HTMLElement` using the `appendChild` method. If the given |
|
3049 * value is a `String`, the method will convert the the value to a `textNode` before appending to the |
|
3050 * `HTMLElement` element. This method will not convert an `HTMLString` to an `HTMLElement`. |
|
3051 * |
|
3052 * @attribute appendTitleFunction |
|
3053 * @type Function |
|
3054 */ |
|
3055 appendTitleFunction: { |
|
3056 valueFn: function() |
|
3057 { |
|
3058 return this._setText; |
|
3059 } |
|
3060 }, |
|
3061 |
|
3062 /** |
|
3063 * An array containing the unformatted values of the axis labels. By default, TimeAxis, NumericAxis and |
|
3064 * StackedAxis labelValues are determined by the majorUnit style. By default, CategoryAxis labels are |
|
3065 * determined by the values of the dataProvider. |
|
3066 * <p>When the labelValues attribute is explicitly set, the labelValues are dictated by the set value and |
|
3067 * the position of ticks and labels are determined by where those values would fall on the axis. </p> |
|
3068 * |
|
3069 * @attribute labelValues |
|
3070 * @type Array |
|
3071 */ |
|
3072 labelValues: { |
|
3073 lazyAdd: false, |
|
3074 |
|
3075 setter: function(val) |
|
3076 { |
|
3077 var opts = arguments[2]; |
|
3078 if(!val || (opts && opts.src && opts.src === "internal")) |
|
3079 { |
|
3080 this._labelValuesExplicitlySet = false; |
|
3081 } |
|
3082 else |
|
3083 { |
|
3084 this._labelValuesExplicitlySet = true; |
|
3085 } |
|
3086 return val; |
|
3087 } |
|
3088 }, |
|
3089 |
|
3090 /** |
|
3091 * Suppresses the creation of the the first visible label and tick. |
|
3092 * |
|
3093 * @attribute hideFirstMajorUnit |
|
3094 * @type Boolean |
|
3095 */ |
|
3096 hideFirstMajorUnit: { |
|
3097 value: false |
|
3098 }, |
|
3099 |
|
3100 /** |
|
3101 * Suppresses the creation of the the last visible label and tick. |
|
3102 * |
|
3103 * @attribute hideLastMajorUnit |
|
3104 * @type Boolean |
|
3105 */ |
|
3106 hideLastMajorUnit: { |
|
3107 value: false |
|
3108 } |
|
3109 |
|
3110 /** |
|
3111 * Style properties used for drawing an axis. This attribute is inherited from `Renderer`. Below are the default values: |
|
3112 * <dl> |
|
3113 * <dt>majorTicks</dt><dd>Properties used for drawing ticks. |
|
3114 * <dl> |
|
3115 * <dt>display</dt><dd>Position of the tick. Possible values are `inside`, `outside`, `cross` and `none`. |
|
3116 * The default value is `inside`.</dd> |
|
3117 * <dt>length</dt><dd>The length (in pixels) of the tick. The default value is 4.</dd> |
|
3118 * <dt>color</dt><dd>The color of the tick. The default value is `#dad8c9`</dd> |
|
3119 * <dt>weight</dt><dd>Number indicating the width of the tick. The default value is 1.</dd> |
|
3120 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the tick. The default value is 1.</dd> |
|
3121 * </dl> |
|
3122 * </dd> |
|
3123 * <dt>line</dt><dd>Properties used for drawing the axis line. |
|
3124 * <dl> |
|
3125 * <dt>weight</dt><dd>Number indicating the width of the axis line. The default value is 1.</dd> |
|
3126 * <dt>color</dt><dd>The color of the axis line. The default value is `#dad8c9`.</dd> |
|
3127 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the tick. The default value is 1.</dd> |
|
3128 * </dl> |
|
3129 * </dd> |
|
3130 * <dt>majorUnit</dt><dd>Properties used to calculate the `majorUnit` for the axis. |
|
3131 * <dl> |
|
3132 * <dt>determinant</dt><dd>The algorithm used for calculating distance between ticks. The possible options are |
|
3133 * `count` and `distance`. If the `determinant` is `count`, the axis ticks will spaced so that a specified number |
|
3134 * of ticks appear on the axis. If the `determinant` is `distance`, the axis ticks will spaced out according to |
|
3135 * the specified distance. The default value is `count`.</dd> |
|
3136 * <dt>count</dt><dd>Number of ticks to appear on the axis when the `determinant` is `count`. The default value is 11.</dd> |
|
3137 * <dt>distance</dt><dd>The distance (in pixels) between ticks when the `determinant` is `distance`. The default |
|
3138 * value is 75.</dd> |
|
3139 * </dl> |
|
3140 * </dd> |
|
3141 * <dt>label</dt><dd>Properties and styles applied to the axis labels. |
|
3142 * <dl> |
|
3143 * <dt>color</dt><dd>The color of the labels. The default value is `#808080`.</dd> |
|
3144 * <dt>alpha</dt><dd>Number between 0 and 1 indicating the opacity of the labels. The default value is 1.</dd> |
|
3145 * <dt>fontSize</dt><dd>The font-size of the labels. The default value is 85%</dd> |
|
3146 * <dt>rotation</dt><dd>The rotation, in degrees (between -90 and 90) of the labels. The default value is 0.</dd> |
|
3147 * <dt>offset</td><dd>A number between 0 and 1 indicating the relationship of the label to a tick. For a horizontal axis |
|
3148 * label, a value of 0 will position the label's left side even to the the tick. A position of 1 would position the |
|
3149 * right side of the label with the tick. A position of 0.5 would center the label horizontally with the tick. For a |
|
3150 * vertical axis, a value of 0 would position the top of the label with the tick, a value of 1 would position the bottom |
|
3151 * of the label with the tick and a value 0 would center the label vertically with the tick. The default value is 0.5.</dd> |
|
3152 * <dt>margin</dt><dd>The distance between the label and the axis/tick. Depending on the position of the `Axis`, |
|
3153 * only one of the properties used. |
|
3154 * <dl> |
|
3155 * <dt>top</dt><dd>Pixel value used for an axis with a `position` of `bottom`. The default value is 4.</dd> |
|
3156 * <dt>right</dt><dd>Pixel value used for an axis with a `position` of `left`. The default value is 4.</dd> |
|
3157 * <dt>bottom</dt><dd>Pixel value used for an axis with a `position` of `top`. The default value is 4.</dd> |
|
3158 * <dt>left</dt><dd>Pixel value used for an axis with a `position` of `right`. The default value is 4.</dd> |
|
3159 * </dl> |
|
3160 * </dd> |
|
3161 * </dl> |
|
3162 * </dd> |
|
3163 * </dl> |
|
3164 * |
|
3165 * @attribute styles |
|
3166 * @type Object |
|
3167 */ |
|
3168 } |
|
3169 }); |
|
3170 Y.AxisType = Y.Base.create("baseAxis", Y.Axis, [], {}); |
|
3171 |
|
3172 |
|
3173 }, '@VERSION@', {"requires": ["dom", "widget", "widget-position", "widget-stack", "graphics", "axis-base"]}); |