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