]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/charts/charts.js
Release 6.2.2
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / charts / charts.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('charts', function(Y) {
9
10 /**
11  * The Charts widget provides an api for displaying data
12  * graphically.
13  *
14  * @module charts
15  */
16 var ISCHROME = Y.UA.chrome,
17     DRAWINGAPI,
18     canvas = document.createElement("canvas");
19 if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
20 {
21     DRAWINGAPI = "svg";
22 }
23 else if(canvas && canvas.getContext && canvas.getContext("2d"))
24 {
25     DRAWINGAPI = "canvas";
26 }
27 else
28 {
29     DRAWINGAPI = "vml";
30 }
31
32 /**
33  * Graphic is a simple drawing api that allows for basic drawing operations.
34  *
35  * @class Graphic
36  * @constructor
37  */
38 var Graphic = function(config) {
39     
40     this.initializer.apply(this, arguments);
41 };
42
43 Graphic.prototype = {
44     /**
45      * Indicates whether or not the instance will size itself based on its contents.
46      *
47      * @property autoSize 
48      * @type String
49      */
50     autoSize: true,
51
52     /**
53      * Initializes the class.
54      *
55      * @method initializer
56      * @private
57      */
58     initializer: function(config) {
59         config = config || {};
60         var w = config.width || 0,
61             h = config.height || 0;
62         if(config.node)
63         {
64             this.node = config.node;
65             this._styleGroup(this.node);
66         }
67         else
68         {
69             this.node = this._createGraphics();
70             this.setSize(w, h);
71         }
72         this._initProps();
73     },
74
75     /** 
76      * Specifies a bitmap fill used by subsequent calls to other drawing methods.
77      * 
78      * @method beginBitmapFill
79      * @param {Object} config
80      */
81     beginBitmapFill: function(config) {
82        
83         var fill = {};
84         fill.src = config.bitmap.src;
85         fill.type = "tile";
86         this._fillProps = fill;
87         if(!isNaN(config.tx) ||
88             !isNaN(config.ty) ||
89             !isNaN(config.width) ||
90             !isNaN(config.height))
91         {
92             this._gradientBox = {
93                 tx:config.tx,
94                 ty:config.ty,
95                 width:config.width,
96                 height:config.height
97             };
98         }
99         else
100         {
101             this._gradientBox = null;
102         }
103     },
104
105     /**
106      * Specifes a solid fill used by subsequent calls to other drawing methods.
107      *
108      * @method beginFill
109      * @param {String} color Hex color value for the fill.
110      * @param {Number} alpha Value between 0 and 1 used to specify the opacity of the fill.
111      */
112     beginFill: function(color, alpha) {
113         if (color) {
114             this._fillAlpha = Y.Lang.isNumber(alpha) ? alpha : 1;
115             this._fillColor = color;
116             this._fillType = 'solid';
117             this._fill = 1;
118         }
119         return this;
120     },
121     
122     /** 
123      * Specifies a gradient fill used by subsequent calls to other drawing methods.
124      *
125      * @method beginGradientFill
126      * @param {Object} config
127      */
128     beginGradientFill: function(config) {
129         var alphas = config.alphas || [];
130         if(!this._defs)
131         {
132             this._defs = this._createGraphicNode("defs");
133             this.node.appendChild(this._defs);
134         }
135         this._fillAlphas = alphas;
136         this._fillColors = config.colors;
137         this._fillType =  config.type || "linear";
138         this._fillRatios = config.ratios || [];
139         this._fillRotation = config.rotation || 0;
140         this._fillWidth = config.width || null;
141         this._fillHeight = config.height || null;
142         this._fillX = !isNaN(config.tx) ? config.tx : NaN;
143         this._fillY = !isNaN(config.ty) ? config.ty : NaN;
144         this._gradientId = "lg" + Math.round(100000 * Math.random());
145         return this;
146     },
147
148     /**
149      * Removes all nodes.
150      *
151      * @method destroy
152      */
153     destroy: function()
154     {
155         this._removeChildren(this.node);
156         if(this.node && this.node.parentNode)
157         {
158             this.node.parentNode.removeChild(this.node);
159         }
160     },
161     
162     /**
163      * Removes all child nodes.
164      *
165      * @method _removeChildren
166      * @param {HTMLElement} node
167      * @private
168      */
169     _removeChildren: function(node)
170     {
171         if(node.hasChildNodes())
172         {
173             var child;
174             while(node.firstChild)
175             {
176                 child = node.firstChild;
177                 this._removeChildren(child);
178                 node.removeChild(child);
179             }
180         }
181     },
182
183     /**
184      * Shows and and hides a the graphic instance.
185      *
186      * @method toggleVisible
187      * @param val {Boolean} indicates whether the instance should be visible.
188      */
189     toggleVisible: function(val)
190     {
191         this._toggleVisible(this.node, val);
192     },
193
194     /**
195      * Toggles visibility
196      *
197      * @method _toggleVisible
198      * @param {HTMLElement} node element to toggle
199      * @param {Boolean} val indicates visibilitye
200      * @private
201      */
202     _toggleVisible: function(node, val)
203     {
204         var children = Y.Selector.query(">/*", node),
205             visibility = val ? "visible" : "hidden",
206             i = 0,
207             len;
208         if(children)
209         {
210             len = children.length;
211             for(; i < len; ++i)
212             {
213                 this._toggleVisible(children[i], val);
214             }
215         }
216         node.style.visibility = visibility;
217     },
218
219     /**
220      * Clears the graphics object.
221      *
222      * @method clear
223      */
224     clear: function() {
225         if(this._graphicsList)
226         {
227             while(this._graphicsList.length > 0)
228             {
229                 this.node.removeChild(this._graphicsList.shift());
230             }
231         }
232         this.path = '';
233     },
234
235     /**
236      * Draws a bezier curve.
237      *
238      * @method curveTo
239      * @param {Number} cp1x x-coordinate for the first control point.
240      * @param {Number} cp1y y-coordinate for the first control point.
241      * @param {Number} cp2x x-coordinate for the second control point.
242      * @param {Number} cp2y y-coordinate for the second control point.
243      * @param {Number} x x-coordinate for the end point.
244      * @param {Number} y y-coordinate for the end point.
245      */
246     curveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
247         this._shapeType = "path";
248         if(this.path.indexOf("C") < 0 || this._pathType !== "C")
249         {
250             this._pathType = "C";
251             this.path += ' C';
252         }
253         this.path += Math.round(cp1x) + ", " + Math.round(cp1y) + ", " + Math.round(cp2x) + ", " + Math.round(cp2y) + ", " + x + ", " + y + " ";
254         this._trackSize(x, y);
255     },
256
257     /**
258      * Draws a quadratic bezier curve.
259      *
260      * @method quadraticCurveTo
261      * @param {Number} cpx x-coordinate for the control point.
262      * @param {Number} cpy y-coordinate for the control point.
263      * @param {Number} x x-coordinate for the end point.
264      * @param {Number} y y-coordinate for the end point.
265      */
266     quadraticCurveTo: function(cpx, cpy, x, y) {
267         if(this.path.indexOf("Q") < 0 || this._pathType !== "Q")
268         {
269             this._pathType = "Q";
270             this.path += " Q";
271         }
272         this.path +=  Math.round(cpx) + " " + Math.round(cpy) + " " + Math.round(x) + " " + Math.round(y);
273     },
274
275     /**
276      * Draws a circle.
277      *
278      * @method drawCircle
279      * @param {Number} x y-coordinate
280      * @param {Number} y x-coordinate
281      * @param {Number} r radius
282      */
283         drawCircle: function(x, y, r) {
284         this._shape = {
285             x:x - r,
286             y:y - r,
287             w:r * 2,
288             h:r * 2
289         };
290         this._attributes = {cx:x, cy:y, r:r};
291         this._width = this._height = r * 2;
292         this._x = x - r;
293         this._y = y - r;
294         this._shapeType = "circle";
295         this._draw();
296         },
297
298     /**
299      * Draws an ellipse.
300      *
301      * @method drawEllipse
302      * @param {Number} x x-coordinate
303      * @param {Number} y y-coordinate
304      * @param {Number} w width
305      * @param {Number} h height
306      */
307     drawEllipse: function(x, y, w, h) {
308         this._shape = {
309             x:x,
310             y:y,
311             w:w,
312             h:h
313         };
314         this._width = w;
315         this._height = h;
316         this._x = x;
317         this._y = y;
318         this._shapeType = "ellipse";
319         this._draw();
320     },
321
322     /**
323      * Draws a rectangle.
324      *
325      * @method drawRect
326      * @param {Number} x x-coordinate
327      * @param {Number} y y-coordinate
328      * @param {Number} w width
329      * @param {Number} h height
330      */
331     drawRect: function(x, y, w, h) {
332         this._shape = {
333             x:x,
334             y:y,
335             w:w,
336             h:h
337         };
338         this._x = x;
339         this._y = y;
340         this._width = w;
341         this._height = h;
342         this.moveTo(x, y);
343         this.lineTo(x + w, y);
344         this.lineTo(x + w, y + h);
345         this.lineTo(x, y + h);
346         this.lineTo(x, y);
347         this._draw();
348     },
349
350     /**
351      * Draws a rectangle with rounded corners.
352      * 
353      * @method drawRect
354      * @param {Number} x x-coordinate
355      * @param {Number} y y-coordinate
356      * @param {Number} w width
357      * @param {Number} h height
358      * @param {Number} ew width of the ellipse used to draw the rounded corners
359      * @param {Number} eh height of the ellipse used to draw the rounded corners
360      */
361     drawRoundRect: function(x, y, w, h, ew, eh) {
362         this._shape = {
363             x:x,
364             y:y,
365             w:w,
366             h:h
367         };
368         this._x = x;
369         this._y = y;
370         this._width = w;
371         this._height = h;
372         this.moveTo(x, y + eh);
373         this.lineTo(x, y + h - eh);
374         this.quadraticCurveTo(x, y + h, x + ew, y + h);
375         this.lineTo(x + w - ew, y + h);
376         this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
377         this.lineTo(x + w, y + eh);
378         this.quadraticCurveTo(x + w, y, x + w - ew, y);
379         this.lineTo(x + ew, y);
380         this.quadraticCurveTo(x, y, x, y + eh);
381         this._draw();
382         },
383
384     /**
385      * Draws a wedge.
386      * 
387      * @param {Number} x                        x-coordinate of the wedge's center point
388      * @param {Number} y                        y-coordinate of the wedge's center point
389      * @param {Number} startAngle       starting angle in degrees
390      * @param {Number} arc                      sweep of the wedge. Negative values draw clockwise.
391      * @param {Number} radius           radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
392      * @param {Number} yRadius          [optional] y radius for wedge.
393      */
394     drawWedge: function(x, y, startAngle, arc, radius, yRadius)
395     {
396         this._drawingComplete = false;
397         this.path = this._getWedgePath({x:x, y:y, startAngle:startAngle, arc:arc, radius:radius, yRadius:yRadius});
398         this._width = radius * 2;
399         this._height = this._width;
400         this._shapeType = "path";
401         this._draw();
402
403     },
404
405     /**
406      * Completes a drawing operation. 
407      *
408      * @method end
409      */
410     end: function() {
411         if(this._shapeType)
412         {
413             this._draw();
414         }
415         this._initProps();
416     },
417
418     /**
419      * Specifies a gradient to use for the stroke when drawing lines.
420      * Not implemented
421      *
422      * @method lineGradientStyle
423      * @private
424      */
425     lineGradientStyle: function() {
426     },
427      
428     /**
429      * Specifies a line style used for subsequent calls to drawing methods.
430      * 
431      * @method lineStyle
432      * @param {Number} thickness indicates the thickness of the line
433      * @param {String} color hex color value for the line
434      * @param {Number} alpha Value between 0 and 1 used to specify the opacity of the fill.
435      */
436     lineStyle: function(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit) {
437         this._stroke = 1;
438         this._strokeWeight = thickness;
439         if (color) {
440             this._strokeColor = color;
441         }
442         this._strokeAlpha = Y.Lang.isNumber(alpha) ? alpha : 1;
443     },
444     
445     /**
446      * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
447      * 
448      * @method lineTo
449      * @param {Number} point1 x-coordinate for the end point.
450      * @param {Number} point2 y-coordinate for the end point.
451      */
452     lineTo: function(point1, point2, etc) {
453         var args = arguments,
454             i,
455             len;
456         if (typeof point1 === 'string' || typeof point1 === 'number') {
457             args = [[point1, point2]];
458         }
459         len = args.length;
460         this._shapeType = "path";
461         if(this.path.indexOf("L") < 0 || this._pathType !== "L")
462         {
463             this._pathType = "L";
464             this.path += ' L';
465         }
466         for (i = 0; i < len; ++i) {
467             this.path += args[i][0] + ', ' + args[i][1] + " ";
468
469             this._trackSize.apply(this, args[i]);
470         }
471     },
472
473     /**
474      * Moves the current drawing position to specified x and y coordinates.
475      *
476      * @method moveTo
477      * @param {Number} x x-coordinate for the end point.
478      * @param {Number} y y-coordinate for the end point.
479      */
480     moveTo: function(x, y) {
481         this._pathType = "M";
482         this.path += ' M' + x + ', ' + y;
483     },
484
485     /**
486      * Generates a path string for a wedge shape
487      *
488      * @method _getWedgePath
489      * @param {Object} config attributes used to create the path
490      * @return String
491      * @private
492      */
493     _getWedgePath: function(config)
494     {
495         var x = config.x,
496             y = config.y,
497             startAngle = config.startAngle,
498             arc = config.arc,
499             radius = config.radius,
500             yRadius = config.yRadius || radius,
501             segs,
502             segAngle,
503             theta,
504             angle,
505             angleMid,
506             ax,
507             ay,
508             bx,
509             by,
510             cx,
511             cy,
512             i = 0,
513             path = ' M' + x + ', ' + y;  
514         
515         // limit sweep to reasonable numbers
516         if(Math.abs(arc) > 360)
517         {
518             arc = 360;
519         }
520         
521         // First we calculate how many segments are needed
522         // for a smooth arc.
523         segs = Math.ceil(Math.abs(arc) / 45);
524         
525         // Now calculate the sweep of each segment.
526         segAngle = arc / segs;
527         
528         // The math requires radians rather than degrees. To convert from degrees
529         // use the formula (degrees/180)*Math.PI to get radians.
530         theta = -(segAngle / 180) * Math.PI;
531         
532         // convert angle startAngle to radians
533         angle = (startAngle / 180) * Math.PI;
534         if(segs > 0)
535         {
536             // draw a line from the center to the start of the curve
537             ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
538             ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
539             path += " L" + Math.round(ax) + ", " +  Math.round(ay);
540             path += " Q";
541             for(; i < segs; ++i)
542             {
543                 angle += theta;
544                 angleMid = angle - (theta / 2);
545                 bx = x + Math.cos(angle) * radius;
546                 by = y + Math.sin(angle) * yRadius;
547                 cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
548                 cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
549                 path +=  Math.round(cx) + " " + Math.round(cy) + " " + Math.round(bx) + " " + Math.round(by) + " ";
550             }
551             path += ' L' + x + ", " + y;
552         }
553         return path;
554     },
555
556     /**
557      * Sets the size of the graphics object.
558      * 
559      * @method setSize
560      * @param w {Number} width to set for the instance.
561      * @param h {Number} height to set for the instance.
562      */
563     setSize: function(w, h) {
564         if(this.autoSize)
565         {
566             if(w > this.node.getAttribute("width"))
567             {
568                 this.node.setAttribute("width",  w);
569             }
570             if(h > this.node.getAttribute("height"))
571             {
572                 this.node.setAttribute("height", h);
573             }
574         }
575     },
576
577     /**
578      * Updates the size of the graphics object
579      *
580      * @method _trackSize
581      * @param {Number} w width
582      * @param {Number} h height
583      * @private
584      */
585     _trackSize: function(w, h) {
586         if (w > this._width) {
587             this._width = w;
588         }
589         if (h > this._height) {
590             this._height = h;
591         }
592         this.setSize(w, h);
593     },
594
595     /**
596      * Sets the positon of the graphics object.
597      *
598      * @method setPosition
599      * @param {Number} x x-coordinate for the object.
600      * @param {Number} y y-coordinate for the object.
601      */
602     setPosition: function(x, y)
603     {
604         this.node.setAttribute("x", x);
605         this.node.setAttribute("y", y);
606     },
607
608     /**
609      * Adds the graphics node to the dom.
610      * 
611      * @method render
612      * @param {HTMLElement} parentNode node in which to render the graphics node into.
613      */
614     render: function(parentNode) {
615         var w = parentNode.get("width") || parentNode.get("offsetWidth"),
616             h = parentNode.get("height") || parentNode.get("offsetHeight");
617         parentNode = parentNode || Y.config.doc.body;
618         parentNode.appendChild(this.node);
619         this.setSize(w, h);
620         this._initProps();
621         return this;
622     },
623
624     /**
625      * Clears the properties
626      *
627      * @method _initProps
628      * @private
629      */
630     _initProps: function() {
631         this._shape = null;
632         this._fillColor = null;
633         this._strokeColor = null;
634         this._strokeWeight = 0;
635         this._fillProps = null;
636         this._fillAlphas = null;
637         this._fillColors = null;
638         this._fillType =  null;
639         this._fillRatios = null;
640         this._fillRotation = null;
641         this._fillWidth = null;
642         this._fillHeight = null;
643         this._fillX = NaN;
644         this._fillY = NaN;
645         this.path = '';
646         this._width = 0;
647         this._height = 0;
648         this._x = 0;
649         this._y = 0;
650         this._fill = null;
651         this._stroke = 0;
652         this._stroked = false;
653         this._pathType = null;
654         this._attributes = {};
655     },
656
657     /**
658      * Clears path properties
659      * 
660      * @method _clearPath
661      * @private
662      */
663     _clearPath: function()
664     {
665         this._shape = null;
666         this._shapeType = null;
667         this.path = '';
668         this._width = 0;
669         this._height = 0;
670         this._x = 0;
671         this._y = 0;
672         this._pathType = null;
673         this._attributes = {};
674     },
675
676     /**
677      * Completes a shape
678      *
679      * @method _draw
680      * @private 
681      */
682     _draw: function()
683     {
684         var shape = this._createGraphicNode(this._shapeType),
685             i,
686             gradFill;
687         if(this.path)
688         {
689             if(this._fill)
690             {
691                 this.path += 'z';
692             }
693             shape.setAttribute("d", this.path);
694         }
695         else
696         {
697             for(i in this._attributes)
698             {
699                 if(this._attributes.hasOwnProperty(i))
700                 {
701                     shape.setAttribute(i, this._attributes[i]);
702                 }
703             }
704         }
705         shape.setAttribute("stroke-width",  this._strokeWeight);
706         if(this._strokeColor)
707         {
708             shape.setAttribute("stroke", this._strokeColor);
709             shape.setAttribute("stroke-opacity", this._strokeAlpha);
710         }
711         if(!this._fillType || this._fillType === "solid")
712         {
713             if(this._fillColor)
714             {
715                shape.setAttribute("fill", this._fillColor);
716                shape.setAttribute("fill-opacity", this._fillAlpha);
717             }
718             else
719             {
720                 shape.setAttribute("fill", "none");
721             }
722         }
723         else if(this._fillType === "linear")
724         {
725             gradFill = this._getFill();
726             gradFill.setAttribute("id", this._gradientId);
727             this._defs.appendChild(gradFill);
728             shape.setAttribute("fill", "url(#" + this._gradientId + ")");
729
730         }
731         this.node.appendChild(shape);
732         this._clearPath();
733     },
734
735     /**
736      * Returns ths actual fill object to be used in a drawing or shape
737      *
738      * @method _getFill
739      * @private
740      */
741     _getFill: function() {
742         var type = this._fillType,
743             fill;
744
745         switch (type) {
746             case 'linear': 
747                 fill = this._getLinearGradient('fill');
748                 break;
749             case 'radial': 
750                 //fill = this._getRadialGradient('fill');
751                 break;
752             case 'bitmap':
753                 //fill = this._bitmapFill;
754                 break;
755         }
756         return fill;
757     },
758
759     /**
760      * Returns a linear gradient fill
761      *
762      * @method _getLinearGradient
763      * @param {String} type gradient type
764      * @private
765      */
766     _getLinearGradient: function(type) {
767         var fill = this._createGraphicNode("linearGradient"),
768             prop = '_' + type,
769             colors = this[prop + 'Colors'],
770             ratios = this[prop + 'Ratios'],
771             alphas = this[prop + 'Alphas'],
772             w = this._fillWidth || (this._shape.w),
773             h = this._fillHeight || (this._shape.h),
774             r = this[prop + 'Rotation'],
775             i,
776             l,
777             color,
778             ratio,
779             alpha,
780             def,
781             stop,
782             x1, x2, y1, y2,
783             cx = w/2,
784             cy = h/2,
785             radCon,
786             tanRadians;
787         /*
788         if(r > 0 && r < 90)
789         {
790             r *= h/w;
791         }
792         else if(r > 90 && r < 180)
793         {
794
795             r =  90 + ((r-90) * w/h);
796         }
797 */
798         radCon = Math.PI/180;
799         tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
800         if(Math.abs(tanRadians) * w/2 >= h/2)
801         {
802             if(r < 180)
803             {
804                 y1 = 0;
805                 y2 = h;
806             }
807             else
808             {
809                 y1 = h;
810                 y2 = 0;
811             }
812             x1 = cx - ((cy - y1)/tanRadians);
813             x2 = cx - ((cy - y2)/tanRadians); 
814         }
815         else
816         {
817             if(r > 90 && r < 270)
818             {
819                 x1 = w;
820                 x2 = 0;
821             }
822             else
823             {
824                 x1 = 0;
825                 x2 = w;
826             }
827             y1 = ((tanRadians * (cx - x1)) - cy) * -1;
828             y2 = ((tanRadians * (cx - x2)) - cy) * -1;
829         }
830         /*
831         fill.setAttribute("spreadMethod", "pad");
832         
833         fill.setAttribute("x1", Math.round(100 * x1/w) + "%");
834         fill.setAttribute("y1", Math.round(100 * y1/h) + "%");
835         fill.setAttribute("x2", Math.round(100 * x2/w) + "%");
836         fill.setAttribute("y2", Math.round(100 * y2/h) + "%");
837         */
838         fill.setAttribute("gradientTransform", "rotate(" + r + ")");//," + (w/2) + ", " + (h/2) + ")");
839         fill.setAttribute("width", w);
840         fill.setAttribute("height", h);
841         fill.setAttribute("gradientUnits", "userSpaceOnUse");
842         l = colors.length;
843         def = 0;
844         for(i = 0; i < l; ++i)
845         {
846             alpha = alphas[i];
847             color = colors[i];
848             ratio = ratios[i] || i/(l - 1);
849             ratio = Math.round(ratio * 100) + "%";
850             alpha = Y.Lang.isNumber(alpha) ? alpha : "1";
851             def = (i + 1) / l;
852             stop = this._createGraphicNode("stop");
853             stop.setAttribute("offset", ratio);
854             stop.setAttribute("stop-color", color);
855             stop.setAttribute("stop-opacity", alpha);
856             fill.appendChild(stop);
857         }
858         return fill;
859     },
860
861     /**
862      * Creates a group element
863      *
864      * @method _createGraphics
865      * @private
866      */
867     _createGraphics: function() {
868         var group = this._createGraphicNode("svg");
869         this._styleGroup(group);
870         return group;
871     },
872
873     /**
874      * Styles a group element
875      *
876      * @method _styleGroup
877      * @private
878      */
879     _styleGroup: function(group)
880     {
881         group.style.position = "absolute";
882         group.style.top = "0px";
883         group.style.overflow = "visible";
884         group.style.left = "0px";
885         group.setAttribute("pointer-events", "none");
886     },
887
888     /**
889      * Creates a graphic node
890      *
891      * @method _createGraphicNode
892      * @param {String} type node type to create
893      * @param {String} pe specified pointer-events value
894      * @return HTMLElement
895      * @private
896      */
897     _createGraphicNode: function(type, pe)
898     {
899         var node = document.createElementNS("http://www.w3.org/2000/svg", "svg:" + type),
900             v = pe || "none";
901         if(type !== "defs" && type !== "stop" && type !== "linearGradient")
902         {
903             node.setAttribute("pointer-events", v);
904         }
905         if(type != "svg")
906         {
907             if(!this._graphicsList)
908             {
909                 this._graphicsList = [];
910             }
911             this._graphicsList.push(node);
912         }
913         return node;
914     },
915
916     /**
917      * Creates a Shape instance and adds it to the graphics object.
918      *
919      * @method getShape
920      * @param {Object} config Object literal of properties used to construct a Shape.
921      * @return Shape
922      */
923     getShape: function(config) {
924         config.graphic = this;
925         return new Y.Shape(config); 
926     }
927
928 };
929 Y.Graphic = Graphic;
930
931 /**
932  * Set of drawing apis for canvas based classes.
933  *
934  * @class CanvasDrawingUtil
935  * @constructor
936  */
937 function CanvasDrawingUtil()
938 {
939     this.initializer.apply(this, arguments);
940 }
941
942 CanvasDrawingUtil.prototype = {
943     /**
944      * Initializes the class.
945      *
946      * @method initializer
947      * @private
948      */
949     initializer: function(config) {
950         this._dummy = this._createDummy();
951         this._canvas = this._createGraphic();
952         this._context = this._canvas.getContext('2d');
953         this._initProps();
954     },
955
956     /** 
957      * Specifies a bitmap fill used by subsequent calls to other drawing methods.
958      * 
959      * @method beginBitmapFill
960      * @param {Object} config
961      */
962     beginBitmapFill: function(config) {
963         var context = this._context,
964             bitmap = config.bitmap,
965             repeat = config.repeat || 'repeat';
966         this._fillWidth = config.width || null;
967         this._fillHeight = config.height || null;
968         this._fillX = !isNaN(config.tx) ? config.tx : NaN;
969         this._fillY = !isNaN(config.ty) ? config.ty : NaN;
970         this._fillType =  'bitmap';
971         this._bitmapFill = context.createPattern(bitmap, repeat);
972         return this;
973     },
974
975     /**
976      * Specifes a solid fill used by subsequent calls to other drawing methods.
977      *
978      * @method beginFill
979      * @param {String} color Hex color value for the fill.
980      * @param {Number} alpha Value between 0 and 1 used to specify the opacity of the fill.
981      */
982     beginFill: function(color, alpha) {
983         var context = this._context;
984         context.beginPath();
985         if (color) {
986             if (alpha) {
987                color = this._2RGBA(color, alpha);
988             } else {
989                 color = this._2RGB(color);
990             }
991
992             this._fillColor = color;
993             this._fillType = 'solid';
994         }
995         return this;
996     },
997
998     /** 
999      * Specifies a gradient fill used by subsequent calls to other drawing methods.
1000      *
1001      * @method beginGradientFill
1002      * @param {Object} config
1003      */
1004     beginGradientFill: function(config) {
1005         var color,
1006             alpha,
1007             i = 0,
1008             colors = config.colors,
1009             alphas = config.alphas || [],
1010             len = colors.length;
1011         this._fillAlphas = alphas;
1012         this._fillColors = colors;
1013         this._fillType =  config.type || "linear";
1014         this._fillRatios = config.ratios || [];
1015         this._fillRotation = config.rotation || 0;
1016         this._fillWidth = config.width || null;
1017         this._fillHeight = config.height || null;
1018         this._fillX = !isNaN(config.tx) ? config.tx : NaN;
1019         this._fillY = !isNaN(config.ty) ? config.ty : NaN;
1020         for(;i < len; ++i)
1021         {
1022             alpha = alphas[i];
1023             color = colors[i];
1024             if (alpha) {
1025                color = this._2RGBA(color, alpha);
1026             } else {
1027                 color = this._2RGB(color);
1028             }
1029             colors[i] = color;
1030         }
1031         this._context.beginPath();
1032         return this;
1033     },
1034     
1035     /**
1036      * Specifies a line style used for subsequent calls to drawing methods.
1037      * 
1038      * @method lineStyle
1039      * @param {Number} thickness indicates the thickness of the line
1040      * @param {String} color hex color value for the line
1041      * @param {Number} alpha Value between 0 and 1 used to specify the opacity of the fill.
1042      */
1043     lineStyle: function(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit) {
1044         color = color || '#000000';
1045         var context = this._context;
1046         if(this._stroke)
1047         {
1048             context.stroke();
1049         }
1050         context.lineWidth = thickness;
1051
1052         if (thickness) {
1053             this._stroke = 1;
1054         } else {
1055             this._stroke = 0;
1056         }
1057
1058         if (color) {
1059             this._strokeStyle = color;
1060             if (alpha) {
1061                 this._strokeStyle = this._2RGBA(this._strokeStyle, alpha);
1062             }
1063         }
1064         
1065         if(!this._fill)
1066         {
1067             context.beginPath();
1068         }
1069
1070         if (caps === 'butt') {
1071             caps = 'none';
1072         }
1073         
1074         if (context.lineCap) { // FF errors when trying to set
1075             //context.lineCap = caps;
1076         }
1077         this._drawingComplete = false;
1078         return this;
1079     },
1080
1081     /**
1082      * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
1083      * 
1084      * @method lineTo
1085      * @param {Number} point1 x-coordinate for the end point.
1086      * @param {Number} point2 y-coordinate for the end point.
1087      */
1088     lineTo: function(point1, point2, etc) {
1089         var args = arguments, 
1090             context = this._context,
1091             i, len;
1092         if (typeof point1 === 'string' || typeof point1 === 'number') {
1093             args = [[point1, point2]];
1094         }
1095
1096         for (i = 0, len = args.length; i < len; ++i) {
1097             context.lineTo(args[i][0], args[i][1]);
1098             this._updateShapeProps.apply(this, args[i]);
1099             this._trackSize.apply(this, args[i]);
1100         }
1101         this._drawingComplete = false;
1102         return this;
1103     },
1104
1105     /**
1106      * Moves the current drawing position to specified x and y coordinates.
1107      *
1108      * @method moveTo
1109      * @param {Number} x x-coordinate for the end point.
1110      * @param {Number} y y-coordinate for the end point.
1111      */
1112     moveTo: function(x, y) {
1113         this._context.moveTo(x, y);
1114         this._trackPos(x, y);
1115         this._updateShapeProps(x, y);
1116         this._drawingComplete = false;
1117         return this;
1118     },
1119    
1120     /**
1121      * Clears the graphics object.
1122      *
1123      * @method clear
1124      */
1125     clear: function() {
1126         this._initProps();
1127         this._canvas.width = this._canvas.width;
1128         this._canvas.height = this._canvas.height;
1129         return this;
1130     },
1131
1132     /**
1133      * Draws a bezier curve.
1134      *
1135      * @method curveTo
1136      * @param {Number} cp1x x-coordinate for the first control point.
1137      * @param {Number} cp1y y-coordinate for the first control point.
1138      * @param {Number} cp2x x-coordinate for the second control point.
1139      * @param {Number} cp2y y-coordinate for the second control point.
1140      * @param {Number} x x-coordinate for the end point.
1141      * @param {Number} y y-coordinate for the end point.
1142      */
1143     curveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
1144         this._context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
1145         this._drawingComplete = false;
1146         this._updateShapeProps(x, y);
1147         this._trackSize(x, y);
1148         this._trackPos(x, y);
1149         return this;
1150     },
1151
1152     /**
1153      * Draws a quadratic bezier curve.
1154      *
1155      * @method quadraticCurveTo
1156      * @param {Number} cpx x-coordinate for the control point.
1157      * @param {Number} cpy y-coordinate for the control point.
1158      * @param {Number} x x-coordinate for the end point.
1159      * @param {Number} y y-coordinate for the end point.
1160      */
1161     quadraticCurveTo: function(controlX, controlY, anchorX, anchorY) {
1162         this._context.quadraticCurveTo(controlX, controlY, anchorX, anchorY);
1163         this._drawingComplete = false;
1164         this._updateShapeProps(anchorX, anchorY);
1165         return this;
1166     },
1167
1168     /**
1169      * Draws a circle.
1170      *
1171      * @method drawCircle
1172      * @param {Number} x y-coordinate
1173      * @param {Number} y x-coordinate
1174      * @param {Number} r radius
1175      */
1176         drawCircle: function(x, y, radius) {
1177         var context = this._context,
1178             startAngle = 0,
1179             endAngle = 2 * Math.PI;
1180         this._shape = {
1181             x:x - radius,
1182             y:y - radius,
1183             w:radius * 2,
1184             h:radius * 2
1185         };
1186         this._drawingComplete = false;
1187         this._trackPos(x, y);
1188         this._trackSize(radius * 2, radius * 2);
1189         context.beginPath();
1190         context.arc(x, y, radius, startAngle, endAngle, false);
1191         this._draw();
1192         return this;
1193     },
1194     
1195     /**
1196      * Draws an ellipse.
1197      *
1198      * @method drawEllipse
1199      * @param {Number} x x-coordinate
1200      * @param {Number} y y-coordinate
1201      * @param {Number} w width
1202      * @param {Number} h height
1203      */
1204         drawEllipse: function(x, y, w, h) {
1205         this._shape = {
1206             x:x,
1207             y:y,
1208             w:w,
1209             h:h
1210         };
1211         if(this._stroke && this._context.lineWidth > 0)
1212         {
1213             w -= this._context.lineWidth * 2;
1214             h -= this._context.lineWidth * 2;
1215             x += this._context.lineWidth;
1216             y += this._context.lineWidth;
1217         }
1218         var context = this._context,
1219             l = 8,
1220             theta = -(45/180) * Math.PI,
1221             angle = 0,
1222             angleMid,
1223             radius = w/2,
1224             yRadius = h/2,
1225             i = 0,
1226             centerX = x + radius,
1227             centerY = y + yRadius,
1228             ax, ay, bx, by, cx, cy;
1229         this._drawingComplete = false;
1230         this._trackPos(x, y);
1231         this._trackSize(x + w, y + h);
1232
1233         context.beginPath();
1234         ax = centerX + Math.cos(0) * radius;
1235         ay = centerY + Math.sin(0) * yRadius;
1236         context.moveTo(ax, ay);
1237         
1238         for(; i < l; i++)
1239         {
1240             angle += theta;
1241             angleMid = angle - (theta / 2);
1242             bx = centerX + Math.cos(angle) * radius;
1243             by = centerY + Math.sin(angle) * yRadius;
1244             cx = centerX + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
1245             cy = centerY + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
1246             context.quadraticCurveTo(cx, cy, bx, by);
1247         }
1248         this._draw();
1249         return this;
1250         },
1251
1252     /**
1253      * Draws a rectangle.
1254      *
1255      * @method drawRect
1256      * @param {Number} x x-coordinate
1257      * @param {Number} y y-coordinate
1258      * @param {Number} w width
1259      * @param {Number} h height
1260      */
1261     drawRect: function(x, y, w, h) {
1262         var ctx = this._context;
1263         this._shape = {
1264             x:x,
1265             y:y,
1266             w:w,
1267             h:h
1268         };
1269         this._drawingComplete = false;
1270         ctx.beginPath();
1271         ctx.moveTo(x, y);
1272         ctx.lineTo(x + w, y);
1273         ctx.lineTo(x + w, y + h);
1274         ctx.lineTo(x, y + h);
1275         ctx.lineTo(x, y);
1276         this._trackPos(x, y);
1277         this._trackSize(w, h);
1278         this._draw();
1279         return this;
1280     },
1281
1282     /**
1283      * Draws a rectangle with rounded corners.
1284      * 
1285      * @method drawRect
1286      * @param {Number} x x-coordinate
1287      * @param {Number} y y-coordinate
1288      * @param {Number} w width
1289      * @param {Number} h height
1290      * @param {Number} ew width of the ellipse used to draw the rounded corners
1291      * @param {Number} eh height of the ellipse used to draw the rounded corners
1292      */
1293     drawRoundRect: function(x, y, w, h, ew, eh) {
1294         this._shape = {
1295             x:x,
1296             y:y,
1297             w:w,
1298             h:h
1299         };
1300         var ctx = this._context;
1301         this._drawingComplete = false;
1302         ctx.beginPath();
1303         ctx.moveTo(x, y + eh);
1304         ctx.lineTo(x, y + h - eh);
1305         ctx.quadraticCurveTo(x, y + h, x + ew, y + h);
1306         ctx.lineTo(x + w - ew, y + h);
1307         ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
1308         ctx.lineTo(x + w, y + eh);
1309         ctx.quadraticCurveTo(x + w, y, x + w - ew, y);
1310         ctx.lineTo(x + ew, y);
1311         ctx.quadraticCurveTo(x, y, x, y + eh);
1312         this._trackPos(x, y);
1313         this._trackSize(w, h);
1314         this._draw();
1315         return this;
1316     },
1317
1318     /**
1319      * @private
1320      * Draws a wedge.
1321      * 
1322      * @param x                         x component of the wedge's center point
1323      * @param y                         y component of the wedge's center point
1324      * @param startAngle        starting angle in degrees
1325      * @param arc                       sweep of the wedge. Negative values draw clockwise.
1326      * @param radius            radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
1327      * @param yRadius           [optional] y radius for wedge.
1328      */
1329     drawWedge: function(cfg)
1330     {
1331         var x = cfg.x,
1332             y = cfg.y, 
1333             startAngle = cfg.startAngle, 
1334             arc = cfg.arc, 
1335             radius = cfg.radius, 
1336             yRadius = cfg.yRadius,
1337             segs,
1338             segAngle,
1339             theta,
1340             angle,
1341             angleMid,
1342             ax,
1343             ay,
1344             bx,
1345             by,
1346             cx,
1347             cy,
1348             i = 0;
1349
1350         this._drawingComplete = false;
1351         // move to x,y position
1352         this.moveTo(x, y);
1353         
1354         yRadius = yRadius || radius;
1355         
1356         // limit sweep to reasonable numbers
1357         if(Math.abs(arc) > 360)
1358         {
1359             arc = 360;
1360         }
1361         
1362         // First we calculate how many segments are needed
1363         // for a smooth arc.
1364         segs = Math.ceil(Math.abs(arc) / 45);
1365         
1366         // Now calculate the sweep of each segment.
1367         segAngle = arc / segs;
1368         
1369         // The math requires radians rather than degrees. To convert from degrees
1370         // use the formula (degrees/180)*Math.PI to get radians.
1371         theta = -(segAngle / 180) * Math.PI;
1372         
1373         // convert angle startAngle to radians
1374         angle = (startAngle / 180) * Math.PI;
1375         
1376         // draw the curve in segments no larger than 45 degrees.
1377         if(segs > 0)
1378         {
1379             // draw a line from the center to the start of the curve
1380             ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
1381             ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
1382             this.lineTo(ax, ay);
1383             // Loop for drawing curve segments
1384             for(; i < segs; ++i)
1385             {
1386                 angle += theta;
1387                 angleMid = angle - (theta / 2);
1388                 bx = x + Math.cos(angle) * radius;
1389                 by = y + Math.sin(angle) * yRadius;
1390                 cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
1391                 cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
1392                 this.quadraticCurveTo(cx, cy, bx, by);
1393             }
1394             // close the wedge by drawing a line to the center
1395             this.lineTo(x, y);
1396         }
1397         this._trackPos(x, y);
1398         this._trackSize(radius, radius);
1399         this._draw();
1400     },
1401
1402     /**
1403      * Completes a drawing operation. 
1404      *
1405      * @method end
1406      */
1407     end: function() {
1408         this._draw();
1409         this._initProps();
1410         return this;
1411     },
1412     
1413     /**
1414      * @private
1415      * Not implemented
1416      * Specifies a gradient to use for the stroke when drawing lines.
1417      */
1418     lineGradientStyle: function() {
1419         return this;
1420     },
1421
1422     /**
1423      * Sets the size of the graphics object.
1424      * 
1425      * @method setSize
1426      * @param w {Number} width to set for the instance.
1427      * @param h {Number} height to set for the instance.
1428      */
1429     setSize: function(w, h)
1430     {
1431         this._canvas.width = w;
1432         this._canvas.height = h;
1433     },
1434
1435     /**
1436      * Clears all values
1437      *
1438      * @method _initProps
1439      * @private
1440      */
1441     _initProps: function() {
1442         var context = this._context;
1443         
1444         context.fillStyle = 'rgba(0, 0, 0, 1)'; // use transparent when no fill
1445         context.lineWidth = 1;
1446         //context.lineCap = 'butt';
1447         context.lineJoin = 'miter';
1448         context.miterLimit = 3;
1449         this._strokeStyle = 'rgba(0, 0, 0, 1)';
1450
1451         this._width = 0;
1452         this._height = 0;
1453         //this._shape = null;
1454         this._x = 0;
1455         this._y = 0;
1456         this._fillType = null;
1457         this._stroke = null;
1458         this._bitmapFill = null;
1459         this._drawingComplete = false;
1460     },
1461
1462     /**
1463      * Returns ths actual fill object to be used in a drawing or shape
1464      *
1465      * @method _getFill
1466      * @private
1467      */
1468     _getFill: function() {
1469         var type = this._fillType,
1470             fill;
1471
1472         switch (type) {
1473             case 'linear': 
1474                 fill = this._getLinearGradient('fill');
1475                 break;
1476
1477             case 'radial': 
1478                 fill = this._getRadialGradient('fill');
1479                 break;
1480             case 'bitmap':
1481                 fill = this._bitmapFill;
1482                 break;
1483             case 'solid': 
1484                 fill = this._fillColor;
1485                 break;
1486         }
1487         return fill;
1488     },
1489
1490     /**
1491      * Returns a linear gradient fill
1492      *
1493      * @method _getLinearGradient
1494      * @private
1495      */
1496     _getLinearGradient: function(type) {
1497         var prop = '_' + type,
1498             colors = this[prop + 'Colors'],
1499             ratios = this[prop + 'Ratios'],
1500             x = !isNaN(this._fillX) ? this._fillX : this._shape.x,
1501             y = !isNaN(this._fillY) ? this._fillY : this._shape.y,
1502             w = this._fillWidth || (this._shape.w),
1503             h = this._fillHeight || (this._shape.h),
1504             ctx = this._context,
1505             r = this[prop + 'Rotation'],
1506             i,
1507             l,
1508             color,
1509             ratio,
1510             def,
1511             grad,
1512             x1, x2, y1, y2,
1513             cx = x + w/2,
1514             cy = y + h/2,
1515             radCon = Math.PI/180,
1516             tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
1517         if(Math.abs(tanRadians) * w/2 >= h/2)
1518         {
1519             if(r < 180)
1520             {
1521                 y1 = y;
1522                 y2 = y + h;
1523             }
1524             else
1525             {
1526                 y1 = y + h;
1527                 y2 = y;
1528             }
1529             x1 = cx - ((cy - y1)/tanRadians);
1530             x2 = cx - ((cy - y2)/tanRadians); 
1531         }
1532         else
1533         {
1534             if(r > 90 && r < 270)
1535             {
1536                 x1 = x + w;
1537                 x2 = x;
1538             }
1539             else
1540             {
1541                 x1 = x;
1542                 x2 = x + w;
1543             }
1544             y1 = ((tanRadians * (cx - x1)) - cy) * -1;
1545             y2 = ((tanRadians * (cx - x2)) - cy) * -1;
1546         }
1547         grad = ctx.createLinearGradient(x1, y1, x2, y2);
1548         l = colors.length;
1549         def = 0;
1550         for(i = 0; i < l; ++i)
1551         {
1552             color = colors[i];
1553             ratio = ratios[i] || i/(l - 1);
1554             grad.addColorStop(ratio, color);
1555             def = (i + 1) / l;
1556         }
1557         
1558         return grad;
1559     },
1560
1561     /**
1562      * Returns a radial gradient fill
1563      *
1564      * @method _getRadialGradient
1565      * @private
1566      */
1567     _getRadialGradient: function(type) {
1568         var prop = '_' + type,
1569             colors = this[prop + "Colors"],
1570             ratios = this[prop + "Ratios"],
1571             i,
1572             l,
1573             w = this._fillWidth || this._shape.w,
1574             h = this._fillHeight || this._shape.h,
1575             x = !isNaN(this._fillX) ? this._fillX : this._shape.x,
1576             y = !isNaN(this._fillY) ? this._fillY : this._shape.y,
1577             color,
1578             ratio,
1579             def,
1580             grad,
1581             ctx = this._context;
1582             x += w/2;
1583             y += h/2;
1584         grad = ctx.createRadialGradient(x, y, 1, x, y, w/2);
1585         l = colors.length;
1586         def = 0;
1587         for(i = 0; i < l; ++i) {
1588             color = colors[i];
1589             ratio = ratios[i] || i/(l - 1);
1590             grad.addColorStop(ratio, color);
1591         }
1592         return grad;
1593     },
1594    
1595     /**
1596      * Completes a shape or drawing
1597      *
1598      * @method _draw
1599      * @private
1600      */
1601     _draw: function()
1602     {
1603         if(this._drawingComplete || !this._shape)
1604         {
1605             return;
1606         }
1607         var context = this._context,
1608             fill;
1609
1610         if (this._fillType) {
1611             fill = this._getFill();
1612             if (fill) {
1613                 context.fillStyle = fill;
1614             }
1615             context.closePath();
1616         }
1617
1618         if (this._fillType) {
1619             context.fill();
1620         }
1621
1622         if (this._stroke) {
1623             context.strokeStyle = this._strokeStyle;
1624             context.stroke();
1625         }
1626         this._drawingComplete = true;
1627     },
1628
1629     /**
1630      * @private
1631      */
1632     _drawingComplete: false,
1633
1634     /**
1635      * Regex expression used for converting hex strings to rgb
1636      *
1637      * @property _reHex
1638      * @private
1639      */
1640     _reHex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
1641
1642     /**
1643      * Parses hex color string and alpha value to rgba
1644      *
1645      * @method _2RGBA
1646      * @private
1647      */
1648     _2RGBA: function(val, alpha) {
1649         alpha = (alpha !== undefined) ? alpha : 1;
1650         if (this._reHex.exec(val)) {
1651             val = 'rgba(' + [
1652                 parseInt(RegExp.$1, 16),
1653                 parseInt(RegExp.$2, 16),
1654                 parseInt(RegExp.$3, 16)
1655             ].join(',') + ',' + alpha + ')';
1656         }
1657         return val;
1658     },
1659
1660     /**
1661      * Creates dom element used for converting color string to rgb
1662      *
1663      * @method _createDummy
1664      * @private
1665      */
1666     _createDummy: function() {
1667         var dummy = Y.config.doc.createElement('div');
1668         dummy.style.height = 0;
1669         dummy.style.width = 0;
1670         dummy.style.overflow = 'hidden';
1671         Y.config.doc.documentElement.appendChild(dummy);
1672         return dummy;
1673     },
1674
1675     /**
1676      * Creates canvas element
1677      *
1678      * @method _createGraphic
1679      * @private
1680      */
1681     _createGraphic: function(config) {
1682         var graphic = Y.config.doc.createElement('canvas');
1683         // no size until drawn on
1684         graphic.width = 600;
1685         graphic.height = 600;
1686         return graphic;
1687     },
1688
1689     /**
1690      * Converts color to rgb format
1691      *
1692      * @method _2RGB
1693      * @private 
1694      */
1695     _2RGB: function(val) {
1696         this._dummy.style.background = val;
1697         return this._dummy.style.backgroundColor;
1698     },
1699     
1700     /**
1701      * Updates the size of the graphics object
1702      *
1703      * @method _trackSize
1704      * @param {Number} w width
1705      * @param {Number} h height
1706      * @private
1707      */
1708     _trackSize: function(w, h) {
1709         if (w > this._width) {
1710             this._width = w;
1711         }
1712         if (h > this._height) {
1713             this._height = h;
1714         }
1715     },
1716
1717     /**
1718      * Updates the position of the current drawing
1719      *
1720      * @method _trackPos
1721      * @param {Number} x x-coordinate
1722      * @param {Number} y y-coordinate
1723      * @private
1724      */
1725     _trackPos: function(x, y) {
1726         if (x > this._x) {
1727             this._x = x;
1728         }
1729         if (y > this._y) {
1730             this._y = y;
1731         }
1732     },
1733
1734     /**
1735      * Updates the position and size of the current drawing
1736      *
1737      * @method _updateShapeProps
1738      * @param {Number} x x-coordinate
1739      * @param {Number} y y-coordinate
1740      * @private
1741      */
1742     _updateShapeProps: function(x, y)
1743     {
1744         var w,h;
1745         if(!this._shape)
1746         {
1747             this._shape = {};
1748         }
1749         if(!this._shape.x)
1750         {
1751             this._shape.x = x;
1752         }
1753         else
1754         {
1755             this._shape.x = Math.min(this._shape.x, x);
1756         }
1757         if(!this._shape.y)
1758         {
1759             this._shape.y = y;
1760         }
1761         else
1762         {
1763             this._shape.y = Math.min(this._shape.y, y);
1764         }
1765         w = Math.abs(x - this._shape.x);
1766         if(!this._shape.w)
1767         {
1768             this._shape.w = w;
1769         }
1770         else
1771         {
1772             this._shape.w = Math.max(w, this._shape.w);
1773         }
1774         h = Math.abs(y - this._shape.y);
1775         if(!this._shape.h)
1776         {
1777             this._shape.h = h;
1778         }
1779         else
1780         {
1781             this._shape.h = Math.max(h, this._shape.h);
1782         }
1783     },
1784     
1785     /**
1786      * Creates a Shape instance and adds it to the graphics object.
1787      *
1788      * @method getShape
1789      * @param {Object} config Object literal of properties used to construct a Shape.
1790      * @return Shape
1791      */
1792     getShape: function(config) {
1793         config.graphic = this;
1794         return new Y.Shape(config); 
1795     }
1796 };
1797
1798 Y.CanvasDrawingUtil = CanvasDrawingUtil;
1799 /**
1800  * CanvasGraphics is a fallback drawing api used for basic drawing operations when SVG is not available.
1801  *
1802  * @class CanvasGraphics
1803  * @constructor
1804  */
1805 Y.CanvasGraphic = Y.Base.create("graphic",  Y.CanvasDrawingUtil, [], {
1806     autoSize: true,
1807
1808     /**
1809      * Sets the size of the graphics object.
1810      * 
1811      * @method setSize
1812      * @param w {Number} width to set for the instance.
1813      * @param h {Number} height to set for the instance.
1814      */
1815     setSize: function(w, h) {
1816         if(this.autoSize)
1817         {
1818             if(w > this.node.getAttribute("width"))
1819             {
1820                 this.node.style.width = w + "px";
1821                 this._canvas.style.width = w + "px";
1822                 this._canvas.width = w;
1823                 this.node.setAttribute("width", w);
1824             }
1825             if(h > this.node.getAttribute("height"))
1826             {
1827                 this.node.style.height = h + "px";
1828                 this._canvas.style.height = h + "px";
1829                 this._canvas.height = h;
1830                 this.node.setAttribute("height", h);
1831             }
1832         }
1833     },
1834
1835     /**
1836      * Updates the size of the graphics object
1837      *
1838      * @method _trackSize
1839      * @param {Number} w width
1840      * @param {Number} h height
1841      * @private
1842      */
1843     _trackSize: function(w, h) {
1844         if (w > this._width) {
1845             this._width = w;
1846         }
1847         if (h > this._height) {
1848             this._height = h;
1849         }
1850         this.setSize(w, h);
1851     },
1852
1853     /**
1854      * Sets the positon of the graphics object.
1855      *
1856      * @method setPosition
1857      * @param {Number} x x-coordinate for the object.
1858      * @param {Number} y y-coordinate for the object.
1859      */
1860     setPosition: function(x, y)
1861     {
1862         this.node.style.left = x + "px";
1863         this.node.style.top = y + "px";
1864     },
1865
1866     /**
1867      * Adds the graphics node to the dom.
1868      * 
1869      * @method render
1870      * @param {HTMLElement} parentNode node in which to render the graphics node into.
1871      */
1872     render: function(node) {
1873         node = node || Y.config.doc.body;
1874         this.node = document.createElement("div");
1875         this.node.style.width = node.offsetWidth + "px";
1876         this.node.style.height = node.offsetHeight + "px";
1877         this.node.style.display = "block";
1878         this.node.style.position = "absolute";
1879         this.node.style.left = node.getStyle("left");
1880         this.node.style.top = node.getStyle("top");
1881         this.node.style.pointerEvents = "none";
1882         node.appendChild(this.node);
1883         this.node.appendChild(this._canvas);
1884         this._canvas.width = node.offsetWidth > 0 ? node.offsetWidth : 100;
1885         this._canvas.height = node.offsetHeight > 0 ? node.offsetHeight : 100;
1886         this._canvas.style.position = "absolute";
1887
1888         return this;
1889     },
1890     
1891     /**
1892      * Shows and and hides a the graphic instance.
1893      *
1894      * @method toggleVisible
1895      * @param val {Boolean} indicates whether the instance should be visible.
1896      */
1897     toggleVisible: function(val)
1898     {
1899         this.node.style.visibility = val ? "visible" : "hidden";
1900     },
1901
1902     /**
1903      * Creates a graphic node
1904      *
1905      * @method _createGraphicNode
1906      * @param {String} type node type to create
1907      * @param {String} pe specified pointer-events value
1908      * @return HTMLElement
1909      * @private
1910      */
1911     _createGraphicNode: function(pe)
1912     {
1913         var node = Y.config.doc.createElement('canvas');
1914         node.style.pointerEvents = pe || "none";
1915         if(!this._graphicsList)
1916         {
1917             this._graphicsList = [];
1918         }
1919         this._graphicsList.push(node);
1920         return node;
1921     },
1922
1923     /**
1924      * Removes all nodes.
1925      *
1926      * @method destroy
1927      */
1928     destroy: function()
1929     {
1930         this._removeChildren(this.node);
1931         if(this.node && this.node.parentNode)
1932         {
1933             this.node.parentNode.removeChild(this.node);
1934         }
1935     },
1936     
1937     /**
1938      * Removes all child nodes.
1939      *
1940      * @method _removeChildren
1941      * @param {HTMLElement} node
1942      * @private
1943      */
1944     _removeChildren: function(node)
1945     {
1946         if(node.hasChildNodes())
1947         {
1948             var child;
1949             while(node.firstChild)
1950             {
1951                 child = node.firstChild;
1952                 this._removeChildren(child);
1953                 node.removeChild(child);
1954             }
1955         }
1956     },
1957
1958     /**
1959      * @private
1960      * Reference to the node for the graphics object
1961      */
1962     node: null
1963 });
1964
1965 if(DRAWINGAPI == "canvas")
1966 {
1967     Y.Graphic = Y.CanvasGraphic;
1968 }
1969 /**
1970  * VMLGraphics is a fallback drawing api used for basic drawing operations when SVG is not available.
1971  *
1972  * @class VMLGraphics
1973  * @constructor
1974  */
1975 var VMLGraphics = function(config) {
1976     
1977     this.initializer.apply(this, arguments);
1978 };
1979
1980 VMLGraphics.prototype = {
1981     /**
1982      * Indicates whether or not the instance will size itself based on its contents.
1983      *
1984      * @property autoSize 
1985      * @type String
1986      */
1987     initializer: function(config) {
1988         config = config || {};
1989         var w = config.width || 0,
1990             h = config.height || 0;
1991         this.node = this._createGraphics();
1992         this.setSize(w, h);
1993         this._initProps();
1994     },
1995
1996     /** 
1997      * Specifies a bitmap fill used by subsequent calls to other drawing methods.
1998      * 
1999      * @method beginBitmapFill
2000      * @param {Object} config
2001      */
2002     beginBitmapFill: function(config) {
2003        
2004         var fill = {};
2005         fill.src = config.bitmap.src;
2006         fill.type = "tile";
2007         this._fillProps = fill;
2008         if(!isNaN(config.tx) ||
2009             !isNaN(config.ty) ||
2010             !isNaN(config.width) ||
2011             !isNaN(config.height))
2012         {
2013             this._gradientBox = {
2014                 tx:config.tx,
2015                 ty:config.ty,
2016                 width:config.width,
2017                 height:config.height
2018             };
2019         }
2020         else
2021         {
2022             this._gradientBox = null;
2023         }
2024     },
2025
2026     /**
2027      * Specifes a solid fill used by subsequent calls to other drawing methods.
2028      *
2029      * @method beginFill
2030      * @param {String} color Hex color value for the fill.
2031      * @param {Number} alpha Value between 0 and 1 used to specify the opacity of the fill.
2032      */
2033     beginFill: function(color, alpha) {
2034         if (color) {
2035             if (Y.Lang.isNumber(alpha)) {
2036                 this._fillProps = {
2037                     type:"solid",
2038                     opacity: alpha
2039                 };
2040             }
2041             this._fillColor = color;
2042             this._fill = 1;
2043         }
2044         return this;
2045     },
2046
2047     /** 
2048      * Specifies a gradient fill used by subsequent calls to other drawing methods.
2049      *
2050      * @method beginGradientFill
2051      * @param {Object} config
2052      */
2053     beginGradientFill: function(config) {
2054         var type = config.type,
2055             colors = config.colors,
2056             alphas = config.alphas || [],
2057             ratios = config.ratios || [],
2058             fill = {
2059                 colors:colors,
2060                 ratios:ratios
2061             },
2062             len = alphas.length,
2063             i = 0,
2064             alpha,
2065             oi,
2066             rotation = config.rotation || 0;
2067     
2068         for(;i < len; ++i)
2069         {
2070             alpha = alphas[i];
2071             alpha = Y.Lang.isNumber(alpha) ? alpha : 1;
2072             oi = i > 0 ? i + 1 : "";
2073             alphas[i] = Math.round(alpha * 100) + "%";
2074             fill["opacity" + oi] = alpha;
2075         }
2076         if(type === "linear")
2077         {
2078             if(config)
2079             {
2080             }
2081             if(rotation > 0 && rotation <= 90)
2082             {
2083                 rotation = 450 - rotation;
2084             }
2085             else if(rotation <= 270)
2086             {
2087                 rotation = 270 - rotation;
2088             }
2089             else if(rotation <= 360)
2090             {
2091                 rotation = 630 - rotation;
2092             }
2093             else
2094             {
2095                 rotation = 270;
2096             }
2097             fill.type = "gradientunscaled";
2098             fill.angle = rotation;
2099         }
2100         else if(type === "radial")
2101         {
2102             fill.alignshape = false;
2103             fill.type = "gradientradial";
2104             fill.focus = "100%";
2105             fill.focusposition = "50%,50%";
2106         }
2107         fill.ratios = ratios || [];
2108         
2109         if(!isNaN(config.tx) ||
2110             !isNaN(config.ty) ||
2111             !isNaN(config.width) ||
2112             !isNaN(config.height))
2113         {
2114             this._gradientBox = {
2115                 tx:config.tx,
2116                 ty:config.ty,
2117                 width:config.width,
2118                 height:config.height
2119             };
2120         }
2121         else
2122         {
2123             this._gradientBox = null;
2124         }
2125         this._fillProps = fill;
2126     },
2127
2128     /**
2129      * Clears the graphics object.
2130      *
2131      * @method clear
2132      */
2133     clear: function() {
2134         this._path = '';
2135         this._removeChildren(this.node);
2136     },
2137
2138     /**
2139      * Removes all nodes.
2140      *
2141      * @method destroy
2142      */
2143     destroy: function()
2144     {
2145         this._removeChildren(this.node);
2146         this.node.parentNode.removeChild(this.node);
2147     },
2148
2149     /**
2150      * Removes all child nodes.
2151      *
2152      * @method _removeChildren
2153      * @param node
2154      * @private
2155      */
2156     _removeChildren: function(node)
2157     {
2158         if(node.hasChildNodes())
2159         {
2160             var child;
2161             while(node.firstChild)
2162             {
2163                 child = node.firstChild;
2164                 this._removeChildren(child);
2165                 node.removeChild(child);
2166             }
2167         }
2168     },
2169
2170     /**
2171      * Shows and and hides a the graphic instance.
2172      *
2173      * @method toggleVisible
2174      * @param val {Boolean} indicates whether the instance should be visible.
2175      */
2176     toggleVisible: function(val)
2177     {
2178         this._toggleVisible(this.node, val);
2179     },
2180
2181     /**
2182      * Toggles visibility
2183      *
2184      * @method _toggleVisible
2185      * @param {HTMLElement} node element to toggle
2186      * @param {Boolean} val indicates visibilitye
2187      * @private
2188      */
2189     _toggleVisible: function(node, val)
2190     {
2191         var children = Y.one(node).get("children"),
2192             visibility = val ? "visible" : "hidden",
2193             i = 0,
2194             len;
2195         if(children)
2196         {
2197             len = children.length;
2198             for(; i < len; ++i)
2199             {
2200                 this._toggleVisible(children[i], val);
2201             }
2202         }
2203         node.style.visibility = visibility;
2204     },
2205
2206     /**
2207      * Draws a bezier curve.
2208      *
2209      * @method curveTo
2210      * @param {Number} cp1x x-coordinate for the first control point.
2211      * @param {Number} cp1y y-coordinate for the first control point.
2212      * @param {Number} cp2x x-coordinate for the second control point.
2213      * @param {Number} cp2y y-coordinate for the second control point.
2214      * @param {Number} x x-coordinate for the end point.
2215      * @param {Number} y y-coordinate for the end point.
2216      */
2217     curveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
2218         this._shape = "shape";
2219         this._path += ' c ' + Math.round(cp1x) + ", " + Math.round(cp1y) + ", " + Math.round(cp2x) + ", " + Math.round(cp2y) + ", " + x + ", " + y;
2220         this._trackSize(x, y);
2221     },
2222
2223     /**
2224      * Draws a quadratic bezier curve.
2225      *
2226      * @method quadraticCurveTo
2227      * @param {Number} cpx x-coordinate for the control point.
2228      * @param {Number} cpy y-coordinate for the control point.
2229      * @param {Number} x x-coordinate for the end point.
2230      * @param {Number} y y-coordinate for the end point.
2231      */
2232     quadraticCurveTo: function(cpx, cpy, x, y) {
2233         this._path += ' qb ' + cpx + ", " + cpy + ", " + x + ", " + y;
2234     },
2235
2236     /**
2237      * Draws a circle.
2238      *
2239      * @method drawCircle
2240      * @param {Number} x y-coordinate
2241      * @param {Number} y x-coordinate
2242      * @param {Number} r radius
2243      */
2244     drawCircle: function(x, y, r) {
2245         this._width = this._height = r * 2;
2246         this._x = x - r;
2247         this._y = y - r;
2248         this._shape = "oval";
2249         this._draw();
2250     },
2251
2252     /**
2253      * Draws an ellipse.
2254      *
2255      * @method drawEllipse
2256      * @param {Number} x x-coordinate
2257      * @param {Number} y y-coordinate
2258      * @param {Number} w width
2259      * @param {Number} h height
2260      */
2261     drawEllipse: function(x, y, w, h) {
2262         this._width = w;
2263         this._height = h;
2264         this._x = x;
2265         this._y = y;
2266         this._shape = "oval";
2267         this._draw();
2268     },
2269
2270     /**
2271      * Draws a rectangle.
2272      *
2273      * @method drawRect
2274      * @param {Number} x x-coordinate
2275      * @param {Number} y y-coordinate
2276      * @param {Number} w width
2277      * @param {Number} h height
2278      */
2279     drawRect: function(x, y, w, h) {
2280         this._x = x;
2281         this._y = y;
2282         this._width = w;
2283         this._height = h;
2284         this.moveTo(x, y);
2285         this.lineTo(x + w, y);
2286         this.lineTo(x + w, y + h);
2287         this.lineTo(x, y + h);
2288         this.lineTo(x, y);
2289         this._draw();
2290     },
2291
2292     /**
2293      * Draws a rectangle with rounded corners.
2294      * 
2295      * @method drawRect
2296      * @param {Number} x x-coordinate
2297      * @param {Number} y y-coordinate
2298      * @param {Number} w width
2299      * @param {Number} h height
2300      * @param {Number} ew width of the ellipse used to draw the rounded corners
2301      * @param {Number} eh height of the ellipse used to draw the rounded corners
2302      */
2303     drawRoundRect: function(x, y, w, h, ew, eh) {
2304         this._x = x;
2305         this._y = y;
2306         this._width = w;
2307         this._height = h;
2308         this.moveTo(x, y + eh);
2309         this.lineTo(x, y + h - eh);
2310         this.quadraticCurveTo(x, y + h, x + ew, y + h);
2311         this.lineTo(x + w - ew, y + h);
2312         this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
2313         this.lineTo(x + w, y + eh);
2314         this.quadraticCurveTo(x + w, y, x + w - ew, y);
2315         this.lineTo(x + ew, y);
2316         this.quadraticCurveTo(x, y, x, y + eh);
2317         this._draw();
2318     },
2319
2320     /**
2321      * Draws a wedge.
2322      * 
2323      * @param {Number} x                        x-coordinate of the wedge's center point
2324      * @param {Number} y                        y-coordinate of the wedge's center point
2325      * @param {Number} startAngle       starting angle in degrees
2326      * @param {Number} arc                      sweep of the wedge. Negative values draw clockwise.
2327      * @param {Number} radius           radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
2328      * @param {Number} yRadius          [optional] y radius for wedge.
2329      */
2330     drawWedge: function(x, y, startAngle, arc, radius, yRadius)
2331     {
2332         this._drawingComplete = false;
2333         this._width = radius;
2334         this._height = radius;
2335         yRadius = yRadius || radius;
2336         this._path += this._getWedgePath({x:x, y:y, startAngle:startAngle, arc:arc, radius:radius, yRadius:yRadius});
2337         this._width = radius * 2;
2338         this._height = this._width;
2339         this._shape = "shape";
2340         this._draw();
2341     },
2342
2343     /**
2344      * Generates a path string for a wedge shape
2345      *
2346      * @method _getWedgePath
2347      * @param {Object} config attributes used to create the path
2348      * @return String
2349      * @private
2350      */
2351     _getWedgePath: function(config)
2352     {
2353         var x = config.x,
2354             y = config.y,
2355             startAngle = config.startAngle,
2356             arc = config.arc,
2357             radius = config.radius,
2358             yRadius = config.yRadius || radius,
2359             path;  
2360         if(Math.abs(arc) > 360)
2361         {
2362             arc = 360;
2363         }
2364         startAngle *= -65535;
2365         arc *= 65536;
2366         path = " m " + x + " " + y + " ae " + x + " " + y + " " + radius + " " + yRadius + " " + startAngle + " " + arc;
2367         return path;
2368     },
2369     
2370     /**
2371      * Completes a drawing operation. 
2372      *
2373      * @method end
2374      */
2375     end: function() {
2376         if(this._shape)
2377         {
2378             this._draw();
2379         }
2380         this._initProps();
2381     },
2382
2383     /**
2384      * Specifies a gradient to use for the stroke when drawing lines.
2385      * Not implemented
2386      *
2387      * @method lineGradientStyle
2388      * @private
2389      */
2390     lineGradientStyle: function() {
2391     },
2392     
2393     /**
2394      * Specifies a line style used for subsequent calls to drawing methods.
2395      * 
2396      * @method lineStyle
2397      * @param {Number} thickness indicates the thickness of the line
2398      * @param {String} color hex color value for the line
2399      * @param {Number} alpha Value between 0 and 1 used to specify the opacity of the fill.
2400      */
2401     lineStyle: function(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit) {
2402         this._stroke = 1;
2403         this._strokeWeight = thickness * 0.7;
2404         this._strokeColor = color;
2405         this._strokeOpacity = Y.Lang.isNumber(alpha) ? alpha : 1;
2406     },
2407
2408     /**
2409      * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
2410      * 
2411      * @method lineTo
2412      * @param {Number} point1 x-coordinate for the end point.
2413      * @param {Number} point2 y-coordinate for the end point.
2414      */
2415     lineTo: function(point1, point2, etc) {
2416         var args = arguments,
2417             i,
2418             len;
2419         if (typeof point1 === 'string' || typeof point1 === 'number') {
2420             args = [[point1, point2]];
2421         }
2422         len = args.length;
2423         this._shape = "shape";
2424         this._path += ' l ';
2425         for (i = 0; i < len; ++i) {
2426             this._path += ' ' + Math.round(args[i][0]) + ', ' + Math.round(args[i][1]);
2427             this._trackSize.apply(this, args[i]);
2428         }
2429     },
2430
2431     /**
2432      * Moves the current drawing position to specified x and y coordinates.
2433      *
2434      * @method moveTo
2435      * @param {Number} x x-coordinate for the end point.
2436      * @param {Number} y y-coordinate for the end point.
2437      */
2438     moveTo: function(x, y) {
2439         this._path += ' m ' + Math.round(x) + ', ' + Math.round(y);
2440     },
2441
2442     /**
2443      * Sets the size of the graphics object.
2444      * 
2445      * @method setSize
2446      * @param w {Number} width to set for the instance.
2447      * @param h {Number} height to set for the instance.
2448      */
2449     setSize: function(w, h) {
2450         w = Math.round(w);
2451         h = Math.round(h);
2452         this.node.style.width = w + 'px';
2453         this.node.style.height = h + 'px';
2454         this.node.coordSize = w + ' ' + h;
2455         this._canvasWidth = w;
2456         this._canvasHeight = h;
2457     },
2458    
2459     /**
2460      * Sets the positon of the graphics object.
2461      *
2462      * @method setPosition
2463      * @param {Number} x x-coordinate for the object.
2464      * @param {Number} y y-coordinate for the object.
2465      */
2466     setPosition: function(x, y)
2467     {
2468         x = Math.round(x);
2469         y = Math.round(y);
2470         this.node.style.left = x + "px";
2471         this.node.style.top = y + "px";
2472     },
2473
2474     /**
2475      * Adds the graphics node to the dom.
2476      * 
2477      * @method render
2478      * @param {HTMLElement} parentNode node in which to render the graphics node into.
2479      */
2480     render: function(parentNode) {
2481         var w = Math.max(parentNode.offsetWidth || 0, this._canvasWidth),
2482             h = Math.max(parentNode.offsetHeight || 0, this._canvasHeight);
2483         parentNode = parentNode || Y.config.doc.body;
2484         parentNode.appendChild(this.node);
2485         this.setSize(w, h);
2486         this._initProps();
2487         return this;
2488     },
2489
2490     /**
2491      * @private
2492      */
2493     _shape: null,
2494
2495     /**
2496      * Updates the size of the graphics object
2497      *
2498      * @method _trackSize
2499      * @param {Number} w width
2500      * @param {Number} h height
2501      * @private
2502      */
2503     _trackSize: function(w, h) {
2504         if (w > this._width) {
2505             this._width = w;
2506         }
2507         if (h > this._height) {
2508             this._height = h;
2509         }
2510     },
2511
2512     /**
2513      * Clears the properties
2514      *
2515      * @method _initProps
2516      * @private
2517      */
2518     _initProps: function() {
2519         this._fillColor = null;
2520         this._strokeColor = null;
2521         this._strokeOpacity = null;
2522         this._strokeWeight = 0;
2523         this._fillProps = null;
2524         this._path = '';
2525         this._width = 0;
2526         this._height = 0;
2527         this._x = 0;
2528         this._y = 0;
2529         this._fill = null;
2530         this._stroke = 0;
2531         this._stroked = false;
2532     },
2533
2534     /**
2535      * Clears path properties
2536      * 
2537      * @method _clearPath
2538      * @private
2539      */
2540     _clearPath: function()
2541     {
2542         this._shape = null;
2543         this._path = '';
2544         this._width = 0;
2545         this._height = 0;
2546         this._x = 0;
2547         this._y = 0;
2548     },
2549
2550     /**
2551      * Completes a shape
2552      *
2553      * @method _draw
2554      * @private 
2555      */
2556     _draw: function()
2557     {
2558         var shape = this._createGraphicNode(this._shape),
2559             w = Math.round(this._width),
2560             h = Math.round(this._height),
2561             strokeNode,
2562             fillProps = this._fillProps;
2563             this.setSize(w, h);
2564         if(this._path)
2565         {
2566             if(this._fill || this._fillProps)
2567             {
2568                 this._path += ' x';
2569             }
2570             if(this._stroke)
2571             {
2572                 this._path += ' e';
2573             }
2574             shape.path = this._path;
2575             shape.coordSize = w + ', ' + h;
2576         }
2577         else
2578         {
2579             shape.style.display = "block";
2580             shape.style.position = "absolute";
2581             shape.style.left = this._x + "px";
2582             shape.style.top = this._y + "px";
2583         }
2584         
2585         if (this._fill) {
2586             shape.fillColor = this._fillColor;
2587         }
2588         else
2589         {
2590             shape.filled = false;
2591         }
2592         if (this._stroke && this._strokeWeight > 0) {
2593             shape.strokeColor = this._strokeColor;
2594             shape.strokeWeight = this._strokeWeight;
2595             if(Y.Lang.isNumber(this._strokeOpacity) && this._strokeOpacity < 1)
2596             {    
2597                 strokeNode = this._createGraphicNode("stroke");
2598                 shape.appendChild(strokeNode);
2599                 strokeNode.opacity = this._strokeOpacity;
2600             }
2601         } else {
2602             shape.stroked = false;
2603         }
2604         shape.style.width = w + 'px';
2605         shape.style.height = h + 'px';
2606         if (fillProps) {
2607             shape.filled = true;
2608             shape.appendChild(this._getFill());
2609         }
2610         this.node.appendChild(shape);
2611         this._clearPath();
2612     },
2613
2614     /**
2615      * Returns ths actual fill object to be used in a drawing or shape
2616      *
2617      * @method _getFill
2618      * @private
2619      */
2620     _getFill: function() {
2621         var fill = this._createGraphicNode("fill"),
2622             w = this._width,
2623             h = this._height,
2624             fillProps = this._fillProps,
2625             prop,
2626             pct,
2627             i = 0,
2628             colors,
2629             colorstring = "",
2630             len,
2631             ratios,
2632             hyp = Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2)),
2633             cx = 50,
2634             cy = 50;
2635         if(this._gradientBox)
2636         {
2637             cx= Math.round( (this._gradientBox.width/2 - ((this._x - this._gradientBox.tx) * hyp/w))/(w * w/hyp) * 100);
2638             cy = Math.round( (this._gradientBox.height/2 - ((this._y - this._gradientBox.ty) * hyp/h))/(h * h/hyp) * 100);
2639             fillProps.focussize = (this._gradientBox.width/w)/10 + " " + (this._gradientBox.height/h)/10;
2640         }
2641         if(fillProps.colors)
2642         {
2643             colors = fillProps.colors.concat();
2644             ratios = fillProps.ratios.concat();
2645             len = colors.length;
2646             for(;i < len; ++i) {
2647                 pct = ratios[i] || i/(len-1);
2648                 pct = Math.round(100 * pct) + "%";
2649                 colorstring += ", " + pct + " " + colors[i];
2650             }
2651             if(parseInt(pct, 10) < 100)
2652             {
2653                 colorstring += ", 100% " + colors[len-1];
2654             }
2655         }
2656         for (prop in fillProps) {
2657             if(fillProps.hasOwnProperty(prop)) {
2658                 fill.setAttribute(prop, fillProps[prop]);
2659            }
2660         }
2661         fill.colors = colorstring.substr(2);
2662         if(fillProps.type === "gradientradial")
2663         {
2664             fill.focusposition = cx + "%," + cy + "%";
2665         }
2666         return fill;
2667     },
2668
2669     /**
2670      * Creates a group element
2671      *
2672      * @method _createGraphics
2673      * @private
2674      */
2675     _createGraphics: function() {
2676         var group = this._createGraphicNode("group");
2677         group.style.display = "inline-block";
2678         group.style.position = 'absolute';
2679         return group;
2680     },
2681
2682     /**
2683      * Creates a graphic node
2684      *
2685      * @method _createGraphicNode
2686      * @param {String} type node type to create
2687      * @param {String} pe specified pointer-events value
2688      * @return HTMLElement
2689      * @private
2690      */
2691     _createGraphicNode: function(type)
2692     {
2693         return document.createElement('<' + type + ' xmlns="urn:schemas-microsft.com:vml" class="vml' + type + '"/>');
2694     
2695     },
2696     
2697     /**
2698      * Converts a shape type to the appropriate vml node type.
2699      *
2700      * @method _getNodeShapeType
2701      * @param {String} type The shape to convert.
2702      * @return String
2703      * @private
2704      */
2705     _getNodeShapeType: function(type)
2706     {
2707         var shape = "shape";
2708         if(this._typeConversionHash.hasOwnProperty(type))
2709         {
2710             shape = this._typeConversionHash[type];
2711         }
2712         return shape;
2713     },
2714
2715     /**
2716      * Used to convert certain shape types to the appropriate vml node type.
2717      *
2718      * @property _typeConversionHash
2719      * @type Object
2720      * @private
2721      */
2722     _typeConversionHash: {
2723         circle: "oval",
2724         ellipse: "oval",
2725         rect: "rect"
2726     },
2727     
2728     /**
2729      * Creates a Shape instance and adds it to the graphics object.
2730      *
2731      * @method getShape
2732      * @param {Object} config Object literal of properties used to construct a Shape.
2733      * @return Shape
2734      */
2735     getShape: function(config) {
2736         config.graphic = this;
2737         return new Y.Shape(config); 
2738     },
2739
2740     /**
2741      * Adds a child to the <code>node</code>.
2742      *
2743      * @method addChild
2744      * @param {HTMLElement} element to add
2745      * @private
2746      */
2747     addChild: function(child)
2748     {
2749         this.node.appendChild(child);
2750     }
2751 };
2752
2753 if(DRAWINGAPI == "vml")
2754 {
2755     var sheet = document.createStyleSheet();
2756     sheet.addRule(".vmlgroup", "behavior:url(#default#VML)", sheet.rules.length);
2757     sheet.addRule(".vmlgroup", "display:inline-block", sheet.rules.length);
2758     sheet.addRule(".vmlgroup", "zoom:1", sheet.rules.length);
2759     sheet.addRule(".vmlshape", "behavior:url(#default#VML)", sheet.rules.length);
2760     sheet.addRule(".vmlshape", "display:inline-block", sheet.rules.length);
2761     sheet.addRule(".vmloval", "behavior:url(#default#VML)", sheet.rules.length);
2762     sheet.addRule(".vmloval", "display:inline-block", sheet.rules.length);
2763     sheet.addRule(".vmlrect", "behavior:url(#default#VML)", sheet.rules.length);
2764     sheet.addRule(".vmlrect", "display:block", sheet.rules.length);
2765     sheet.addRule(".vmlfill", "behavior:url(#default#VML)", sheet.rules.length);
2766     sheet.addRule(".vmlstroke", "behavior:url(#default#VML)", sheet.rules.length);
2767     Y.Graphic = VMLGraphics;
2768 }
2769
2770 /**
2771  * The Shape class creates a graphic object with editable 
2772  * properties.
2773  *
2774  * @class Shape
2775  * @extends Graphic
2776  * @constructor
2777  */
2778 function Shape(cfg)
2779 {
2780     this._initialize(cfg);
2781     this._draw();
2782 }
2783
2784 Y.extend(Shape, Y.Graphic, {
2785     /**
2786      * Indicates the type of shape. 
2787      *
2788      * @property type 
2789      * @type string
2790      */
2791     type: "shape",
2792
2793     /**
2794      * Indicates whether or not the instance will size itself based on its contents.
2795      *
2796      * @property autoSize 
2797      * @type string
2798      */
2799     autoSize: false,
2800
2801     /**
2802      * Determines whether the instance will receive mouse events.
2803      * 
2804      * @property pointerEvents
2805      * @type string
2806      */
2807     pointerEvents: "visiblePainted", 
2808
2809     /**
2810      * Initializes the graphic instance.
2811      *
2812      * @method _initialize
2813      * @private
2814      */
2815     _initialize: function(cfg) 
2816     {
2817         if(!cfg.graphic)
2818         {
2819             cfg.graphic = new Y.Graphic();
2820         }
2821         this._setProps(cfg);
2822     },
2823   
2824     /**
2825      * Updates properties for the shape.
2826      *
2827      * @method _setProps
2828      * @param {Object} cfg Properties to update.
2829      * @private
2830      */
2831     _setProps: function(cfg)
2832     {
2833         this.autoSize = cfg.autoSize || this.autoSize; 
2834         this.pointerEvents = cfg.pointerEvents || this.pointerEvents;
2835         this.width = cfg.width || this.width;
2836         this.height = cfg.height || this.height;
2837         this.border = cfg.border || this.border;
2838         this.graphics = cfg.graphic || this.graphics;
2839         this.canvas = this.graphics;
2840         this.parentNode = this.graphics.node;
2841         this.fill = cfg.fill || this.fill;
2842         this.type = cfg.shape || this.type;
2843         this.nodetype = this._getNodeShapeType(this.type); 
2844         this.props = cfg.props || this.props;
2845         this.path = cfg.path || this.path;
2846     },
2847
2848     /**
2849      * Draws the graphic.
2850      *
2851      * @method _draw
2852      * @private
2853      */
2854     _draw: function()
2855     {
2856         var cx,
2857             cy,
2858             rx,
2859             ry,
2860             parentNode = this.parentNode,
2861             borderWeight = 0,
2862             fillWidth = this.width || 0,
2863             fillHeight = this.height || 0;
2864         if(!this.node)
2865         {
2866             this.node = this._createGraphicNode(this.nodetype, this.pointerEvents);
2867             parentNode.appendChild(this.node);
2868         }
2869         if(this.type == "wedge")
2870         {
2871             this.path = this._getWedgePath(this.props);
2872         }
2873         if(this.nodetype == "path")
2874         {
2875             this._setPath();
2876         }
2877         if(this.border && this.border.weight && this.border.weight > 0)
2878         {
2879             borderWeight = this.border.weight;
2880             fillWidth -= borderWeight * 2;
2881             fillHeight -= borderWeight * 2;
2882         }
2883         this._addBorder();
2884         if(this.nodetype === "ellipse")
2885         {
2886             rx = this.width/2;
2887             cx = this.width/2;
2888             ry = this.height/2;
2889             cy = this.height/2;
2890             rx -= borderWeight;
2891             ry -= borderWeight;
2892             this.node.setAttribute("cx", cx);
2893             this.node.setAttribute("cy", cy);
2894             this.node.setAttribute("rx", rx);
2895             this.node.setAttribute("ry", ry);
2896         }
2897         else
2898         {
2899             this.node.setAttribute("width", fillWidth);
2900             this.node.setAttribute("height", fillHeight);
2901             this.node.style.width = fillWidth + "px";
2902             this.node.style.height = fillHeight + "px";
2903         }
2904         this._addFill();
2905         parentNode.style.width = this.width + "px";
2906         parentNode.style.height = this.height + "px";
2907         parentNode.setAttribute("width", this.width);
2908         parentNode.setAttribute("height", this.height);
2909         this.node.style.visibility = "visible";
2910         this.node.setAttribute("x", borderWeight); 
2911         this.node.setAttribute("y", borderWeight); 
2912         return this;       
2913     },
2914
2915     /**
2916      * Adds a path to the shape node.
2917      * 
2918      * @method _setPath
2919      * @private
2920      */
2921     _setPath: function()
2922     {
2923         if(this.path)
2924         {
2925             this.path += " Z";
2926             this.node.setAttribute("d", this.path);
2927         }
2928     },
2929
2930     /**
2931      * Adds a border to the shape node.
2932      *
2933      * @method _addBorder
2934      * @private
2935      */
2936     _addBorder: function()
2937     {
2938         if(this.border && this.border.weight && this.border.weight > 0)
2939         {
2940             var borderAlpha = this.border.alpha;
2941             this.border.color = this.border.color || "#000000";
2942             this.border.weight = this.border.weight || 1;
2943             this.border.alpha = Y.Lang.isNumber(borderAlpha) ? borderAlpha : 1;
2944             this.border.linecap = this.border.linecap || "square";
2945             this.node.setAttribute("stroke", this.border.color);
2946             this.node.setAttribute("stroke-linecap", this.border.linecap);
2947             this.node.setAttribute("stroke-width",  this.border.weight);
2948             this.node.setAttribute("stroke-opacity", this.border.alpha);
2949         }
2950         else
2951         {
2952             this.node.setAttribute("stroke", "none");
2953         }
2954     },
2955
2956     /**
2957      * Adds a fill to the shape node.
2958      *
2959      * @method _addFill
2960      * @private
2961      */
2962     _addFill: function()
2963     {
2964         var fillAlpha;
2965         if(this.fill.type === "linear" || this.fill.type === "radial")
2966         {
2967             this.beginGradientFill(this.fill);
2968             this.node.appendChild(this._getFill());
2969         }
2970         else if(this.fill.type === "bitmap")
2971         {
2972             this.beginBitmapFill(this.fill);
2973             this.node.appendChild(this._getFill());
2974         }
2975         else
2976         {
2977             if(!this.fill.color)
2978             {
2979                 this.node.setAttribute("fill", "none");
2980             }
2981             else
2982             {
2983                 fillAlpha = this.fill.alpha; 
2984                 this.fill.alpha = Y.Lang.isNumber(fillAlpha) ? fillAlpha : 1;
2985                 this.node.setAttribute("fill", this.fill.color);
2986                 this.node.setAttribute("fill-opacity", fillAlpha);
2987             }
2988         }
2989     },
2990
2991     /**
2992      * Completes a drawing operation. 
2993      *
2994      * @method end
2995      */
2996     end: function()
2997     {
2998         this._setPath();
2999     },
3000
3001     /**
3002      * Updates the properties of the shape instance.
3003      *
3004      * @method update
3005      * @param {Object} cfg Object literal containing properties to update.
3006      */
3007     update: function(cfg)
3008     {
3009         this._setProps(cfg);
3010         this._draw();
3011         return this;
3012     },
3013     
3014     /**
3015      * Converts a shape type to the appropriate node attribute.
3016      *
3017      * @private
3018      * @method _getNodeShapeType
3019      * @param {String} type The type of shape.
3020      * @return String
3021      */
3022     _getNodeShapeType: function(type)
3023     {
3024         if(this._typeConversionHash.hasOwnProperty(type))
3025         {
3026             type = this._typeConversionHash[type];
3027         }
3028         return type;
3029     },
3030
3031     /**
3032      * Sets the visibility of a shape.
3033      * 
3034      * @method toggleVisible
3035      * @param {Boolean} val indicates whether or not the shape is visible.
3036      */
3037     toggleVisible: function(val)
3038     {
3039         var visibility = val ? "visible" : "hidden";
3040         if(this.node)
3041         {
3042             this.node.style.visibility = visibility;
3043         }
3044     },
3045
3046     /**
3047      * Adds a class to the shape's node.
3048      *
3049      * @method addClass
3050      * @param {String} className Name of the class to add.
3051      */
3052     addClass: function(className)
3053     {
3054         var node = this.node;
3055         if(node)
3056         {
3057             if(node.className && node.className.baseVal)
3058             {
3059                 node.className.baseVal = Y.Lang.trim([node.className.baseVal, className].join(' '));
3060             }
3061             else
3062             {
3063                 node.setAttribute("class", className);
3064             }
3065         }
3066     },
3067
3068     /**
3069      * Positions the parent node of the shape.
3070      *
3071      * @method setPosition
3072      * @param {Number}, x The x-coordinate
3073      * @param {Number}, y The y-coordinate
3074      */
3075     setPosition: function(x, y)
3076     {
3077         var pNode = Y.one(this.parentNode),
3078             hotspot = this.hotspot;
3079         pNode.setStyle("position", "absolute");
3080         pNode.setStyle("left", x);
3081         pNode.setStyle("top", y);
3082         if(hotspot)
3083         {
3084             hotspot.setStyle("position", "absolute");
3085             hotspot.setStyle("left", x);
3086             hotspot.setStyle("top", y);
3087         }
3088     },
3089
3090     /**
3091      * Used to convert shape declarations to the appropriate node type.
3092      *
3093      * @property _typeConversionHash
3094      * @type Object
3095      * @private
3096      */
3097     _typeConversionHash: {
3098         circle: "ellipse",
3099         wedge: "path"
3100     }
3101 });
3102
3103 Y.Shape = Shape;
3104 /**
3105  * The Shape class creates a graphic object with editable 
3106  * properties.
3107  *
3108  * @class CanvasShape
3109  * @extends CanvasGraphic
3110  * @constructor
3111  */
3112 function CanvasShape(cfg)
3113 {
3114     this._dummy = this._createDummy();
3115     this._canvas = this._createGraphic();
3116     this.node = this._canvas;
3117     this._context = this._canvas.getContext('2d');
3118     this._initialize(cfg);
3119     this._validate();
3120 }
3121
3122 Y.extend(CanvasShape, Y.CanvasDrawingUtil, {
3123     /**
3124      * Indicates the type of shape. 
3125      *
3126      * @property type 
3127      * @type string
3128      */
3129     type: "shape",
3130
3131     /**
3132      * Indicates whether or not the instance will size itself based on its contents.
3133      *
3134      * @property autoSize 
3135      * @type string
3136      */
3137     autoSize: false,
3138
3139     /**
3140      * Initializes the graphic instance.
3141      *
3142      * @method _initialize
3143      * @private
3144      */
3145     _initialize: function(cfg) 
3146     {
3147         this._canvas.style.position = "absolute";
3148         if(cfg.graphic)
3149         {
3150             cfg.graphic.node.appendChild(this._canvas);
3151         }
3152         this._setProps(cfg);
3153     },
3154   
3155     /**
3156      * Updates properties for the shape.
3157      *
3158      * @method _setProps
3159      * @param {Object} cfg Properties to update.
3160      * @private
3161      */
3162     _setProps: function(cfg)
3163     {
3164         this.autoSize = cfg.autoSize || this.autoSize; 
3165         this.width = cfg.width || this.width;
3166         this.height = cfg.height || this.height;
3167         this.border = cfg.border || this.border;
3168         this.graphics = cfg.graphic || this.graphics;
3169         this.fill = cfg.fill || this.fill;
3170         this.type = cfg.shape || this.type;
3171         this.props = cfg.props || this.props;
3172         this.path = cfg.path || this.path;
3173         this.props = cfg.props || this.props;
3174         this.parentNode = this.graphics.node;
3175     },
3176
3177     /**
3178      * Draws the graphic.
3179      *
3180      * @method _validate
3181      * @private
3182      */
3183     _validate: function()
3184     {
3185         var w = this.width,
3186             h = this.height,
3187             border = this.border,
3188             type = this.type,
3189             fill = this.fill;
3190         this.clear();
3191         this.setSize(this.width, this.height);
3192         this._canvas.style.top = "0px";
3193         this._canvas.style.left = "0px";
3194         if(border && border.weight && border.weight > 0)
3195         {
3196             border.color = border.color || "#000";
3197             border.alpha = border.alpha || 1;
3198             this.lineStyle(border.weight, border.color, border.alpha);
3199         }
3200         if(fill.type === "radial" || fill.type === "linear")
3201         {
3202             this.beginGradientFill(fill);
3203         }
3204         else if(fill.type === "bitmap")
3205         {
3206             this.beginBitmapFill(fill);
3207         }   
3208         else
3209         {
3210             this.beginFill(fill.color, fill.alpha);
3211         }
3212         switch(type)
3213         {
3214             case "circle" :
3215                 this.drawEllipse(0, 0, w, h);
3216             break;
3217             case "rect" :
3218                 this.drawRect(0, 0, w, h);
3219             break;
3220             case "wedge" :
3221                 this.drawWedge(this.props);
3222             break;
3223         }
3224         return this;       
3225     },
3226
3227     /**
3228      * Updates the properties of the shape instance.
3229      *
3230      * @method update
3231      * @param {Object} cfg Object literal containing properties to update.
3232      */
3233     update: function(cfg)
3234     {
3235         this._setProps(cfg);
3236         this._validate();
3237         return this;
3238     },
3239
3240     /**
3241      * Sets the visibility of a shape.
3242      * 
3243      * @method toggleVisible
3244      * @param {Boolean} val indicates whether or not the shape is visible.
3245      */
3246     toggleVisible: function(val)
3247     {
3248         var visibility = val ? "visible" : "hidden";
3249         if(this.node)
3250         {
3251             this.node.style.visibility = visibility;
3252         }
3253     },
3254
3255     /**
3256      * Positions the parent node of the shape.
3257      *
3258      * @method setPosition
3259      * @param {Number}, x The x-coordinate
3260      * @param {Number}, y The y-coordinate
3261      */
3262     setPosition: function(x, y)
3263     {
3264         var pNode = Y.one(this.parentNode);
3265         pNode.setStyle("position", "absolute");
3266         pNode.setStyle("left", x);
3267         pNode.setStyle("top", y);
3268     },
3269     
3270     /**
3271      * Adds a class to the shape's node.
3272      *
3273      * @method addClass
3274      * @param {String} className Name of the class to add.
3275      */
3276     addClass: function(val)
3277     {
3278         if(this.node)
3279         {
3280             this.node.style.pointerEvents = "painted";
3281             this.node.setAttribute("class", val);
3282         }
3283     }
3284 });
3285
3286 Y.CanvasShape = CanvasShape;
3287
3288 if(DRAWINGAPI == "canvas")
3289 {
3290     Y.Shape = Y.CanvasShape;
3291 }
3292 /**
3293  * VMLShape is a fallback class for Shape. It creates a graphic object with editable properties when 
3294  * SVG is not available.
3295  *
3296  * @class VMLShape
3297  * @constructor
3298  */
3299 function VMLShape(cfg)
3300 {
3301     this._initialize(cfg);
3302     this._draw();
3303 }
3304
3305 VMLShape.prototype = {
3306     /**
3307      * Indicates the type of shape. 
3308      *
3309      * @property type 
3310      * @type string
3311      */
3312     type: "shape",
3313     
3314     /**
3315      * Initializes the graphic instance.
3316      *
3317      * @method _initialize
3318      * @private
3319      */
3320     _initialize: function(cfg) 
3321     {
3322         if(!cfg.graphic)
3323         {
3324             cfg.graphic = new Y.Graphic();
3325         }
3326         this._setProps(cfg);
3327     },
3328
3329     /**
3330      * @private
3331      */
3332     width: 0,
3333
3334     /**
3335      * @private
3336      */
3337     height: 0,
3338
3339     /**
3340      * Updates properties for the shape.
3341      *
3342      * @method _setProps
3343      * @param {Object} cfg Properties to update.
3344      * @private
3345      */
3346     _setProps: function(cfg) {
3347         this.width = cfg.width && cfg.width >= 0 ? cfg.width : this.width;
3348         this.height = cfg.height && cfg.height >= 0 ? cfg.height : this.height;
3349         this.border = cfg.border || this.border;
3350         this.graphics = cfg.graphic || this.graphics;
3351         this.canvas = this.graphics;
3352         this.parentNode = this.graphics.node;
3353         this.fill = cfg.fill || this.fill;
3354         this.type = cfg.shape || this.type;
3355         this.props = cfg.props || this.props;
3356     },
3357
3358     /**
3359      * Draws the graphic.
3360      *
3361      * @method _draw
3362      * @private
3363      */
3364     _draw: function()
3365     {
3366         var path,
3367             borderWeight = 0,
3368             fillWidth = this.width || 0,
3369             fillHeight = this.height || 0;
3370         this.graphics.setSize(fillWidth, fillHeight);
3371         if(this.node)
3372         {
3373             this.node.style.visible = "hidden";
3374         }
3375         else if(!this.node)
3376         {
3377             this.node = this.graphics._createGraphicNode(this.graphics._getNodeShapeType(this.type));
3378             this.graphics.node.appendChild(this.node);
3379         }
3380         if(this.type === "wedge")
3381         {
3382             path = this.graphics._getWedgePath(this.props);
3383             if(this.fill)
3384             {
3385                 path += ' x';
3386             }
3387             if(this.border)
3388             {
3389                 path += ' e';
3390             }
3391             this.node.path = path;
3392         }
3393         this._addBorder();
3394         if(this.border && this.border.weight && this.border.weight > 0)
3395         {
3396             borderWeight = this.border.weight;
3397             fillWidth -= borderWeight;
3398             fillHeight -= borderWeight;
3399         }
3400         this.node.style.width = Math.max(fillWidth, 0) + "px";
3401         this.node.style.height = Math.max(fillHeight, 0) + "px";
3402         this._addFill();
3403         return this;
3404     },
3405     
3406     /**
3407      * Adds a border to the shape node.
3408      *
3409      * @method _addBorder
3410      * @private
3411      */
3412     _addBorder: function()
3413     {
3414         if(this.border && this.border.weight && this.border.weight > 0)
3415         {
3416             var borderAlpha = this.border.alpha,
3417                 borderWeight = this.borderWeight;
3418             borderAlpha = Y.Lang.isNumber(borderAlpha) ? borderAlpha : 1;
3419             borderWeight = Y.Lang.isNumber(borderWeight) ? borderWeight : 1;
3420             this.node.strokecolor = this.border.color || "#000000";
3421             this.node.strokeweight = borderWeight;
3422             if(borderAlpha < 1)
3423             {
3424                 if(!this._strokeNode)
3425                 {
3426                     this._strokeNode = this.graphics._createGraphicNode("stroke");
3427                     this.node.appendChild(this._strokeNode);
3428                 }
3429                 this._strokeNode.opacity = borderAlpha;
3430             }
3431             else if(this._strokeNode)
3432             {
3433                 this._strokeNode.opacity = borderAlpha;
3434             }
3435             this.node.stroked = true;
3436         }
3437         else
3438         {
3439             this.node.stroked = false;
3440         }
3441     },
3442
3443     /**
3444      * Adds a fill to the shape node.
3445      *
3446      * @method _addFill
3447      * @private
3448      */
3449     _addFill: function()
3450     {
3451         var fillAlpha;
3452         this.node.filled = true;
3453         if(this.fill.type === "linear" || this.fill.type === "radial")
3454         {
3455             this.graphics.beginGradientFill(this.fill);
3456             this.node.appendChild(this.graphics._getFill());
3457         }
3458         else if(this.fill.type === "bitmap")
3459         {
3460             this.graphics.beginBitmapFill(this.fill);
3461             this.node.appendChild(this.graphics._getFill());
3462         }
3463         else
3464         {
3465             if(!this.fill.color)
3466             {
3467                 this.node.filled = false;
3468             }
3469             else
3470             {
3471                 if(this.fillnode)
3472                 {
3473                     this.graphics._removeChildren(this.fillnode);
3474                 }
3475                 fillAlpha = this.fill.alpha;
3476                 fillAlpha = Y.Lang.isNumber(fillAlpha) ? fillAlpha : 1;
3477                 this.fill.alpha = fillAlpha;
3478                 this.fillnode = this.graphics._createGraphicNode("fill");
3479                 this.fillnode.type = "solid";
3480                 this.fillnode.color = this.fill.color;
3481                 this.fillnode.opacity = fillAlpha;
3482                 this.node.appendChild(this.fillnode);
3483             }
3484         }
3485     },
3486     
3487     /**
3488      * Adds a class to the shape's node.
3489      *
3490      * @method addClass
3491      * @param {String} className Name of the class to add.
3492      */
3493     addClass: function(val)
3494     {
3495         var node = this.node;
3496         if(node)
3497         {
3498             Y.one(node).addClass(val);
3499         }
3500     },
3501
3502     /**
3503      * Sets the visibility of a shape.
3504      * 
3505      * @method toggleVisible
3506      * @param {Boolean} val indicates whether or not the shape is visible.
3507      */
3508     toggleVisible: function(val)
3509     {
3510         var visibility = val ? "visible" : "hidden";
3511         if(this.node)
3512         {
3513             Y.one(this.node).setStyle("visibility", visibility);
3514         }
3515     },
3516
3517     /**
3518      * Positions the parent node of the shape.
3519      *
3520      * @method setPosition
3521      * @param {Number}, x The x-coordinate
3522      * @param {Number}, y The y-coordinate
3523      */
3524     setPosition: function(x, y)
3525     {
3526         var pNode = Y.one(this.parentNode);
3527         pNode.setStyle("position", "absolute");
3528         pNode.setStyle("left", x);
3529         pNode.setStyle("top", y);
3530     },
3531     
3532     /**
3533      * Updates the properties of the shape instance.
3534      *
3535      * @method update
3536      * @param {Object} cfg Object literal containing properties to update.
3537      */
3538     update: function(cfg)
3539     {
3540         this._setProps(cfg);
3541         this._draw();
3542         return this;
3543     }
3544 };
3545
3546 Y.VMLShape = VMLShape;
3547
3548 if (DRAWINGAPI == "vml") {
3549     Y.Shape = VMLShape;
3550 }
3551 /**
3552  * The Renderer class is a base class for chart components that use the <code>styles</code>
3553  * attribute.
3554  *
3555  * @class Renderer
3556  * @constructor
3557  */
3558 function Renderer(){}
3559
3560 Renderer.ATTRS = {
3561         /**
3562          * Hash of style properties for class
3563          * 
3564          * @attribute styles
3565          * @type Object
3566          */
3567         styles:
3568         {
3569             getter: function()
3570             {
3571                 this._styles = this._styles || this._getDefaultStyles();
3572                 return this._styles;
3573             },
3574
3575             setter: function(val)
3576             {
3577                 this._styles = this._setStyles(val);
3578             }
3579         },
3580         
3581         /**
3582          * The graphic in which drawings will be rendered.
3583          *
3584          * @attribute graphic
3585          * @type Graphic
3586          */
3587         graphic: {}
3588 };
3589 Renderer.NAME = "renderer";
3590
3591 Renderer.prototype = {
3592     /**
3593      * @private
3594      */
3595         _styles: null,
3596         
3597     /**
3598      * @protected
3599      *
3600      * Method used by <code>styles</code> setter.
3601      *
3602      * @method _setStyles
3603      * @param {Object} newStyles Hash of properties to update.
3604      * @return Object
3605      */
3606         _setStyles: function(newstyles)
3607         {
3608                 var styles = this.get("styles");
3609         return this._mergeStyles(newstyles, styles);
3610         },
3611     
3612     /**
3613      * @protected
3614      *
3615      * Merges to object literals so that only specified properties are 
3616      * overwritten.
3617      *
3618      * @method _mergeStyles
3619      * @param {Object} a Hash of new styles
3620      * @param {Object} b Hash of original styles
3621      * @return Object
3622      */
3623     _mergeStyles: function(a, b)
3624     {
3625         if(!b)
3626         {
3627             b = {};
3628         }
3629         var newstyles = Y.merge(b, {});
3630         Y.Object.each(a, function(value, key, a)
3631         {
3632             if(b.hasOwnProperty(key) && Y.Lang.isObject(value) && !Y.Lang.isArray(value))
3633             {
3634                 newstyles[key] = this._mergeStyles(value, b[key]);
3635             }
3636             else
3637             {
3638                 newstyles[key] = value;
3639             }
3640         }, this);
3641         return newstyles;
3642     },
3643
3644     /**
3645      * @protected
3646      *
3647      * Gets the default value for the <code>styles</code> attribute. 
3648      *
3649      * @method _getDefaultStyles
3650      * @return Object
3651      */
3652     _getDefaultStyles: function()
3653     {
3654         return {padding:{
3655             top:0,
3656             right: 0,
3657             bottom: 0,
3658             left: 0
3659         }};
3660     }
3661 };
3662
3663 Y.augment(Renderer, Y.Attribute);
3664 Y.Renderer = Renderer;
3665
3666 /**
3667  * The Axis class. Generates axes for a chart.
3668  *
3669  * @class Axis
3670  * @extends Renderer
3671  * @constructor
3672  */
3673 Y.Axis = Y.Base.create("axis", Y.Widget, [Y.Renderer], {
3674     /**
3675      * @private
3676      */
3677     _dataChangeHandler: function(e)
3678     {
3679         if(this.get("rendered"))
3680         {
3681             this._drawAxis();
3682         }
3683     },
3684
3685     /**
3686      * @private
3687      */
3688     _updateHandler: function(e)
3689     {
3690         if(this.get("rendered"))
3691         {
3692             this._drawAxis();
3693         }
3694     },
3695
3696     /**
3697      * @private
3698      */
3699     _positionChangeHandler: function(e)
3700     {
3701         var position = this.get("position");
3702         if(position == "none")
3703         {
3704             return;
3705         }
3706         this._layout =this.getLayout(this.get("position"));
3707         if(this.get("rendered"))
3708         {
3709             this._drawAxis();
3710         }
3711     },
3712
3713     /**
3714      * @private
3715      */
3716     renderUI: function()
3717     {
3718         var pos = this.get("position");
3719         if(pos && pos != "none")
3720         {
3721             this._layout =this.getLayout(pos);
3722             this._setCanvas();
3723         }
3724     },
3725    
3726     /**
3727      * @private
3728      */
3729     syncUI: function()
3730     {
3731         this._drawAxis();
3732     },
3733
3734     /**
3735      * @private
3736      */
3737     _setCanvas: function()
3738     {
3739         var cb = this.get("contentBox"),
3740             bb = this.get("boundingBox"),
3741             p = this.get("position"),
3742             pn = this._parentNode,
3743             w = this.get("width"),
3744             h = this.get("height");
3745         bb.setStyle("position", "absolute");
3746         w = w ? w + "px" : pn.getStyle("width");
3747         h = h ? h + "px" : pn.getStyle("height");
3748         if(p === "top" || p === "bottom")
3749         {
3750             cb.setStyle("width", w);
3751         }
3752         else
3753         {
3754             cb.setStyle("height", h);
3755         }
3756         cb.setStyle("position", "relative");
3757         cb.setStyle("left", "0px");
3758         cb.setStyle("top", "0px");
3759         this.set("graphic", new Y.Graphic());
3760         this.get("graphic").render(cb);
3761     },
3762         
3763     /**
3764      * @protected
3765      *
3766      * Gets the default value for the <code>styles</code> attribute. Overrides
3767      * base implementation.
3768      *
3769      * @method _getDefaultStyles
3770      * @return Object
3771      */
3772     _getDefaultStyles: function()
3773     {
3774         var axisstyles = {
3775             majorTicks: {
3776                 display:"inside",
3777                 length:4,
3778                 color:"#dad8c9",
3779                 weight:1,
3780                 alpha:1
3781             },
3782             minorTicks: {
3783                 display:"none",
3784                 length:2,
3785                 color:"#dad8c9",
3786                 weight:1
3787             },
3788             line: {
3789                 weight:1,
3790                 color:"#dad8c9",
3791                 alpha:1
3792             },
3793             majorUnit: {
3794                 determinant:"count",
3795                 count:11,
3796                 distance:75
3797             },
3798             top: "0px",
3799             left: "0px",
3800             width: "100px",
3801             height: "100px",
3802             label: {
3803                 color:"#808080",
3804                 alpha: 1,
3805                 fontSize:"85%",
3806                 rotation: 0,
3807                 margin: {
3808                     top:4,
3809                     right:4,
3810                     bottom:4,
3811                     left:4
3812                 }
3813             },
3814             hideOverlappingLabelTicks: false
3815         };
3816         
3817         return Y.merge(Y.Renderer.prototype._getDefaultStyles(), axisstyles); 
3818     },
3819
3820     /**
3821      * @private
3822      */
3823     _handleSizeChange: function(e)
3824     {
3825         var attrName = e.attrName,
3826             pos = this.get("position"),
3827             vert = pos == "left" || pos == "right",
3828             cb = this.get("contentBox"),
3829             hor = pos == "bottom" || pos == "top";
3830         cb.setStyle("width", this.get("width"));
3831         cb.setStyle("height", this.get("height"));
3832         if((hor && attrName == "width") || (vert && attrName == "height"))
3833         {
3834             this._drawAxis();
3835         }
3836     },
3837
3838     /**
3839      * @private
3840      */
3841     _layout: null,
3842
3843     /**
3844      * @private 
3845      */
3846     getLayout: function(pos)
3847     {
3848         var l;
3849         switch(pos)
3850         {
3851             case "top" :
3852                 l = new Y.TopAxisLayout({axisRenderer:this});
3853             break;
3854             case "bottom" : 
3855                 l = new Y.BottomAxisLayout({axisRenderer:this});
3856             break;
3857             case "left" :
3858                 l = new Y.LeftAxisLayout({axisRenderer:this});
3859             break;
3860             case "right" :
3861                 l = new Y.RightAxisLayout({axisRenderer:this});
3862             break;
3863         }
3864         return l;
3865     },
3866     
3867     /**
3868      * @private
3869      */
3870     drawLine: function(startPoint, endPoint, line)
3871     {
3872         var graphic = this.get("graphic");
3873         graphic.lineStyle(line.weight, line.color, line.alpha);
3874         graphic.moveTo(startPoint.x, startPoint.y);
3875         graphic.lineTo(endPoint.x, endPoint.y);
3876         graphic.end();
3877     },
3878
3879     /**
3880      * @private
3881      */
3882     _drawAxis: function ()
3883     {
3884         if(this._drawing)
3885         {
3886             this._callLater = true;
3887             return;
3888         }
3889         this._drawing = true;
3890         this._callLater = false;
3891         if(this.get("position") != "none")
3892         {
3893             var styles = this.get("styles"),
3894                 majorTickStyles = styles.majorTicks,
3895                 drawTicks = majorTickStyles.display != "none",
3896                 tickPoint,
3897                 majorUnit = styles.majorUnit,
3898                 len,
3899                 majorUnitDistance,
3900                 i = 0,
3901                 layoutLength,
3902                 position,
3903                 lineStart,
3904                 label,
3905                 layout = this._layout,
3906                 labelFunction = this.get("labelFunction"),
3907                 labelFunctionScope = this.get("labelFunctionScope"),
3908                 labelFormat = this.get("labelFormat"),
3909                 graphic = this.get("graphic");
3910             graphic.clear();
3911             layout.setTickOffsets();
3912             layoutLength = this.getLength();
3913             lineStart = layout.getLineStart();
3914             len = this.getTotalMajorUnits(majorUnit);
3915             majorUnitDistance = this.getMajorUnitDistance(len, layoutLength, majorUnit);
3916             this.set("edgeOffset", this.getEdgeOffset(len, layoutLength) * 0.5);
3917             tickPoint = this.getFirstPoint(lineStart);
3918             this.drawLine(lineStart, this.getLineEnd(tickPoint), styles.line);
3919             if(drawTicks) 
3920             {
3921                layout.drawTick(tickPoint, majorTickStyles);
3922             }
3923             if(len < 1)
3924             {
3925                 this._clearLabelCache();
3926                 return;
3927             }
3928             this._createLabelCache();
3929             this._tickPoints = [];
3930             layout.set("maxLabelSize", 0); 
3931             for(; i < len; ++i)
3932             {
3933                 if(drawTicks) 
3934                 {
3935                     layout.drawTick(tickPoint, majorTickStyles);
3936                 }
3937                 position = this.getPosition(tickPoint);
3938                 label = this.getLabel(tickPoint);
3939                 label.innerHTML = labelFunction.apply(labelFunctionScope, [this.getLabelByIndex(i, len), labelFormat]);
3940                 tickPoint = this.getNextPoint(tickPoint, majorUnitDistance);
3941             }
3942             this._clearLabelCache();
3943             layout.setSizeAndPosition();
3944             if(this.get("overlapGraph"))
3945             {
3946                layout.offsetNodeForTick(this.get("contentBox"));
3947             }
3948             layout.setCalculatedSize();
3949             for(i = 0; i < len; ++i)
3950             {
3951                 layout.positionLabel(this.get("labels")[i], this._tickPoints[i]);
3952             }
3953         }
3954         this._drawing = false;
3955         if(this._callLater)
3956         {
3957             this._drawAxis();
3958         }
3959         else
3960         {
3961             this.fire("axisRendered");
3962         }
3963     },
3964
3965     /**
3966      * @private
3967      */
3968     _labels: null,
3969
3970     /**
3971      * @private 
3972      */
3973     _labelCache: null,
3974
3975     /**
3976      * @private
3977      */
3978     getLabel: function(pt, pos)
3979     {
3980         var i,
3981             label,
3982             customStyles = {
3983                 rotation: "rotation",
3984                 margin: "margin",
3985                 alpha: "alpha"
3986             },
3987             cache = this._labelCache,
3988             styles = this.get("styles").label;
3989         if(cache.length > 0)
3990         {
3991             label = cache.shift();
3992         }
3993         else
3994         {
3995             label = document.createElement("span");
3996             label.style.display = "block";
3997             label.style.whiteSpace = "nowrap";
3998             Y.one(label).addClass("axisLabel");
3999             this.get("contentBox").appendChild(label);
4000         }
4001         label.style.position = "absolute";
4002         this._labels.push(label);
4003         this._tickPoints.push({x:pt.x, y:pt.y});
4004         this._layout.updateMaxLabelSize(label);
4005         for(i in styles)
4006         {
4007             if(styles.hasOwnProperty(i) && !customStyles.hasOwnProperty(i))
4008             {
4009                 label.style[i] = styles[i];
4010             }
4011         }
4012         return label;
4013     },   
4014
4015     /**
4016      * @private
4017      */
4018     _createLabelCache: function()
4019     {
4020         if(this._labels)
4021         {
4022             if(this._labelCache)
4023             {
4024                 this._labelCache = this._labels.concat(this._labelCache);
4025             }
4026             else
4027             {
4028                 this._labelCache = this._labels.concat();
4029             }
4030         }
4031         else
4032         {
4033             this._clearLabelCache();
4034         }
4035         this._labels = [];
4036     },
4037     
4038     /**
4039      * @private
4040      */
4041     _clearLabelCache: function()
4042     {
4043         if(this._labelCache)
4044         {
4045             var len = this._labelCache.length,
4046                 i = 0,
4047                 label,
4048                 labelCache = this._labelCache;
4049             for(; i < len; ++i)
4050             {
4051                 label = labelCache[i];
4052                 label.parentNode.removeChild(label);
4053             }
4054         }
4055         this._labelCache = [];
4056     },
4057
4058     /**
4059      * @private
4060      */
4061     _calculateSizeByTickLength: true,
4062
4063     /**
4064      * @private 
4065      */
4066     getLineEnd: function(pt)
4067     {
4068         var w = this.get("width"),
4069             h = this.get("height"),
4070             pos = this.get("position");
4071         if(pos === "top" || pos === "bottom")
4072         {
4073             return {x:w, y:pt.y};
4074         }
4075         else
4076         {
4077             return {x:pt.x, y:h};
4078         }
4079     },
4080
4081     /**
4082      * @private
4083      */
4084     getLength: function()
4085     {
4086         var l,
4087             style = this.get("styles"),
4088             padding = style.padding,
4089             w = this.get("width"),
4090             h = this.get("height"),
4091             pos = this.get("position");
4092         if(pos === "top" || pos === "bottom")
4093         {
4094             l = w - (padding.left + padding.right);
4095         }
4096         else
4097         {
4098             l = h - (padding.top + padding.bottom);
4099         }
4100         return l;
4101     },
4102
4103     /**
4104      * @private
4105      */
4106     getFirstPoint:function(pt)
4107     {
4108         var style = this.get("styles"),
4109             pos = this.get("position"),
4110             padding = style.padding,
4111             np = {x:pt.x, y:pt.y};
4112         if(pos === "top" || pos === "bottom")
4113         {
4114             np.x += padding.left + this.get("edgeOffset");
4115         }
4116         else
4117         {
4118             np.y += this.get("height") - (padding.top + this.get("edgeOffset"));
4119         }
4120         return np;
4121     },
4122
4123     /**
4124      * @private
4125      */
4126     getNextPoint: function(point, majorUnitDistance)
4127     {
4128         var pos = this.get("position");
4129         if(pos === "top" || pos === "bottom")
4130         {
4131             point.x = point.x + majorUnitDistance;              
4132         }
4133         else
4134         {
4135             point.y = point.y - majorUnitDistance;
4136         }
4137         return point;
4138     },
4139
4140     /**
4141      * @private 
4142      */
4143     getLastPoint: function()
4144     {
4145         var style = this.get("styles"),
4146             padding = style.padding,
4147             w = this.get("width"),
4148             pos = this.get("position");
4149         if(pos === "top" || pos === "bottom")
4150         {
4151             return {x:w - padding.right, y:padding.top};
4152         }
4153         else
4154         {
4155             return {x:padding.left, y:padding.top};
4156         }
4157     },
4158
4159     /**
4160      * @private 
4161      */
4162     getPosition: function(point)
4163     {
4164         var p,
4165             h = this.get("height"),
4166             style = this.get("styles"),
4167             padding = style.padding,
4168             pos = this.get("position"),
4169             dataType = this.get("dataType");
4170         if(pos === "left" || pos === "right") 
4171         {
4172             //Numeric data on a vertical axis is displayed from bottom to top.
4173             //Categorical and Timeline data is displayed from top to bottom.
4174             if(dataType === "numeric")
4175             {
4176                 p = (h - (padding.top + padding.bottom)) - (point.y - padding.top);
4177             }
4178             else
4179             {
4180                 p = point.y - padding.top;
4181             }
4182         }
4183         else
4184         {
4185             p = point.x - padding.left;
4186         }
4187         return p;
4188     }
4189 }, {
4190     ATTRS: 
4191     {
4192         /**
4193          * @protected
4194          *
4195          * Difference betweend the first/last tick and edge of axis.
4196          *
4197          * @attribute edgeOffset
4198          * @type Number
4199          */
4200         edgeOffset: 
4201         {
4202             value: 0
4203         },
4204
4205         /**
4206          * The graphic in which the axis line and ticks will be rendered.
4207          *
4208          * @attribute graphic
4209          * @type Graphic
4210          */
4211         graphic: {},
4212         
4213         /**
4214          * Contains the contents of the axis. 
4215          *
4216          * @attribute node
4217          * @type HTMLElement
4218          */
4219         node: {},
4220
4221         /**
4222          * Direction of the axis.
4223          *
4224          * @attribute position
4225          * @type String
4226          */
4227         position: {
4228             lazyAdd: false,
4229
4230             setOnce: true,
4231
4232             setter: function(val)
4233             {
4234                 if(val == "none")
4235                 {
4236                     this.bindUI();
4237                 }
4238                 return val;
4239             }
4240         },
4241
4242         /**
4243          * Distance determined by the tick styles used to calculate the distance between the axis
4244          * line in relation to the top of the axis.
4245          *
4246          * @attribute topTickOffset
4247          * @type Number
4248          */
4249         topTickOffset: {
4250             value: 0
4251         },
4252
4253         /**
4254          * Distance determined by the tick styles used to calculate the distance between the axis
4255          * line in relation to the bottom of the axis.
4256          *
4257          * @attribute bottomTickOffset
4258          * @type Number
4259          */
4260         bottomTickOffset: {
4261             value: 0
4262         },
4263
4264         /**
4265          * Distance determined by the tick styles used to calculate the distance between the axis
4266          * line in relation to the left of the axis.
4267          *
4268          * @attribute leftTickOffset
4269          * @type Number
4270          */
4271         leftTickOffset: {
4272             value: 0
4273         },
4274
4275         /**
4276          * Distance determined by the tick styles used to calculate the distance between the axis
4277          * line in relation to the right side of the axis.
4278          *
4279          * @attribute rightTickOffset
4280          * @type Number
4281          */
4282         rightTickOffset: {
4283             value: 0
4284         },
4285         
4286         /**
4287          * Collection of labels used to render the axis.
4288          *
4289          * @attribute labels
4290          * @type Array
4291          */
4292         labels: {
4293             readOnly: true,
4294             getter: function()
4295             {
4296                 return this._labels;
4297             }
4298         },
4299
4300         /**
4301          * Collection of points used for placement of labels and ticks along the axis.
4302          *
4303          * @attribute tickPoints
4304          * @type Array
4305          */
4306         tickPoints: {
4307             readOnly: true,
4308
4309             getter: function()
4310             {
4311                 if(this.get("position") == "none")
4312                 {
4313                     return this.get("styles").majorUnit.count;
4314                 }
4315                 return this._tickPoints;
4316             }
4317         },
4318
4319         /**
4320          * Indicates whether the axis overlaps the graph. If an axis is the inner most axis on a given
4321          * position and the tick position is inside or cross, the axis will need to overlap the graph.
4322          *
4323          * @attribute overlapGraph
4324          * @type Boolean
4325          */
4326         overlapGraph: {
4327             value:true,
4328
4329             validator: function(val)
4330             {
4331                 return Y.Lang.isBoolean(val);
4332             }
4333         },
4334
4335         /**
4336          * Object which should have by the labelFunction
4337          *
4338          * @attribute labelFunctionScope
4339          * @type Object
4340          */
4341         labelFunctionScope: {}
4342             
4343         /**
4344          * Style properties used for drawing an axis. This attribute is inherited from <code>Renderer</code>. Below are the default values:
4345          *  <dl>
4346          *      <dt>majorTicks</dt><dd>Properties used for drawing ticks.
4347          *          <dl>
4348          *              <dt>display</dt><dd>Position of the tick. Possible values are <code>inside</code>, <code>outside</code>, <code>cross</code> and <code>none</code>. The
4349          *              default value is <code>inside</code>.</dd>
4350          *              <dt>length</dt><dd>The length (in pixels) of the tick. The default value is 4.</dd>
4351          *              <dt>color</dt><dd>The color of the tick. The default value is <code>#dad8c9</code></dd>
4352          *              <dt>weight</dt><dd>Number indicating the width of the tick. The default value is 1.</dd>
4353          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the tick. The default value is 1.</dd>
4354          *          </dl>
4355          *      </dd>
4356          *      <dt>line</dt><dd>Properties used for drawing the axis line. 
4357          *          <dl>
4358          *              <dt>weight</dt><dd>Number indicating the width of the axis line. The default value is 1.</dd>
4359          *              <dt>color</dt><dd>The color of the axis line. The default value is <code>#dad8c9</code>.</dd>
4360          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the tick. The default value is 1.</dd>
4361          *          </dl>
4362          *      </dd>
4363          *      <dt>majorUnit</dt><dd>Properties used to calculate the <code>majorUnit</code> for the axis. 
4364          *          <dl>
4365          *              <dt>determinant</dt><dd>The algorithm used for calculating distance between ticks. The possible options are <code>count</code> and <code>distance</code>. If
4366          *              the <code>determinant</code> is <code>count</code>, the axis ticks will spaced so that a specified number of ticks appear on the axis. If the <code>determinant</code>
4367          *              is <code>distance</code>, the axis ticks will spaced out according to the specified distance. The default value is <code>count</code>.</dd>
4368          *              <dt>count</dt><dd>Number of ticks to appear on the axis when the <code>determinant</code> is <code>count</code>. The default value is 11.</dd>
4369          *              <dt>distance</dt><dd>The distance (in pixels) between ticks when the <code>determinant</code> is <code>distance</code>. The default value is 75.</dd>
4370          *          </dl>
4371          *      </dd>
4372          *      <dt>label</dt><dd>Properties and styles applied to the axis labels.
4373          *          <dl>
4374          *              <dt>color</dt><dd>The color of the labels. The default value is <code>#808080</code>.</dd>
4375          *              <dt>alpha</dt><dd>Number between 0 and 1 indicating the opacity of the labels. The default value is 1.</dd>
4376          *              <dt>fontSize</dt><dd>The font-size of the labels. The default value is 85%</dd>
4377          *              <dt>rotation</dt><dd>The rotation, in degrees (between -90 and 90) of the labels. The default value is 0.</dd>
4378          *              <dt>margin</dt><dd>The distance between the label and the axis/tick. Depending on the position of the <code>Axis</code>, only one of the properties used.
4379          *                  <dl>
4380          *                      <dt>top</dt><dd>Pixel value used for an axis with a <code>position</code> of <code>bottom</code>. The default value is 4.</dd>
4381          *                      <dt>right</dt><dd>Pixel value used for an axis with a <code>position</code> of <code>left</code>. The default value is 4.</dd>
4382          *                      <dt>bottom</dt><dd>Pixel value used for an axis with a <code>position</code> of <code>top</code>. The default value is 4.</dd>
4383          *                      <dt>left</dt><dd>Pixel value used for an axis with a <code>position</code> of <code>right</code>. The default value is 4.</dd>
4384          *                  </dl>
4385          *              </dd>
4386          *          </dl>
4387          *      </dd>
4388          *  </dl>
4389          *
4390          * @attribute styles
4391          * @type Object
4392          */
4393     }
4394 });
4395 /**
4396  * Algorithmic strategy for rendering a left axis.
4397  *
4398  * @class LeftAxisLayout
4399  * @extends Base
4400  * @param {Object} config
4401  * @constructor
4402  */
4403 function LeftAxisLayout(config)
4404 {
4405     LeftAxisLayout.superclass.constructor.apply(this, arguments);
4406 }
4407
4408 LeftAxisLayout.ATTRS = {
4409     /**
4410      * Reference to the <code>Axis</code> using the strategy.
4411      *
4412      * @attribute axisRenderer
4413      * @type Axis
4414      * @protected
4415      */
4416     axisRenderer: {
4417         value: null
4418     },
4419
4420     /**
4421      * @private
4422      */
4423     maxLabelSize: {
4424         value: 0
4425     }
4426 };
4427
4428 Y.extend(LeftAxisLayout, Y.Base, {
4429     /**
4430      * Sets the length of the tick on either side of the axis line.
4431      *
4432      * @method setTickOffset
4433      * @protected
4434      */
4435     setTickOffsets: function()
4436     {
4437         var ar = this.get("axisRenderer"),
4438             majorTicks = ar.get("styles").majorTicks,
4439             tickLength = majorTicks.length,
4440             halfTick = tickLength * 0.5,
4441             display = majorTicks.display;
4442         ar.set("topTickOffset",  0);
4443         ar.set("bottomTickOffset",  0);
4444         
4445         switch(display)
4446         {
4447             case "inside" :
4448                 ar.set("rightTickOffset",  tickLength);
4449                 ar.set("leftTickOffset", 0);
4450             break;
4451             case "outside" : 
4452                 ar.set("rightTickOffset", 0);
4453                 ar.set("leftTickOffset",  tickLength);
4454             break;
4455             case "cross":
4456                 ar.set("rightTickOffset", halfTick); 
4457                 ar.set("leftTickOffset",  halfTick);
4458             break;
4459             default:
4460                 ar.set("rightTickOffset", 0);
4461                 ar.set("leftTickOffset", 0);
4462             break;
4463         }
4464     },
4465     
4466     /**
4467      * Draws a tick
4468      *
4469      * @method drawTick
4470      * @param {Object} pt Point on the axis in which the tick will intersect.
4471      * @param {Object) tickStyle Hash of properties to apply to the tick.
4472      * @protected
4473      */
4474     drawTick: function(pt, tickStyles)
4475     {
4476         var ar = this.get("axisRenderer"),
4477             style = ar.get("styles"),
4478             padding = style.padding,
4479             tickLength = tickStyles.length,
4480             start = {x:padding.left, y:pt.y},
4481             end = {x:tickLength + padding.left, y:pt.y};
4482         ar.drawLine(start, end, tickStyles);
4483     },
4484
4485     /**
4486      * Calculates the coordinates for the first point on an axis.
4487      *
4488      * @method getLineStart
4489      * @return {Object}
4490      * @protected
4491      */
4492     getLineStart: function()
4493     {
4494         var ar = this.get("axisRenderer"),
4495             style = ar.get("styles"),
4496             padding = style.padding,
4497             majorTicks = style.majorTicks,
4498             tickLength = majorTicks.length,
4499             display = majorTicks.display,
4500             pt = {x:padding.left, y:0};
4501         if(display === "outside")
4502         {
4503             pt.x += tickLength;
4504         }
4505         else if(display === "cross")
4506         {
4507             pt.x += tickLength/2;
4508         }
4509         return pt; 
4510     },
4511     
4512     /**
4513      * Calculates the point for a label.
4514      *
4515      * @method getLabelPoint
4516      * @param {Object} point Point on the axis in which the tick will intersect.
4517      * @return {Object} 
4518      * @protected
4519      */
4520     getLabelPoint: function(point)
4521     {
4522         var ar = this.get("axisRenderer");
4523         return {x:point.x - ar.get("leftTickOffset"), y:point.y};
4524     },
4525     
4526     /**
4527      * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
4528      *
4529      * @method updateMaxLabelSize
4530      * @param {HTMLElement} label to measure
4531      * @protected
4532      */
4533     updateMaxLabelSize: function(label)
4534     {
4535         var ar = this.get("axisRenderer"),
4536             style = ar.get("styles").label,
4537             rot =  Math.min(90, Math.max(-90, style.rotation)),
4538             absRot = Math.abs(rot),
4539             radCon = Math.PI/180,
4540             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
4541             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
4542             m11 = cosRadians,
4543             m12 = rot > 0 ? -sinRadians : sinRadians,
4544             m21 = -m12,
4545             m22 = m11,
4546             max;
4547         if(!document.createElementNS)
4548         {
4549             label.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
4550             this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), label.offsetWidth));
4551         }
4552         else
4553         {
4554             label.style.msTransform = "rotate(0deg)";
4555             if(rot === 0)
4556             {
4557                 max = label.offsetWidth;
4558             }
4559             else if(absRot === 90)
4560             {
4561                 max = label.offsetHeight;
4562             }
4563             else
4564             {
4565                 max = (cosRadians * label.offsetWidth) + (sinRadians * label.offsetHeight);
4566             }
4567             this.set("maxLabelSize",  Math.max(this.get("maxLabelSize"), max));
4568         }
4569     },
4570
4571     /**
4572      * Rotate and position labels.
4573      *
4574      * @method positionLabel
4575      * @param {HTMLElement} label to rotate position
4576      * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned
4577      * against.
4578      * @protected
4579      */
4580     positionLabel: function(label, pt)
4581     {
4582         var ar = this.get("axisRenderer"),
4583             tickOffset = ar.get("leftTickOffset"),
4584             style = ar.get("styles").label,
4585             labelAlpha = style.alpha,
4586             filterString,
4587             margin = 0,
4588             leftOffset = pt.x,
4589             topOffset = pt.y,
4590             rot =  Math.min(90, Math.max(-90, style.rotation)),
4591             absRot = Math.abs(rot),
4592             radCon = Math.PI/180,
4593             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
4594             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
4595             m11 = cosRadians,
4596             m12 = rot > 0 ? -sinRadians : sinRadians,
4597             m21 = -m12,
4598             m22 = m11,
4599             maxLabelSize = this.get("maxLabelSize"),
4600             labelWidth = Math.round(label.offsetWidth),
4601             labelHeight = Math.round(label.offsetHeight);
4602         if(style.margin && style.margin.right)
4603         {
4604             margin = style.margin.right;
4605         }
4606         if(!document.createElementNS)
4607         {
4608             label.style.filter = null; 
4609             labelWidth = Math.round(label.offsetWidth);
4610             labelHeight = Math.round(label.offsetHeight);
4611             if(rot === 0)
4612             {
4613                 leftOffset = labelWidth;
4614                 topOffset -= labelHeight * 0.5;
4615             }
4616             else if(absRot === 90)
4617             {
4618                 leftOffset = labelHeight;
4619                 topOffset -= labelWidth * 0.5;
4620             }
4621             else if(rot > 0)
4622             {
4623                 leftOffset = (cosRadians * labelWidth) + (labelHeight * rot/90);
4624                 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight * 0.5));
4625             }
4626             else
4627             {
4628                 leftOffset = (cosRadians * labelWidth) + (absRot/90 * labelHeight);
4629                 topOffset -= cosRadians * (labelHeight * 0.5);
4630             }
4631             leftOffset += tickOffset;
4632             label.style.left = ((pt.x + maxLabelSize) - leftOffset) + "px";
4633             label.style.top = topOffset + "px";
4634             if(filterString)
4635             {
4636                 filterString += " ";
4637             }
4638             if(Y.Lang.isNumber(labelAlpha) && labelAlpha < 1 && labelAlpha > -1 && !isNaN(labelAlpha))
4639             {
4640                 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
4641             }
4642             if(rot !== 0)
4643             {
4644                 if(filterString)
4645                 {
4646                     filterString += " ";
4647                 }
4648                 else
4649                 {
4650                     filterString = ""; 
4651                 }
4652                 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
4653             }
4654             if(filterString)
4655             {
4656                 label.style.filter = filterString;
4657             }
4658             return;
4659         }
4660         label.style.msTransform = "rotate(0deg)";
4661         labelWidth = Math.round(label.offsetWidth);
4662         labelHeight = Math.round(label.offsetHeight);
4663         if(rot === 0)
4664         {
4665             leftOffset -= labelWidth;
4666             topOffset -= labelHeight * 0.5;
4667         }
4668         else if(rot === 90)
4669         {
4670             topOffset -= labelWidth * 0.5;
4671         }
4672         else if(rot === -90)
4673         {
4674             leftOffset -= labelHeight;
4675             topOffset += labelWidth * 0.5;
4676         }
4677         else
4678         {
4679             if(rot < 0)
4680             {
4681                 leftOffset -= (cosRadians * labelWidth) + (sinRadians * labelHeight);
4682                 topOffset += (sinRadians * labelWidth) - (cosRadians * (labelHeight * 0.6)); 
4683             }
4684             else
4685             {
4686                 leftOffset -= (cosRadians * labelWidth);
4687                 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight * 0.6));
4688             }
4689         }
4690         leftOffset -= tickOffset;
4691         label.style.left = (this.get("maxLabelSize") + leftOffset) + "px";
4692         label.style.top = topOffset + "px";
4693         label.style.MozTransformOrigin =  "0 0";
4694         label.style.MozTransform = "rotate(" + rot + "deg)";
4695         label.style.webkitTransformOrigin = "0 0";
4696         label.style.webkitTransform = "rotate(" + rot + "deg)";
4697         label.style.msTransformOrigin =  "0 0";
4698         label.style.msTransform = "rotate(" + rot + "deg)";
4699         label.style.OTransformOrigin =  "0 0";
4700         label.style.OTransform = "rotate(" + rot + "deg)";
4701     },
4702
4703     /**
4704      * @protected
4705      *
4706      * Calculates the size and positions the content elements.
4707      *
4708      * @method setSizeAndPosition
4709      * @protected
4710      */
4711     setSizeAndPosition: function()
4712     {
4713         var labelSize = this.get("maxLabelSize"),
4714             ar = this.get("axisRenderer"),
4715             style = ar.get("styles"),
4716             leftTickOffset = ar.get("leftTickOffset"),
4717             sz = labelSize + leftTickOffset,
4718             graphic = ar.get("graphic"),
4719             margin = style.label.margin;
4720         if(margin && margin.right)
4721         {
4722             sz += margin.right;
4723         }
4724         sz = Math.round(sz);
4725         ar.set("width", sz);
4726         ar.get("contentBox").setStyle("width", sz);
4727         Y.one(graphic.node).setStyle("left", labelSize + margin.right);
4728     },
4729     
4730     /**
4731      * Adjust the position of the Axis widget's content box for internal axes.
4732      *
4733      * @method offsetNodeForTick
4734      * @param {Node} cb Content box of the Axis.
4735      * @protected
4736      */
4737     offsetNodeForTick: function(cb)
4738     {
4739     },
4740
4741     /**
4742      * Sets the width of the axis based on its contents.
4743      *
4744      * @method setCalculatedSize
4745      * @protected
4746      */
4747     setCalculatedSize: function()
4748     {
4749         var ar = this.get("axisRenderer"),
4750             style = ar.get("styles"),
4751             label = style.label,
4752             tickOffset = ar.get("leftTickOffset"),
4753             max = this.get("maxLabelSize"),
4754             ttl = Math.round(tickOffset + max + label.margin.right);
4755         ar.get("contentBox").setStyle("width", ttl);
4756         ar.set("width", ttl);
4757     }
4758 });
4759
4760 Y.LeftAxisLayout = LeftAxisLayout;
4761 /**
4762  * RightAxisLayout contains algorithms for rendering a right axis.
4763  *
4764  * @constructor
4765  * @class RightAxisLayout
4766  * @extends Base
4767  * @param {Object} config
4768  */
4769 function RightAxisLayout(config)
4770 {
4771     RightAxisLayout.superclass.constructor.apply(this, arguments);
4772 }
4773
4774 RightAxisLayout.ATTRS = {
4775     /**
4776      * Reference to the <code>Axis</code> using the strategy.
4777      *
4778      * @attribute axisRenderer
4779      * @type Axis
4780      * @protected
4781      */
4782     axisRenderer: {
4783         value: null
4784     }
4785 };
4786
4787 Y.extend(RightAxisLayout, Y.Base, {
4788     /**
4789      * Sets the length of the tick on either side of the axis line.
4790      *
4791      * @method setTickOffset
4792      * @protected
4793      */
4794     setTickOffsets: function()
4795     {
4796         var ar = this.get("axisRenderer"),
4797             majorTicks = ar.get("styles").majorTicks,
4798             tickLength = majorTicks.length,
4799             halfTick = tickLength * 0.5,
4800             display = majorTicks.display;
4801         ar.set("topTickOffset",  0);
4802         ar.set("bottomTickOffset",  0);
4803         
4804         switch(display)
4805         {
4806             case "inside" :
4807                 ar.set("leftTickOffset", tickLength);
4808                 ar.set("rightTickOffset", 0);
4809             break;
4810             case "outside" : 
4811                 ar.set("leftTickOffset", 0);
4812                 ar.set("rightTickOffset", tickLength);
4813             break;
4814             case "cross" :
4815                 ar.set("rightTickOffset", halfTick);
4816                 ar.set("leftTickOffset", halfTick);
4817             break;
4818             default:
4819                 ar.set("leftTickOffset", 0);
4820                 ar.set("rightTickOffset", 0);
4821             break;
4822         }
4823     },
4824
4825     /**
4826      * Draws a tick
4827      *
4828      * @method drawTick
4829      * @param {Object} pt Point on the axis in which the tick will intersect.
4830      * @param {Object) tickStyle Hash of properties to apply to the tick.
4831      * @protected
4832      */
4833     drawTick: function(pt, tickStyles)
4834     {
4835         var ar = this.get("axisRenderer"),
4836             style = ar.get("styles"),
4837             padding = style.padding,
4838             tickLength = tickStyles.length,
4839             start = {x:padding.left, y:pt.y},
4840             end = {x:padding.left + tickLength, y:pt.y};
4841         ar.drawLine(start, end, tickStyles);
4842     },
4843     
4844     /**
4845      * Calculates the coordinates for the first point on an axis.
4846      *
4847      * @method getLineStart
4848      * @return {Object}
4849      * @protected
4850      */
4851     getLineStart: function()
4852     {
4853         var ar = this.get("axisRenderer"),
4854             style = ar.get("styles"),
4855             padding = style.padding,
4856             majorTicks = style.majorTicks,
4857             tickLength = majorTicks.length,
4858             display = majorTicks.display,
4859             pt = {x:padding.left, y:padding.top};
4860         if(display === "inside")
4861         {
4862             pt.x += tickLength;
4863         }
4864         else if(display === "cross")
4865         {
4866             pt.x += tickLength/2;
4867         }
4868         return pt;
4869     },
4870     
4871     /**
4872      * Calculates the point for a label.
4873      *
4874      * @method getLabelPoint
4875      * @param {Object} point Point on the axis in which the tick will intersect.
4876      * @return {Object} 
4877      * @protected
4878      */
4879     getLabelPoint: function(point)
4880     {
4881         var ar = this.get("axisRenderer");
4882         return {x:point.x + ar.get("rightTickOffset"), y:point.y};
4883     },
4884     
4885     /**
4886      * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
4887      *
4888      * @method updateMaxLabelSize
4889      * @param {HTMLElement} label to measure
4890      * @protected
4891      */
4892     updateMaxLabelSize: function(label)
4893     {
4894         var ar = this.get("axisRenderer"),
4895             style = ar.get("styles").label,
4896             rot =  Math.min(90, Math.max(-90, style.rotation)),
4897             absRot = Math.abs(rot),
4898             radCon = Math.PI/180,
4899             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
4900             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
4901             m11 = cosRadians,
4902             m12 = rot > 0 ? -sinRadians : sinRadians,
4903             m21 = -m12,
4904             m22 = m11,
4905             max;
4906         if(!document.createElementNS)
4907         {
4908             label.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
4909             this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), label.offsetWidth));
4910         }
4911         else
4912         {
4913             label.style.msTransform = "rotate(0deg)";
4914             if(rot === 0)
4915             {
4916                 max = label.offsetWidth;
4917             }
4918             else if(absRot === 90)
4919             {
4920                 max = label.offsetHeight;
4921             }
4922             else
4923             {
4924                 max = (cosRadians * label.offsetWidth) + (sinRadians * label.offsetHeight);
4925             }
4926             this.set("maxLabelSize",  Math.max(this.get("maxLabelSize"), max));
4927         }
4928     },
4929
4930     /**
4931      * Rotate and position labels.
4932      *
4933      * @method positionLabel
4934      * @param {HTMLElement} label to rotate position
4935      * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned
4936      * against.
4937      * @protected
4938      */
4939     positionLabel: function(label, pt)
4940     {
4941         var ar = this.get("axisRenderer"),
4942             tickOffset = ar.get("rightTickOffset"),
4943             style = ar.get("styles").label,
4944             labelAlpha = style.alpha,
4945             filterString,
4946             margin = 0,
4947             leftOffset = pt.x,
4948             topOffset = pt.y,
4949             rot =  Math.min(Math.max(style.rotation, -90), 90),
4950             absRot = Math.abs(rot),
4951             radCon = Math.PI/180,
4952             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
4953             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
4954             m11 = cosRadians,
4955             m12 = rot > 0 ? -sinRadians : sinRadians,
4956             m21 = -m12,
4957             m22 = m11,
4958             labelWidth = Math.round(label.offsetWidth),
4959             labelHeight = Math.round(label.offsetHeight);
4960         if(style.margin && style.margin.right)
4961         {
4962             margin = style.margin.right;
4963         }
4964         if(!document.createElementNS)
4965         {
4966             label.style.filter = null;
4967             if(rot === 0)
4968             {
4969                 topOffset -= labelHeight * 0.5;
4970             }
4971             else if(absRot === 90)
4972             {
4973                 topOffset -= labelWidth * 0.5;
4974             }
4975             else if(rot > 0)
4976             {
4977                 topOffset -= (cosRadians * (labelHeight * 0.5));
4978             }
4979             else
4980             {
4981                 topOffset -= (sinRadians * labelWidth) +  (cosRadians * (labelHeight * 0.5));
4982             }
4983             leftOffset += margin;
4984             leftOffset += tickOffset;
4985             label.style.left = leftOffset + "px";
4986             label.style.top = topOffset + "px";
4987             if(Y.Lang.isNumber(labelAlpha) && labelAlpha < 1 && labelAlpha > -1 && !isNaN(labelAlpha))
4988             {
4989                 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
4990             }
4991             if(rot !== 0)
4992             {
4993                 if(filterString)
4994                 {
4995                     filterString += " ";
4996                 }
4997                 else
4998                 {
4999                     filterString = ""; 
5000                 }
5001                 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5002             }
5003             if(filterString)
5004             {
5005                 label.style.filter = filterString;
5006             }
5007             return;
5008         }
5009         label.style.msTransform = "rotate(0deg)";
5010         labelWidth = Math.round(label.offsetWidth);
5011         labelHeight = Math.round(label.offsetHeight);
5012         if(rot === 0)
5013         {
5014             topOffset -= labelHeight * 0.5;
5015         }
5016         else if(rot === 90)
5017         {
5018             leftOffset += labelHeight;
5019             topOffset -= labelWidth * 0.5;
5020         }
5021         else if(rot === -90)
5022         {
5023             topOffset += labelWidth * 0.5;
5024         }
5025         else if(rot < 0)
5026         {
5027             topOffset -= (cosRadians * (labelHeight * 0.6)); 
5028         }
5029         else
5030         {
5031             topOffset -= cosRadians * (labelHeight * 0.6);
5032             leftOffset += sinRadians * labelHeight;
5033         }
5034         leftOffset += margin;
5035         leftOffset += tickOffset;
5036         label.style.left = leftOffset + "px";
5037         label.style.top = topOffset + "px";
5038         label.style.MozTransformOrigin =  "0 0";
5039         label.style.MozTransform = "rotate(" + rot + "deg)";
5040         label.style.webkitTransformOrigin = "0 0";
5041         label.style.webkitTransform = "rotate(" + rot + "deg)";
5042         label.style.msTransformOrigin =  "0 0";
5043         label.style.msTransform = "rotate(" + rot + "deg)";
5044         label.style.OTransformOrigin =  "0 0";
5045         label.style.OTransform = "rotate(" + rot + "deg)";
5046     },
5047
5048     /**
5049      * Calculates the size and positions the content elements.
5050      *
5051      * @method setSizeAndPosition
5052      * @protected
5053      */
5054     setSizeAndPosition: function()
5055     {
5056         var ar = this.get("axisRenderer"),
5057             label = ar.get("styles").label,
5058             labelSize = this.get("maxLabelSize"),
5059             tickOffset = ar.get("rightTickOffset"),
5060             sz = tickOffset + labelSize;
5061         if(label.margin && label.margin.weight)
5062         {
5063             sz += label.margin.weight;
5064         }
5065         ar.set("width", sz);
5066         ar.get("contentBox").setStyle("width", sz);
5067     },
5068     
5069     /**
5070      * Adjusts position for inner ticks.
5071      *
5072      * @method offsetNodeForTick
5073      * @param {Node} cb contentBox of the axis
5074      * @protected
5075      */
5076     offsetNodeForTick: function(cb)
5077     {
5078         var ar = this.get("axisRenderer"),
5079             tickOffset = ar.get("leftTickOffset"),
5080             offset = 0 - tickOffset;
5081         cb.setStyle("left", offset);
5082     },
5083
5084     /**
5085      * Assigns a height based on the size of the contents.
5086      *
5087      * @method setCalculatedSize
5088      * @protected
5089      */
5090     setCalculatedSize: function()
5091     {
5092         var ar = this.get("axisRenderer"),
5093             style = ar.get("styles").label,
5094             ttl = Math.round(ar.get("rightTickOffset") + this.get("maxLabelSize") + style.margin.left);
5095         ar.set("width", ttl);
5096     }
5097 });
5098
5099 Y.RightAxisLayout = RightAxisLayout;
5100 /**
5101  * Contains algorithms for rendering a bottom axis.
5102  *
5103  * @class BottomAxisLayout
5104  * @Constructor
5105  */
5106 function BottomAxisLayout(config)
5107 {
5108     BottomAxisLayout.superclass.constructor.apply(this, arguments);
5109 }
5110
5111 BottomAxisLayout.ATTRS = {
5112     /**
5113      * Reference to the <code>Axis</code> using the strategy.
5114      *
5115      * @attribute axisRenderer
5116      * @type Axis
5117      * @protected
5118      */
5119     axisRenderer: {
5120         value:null
5121     },
5122     
5123     /**
5124      * Length in pixels of largest text bounding box. Used to calculate the height of the axis.
5125      *
5126      * @attribute maxLabelSize
5127      * @type Number
5128      * @protected
5129      */
5130     maxLabelSize: {
5131         value: 0
5132     }
5133 };
5134
5135 Y.extend(BottomAxisLayout, Y.Base, {
5136     /**
5137      * Sets the length of the tick on either side of the axis line.
5138      *
5139      * @method setTickOffsets
5140      * @protected
5141      */
5142     setTickOffsets: function()
5143     {
5144         var ar = this.get("axisRenderer"),
5145             majorTicks = ar.get("styles").majorTicks,
5146             tickLength = majorTicks.length,
5147             halfTick = tickLength * 0.5,
5148             display = majorTicks.display;
5149         ar.set("leftTickOffset",  0);
5150         ar.set("rightTickOffset",  0);
5151
5152         switch(display)
5153         {
5154             case "inside" :
5155                 ar.set("topTickOffset", tickLength);
5156                 ar.set("bottomTickOffset", 0);
5157             break;
5158             case "outside" : 
5159                 ar.set("topTickOffset", 0);
5160                 ar.set("bottomTickOffset", tickLength);
5161             break;
5162             case "cross":
5163                 ar.set("topTickOffset",  halfTick);
5164                 ar.set("bottomTickOffset",  halfTick);
5165             break;
5166             default:
5167                 ar.set("topTickOffset", 0);
5168                 ar.set("bottomTickOffset", 0);
5169             break;
5170         }
5171     },
5172
5173     /**
5174      * Calculates the coordinates for the first point on an axis.
5175      *
5176      * @method getLineStart
5177      * @protected
5178      */
5179     getLineStart: function()
5180     {
5181         var ar = this.get("axisRenderer"),
5182             style = ar.get("styles"),
5183             padding = style.padding,
5184             majorTicks = style.majorTicks,
5185             tickLength = majorTicks.length,
5186             display = majorTicks.display,
5187             pt = {x:0, y:padding.top};
5188         if(display === "inside")
5189         {
5190             pt.y += tickLength;
5191         }
5192         else if(display === "cross")
5193         {
5194             pt.y += tickLength/2;
5195         }
5196         return pt; 
5197     },
5198     
5199     /**
5200      * Draws a tick
5201      *
5202      * @method drawTick
5203      * @param {Object} pt hash containing x and y coordinates
5204      * @param {Object} tickStyles hash of properties used to draw the tick
5205      * @protected
5206      */
5207     drawTick: function(pt, tickStyles)
5208     {
5209         var ar = this.get("axisRenderer"),
5210             style = ar.get("styles"),
5211             padding = style.padding,
5212             tickLength = tickStyles.length,
5213             start = {x:pt.x, y:padding.top},
5214             end = {x:pt.x, y:tickLength + padding.top};
5215         ar.drawLine(start, end, tickStyles);
5216     },
5217
5218     /**
5219      * Calculates the point for a label.
5220      *
5221      * @method getLabelPoint
5222      * @param {Object} pt hash containing x and y coordinates
5223      * @return Object
5224      * @protected
5225      */
5226     getLabelPoint: function(point)
5227     {
5228         var ar = this.get("axisRenderer");
5229         return {x:point.x, y:point.y + ar.get("bottomTickOffset")};
5230     },
5231     
5232     /**
5233      * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
5234      *
5235      * @method updateMaxLabelSize
5236      * @param {HTMLElement} label to measure
5237      * @protected
5238      */
5239     updateMaxLabelSize: function(label)
5240     {
5241         var ar = this.get("axisRenderer"),
5242             style = ar.get("styles").label,
5243             rot =  Math.min(90, Math.max(-90, style.rotation)),
5244             absRot = Math.abs(rot),
5245             radCon = Math.PI/180,
5246             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
5247             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
5248             m11 = cosRadians,
5249             m12 = rot > 0 ? -sinRadians : sinRadians,
5250             m21 = -m12,
5251             m22 = m11,
5252             max;
5253         if(!document.createElementNS)
5254         {
5255             label.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5256             this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), label.offsetHeight));
5257         }
5258         else
5259         {
5260             label.style.msTransform = "rotate(0deg)";
5261             if(rot === 0)
5262             {
5263                 max = label.offsetHeight;
5264             }
5265             else if(absRot === 90)
5266             {
5267                 max = label.offsetWidth;
5268             }
5269             else
5270             {
5271                 max = (sinRadians * label.offsetWidth) + (cosRadians * label.offsetHeight); 
5272             }
5273             this.set("maxLabelSize",  Math.max(this.get("maxLabelSize"), max));
5274         }
5275     },
5276     
5277     /**
5278      * Rotate and position labels.
5279      *
5280      * @method positionLabel
5281      * @param {HTMLElement} label to rotate position
5282      * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned
5283      * against.
5284      * @protected
5285      */
5286     positionLabel: function(label, pt)
5287     {
5288         var ar = this.get("axisRenderer"),
5289             tickOffset = ar.get("bottomTickOffset"),
5290             style = ar.get("styles").label,
5291             labelAlpha = style.alpha,
5292             filterString,
5293             margin = 0,
5294             leftOffset = Math.round(pt.x),
5295             topOffset = Math.round(pt.y),
5296             rot =  Math.min(90, Math.max(-90, style.rotation)),
5297             absRot = Math.abs(rot),
5298             radCon = Math.PI/180,
5299             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
5300             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
5301             m11 = cosRadians,
5302             m12 = rot > 0 ? -sinRadians : sinRadians,
5303             m21 = -m12,
5304             m22 = m11,
5305             labelWidth = Math.round(label.offsetWidth),
5306             labelHeight = Math.round(label.offsetHeight);
5307         if(style.margin && style.margin.top)
5308         {
5309             margin = style.margin.top;
5310         }
5311         if(!document.createElementNS)
5312         {
5313             m11 = cosRadians;
5314             m12 = rot > 0 ? -sinRadians : sinRadians;
5315             m21 = -m12;
5316             m22 = m11;
5317             label.style.filter = null;
5318             labelWidth = Math.round(label.offsetWidth);
5319             labelHeight = Math.round(label.offsetHeight);
5320             if(absRot === 90)
5321             {
5322                 leftOffset -= labelHeight * 0.5;
5323             }
5324             else if(rot < 0)
5325             {
5326                 leftOffset -= cosRadians * labelWidth;
5327                 leftOffset -= sinRadians * (labelHeight * 0.5);
5328             }
5329             else if(rot > 0)
5330             {
5331                leftOffset -= sinRadians * (labelHeight * 0.5);
5332             }
5333             else
5334             {
5335                 leftOffset -= labelWidth * 0.5;
5336             }
5337             topOffset += margin;
5338             topOffset += tickOffset;
5339             label.style.left = Math.round(leftOffset) + "px";
5340             label.style.top = Math.round(topOffset) + "px";
5341             if(Y.Lang.isNumber(labelAlpha) && labelAlpha < 1 && labelAlpha > -1 && !isNaN(labelAlpha))
5342             {
5343                 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
5344             }
5345             if(rot !== 0)
5346             {
5347                 if(filterString)
5348                 {
5349                     filterString += " ";
5350                 }
5351                 else
5352                 {
5353                     filterString = ""; 
5354                 }
5355                 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5356             }
5357             if(filterString)
5358             {
5359                 label.style.filter = filterString;
5360             }
5361             return;
5362         }
5363         label.style.msTransform = "rotate(0deg)";
5364         labelWidth = Math.round(label.offsetWidth);
5365         labelHeight = Math.round(label.offsetHeight);
5366         if(rot === 0)
5367         {
5368             leftOffset -= labelWidth * 0.5;
5369         }
5370         else if(absRot === 90)
5371         {
5372             if(rot === 90)
5373             {
5374                 leftOffset += labelHeight * 0.5;
5375             }
5376             else
5377             {
5378                 topOffset += labelWidth;
5379                 leftOffset -= labelHeight * 0.5;
5380             }
5381         }
5382         else 
5383         {
5384             if(rot < 0)
5385             {
5386                 leftOffset -= (cosRadians * labelWidth) + (sinRadians * (labelHeight * 0.6));
5387                 topOffset += sinRadians * labelWidth;
5388             }
5389             else
5390             {
5391                 leftOffset += Math.round(sinRadians * (labelHeight * 0.6));
5392             }
5393         }
5394         topOffset += margin;
5395         topOffset += tickOffset;
5396         label.style.left = Math.round(leftOffset) + "px";
5397         label.style.top = Math.round(topOffset) + "px";
5398         label.style.MozTransformOrigin =  "0 0";
5399         label.style.MozTransform = "rotate(" + rot + "deg)";
5400         label.style.webkitTransformOrigin = "0 0";
5401         label.style.webkitTransform = "rotate(" + rot + "deg)";
5402         label.style.msTransformOrigin =  "0 0";
5403         label.style.msTransform = "rotate(" + rot + "deg)";
5404         label.style.OTransformOrigin =  "0 0";
5405         label.style.OTransform = "rotate(" + rot + "deg)";
5406     },
5407     
5408     /**
5409      * Calculates the size and positions the content elements.
5410      *
5411      * @method setSizeAndPosition
5412      * @protected
5413      */
5414     setSizeAndPosition: function()
5415     {
5416         var labelSize = this.get("maxLabelSize"),
5417             ar = this.get("axisRenderer"),
5418             tickLength = ar.get("bottomTickLength"),
5419             style = ar.get("styles"),
5420             sz = tickLength + labelSize,
5421             margin = style.label.margin;
5422         if(margin && margin.top)
5423         {   
5424             sz += margin.top;
5425         }
5426         sz = Math.round(sz);
5427         ar.set("height", sz);
5428     },
5429
5430     /**
5431      * Adjusts position for inner ticks.
5432      *
5433      * @method offsetNodeForTick
5434      * @param {Node} cb contentBox of the axis
5435      * @protected
5436      */
5437     offsetNodeForTick: function(cb)
5438     {
5439         var ar = this.get("axisRenderer");
5440         ar.get("contentBox").setStyle("top", 0 - ar.get("topTickOffset"));
5441     },
5442
5443     /**
5444      * Assigns a height based on the size of the contents.
5445      *
5446      * @method setCalculatedSize
5447      * @protected
5448      */
5449     setCalculatedSize: function()
5450     {
5451         var ar = this.get("axisRenderer"),
5452             style = ar.get("styles").label,
5453             ttl = Math.round(ar.get("bottomTickOffset") + this.get("maxLabelSize") + style.margin.top);
5454         ar.set("height", ttl);
5455     }
5456 });
5457
5458 Y.BottomAxisLayout = BottomAxisLayout;
5459 /**
5460  * Contains algorithms for rendering a top axis.
5461  *
5462  * @class TopAxisLayout
5463  * @constructor
5464  */
5465 function TopAxisLayout(config)
5466 {
5467     TopAxisLayout.superclass.constructor.apply(this, arguments);
5468 }
5469
5470 TopAxisLayout.ATTRS = {
5471     /**
5472      * Reference to the <code>Axis</code> using the strategy.
5473      *
5474      * @attribute axisRenderer
5475      * @type Axis
5476      * @protected
5477      */
5478     axisRenderer: {
5479         value: null
5480     },
5481
5482     /**
5483      * Length in pixels of largest text bounding box. Used to calculate the height of the axis.
5484      *
5485      * @attribute maxLabelSize
5486      * @type Number
5487      * @protected
5488      */
5489     maxLabelSize: {
5490         value: 0
5491     }
5492 };
5493
5494 Y.extend(TopAxisLayout, Y.Base, {
5495     /**
5496      * Sets the length of the tick on either side of the axis line.
5497      *
5498      * @method setTickOffsets
5499      * @protected
5500      */
5501     setTickOffsets: function()
5502     {
5503         var ar = this.get("axisRenderer"),
5504             majorTicks = ar.get("styles").majorTicks,
5505             tickLength = majorTicks.length,
5506             halfTick = tickLength * 0.5,
5507             display = majorTicks.display;
5508         ar.set("leftTickOffset",  0);
5509         ar.set("rightTickOffset",  0);
5510         switch(display)
5511         {
5512             case "inside" :
5513                 ar.set("bottomTickOffset", tickLength);
5514                 ar.set("topTickOffset", 0);
5515             break;
5516             case "outside" : 
5517                 ar.set("bottomTickOffset", 0);
5518                 ar.set("topTickOffset",  tickLength);
5519             break;
5520             case "cross" :
5521                 ar.set("topTickOffset", halfTick);
5522                 ar.set("bottomTickOffset", halfTick);
5523             break;
5524             default:
5525                 ar.set("topTickOffset", 0);
5526                 ar.set("bottomTickOffset", 0);
5527             break;
5528         }
5529     },
5530
5531     /**
5532      * Calculates the coordinates for the first point on an axis.
5533      *
5534      * @method getLineStart
5535      * @protected
5536      */
5537     getLineStart: function()
5538     {
5539         var ar = this.get("axisRenderer"),
5540             style = ar.get("styles"),
5541             padding = style.padding,
5542             majorTicks = style.majorTicks,
5543             tickLength = majorTicks.length,
5544             display = majorTicks.display,
5545             pt = {x:0, y:padding.top};
5546         if(display === "outside")
5547         {
5548             pt.y += tickLength;
5549         }
5550         else if(display === "cross")
5551         {
5552             pt.y += tickLength/2;
5553         }
5554         return pt; 
5555     },
5556     
5557     /**
5558      * Draws a tick
5559      *
5560      * @method drawTick
5561      * @param {Object} pt hash containing x and y coordinates
5562      * @param {Object} tickStyles hash of properties used to draw the tick
5563      * @protected
5564      */
5565     drawTick: function(pt, tickStyles)
5566     {
5567         var ar = this.get("axisRenderer"),
5568             style = ar.get("styles"),
5569             padding = style.padding,
5570             tickLength = tickStyles.length,
5571             start = {x:pt.x, y:padding.top},
5572             end = {x:pt.x, y:tickLength + padding.top};
5573         ar.drawLine(start, end, tickStyles);
5574     },
5575     
5576     /**
5577      * Calculates the point for a label.
5578      *
5579      * @method getLabelPoint
5580      * @param {Object} pt hash containing x and y coordinates
5581      * @return Object
5582      * @protected
5583      */
5584     getLabelPoint: function(pt)
5585     {
5586         var ar = this.get("axisRenderer");
5587         return {x:pt.x, y:pt.y - ar.get("topTickOffset")};
5588     },
5589     
5590     /**
5591      * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
5592      *
5593      * @method updateMaxLabelSize
5594      * @param {HTMLElement} label to measure
5595      * @protected
5596      */
5597     updateMaxLabelSize: function(label)
5598     {
5599         var ar = this.get("axisRenderer"),
5600             style = ar.get("styles").label,
5601             rot =  Math.min(90, Math.max(-90, style.rotation)),
5602             absRot = Math.abs(rot),
5603             radCon = Math.PI/180,
5604             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
5605             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
5606             m11 = cosRadians,
5607             m12 = rot > 0 ? -sinRadians : sinRadians,
5608             m21 = -m12,
5609             m22 = m11,
5610             max;
5611         if(!document.createElementNS)
5612         {
5613             label.style.filter = 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5614             this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), label.offsetHeight));
5615         }
5616         else
5617         {
5618             label.style.msTransform = "rotate(0deg)";
5619             if(rot === 0)
5620             {
5621                 max = label.offsetHeight;
5622             }
5623             else if(absRot === 90)
5624             {
5625                 max = label.offsetWidth;
5626             }
5627             else
5628             {
5629                 max = (sinRadians * label.offsetWidth) + (cosRadians * label.offsetHeight); 
5630             }
5631             this.set("maxLabelSize",  Math.max(this.get("maxLabelSize"), max));
5632         }
5633     },
5634
5635     /**
5636      * Rotate and position labels.
5637      *
5638      * @method positionLabel
5639      * @param {HTMLElement} label to rotate position
5640      * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned
5641      * against.
5642      * @protected
5643      */
5644     positionLabel: function(label, pt)
5645     {
5646         var ar = this.get("axisRenderer"),
5647             tickOffset = ar.get("topTickOffset"),
5648             style = ar.get("styles").label,
5649             labelAlpha = style.alpha,
5650             filterString,
5651             margin = 0,
5652             leftOffset = pt.x,
5653             topOffset = pt.y,
5654             rot =  Math.max(-90, Math.min(90, style.rotation)),
5655             absRot = Math.abs(rot),
5656             radCon = Math.PI/180,
5657             sinRadians = parseFloat(parseFloat(Math.sin(absRot * radCon)).toFixed(8)),
5658             cosRadians = parseFloat(parseFloat(Math.cos(absRot * radCon)).toFixed(8)),
5659             m11,
5660             m12,
5661             m21,
5662             m22,
5663             maxLabelSize = this.get("maxLabelSize"),
5664             labelWidth = Math.round(label.offsetWidth),
5665             labelHeight = Math.round(label.offsetHeight);
5666         rot = Math.min(90, rot);
5667         rot = Math.max(-90, rot);
5668         if(style.margin && style.margin.bottom)
5669         {
5670             margin = style.margin.bottom;
5671         }
5672         if(!document.createElementNS)
5673         {
5674             label.style.filter = null;
5675             labelWidth = Math.round(label.offsetWidth);
5676             labelHeight = Math.round(label.offsetHeight);
5677             m11 = cosRadians;
5678             m12 = rot > 0 ? -sinRadians : sinRadians;
5679             m21 = -m12;
5680             m22 = m11;
5681             if(rot === 0)
5682             {
5683                 leftOffset -= labelWidth * 0.5;
5684             }
5685             else if(absRot === 90)
5686             {
5687                 leftOffset -= labelHeight * 0.5;
5688             }
5689             else if(rot > 0)
5690             {
5691                 leftOffset -= (cosRadians * labelWidth) + Math.min((sinRadians * labelHeight), (rot/180 * labelHeight));
5692                 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight));
5693                 topOffset += maxLabelSize;
5694             }
5695             else
5696             {
5697                 leftOffset -= sinRadians * (labelHeight * 0.5);
5698                 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight));
5699                 topOffset += maxLabelSize;
5700             }
5701             topOffset -= tickOffset;
5702             label.style.left = leftOffset;
5703             label.style.top = topOffset;
5704             if(Y.Lang.isNumber(labelAlpha) && labelAlpha < 1 && labelAlpha > -1 && !isNaN(labelAlpha))
5705             {
5706                 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
5707             }
5708             if(rot !== 0)
5709             {
5710                 if(filterString)
5711                 {
5712                     filterString += " ";
5713                 }
5714                 else
5715                 {
5716                     filterString = ""; 
5717                 }
5718                 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5719             }
5720             if(filterString)
5721             {
5722                 label.style.filter = filterString;
5723             }
5724             return;
5725         }
5726         label.style.msTransform = "rotate(0deg)";
5727         labelWidth = Math.round(label.offsetWidth);
5728         labelHeight = Math.round(label.offsetHeight);
5729         if(rot === 0)
5730         {
5731             leftOffset -= labelWidth * 0.5;
5732             topOffset -= labelHeight;
5733         }
5734         else if(rot === 90)
5735         {
5736             leftOffset += labelHeight * 0.5;
5737             topOffset -= labelWidth;
5738         }
5739         else if(rot === -90)
5740         {
5741             leftOffset -= labelHeight * 0.5;
5742             topOffset -= 0;
5743         }
5744         else if(rot < 0)
5745         {
5746             
5747             leftOffset -= (sinRadians * (labelHeight * 0.6));
5748             topOffset -= (cosRadians * labelHeight);
5749         }
5750         else
5751         {
5752             leftOffset -= (cosRadians * labelWidth) - (sinRadians * (labelHeight * 0.6));
5753             topOffset -= (sinRadians * labelWidth) + (cosRadians * labelHeight);
5754         }
5755         topOffset -= tickOffset;
5756         label.style.left = leftOffset + "px";
5757         label.style.top = (this.get("maxLabelSize") + topOffset) + "px";
5758         label.style.MozTransformOrigin =  "0 0";
5759         label.style.MozTransform = "rotate(" + rot + "deg)";
5760         label.style.webkitTransformOrigin = "0 0";
5761         label.style.webkitTransform = "rotate(" + rot + "deg)";
5762         label.style.msTransformOrigin =  "0 0";
5763         label.style.msTransform = "rotate(" + rot + "deg)";
5764         label.style.OTransformOrigin =  "0 0";
5765         label.style.OTransform = "rotate(" + rot + "deg)";
5766     },
5767
5768     /**
5769      * Calculates the size and positions the content elements.
5770      *
5771      * @method setSizeAndPosition
5772      * @protected
5773      */
5774     setSizeAndPosition: function()
5775     {
5776         var labelSize = this.get("maxLabelSize"),
5777             ar = this.get("axisRenderer"),
5778             tickOffset = ar.get("topTickOffset"),
5779             style = ar.get("styles"),
5780             margin = style.label.margin,
5781             graphic = ar.get("graphic"),
5782             sz = tickOffset + labelSize;
5783         if(margin && margin.bottom)
5784         {
5785             sz += margin.bottom;
5786         }
5787         ar.set("height", sz);
5788         Y.one(graphic.node).setStyle("top", labelSize + margin.bottom);
5789     },
5790     
5791     /**
5792      * Adjusts position for inner ticks.
5793      *
5794      * @method offsetNodeForTick
5795      * @param {Node} cb contentBox of the axis
5796      * @protected
5797      */
5798     offsetNodeForTick: function(cb)
5799     {
5800     },
5801
5802     /**
5803      * Assigns a height based on the size of the contents.
5804      *
5805      * @method setCalculatedSize
5806      * @protected
5807      */
5808     setCalculatedSize: function()
5809     {
5810         var ar = this.get("axisRenderer"),
5811             style = ar.get("styles").label,
5812             ttl = Math.round(ar.get("topTickOffset") + this.get("maxLabelSize") + style.margin.bottom);
5813         ar.set("height", ttl);
5814     }
5815 });
5816
5817 Y.TopAxisLayout = TopAxisLayout;
5818
5819 /**
5820  * AxisType is an abstract class that manages the data for an axis.
5821  *
5822  * @param {Object} config (optional) Configuration parameters for the Chart.
5823  * @class AxisType
5824  * @constructor
5825  * @extends Axis
5826  */
5827 Y.AxisType = Y.Base.create("baseAxis", Y.Axis, [], {
5828     /**
5829      * @private
5830      */
5831     bindUI: function()
5832     {
5833         this.after("dataReady", Y.bind(this._dataChangeHandler, this));
5834         this.after("dataUpdate", Y.bind(this._dataChangeHandler, this));
5835         this.after("minimumChange", Y.bind(this._keyChangeHandler, this));
5836         this.after("maximumChange", Y.bind(this._keyChangeHandler, this));
5837         this.after("keysChange", this._keyChangeHandler);
5838         this.after("dataProviderChange", this._dataProviderChangeHandler);
5839         this.after("stylesChange", this._updateHandler);
5840         this.after("positionChange", this._positionChangeHandler);
5841         this.after("overlapGraphChange", this._updateHandler);
5842         this.after("widthChange", this._handleSizeChange);
5843         this.after("heightChange", this._handleSizeChange);
5844         this.after("alwaysShowZeroChange", this._keyChangeHandler);
5845         this.after("roundingMethodChange", this._keyChangeHandler);
5846     },
5847
5848     /**
5849      * @private
5850      */
5851     _dataProviderChangeHandler: function(e)
5852     {
5853         var keyCollection = this.get("keyCollection").concat(),
5854             keys = this.get("keys"),
5855             i;
5856         if(keys)
5857         {
5858             for(i in keys)
5859             {
5860                 if(keys.hasOwnProperty(i))
5861                 {
5862                     delete keys[i];
5863                 }
5864             }
5865         }
5866         if(keyCollection && keyCollection.length)
5867         {
5868             this.set("keys", keyCollection);
5869         }
5870     },
5871
5872     /**
5873      * @private
5874      */
5875     GUID: "yuibaseaxis",
5876         
5877     /**
5878      * @private
5879      */
5880     _type: null,
5881         
5882     /**
5883      * @private
5884      */
5885     _setMaximum: null,
5886         
5887     /**
5888      * @private
5889      */
5890     _dataMaximum: null,
5891         
5892     /**
5893      * @private
5894      */
5895     _setMinimum: null,
5896         
5897     /**
5898      * @private
5899      */
5900     _data: null,
5901
5902     /**
5903      * @private
5904      */
5905     _updateTotalDataFlag: true,
5906
5907     /**
5908      * @private
5909      */
5910     _dataReady: false,
5911         
5912     /**
5913      * Adds an array to the key hash.
5914      *
5915      * @param value Indicates what key to use in retrieving
5916      * the array.
5917      */
5918     addKey: function (value)
5919         {
5920         this.set("keys", value);
5921         },
5922
5923     /**
5924      * @private
5925      */
5926     _getKeyArray: function(key, data)
5927     {
5928         var i = 0,
5929             obj,
5930             keyArray = [],
5931             len = data.length;
5932         for(; i < len; ++i)
5933         {
5934             obj = data[i];
5935             keyArray[i] = obj[key];
5936         }
5937         return keyArray;
5938     },
5939
5940     /**
5941      * @private 
5942      */
5943     _setDataByKey: function(key, data)
5944     {
5945         var i,
5946             obj, 
5947             arr = [], 
5948             dv = this._dataClone.concat(), 
5949             len = dv.length;
5950         for(i = 0; i < len; ++i)
5951         {
5952             obj = dv[i];
5953             arr[i] = obj[key];
5954         }
5955         this.get("keys")[key] = arr;
5956         this._updateTotalDataFlag = true;
5957     },
5958
5959     /**
5960      * @private
5961      */
5962     _updateTotalData: function()
5963     {
5964                 var keys = this.get("keys"),
5965             i;
5966         this._data = [];
5967         for(i in keys)
5968         {
5969             if(keys.hasOwnProperty(i))
5970             {
5971                 this._data = this._data.concat(keys[i]);
5972             }
5973         }
5974         this._updateTotalDataFlag = false;
5975     },
5976
5977     /**
5978      * Removes an array from the key hash.
5979      * 
5980      * @method removeKey
5981      * @param {String} value Indicates what key to use in removing from 
5982      * the hash.
5983      */
5984     removeKey: function(value)
5985     {
5986         var keys = this.get("keys");
5987         if(keys.hasOwnProperty(value)) 
5988         {
5989             delete keys[value];
5990             this._keyChangeHandler();
5991         }
5992     },
5993
5994     /**
5995      * Returns a numeric value based of a key value and an index.
5996      *
5997      * @method getKeyValueAt
5998      * @param {String} key value used to look up the correct array
5999      * @param {Number} index within the array
6000      * @return Object
6001      */
6002     getKeyValueAt: function(key, index)
6003     {
6004         var value = NaN,
6005             keys = this.get("keys");
6006         if(keys[key] && keys[key][index]) 
6007         {
6008             value = keys[key][index];
6009         }
6010         return value;
6011     },
6012
6013     /**
6014      * Returns an array of values based on an identifier key.
6015      *
6016      * @method getDataByKey
6017      * @param {String} value value used to identify the array
6018      * @return Object
6019      */
6020     getDataByKey: function (value)
6021     {
6022         var keys = this.get("keys");
6023         if(keys[value])
6024         {
6025             return keys[value];
6026         }
6027         return null;
6028     },
6029
6030     /**
6031      * @private 
6032      */
6033     _updateMinAndMax: function() 
6034     {
6035         var data = this.get("data"),
6036             max = 0,
6037             min = 0,
6038             len,
6039             num,
6040             i;
6041         if(data && data.length && data.length > 0)
6042         {
6043             len = data.length;
6044             max = min = data[0];
6045             if(len > 1)
6046             {
6047                 for(i = 1; i < len; i++)
6048                 {       
6049                     num = data[i];
6050                     if(isNaN(num))
6051                     {
6052                         continue;
6053                     }
6054                     max = Math.max(num, max);
6055                     min = Math.min(num, min);
6056                 }
6057             }
6058         }
6059         this._dataMaximum = max;
6060         this._dataMinimum = min;
6061     },
6062
6063     /**
6064      * Returns the total number of majorUnits that will appear on an axis.
6065      *
6066      * @method getTotalMajorUnits
6067      * @return Number
6068      */
6069     getTotalMajorUnits: function()
6070     {
6071         var units,
6072             majorUnit = this.get("styles").majorUnit,
6073             len = this.get("length");
6074         if(majorUnit.determinant === "count") 
6075         {
6076             units = majorUnit.count;
6077         }
6078         else if(majorUnit.determinant === "distance") 
6079         {
6080             units = (len/majorUnit.distance) + 1;
6081         }
6082         return units; 
6083     },
6084
6085     /**
6086      * Returns the distance between major units on an axis.
6087      *
6088      * @method getMajorUnitDistance
6089      * @param {Number} len Number of ticks
6090      * @param {Number} uiLen Size of the axis.
6091      * @param {Object} majorUnit Hash of properties used to determine the majorUnit
6092      * @return Number
6093      */
6094     getMajorUnitDistance: function(len, uiLen, majorUnit)
6095     {
6096         var dist;
6097         if(majorUnit.determinant === "count")
6098         {
6099             dist = uiLen/(len - 1);
6100         }
6101         else if(majorUnit.determinant === "distance")
6102         {
6103             dist = majorUnit.distance;
6104         }
6105         return dist;
6106     },
6107     
6108     /**
6109      * Gets the distance that the first and last ticks are offset from there respective
6110      * edges.
6111      *
6112      * @attribute getEdgeOffset
6113      * @type Method
6114      * @param {Number} ct Number of ticks on the axis.
6115      * @param {Number} l Length (in pixels) of the axis.
6116      * @return Number
6117      */
6118     getEdgeOffset: function(ct, l)
6119     {
6120         return 0;
6121     },
6122
6123     /**
6124      * Calculates and returns a value based on the number of labels and the index of
6125      * the current label.
6126      *
6127      * @method getLabelByIndex
6128      * @param {Number} i Index of the label.
6129      * @param {Number} l Total number of labels.
6130      * @return String
6131      */
6132     getLabelByIndex: function(i, l)
6133     {
6134         var min = this.get("minimum"),
6135             max = this.get("maximum"),
6136             increm = (max - min)/(l-1),
6137             label;
6138             l -= 1;
6139         label = min + (i * increm);
6140         return label;
6141     },
6142
6143     /**
6144      * @private
6145      */
6146     _keyChangeHandler: function(e)
6147     {
6148         this._updateMinAndMax();
6149                 this.fire("dataUpdate");
6150     }
6151 }, {
6152     ATTRS: {
6153         /**
6154          * Hash of array identifed by a string value.
6155          *
6156          * @attribute keys
6157          * @type Object
6158          */
6159         keys: {
6160             value: {},
6161
6162             setter: function(val)
6163             {
6164                 var keys = {},
6165                     i, 
6166                     len,
6167                     data = this.get("dataProvider");
6168                 if(Y.Lang.isArray(val))
6169                 {
6170                     len = val.length;
6171                     for(i = 0; i < len; ++i)
6172                     {
6173                         keys[val[i]] = this._getKeyArray(val[i], data);   
6174                     }
6175                     
6176                 }
6177                 else if(Y.Lang.isString(val))
6178                 {
6179                     keys = this.get("keys");
6180                     keys[val] = this._getKeyArray(val, data);
6181                 }
6182                 else
6183                 {
6184                     for(i in val)
6185                     {
6186                         if(val.hasOwnProperty(i))
6187                         {
6188                             keys[i] = this._getKeyArray(i, data);
6189                         }
6190                     }
6191                 }
6192                     this._updateTotalDataFlag = true;
6193                 return keys;
6194             }
6195         },
6196
6197         /**
6198          *Indicates how to round unit values.
6199          *  <dl>
6200          *      <dt>niceNumber</dt><dd>Units will be smoothed based on the number of ticks and data range.</dd>
6201          *      <dt>auto</dt><dd>If the range is greater than 1, the units will be rounded.</dd>
6202          *      <dt>numeric value</dt><dd>Units will be equal to the numeric value.</dd>
6203          *      <dt>null</dt><dd>No rounding will occur.</dd>
6204          *  </dl>
6205          *
6206          * @attribute roundingMethod
6207          * @type String
6208          * @default niceNumber
6209          */
6210         roundingMethod: {
6211             value: "niceNumber"
6212         },
6213
6214         /**
6215          *Returns the type of axis data
6216          *  <dl>
6217          *      <dt>time</dt><dd>Manages time data</dd>
6218          *      <dt>stacked</dt><dd>Manages stacked numeric data</dd>      
6219          *      <dt>numeric</dt><dd>Manages numeric data</dd>
6220          *      <dt>category</dt><dd>Manages categorical data</dd>
6221          *  </dl>
6222          *
6223          * @attribute type
6224          * @type String
6225          */
6226         type:
6227         {
6228             readOnly: true,
6229
6230             getter: function ()
6231             {
6232                 return this._type;
6233             }
6234         },
6235
6236         /**
6237          * Instance of <code>ChartDataProvider</code> that the class uses
6238          * to build its own data.
6239          *
6240          * @attribute dataProvider
6241          * @type Array
6242          */
6243         dataProvider:{
6244             setter: function (value)
6245             {
6246                 return value;
6247             }
6248         },
6249
6250         /**
6251          * The maximum value contained in the <code>data</code> array. Used for
6252          * <code>maximum</code> when <code>autoMax</code> is true.
6253          *
6254          * @attribute dataMaximum
6255          * @type Number
6256          */
6257         dataMaximum: {
6258             getter: function ()
6259             {
6260                 if(!this._dataMaximum)
6261                 {   
6262                     this._updateMinAndMax();
6263                 }
6264                 return this._dataMaximum;
6265             }
6266         },
6267
6268         /**
6269          * The maximum value that will appear on an axis.
6270          *
6271          * @attribute maximum
6272          * @type Number
6273          */
6274         maximum: {
6275             getter: function ()
6276             {
6277                 var max = this.get("dataMaximum");
6278                 if(this.get("setMax")) 
6279                 {
6280                     max = this._setMaximum;
6281                 }
6282                 return max;
6283             },
6284             setter: function (value)
6285             {
6286                 this._setMaximum = parseFloat(value);
6287                 return value;
6288             }
6289         },
6290
6291         /**
6292          * The minimum value contained in the <code>data</code> array. Used for
6293          * <code>minimum</code> when <code>autoMin</code> is true.
6294          *
6295          * @attribute dataMinimum
6296          * @type Number
6297          */
6298         dataMinimum: {
6299             getter: function ()
6300             {
6301                 if(!this._dataMinimum)
6302                 {
6303                     this._updateMinAndMax();
6304                 }
6305                 return this._dataMinimum;
6306             }
6307         },
6308
6309         /**
6310          * The minimum value that will appear on an axis.
6311          *
6312          * @attribute minimum
6313          * @type Number
6314          */
6315         minimum: {
6316             getter: function ()
6317             {
6318                 var min = this.get("dataMinimum");
6319                 if(this.get("setMin"))
6320                 {
6321                     min = this._setMinimum;
6322                 }
6323                 return min;
6324             },
6325             setter: function(val)
6326             {
6327                 this._setMinimum = parseFloat(val);
6328                 return val;
6329             }
6330         },
6331
6332         /**
6333          * Determines whether the maximum is calculated or explicitly 
6334          * set by the user.
6335          *
6336          * @attribute setMax
6337          * @type Boolean
6338          */
6339         setMax: {
6340             readOnly: true,
6341
6342             getter: function()
6343             {
6344                 return Y.Lang.isNumber(this._setMaximum);
6345             }
6346         },
6347
6348         /**
6349          * Determines whether the minimum is calculated or explicitly
6350          * set by the user.
6351          *
6352          * @attribute setMin
6353          * @type Boolean
6354          */
6355         setMin: {
6356             readOnly: true,
6357
6358             getter: function()
6359             {
6360                 return Y.Lang.isNumber(this._setMinimum);
6361             }
6362         },
6363
6364         /**
6365          * Array of axis data
6366          *
6367          * @attribute data
6368          * @type Array
6369          */
6370         data: {
6371             getter: function ()
6372             {
6373                 if(!this._data || this._updateTotalDataFlag)
6374                 {
6375                     this._updateTotalData();
6376                 }
6377                 return this._data;
6378             }
6379         },
6380
6381         /**
6382          * Array containing all the keys in the axis.
6383          *
6384          * @attribute keyCollection
6385          * @type Array
6386          */
6387         keyCollection: {
6388             getter: function()
6389             {
6390                 var keys = this.get("keys"),
6391                     i, 
6392                     col = [];
6393                 for(i in keys)
6394                 {
6395                     if(keys.hasOwnProperty(i))
6396                     {
6397                         col.push(i);
6398                     }
6399                 }
6400                 return col;
6401             },
6402             readOnly: true
6403         },
6404         
6405         /**
6406          * Method used for formatting a label.
6407          *
6408          * @attribute labelFunction
6409          * @type Function
6410          * @param {String} val label to be formatted.
6411          * @param {Object} format temlate for formatting a label.
6412          * @return String
6413          */
6414         labelFunction: {
6415             value: function(val, format)
6416             {
6417                 return val;
6418             }
6419         }
6420     }
6421 });
6422 /**
6423  * NumericAxis manages numeric data on an axis.
6424  *
6425  * @param {Object} config (optional) Configuration parameters for the Chart.
6426  * @class NumericAxis
6427  * @constructor
6428  * @extends AxisType
6429  */
6430 function NumericAxis(config)
6431 {
6432         NumericAxis.superclass.constructor.apply(this, arguments);
6433 }
6434
6435 NumericAxis.NAME = "numericAxis";
6436
6437 NumericAxis.ATTRS = {
6438     /**
6439      * Indicates whether 0 should always be displayed.
6440      *
6441      * @attribute alwaysShowZero
6442      * @type Boolean
6443      */
6444         alwaysShowZero: {
6445             value: true 
6446         },
6447     
6448     /**
6449      * Formats a label.
6450      *
6451      * @attribute labelFunction
6452      * @type Function
6453      * @param {Object} val Value to be formatted. 
6454      * @param {Object} format Hasho of properties used to format the label.
6455      */
6456     labelFunction: { 
6457         value: function(val, format)
6458         {
6459             if(format)
6460             {
6461                 return Y.DataType.Number.format(val, format);
6462             }
6463             return val;
6464         }
6465     },
6466
6467     /**
6468      * Hash of properties used by the <code>labelFunction</code> to format a
6469      * label.
6470      *
6471      * @attribute labelFormat
6472      * @type Object
6473      */
6474     labelFormat: {
6475         value: {
6476             prefix: "",
6477             thousandsSeparator: "",
6478             decimalSeparator: "",
6479             decimalPlaces: "0",
6480             suffix: ""
6481         }
6482     }
6483 };
6484
6485 Y.extend(NumericAxis, Y.AxisType,
6486 {
6487     /**
6488      * @private
6489      */
6490     _type: "numeric",
6491
6492     /**
6493      * @private
6494      */
6495     _getMinimumUnit:function(max, min, units)
6496     {
6497         return this._getNiceNumber(Math.ceil((max - min)/units));
6498     },
6499
6500     /**
6501      * @private
6502      */
6503     _getNiceNumber: function(roundingUnit)
6504     {
6505         var tempMajorUnit = roundingUnit,
6506             order = Math.ceil(Math.log(tempMajorUnit) * 0.4342944819032518),
6507             roundedMajorUnit = Math.pow(10, order),
6508             roundedDiff;
6509
6510         if (roundedMajorUnit / 2 >= tempMajorUnit) 
6511         {
6512             roundedDiff = Math.floor((roundedMajorUnit / 2 - tempMajorUnit) / (Math.pow(10,order-1)/2));
6513             tempMajorUnit = roundedMajorUnit/2 - roundedDiff*Math.pow(10,order-1)/2;
6514         }
6515         else 
6516         {
6517             tempMajorUnit = roundedMajorUnit;
6518         }
6519         if(!isNaN(tempMajorUnit))
6520         {
6521             return tempMajorUnit;
6522         }
6523         return roundingUnit;
6524
6525     },
6526
6527     /**
6528      * @private
6529      */
6530     _updateMinAndMax: function()
6531     {
6532         var data = this.get("data"),
6533             max = 0,
6534             min = 0,
6535             len,
6536             num,
6537             i,
6538             key,
6539             setMax = this.get("setMax"),
6540             setMin = this.get("setMin");
6541         if(!setMax && !setMin)
6542         {
6543             if(data && data.length && data.length > 0)
6544             {
6545                 len = data.length;
6546                 max = min = data[0];
6547                 if(len > 1)
6548                 {
6549                     for(i = 1; i < len; i++)
6550                     {   
6551                         num = data[i];
6552                         if(isNaN(num))
6553                         {
6554                             if(Y.Lang.isObject(num))
6555                             {
6556                                 //hloc values
6557                                 for(key in num)
6558                                 {
6559                                    if(num.hasOwnProperty(key))
6560                                    {
6561                                         max = Math.max(num[key], max);
6562                                         min = Math.min(num[key], min);
6563                                    }
6564                                 }
6565                             }
6566                             continue;
6567                         }
6568                         max = setMax ? this._setMaximum : Math.max(num, max);
6569                         min = setMin ? this._setMinimum : Math.min(num, min);
6570                     }
6571                 }
6572             }
6573             this._roundMinAndMax(min, max);
6574         }
6575     },
6576
6577     /**
6578      * @private
6579      */
6580     _roundMinAndMax: function(min, max)
6581     {
6582         var roundingUnit,
6583             minimumRange,
6584             minGreaterThanZero = min >= 0,
6585             maxGreaterThanZero = max > 0,
6586             dataRangeGreater,
6587             maxRound,
6588             minRound,
6589             topTicks,
6590             botTicks,
6591             tempMax,
6592             tempMin,
6593             units = this.getTotalMajorUnits() - 1,
6594             alwaysShowZero = this.get("alwaysShowZero"),
6595             roundingMethod = this.get("roundingMethod"),
6596             useIntegers = (max - min)/units >= 1;
6597         if(roundingMethod)
6598         {
6599             if(roundingMethod == "niceNumber")
6600             {
6601                 roundingUnit = this._getMinimumUnit(max, min, units);
6602                 if(minGreaterThanZero && maxGreaterThanZero)
6603                 {
6604                     if(alwaysShowZero || min < roundingUnit)
6605                     {
6606                         min = 0;
6607                     }
6608                     roundingUnit = this._getMinimumUnit(max, min, units);
6609                     max = this._roundUpToNearest(max, roundingUnit);
6610                 }
6611                 else if(maxGreaterThanZero && !minGreaterThanZero)
6612                 {
6613                         topTicks = Math.round( units / ((-1 * min)/max + 1)    );
6614                         botTicks = units - topTicks;
6615                         tempMax = Math.ceil( max/topTicks );
6616
6617                         tempMin = Math.floor( min/botTicks ) * -1;
6618                         
6619                         roundingUnit = Math.max(tempMax, tempMin);
6620                         roundingUnit = this._getNiceNumber(roundingUnit);  
6621                         max = roundingUnit * topTicks;
6622                         min = roundingUnit * botTicks * -1;
6623                 }
6624                 else
6625                 {
6626                     if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
6627                     {
6628                         max = 0;
6629                         roundingUnit = this._getMinimumUnit(max, min, units);
6630                     }
6631                     else
6632                     {
6633                         max = this._roundUpToNearest(max, roundingUnit);
6634                     }
6635                     min = max - (roundingUnit * units);
6636                 }
6637             }
6638             else if(roundingMethod == "auto") 
6639             {
6640                 if(minGreaterThanZero && maxGreaterThanZero)
6641                 {
6642                     if(alwaysShowZero || min < (max-min)/units)
6643                     {
6644                         min = 0;
6645                     }
6646                 
6647                     roundingUnit = (max - min)/units;
6648                     if(useIntegers)
6649                     {
6650                         roundingUnit = Math.ceil(roundingUnit);
6651                     }
6652                     max = min + (roundingUnit * units);
6653                 }
6654                 else if(maxGreaterThanZero && !minGreaterThanZero)
6655                 {
6656                     if(alwaysShowZero)
6657                     {
6658                         topTicks = Math.round( units / ( (-1 * min) /max + 1) );
6659                         botTicks = units - topTicks;
6660
6661                         if(useIntegers)
6662                         {
6663                             tempMax = Math.ceil( max/topTicks );
6664                             tempMin = Math.floor( min/botTicks ) * -1;
6665                         }
6666                         else
6667                         {
6668                             tempMax = max/topTicks;
6669                             tempMin = min/botTicks * -1;
6670                         }
6671                         roundingUnit = Math.max(tempMax, tempMin);
6672                         max = roundingUnit * topTicks;
6673                         min = roundingUnit * botTicks * -1;
6674                     }
6675                     else
6676                     {
6677                         roundingUnit = (max - min)/units;
6678                         if(useIntegers)
6679                         {
6680                             roundingUnit = Math.ceil(roundingUnit);
6681                         }
6682                         min = this._roundDownToNearest(min, roundingUnit);
6683                         max = this._roundUpToNearest(max, roundingUnit);
6684                     }
6685                 }
6686                 else
6687                 {
6688                     roundingUnit = (max - min)/units;
6689                     if(useIntegers)
6690                     {   
6691                         roundingUnit = Math.ceil(roundingUnit);
6692                     }
6693                     if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
6694                     {
6695                         max = 0;
6696                         roundingUnit = (max - min)/units;
6697                         if(useIntegers)
6698                         {
6699                             Math.ceil(roundingUnit);
6700                         }
6701                     }
6702                     else
6703                     {
6704                         max = this._roundUpToNearest(max, roundingUnit);
6705                     }
6706                     min = max - (roundingUnit * units);
6707
6708                 }
6709             }
6710             else if(!isNaN(roundingMethod) && isFinite(roundingMethod))
6711             {
6712                 roundingUnit = roundingMethod;
6713                 minimumRange = roundingUnit * units;
6714                 dataRangeGreater = (max - min) > minimumRange;
6715                 minRound = this._roundDownToNearest(min, roundingUnit);
6716                 maxRound = this._roundUpToNearest(max, roundingUnit);
6717                 if(minGreaterThanZero && maxGreaterThanZero)
6718                 {
6719                     if(alwaysShowZero || minRound <= 0)
6720                     {
6721                         min = 0;
6722                     }
6723                     else
6724                     {
6725                         min = minRound;
6726                     }
6727                     if(!dataRangeGreater)
6728                     {
6729                         max = min + minimumRange;
6730                     }
6731                     else
6732                     {
6733                         max = maxRound;
6734                     }
6735                 }
6736                 else if(maxGreaterThanZero && !minGreaterThanZero)
6737                 {
6738                     min = minRound;
6739                     if(!dataRangeGreater)
6740                     {
6741                         max = min + minimumRange;
6742                     }
6743                     else
6744                     {
6745                         max = maxRound;
6746                     }
6747                 }
6748                 else
6749                 {
6750                     if(max === 0 || alwaysShowZero)
6751                     {
6752                         max = 0;
6753                     }
6754                     else
6755                     {
6756                         max = maxRound;
6757                     }
6758                     if(!dataRangeGreater)
6759                     {
6760                         min = max - minimumRange;
6761                     }
6762                     else
6763                     {
6764                         min = minRound;
6765                     }
6766                 }
6767             }
6768         }
6769         this._dataMaximum = max;
6770         this._dataMinimum = min;
6771     },
6772
6773     /**
6774      * Calculates and returns a value based on the number of labels and the index of
6775      * the current label.
6776      *
6777      * @method getLabelByIndex
6778      * @param {Number} i Index of the label.
6779      * @param {Number} l Total number of labels.
6780      * @return String
6781      */
6782     getLabelByIndex: function(i, l)
6783     {
6784         var min = this.get("minimum"),
6785             max = this.get("maximum"),
6786             increm = (max - min)/(l-1),
6787             label;
6788             l -= 1;
6789         label = min + (i * increm);
6790         if(i > 0)
6791         {
6792             label = this._roundToNearest(label, increm);
6793         }
6794         return label;
6795     },
6796
6797     /**
6798      * @private
6799      *
6800      * Rounds a Number to the nearest multiple of an input. For example, by rounding
6801      * 16 to the nearest 10, you will receive 20. Similar to the built-in function Math.round().
6802      */
6803     _roundToNearest: function(number, nearest)
6804     {
6805         nearest = nearest || 1;
6806         if(nearest === 0)
6807         {
6808             return number;
6809         }
6810         var roundedNumber = Math.round(this._roundToPrecision(number / nearest, 10)) * nearest;
6811         return this._roundToPrecision(roundedNumber, 10);
6812     },
6813         
6814     /**
6815      * @private
6816      *
6817      * Rounds a Number <em>up</em> to the nearest multiple of an input. For example, by rounding
6818      * 16 up to the nearest 10, you will receive 20. Similar to the built-in function Math.ceil().
6819      */
6820     _roundUpToNearest: function(number, nearest)
6821     {
6822         nearest = nearest || 1;
6823         if(nearest === 0)
6824         {
6825             return number;
6826         }
6827         return Math.ceil(this._roundToPrecision(number / nearest, 10)) * nearest;
6828     },
6829         
6830     /**
6831      * @private
6832      *
6833      * Rounds a Number <em>down</em> to the nearest multiple of an input. For example, by rounding
6834      * 16 down to the nearest 10, you will receive 10. Similar to the built-in function Math.floor().
6835      */
6836     _roundDownToNearest: function(number, nearest)
6837     {
6838         nearest = nearest || 1;
6839         if(nearest === 0)
6840         {
6841             return number;
6842         }
6843         return Math.floor(this._roundToPrecision(number / nearest, 10)) * nearest;
6844     },
6845
6846     /**
6847      * @private
6848      *
6849      * Rounds a number to a certain level of precision. Useful for limiting the number of
6850      * decimal places on a fractional number.
6851      */
6852     _roundToPrecision: function(number, precision)
6853     {
6854         precision = precision || 0;
6855         var decimalPlaces = Math.pow(10, precision);
6856         return Math.round(decimalPlaces * number) / decimalPlaces;
6857     }
6858 });
6859
6860 Y.NumericAxis = NumericAxis;
6861                 
6862 /**
6863  * StackedAxis manages stacked numeric data on an axis.
6864  *
6865  * @param {Object} config (optional) Configuration parameters for the Chart.
6866  * @class StackedAxis
6867  * @constructor
6868  * @extends NumericAxis
6869  */
6870 function StackedAxis(config)
6871 {
6872         StackedAxis.superclass.constructor.apply(this, arguments);
6873 }
6874
6875 StackedAxis.NAME = "stackedAxis";
6876
6877
6878 Y.extend(StackedAxis, Y.NumericAxis,
6879 {
6880     /**
6881      * @private
6882      */
6883     _updateMinAndMax: function()
6884     {
6885         var max = 0,
6886             min = 0,
6887             pos = 0,
6888             neg = 0,
6889             len = 0,
6890             i = 0,
6891             key,
6892             num,
6893             keys = this.get("keys");
6894
6895         for(key in keys)
6896         {
6897             if(keys.hasOwnProperty(key))
6898             {
6899                 len = Math.max(len, keys[key].length);
6900             }
6901         }
6902         for(; i < len; ++i)
6903         {
6904             pos = 0;
6905             neg = 0;
6906             for(key in keys)
6907             {
6908                 if(keys.hasOwnProperty(key))
6909                 {
6910                     num = keys[key][i];
6911                     if(isNaN(num))
6912                     {
6913                         continue;
6914                     }
6915                     if(num >= 0)
6916                     {
6917                         pos += num;
6918                     }
6919                     else
6920                     {
6921                         neg += num;
6922                     }
6923                 }
6924             }
6925             if(pos > 0)
6926             {
6927                 max = Math.max(max, pos);
6928             }
6929             else 
6930             {
6931                 max = Math.max(max, neg);
6932             }
6933             if(neg < 0)
6934             {
6935                 min = Math.min(min, neg);
6936             }
6937             else
6938             {
6939                 min = Math.min(min, pos);
6940             }
6941         }
6942         this._roundMinAndMax(min, max);
6943     }
6944 });
6945
6946 Y.StackedAxis = StackedAxis;
6947                 
6948 /**
6949  * TimeAxis manages time data on an axis.
6950  *
6951  * @param {Object} config (optional) Configuration parameters for the Chart.
6952  * @class TimeAxis
6953  * @constructor
6954  * @extends AxisType
6955  */
6956 function TimeAxis(config)
6957 {
6958         TimeAxis.superclass.constructor.apply(this, arguments);
6959 }
6960
6961 TimeAxis.NAME = "timeAxis";
6962
6963 TimeAxis.ATTRS = 
6964 {
6965     /**
6966      * @private
6967      */
6968     setMax: {
6969         readOnly: true,
6970
6971         getter: function()
6972         {
6973             var max = this._getNumber(this._setMaximum);
6974             return (Y.Lang.isNumber(max));
6975         }
6976     },
6977
6978     /**
6979      * @private
6980      */
6981     setMin: {
6982         readOnly: true,
6983
6984         getter: function()
6985         {
6986             var min = this._getNumber(this._setMinimum);
6987             return (Y.Lang.isNumber(min));
6988         }
6989     },
6990
6991     /**
6992      * The maximum value that will appear on an axis.
6993      *
6994      * @attribute maximum
6995      * @type Number
6996      */
6997     maximum: {
6998         getter: function ()
6999         {
7000             var max = this._getNumber(this._setMaximum);
7001             if(!Y.Lang.isNumber(max))
7002             {
7003                 max = this._getNumber(this.get("dataMaximum"));
7004             }
7005             return max;
7006         },
7007         setter: function (value)
7008         {
7009             this._setMaximum = this._getNumber(value);
7010             return value;
7011         }
7012     },
7013
7014     /**
7015      * The minimum value that will appear on an axis.
7016      *
7017      * @attribute minimum
7018      * @type Number
7019      */
7020     minimum: {
7021         getter: function ()
7022         {
7023             var min = this._getNumber(this._setMinimum);
7024             if(!Y.Lang.isNumber(min)) 
7025             {
7026                 min = this._getNumber(this.get("dataMinimum"));
7027             }
7028                 return min;
7029         },
7030         setter: function (value)
7031         {
7032             this._setMinimum = this._getNumber(value);
7033             return value;
7034         }
7035     },
7036
7037     /**
7038      * Formats a label.
7039      *
7040      * @attribute labelFunction
7041      * @type Function
7042      * @param {Object} val Value to be formatted. 
7043      * @param {String} format Pattern used to format label.
7044      */
7045     labelFunction: {
7046         value: function(val, format)
7047         {
7048             val = Y.DataType.Date.parse(val);
7049             if(format)
7050             {
7051                 return Y.DataType.Date.format(val, {format:format});
7052             }
7053             return val;
7054         }
7055     },
7056
7057     /**
7058      * Pattern used by the <code>labelFunction</code> to format a label.
7059      *
7060      * @attribute labelFormat
7061      * @type String
7062      */
7063     labelFormat: {
7064         value: "%b %d, %y"
7065     }
7066 };
7067
7068 Y.extend(TimeAxis, Y.AxisType, {
7069     /**
7070      * Constant used to generate unique id.
7071      *
7072      * @property GUID
7073      * @type String
7074      * @private
7075      */
7076     GUID: "yuitimeaxis",
7077         
7078     /**
7079      * @private
7080      */
7081     _dataType: "time",
7082         
7083     /**
7084      * Calculates and returns a value based on the number of labels and the index of
7085      * the current label.
7086      *
7087      * @method getLabelByIndex
7088      * @param {Number} i Index of the label.
7089      * @param {Number} l Total number of labels.
7090      * @return String
7091      */
7092     getLabelByIndex: function(i, l)
7093     {
7094         var min = this.get("minimum"),
7095             max = this.get("maximum"),
7096             position = this.get("position"),
7097             increm,
7098             label;
7099             l -= 1;
7100         increm = ((max - min)/l) * i;
7101         if(position == "bottom" || position == "top")
7102         {
7103             label = min + increm;
7104         }
7105         else
7106         {
7107             label = max - increm;
7108         }
7109         return label;
7110     },
7111
7112     /**
7113      * @private
7114      */
7115     _getKeyArray: function(key, data)
7116     {
7117         var obj,
7118             keyArray = [],
7119             i = 0,
7120             val,
7121             len = data.length;
7122         for(; i < len; ++i)
7123         {
7124             obj = data[i][key];
7125             if(Y.Lang.isDate(obj))
7126             {   
7127                 val = obj.valueOf();
7128             }
7129             else if(!Y.Lang.isNumber(obj))
7130             {
7131                 val = new Date(obj.toString()).valueOf();
7132             }
7133             else
7134             {
7135                 val = obj;
7136             }
7137             keyArray[i] = val;
7138         }
7139         return keyArray;
7140     },
7141
7142     /**
7143      * @private (override)
7144      */
7145     _setDataByKey: function(key, data)
7146     {
7147         var obj, 
7148             arr = [], 
7149             dv = this._dataClone.concat(), 
7150             i, 
7151             val,
7152             len = dv.length;
7153         for(i = 0; i < len; ++i)
7154         {
7155             obj = dv[i][key];
7156             if(Y.Lang.isDate(obj))
7157             {
7158                 val = obj.valueOf();
7159             }
7160             else if(!Y.Lang.isNumber(obj))
7161             {
7162                 val = new Date(obj.toString()).valueOf();
7163             }
7164             else
7165             {
7166                 val = obj;
7167             }
7168             arr[i] = val;
7169         }
7170         this.get("keys")[key] = arr;
7171         this._updateTotalDataFlag = true;
7172     },
7173
7174     /**
7175      * @private
7176      */
7177     _getNumber: function(val)
7178     {
7179         if(Y.Lang.isDate(val))
7180         {
7181             val = val.valueOf();
7182         }
7183         else if(!Y.Lang.isNumber(val) && val)
7184         {
7185             val = new Date(val.toString()).valueOf();
7186         }
7187
7188         return val;
7189     }
7190 });
7191
7192 Y.TimeAxis = TimeAxis;
7193                 
7194 /**
7195  * CategoryAxis manages category data on an axis.
7196  *
7197  * @param {Object} config (optional) Configuration parameters for the Chart.
7198  * @class CategoryAxis
7199  * @constructor
7200  * @extends AxisType
7201  */
7202 function CategoryAxis(config)
7203 {
7204         CategoryAxis.superclass.constructor.apply(this, arguments);
7205 }
7206
7207 CategoryAxis.NAME = "categoryAxis";
7208
7209 Y.extend(CategoryAxis, Y.AxisType,
7210 {
7211     /**
7212      * @private
7213      */
7214     _indices: null,
7215
7216     /**
7217      * @private
7218      */
7219     GUID: "yuicategoryaxis",
7220
7221     /**
7222      * @private
7223      */
7224     _type: "category",
7225         
7226     /**
7227      * @private
7228      */
7229     _updateMinAndMax: function()
7230     {
7231         this._dataMaximum = Math.max(this.get("data").length - 1, 0);
7232         this._dataMinimum = 0;
7233     },
7234
7235     /**
7236      * @private
7237      */
7238     _getKeyArray: function(key, data)
7239     {
7240         var i = 0,
7241             obj,
7242             keyArr = [],
7243             labels = [],
7244             len = data.length;
7245         if(!this._indices)
7246         {
7247             this._indices = {};
7248         }
7249         for(; i < len; ++i)
7250         {
7251             obj = data[i];
7252             keyArr[i] = i;
7253             labels[i] = obj[key];
7254         }
7255         this._indices[key] = keyArr;
7256         return labels;
7257     },
7258
7259     /**
7260      * @private
7261      */
7262     _setDataByKey: function(key)
7263     {
7264         var i,
7265             obj, 
7266             arr = [], 
7267             labels = [], 
7268             dv = this._dataClone.concat(), 
7269             len = dv.length;
7270         if(!this._indices)
7271         {
7272             this._indices = {};
7273         }
7274         for(i = 0; i < len; ++i)
7275         {
7276             obj = dv[i];
7277             arr[i] = i;
7278             labels[i] = obj[key];
7279         }
7280         this._indices[key] = arr;
7281         this.get("keys")[key] = labels.concat();
7282         this._updateTotalDataFlag = true;
7283     },
7284
7285     /**
7286      * Returns an array of values based on an identifier key.
7287      *
7288      * @method getDataByKey
7289      * @param {String} value value used to identify the array
7290      * @return Array
7291      */
7292     getDataByKey: function (value)
7293     {
7294         if(!this._indices)
7295         {
7296             this.get("keys");
7297         }
7298         var keys = this._indices;
7299         if(keys[value])
7300         {
7301             return keys[value];
7302         }
7303         return null;
7304     },
7305
7306     /**
7307      * Returns the total number of majorUnits that will appear on an axis.
7308      *
7309      * @method getTotalMajorUnits
7310      * @return Number
7311      */
7312     getTotalMajorUnits: function(majorUnit, len)
7313     {
7314         return this.get("data").length;
7315     },
7316     
7317     /**
7318      * Returns the distance between major units on an axis.
7319      *
7320      * @method getMajorUnitDistance
7321      * @param {Number} len Number of ticks
7322      * @param {Number} uiLen Size of the axis.
7323      * @param {Object} majorUnit Hash of properties used to determine the majorUnit
7324      * @return Number
7325      */
7326     getMajorUnitDistance: function(len, uiLen, majorUnit)
7327     {
7328         var dist;
7329         if(majorUnit.determinant === "count")
7330         {
7331             dist = uiLen/len;
7332         }
7333         else if(majorUnit.determinant === "distance")
7334         {
7335             dist = majorUnit.distance;
7336         }
7337         return dist;
7338     },
7339    
7340     /**
7341      * Gets the distance that the first and last ticks are offset from there respective
7342      * edges.
7343      *
7344      * @method getEdgeOffset
7345      * @param {Number} ct Number of ticks on the axis.
7346      * @param {Number} l Length (in pixels) of the axis.
7347      * @return Number
7348      */
7349     getEdgeOffset: function(ct, l)
7350     {
7351         return l/ct;
7352     },
7353    
7354     /**
7355      * Calculates and returns a value based on the number of labels and the index of
7356      * the current label.
7357      *
7358      * @method getLabelByIndex
7359      * @param {Number} i Index of the label.
7360      * @param {Number} l Total number of labels.
7361      * @return String
7362      */
7363     getLabelByIndex: function(i, l)
7364     {
7365         var label,
7366             data = this.get("data"),
7367             position = this.get("position");
7368         if(position == "bottom" || position == "top")
7369         {
7370             label = data[i];
7371         }
7372         else
7373         {
7374             label = data[l - (i + 1)];
7375         }   
7376         return label;
7377     }
7378 });
7379
7380 Y.CategoryAxis = CategoryAxis;
7381                 
7382 /**
7383  * Utility class used for calculating curve points.
7384  *
7385  * @class CurveUtil
7386  * @constructor
7387  */
7388 function CurveUtil()
7389 {
7390 }
7391
7392 CurveUtil.prototype = {
7393     /**
7394      * Creates an array of start, end and control points for splines.
7395      *
7396      * @protected
7397      * @param {Array} xcoords Collection of x-coordinates used for calculate the curves
7398      * @param {Array} ycoords Collection of y-coordinates used for calculate the curves
7399      * @return {Object}
7400      */
7401     getCurveControlPoints: function(xcoords, ycoords) 
7402     {
7403                 var outpoints = [],
7404             i = 1,
7405             l = xcoords.length - 1,
7406                     xvals = [],
7407                     yvals = [];
7408                 
7409                 
7410                 // Too few points, need at least two
7411                 if (l < 1) 
7412         {
7413                         return null;
7414                 } 
7415         
7416         outpoints[0] = {
7417             startx: xcoords[0], 
7418             starty: ycoords[0],
7419             endx: xcoords[1],
7420             endy: ycoords[1]
7421         };
7422         
7423                 // Special case, the Bezier should be a straight line
7424         if (l === 1) 
7425         {
7426                         outpoints[0].ctrlx1 = (2.0*xcoords[0] + xcoords[1])/3.0;  
7427                         outpoints[0].ctrly2 = (2.0*ycoords[0] + ycoords[1])/3.0;
7428                         outpoints[0].ctrlx2 = 2.0*outpoints[0].ctrlx1 - xcoords[0];
7429             outpoints[0].ctrly2 = 2.0*outpoints[0].ctrly1 - ycoords[0];
7430             return outpoints;
7431                 }
7432
7433                 for (; i < l; ++i) 
7434         {
7435                         outpoints.push({startx: Math.round(xcoords[i]), starty: Math.round(ycoords[i]), endx: Math.round(xcoords[i+1]), endy: Math.round(ycoords[i+1])});
7436                         xvals[i] = 4.0 * xcoords[i] + 2*xcoords[i+1];
7437                         yvals[i] = 4.0*ycoords[i] + 2*ycoords[i+1];
7438                 }
7439                 
7440                 xvals[0] = xcoords[0] + (2.0 * xcoords[1]);
7441                 xvals[l-1] = (8.0 * xcoords[l-1] + xcoords[l]) / 2.0;
7442                 xvals = this.getControlPoints(xvals.concat());
7443         yvals[0] = ycoords[0] + (2.0 * ycoords[1]);
7444                 yvals[l-1] = (8.0 * ycoords[l-1] + ycoords[l]) / 2.0;   
7445                 yvals = this.getControlPoints(yvals.concat());
7446                 
7447         for (i = 0; i < l; ++i) 
7448         {
7449                         outpoints[i].ctrlx1 = Math.round(xvals[i]);
7450             outpoints[i].ctrly1 = Math.round(yvals[i]);
7451                         
7452                         if (i < l-1) 
7453             {
7454                                 outpoints[i].ctrlx2 = Math.round(2*xcoords[i+1] - xvals[i+1]);
7455                 outpoints[i].ctrly2 = Math.round(2*ycoords[i+1] - yvals[i+1]);
7456                         }
7457                         else 
7458             {
7459                                 outpoints[i].ctrlx2 = Math.round((xcoords[l] + xvals[l-1])/2);
7460                 outpoints[i].ctrly2 = Math.round((ycoords[l] + yvals[l-1])/2);
7461                         }
7462                 }
7463                 
7464                 return outpoints;       
7465         },
7466
7467     /**
7468      * @private
7469      */
7470         getControlPoints: function(vals) 
7471     {
7472                 var l = vals.length,
7473             x = [],
7474             tmp = [],
7475             b = 2.0,
7476             i = 1;
7477                 x[0] = vals[0] / b;
7478                 for (; i < l; ++i) 
7479         {
7480                         tmp[i] = 1/b;
7481                         b = (i < l-1 ? 4.0 : 3.5) - tmp[i];
7482                         x[i] = (vals[i] - x[i-1]) / b;
7483                 }
7484                 
7485                 for (i = 1; i < l; ++i) 
7486         {
7487                         x[l-i-1] -= tmp[l-i] * x[l-i];
7488                 }
7489                 
7490                 return x;
7491         }
7492 };
7493 Y.CurveUtil = CurveUtil;
7494 /**
7495  * Utility class used for creating stacked series.
7496  *
7497  * @class StackingUtil
7498  * @constructor
7499  */
7500 function StackingUtil(){}
7501
7502 StackingUtil.prototype = {
7503     /**
7504      * @protected
7505      *
7506      * Adjusts coordinate values for stacked series.
7507      *
7508      * @method _stackCoordinates
7509      */
7510     _stackCoordinates: function() 
7511     {
7512         var direction = this.get("direction"),
7513             order = this.get("order"),
7514             type = this.get("type"),
7515             graph = this.get("graph"),
7516             h = graph.get("height"), 
7517             seriesCollection = graph.seriesTypes[type],
7518             i = 0,
7519             len,
7520             xcoords = this.get("xcoords"),
7521             ycoords = this.get("ycoords"),
7522             prevXCoords,
7523             prevYCoords;
7524         if(order === 0)
7525         {
7526             return;
7527         }
7528         prevXCoords = seriesCollection[order - 1].get("xcoords").concat();
7529         prevYCoords = seriesCollection[order - 1].get("ycoords").concat();
7530         if(direction === "vertical")
7531         {
7532             len = prevXCoords.length;
7533             for(; i < len; ++i)
7534             {
7535                 if(!isNaN(prevXCoords[i]) && !isNaN(xcoords[i]))
7536                 {
7537                     xcoords[i] += prevXCoords[i];
7538                 }
7539             }
7540         }
7541         else
7542         {
7543             len = prevYCoords.length;
7544             for(; i < len; ++i)
7545             {
7546                 if(!isNaN(prevYCoords[i]) && !isNaN(ycoords[i]))
7547                 {
7548                     ycoords[i] = prevYCoords[i] - (h - ycoords[i]);
7549                 }
7550             }
7551         }
7552     }
7553 };
7554 Y.StackingUtil = StackingUtil;
7555 /**
7556  * Utility class used for drawing lines.
7557  *
7558  * @class Lines
7559  * @constructor
7560  */
7561 function Lines(){}
7562
7563 Lines.prototype = {
7564     /**
7565      * @private
7566      */
7567     _lineDefaults: null,
7568     
7569     /**
7570      * Creates a graphic in which to draw a series.
7571      *
7572      * @method _getGraphic
7573      * @return Graphic
7574      * @private
7575      */
7576     _getGraphic: function()
7577     {
7578         var graph = this.get("graph");
7579         if(!this._lineGraphic)
7580         {
7581             this._lineGraphic = new Y.Graphic();
7582             this._lineGraphic.render(graph.get("contentBox"));
7583         }
7584         this._lineGraphic.clear();
7585         this._lineGraphic.setSize(graph.get("width"), graph.get("height"));
7586         this.autoSize = false;
7587         return this._lineGraphic;
7588     },
7589
7590     /**
7591      * Draws lines for the series.
7592      *
7593      * @method drawLines
7594      * @protected
7595      */
7596     drawLines: function()
7597     {
7598         if(this.get("xcoords").length < 1) 
7599         {
7600             return;
7601         }
7602         var xcoords = this.get("xcoords").concat(),
7603             ycoords = this.get("ycoords").concat(),
7604             direction = this.get("direction"),
7605             len = direction === "vertical" ? ycoords.length : xcoords.length,
7606             lastX,
7607             lastY,
7608             lastValidX = lastX,
7609             lastValidY = lastY,
7610             nextX,
7611             nextY,
7612             i,
7613             styles = this.get("styles").line,
7614             lineType = styles.lineType,
7615             lc = styles.color || this._getDefaultColor(this.get("graphOrder"), "line"),
7616             lineAlpha = styles.alpha,
7617             dashLength = styles.dashLength,
7618             gapSpace = styles.gapSpace,
7619             connectDiscontinuousPoints = styles.connectDiscontinuousPoints,
7620             discontinuousType = styles.discontinuousType,
7621             discontinuousDashLength = styles.discontinuousDashLength,
7622             discontinuousGapSpace = styles.discontinuousGapSpace,
7623             graphic = this._getGraphic();
7624         lastX = lastValidX = xcoords[0];
7625         lastY = lastValidY = ycoords[0];
7626         graphic.lineStyle(styles.weight, lc, lineAlpha);
7627         graphic.moveTo(lastX, lastY);
7628         for(i = 1; i < len; i = ++i)
7629         {
7630             nextX = xcoords[i];
7631             nextY = ycoords[i];
7632             if(isNaN(nextY))
7633             {
7634                 lastValidX = nextX;
7635                 lastValidY = nextY;
7636                 continue;
7637             }
7638             if(lastValidX == lastX)
7639             {
7640                 if(lineType != "dashed")
7641                 {
7642                     graphic.lineTo(nextX, nextY);
7643                 }
7644                 else
7645                 {
7646                     this.drawDashedLine(lastValidX, lastValidY, nextX, nextY, 
7647                                                 dashLength, 
7648                                                 gapSpace);
7649                 }
7650             }
7651             else if(!connectDiscontinuousPoints)
7652             {
7653                 graphic.moveTo(nextX, nextY);
7654             }
7655             else
7656             {
7657                 if(discontinuousType != "solid")
7658                 {
7659                     this.drawDashedLine(lastValidX, lastValidY, nextX, nextY, 
7660                                                 discontinuousDashLength, 
7661                                                 discontinuousGapSpace);
7662                 }
7663                 else
7664                 {
7665                     graphic.lineTo(nextX, nextY);
7666                 }
7667             }
7668         
7669             lastX = lastValidX = nextX;
7670             lastY = lastValidY = nextY;
7671         }
7672         graphic.end();
7673     },
7674     
7675     /**
7676      * Connects data points with a consistent curve for a series.
7677      * 
7678      * @method drawSpline
7679      * @protected
7680      */
7681     drawSpline: function()
7682     {
7683         if(this.get("xcoords").length < 1) 
7684         {
7685             return;
7686         }
7687         var xcoords = this.get("xcoords"),
7688             ycoords = this.get("ycoords"),
7689             curvecoords = this.getCurveControlPoints(xcoords, ycoords),
7690             len = curvecoords.length,
7691             cx1,
7692             cx2,
7693             cy1,
7694             cy2,
7695             x,
7696             y,
7697             i = 0,
7698             styles = this.get("styles").line,
7699             graphic = this._getGraphic(),
7700             lineAlpha = styles.alpha,
7701             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "line");
7702         graphic.lineStyle(styles.weight, color, lineAlpha);
7703         graphic.moveTo(xcoords[0], ycoords[0]);
7704         for(; i < len; i = ++i)
7705         {
7706             x = curvecoords[i].endx;
7707             y = curvecoords[i].endy;
7708             cx1 = curvecoords[i].ctrlx1;
7709             cx2 = curvecoords[i].ctrlx2;
7710             cy1 = curvecoords[i].ctrly1;
7711             cy2 = curvecoords[i].ctrly2;
7712             graphic.curveTo(cx1, cy1, cx2, cy2, x, y);
7713         }
7714         graphic.end();
7715     },
7716
7717     /**
7718      * Draws a dashed line between two points.
7719      * 
7720      * @method drawDashedLine
7721      * @param {Number} xStart   The x position of the start of the line
7722      * @param {Number} yStart   The y position of the start of the line
7723      * @param {Number} xEnd             The x position of the end of the line
7724      * @param {Number} yEnd             The y position of the end of the line
7725      * @param {Number} dashSize the size of dashes, in pixels
7726      * @param {Number} gapSize  the size of gaps between dashes, in pixels
7727      * @private
7728      */
7729     drawDashedLine: function(xStart, yStart, xEnd, yEnd, dashSize, gapSize)
7730     {
7731         dashSize = dashSize || 10;
7732         gapSize = gapSize || 10;
7733         var segmentLength = dashSize + gapSize,
7734             xDelta = xEnd - xStart,
7735             yDelta = yEnd - yStart,
7736             delta = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2)),
7737             segmentCount = Math.floor(Math.abs(delta / segmentLength)),
7738             radians = Math.atan2(yDelta, xDelta),
7739             xCurrent = xStart,
7740             yCurrent = yStart,
7741             i,
7742             graphic = this._getGraphic();
7743         xDelta = Math.cos(radians) * segmentLength;
7744         yDelta = Math.sin(radians) * segmentLength;
7745         
7746         for(i = 0; i < segmentCount; ++i)
7747         {
7748             graphic.moveTo(xCurrent, yCurrent);
7749             graphic.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize);
7750             xCurrent += xDelta;
7751             yCurrent += yDelta;
7752         }
7753         
7754         graphic.moveTo(xCurrent, yCurrent);
7755         delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent));
7756         
7757         if(delta > dashSize)
7758         {
7759             graphic.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize);
7760         }
7761         else if(delta > 0)
7762         {
7763             graphic.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta);
7764         }
7765         
7766         graphic.moveTo(xEnd, yEnd);
7767     },
7768
7769     /**
7770      * Default values for <code>styles</code> attribute.
7771      *
7772      * @method _getLineDefaults
7773      * @return Object
7774      * @protected
7775      */
7776     _getLineDefaults: function()
7777     {
7778         return {
7779             alpha: 1,
7780             weight: 6,
7781             lineType:"solid", 
7782             dashLength:10, 
7783             gapSpace:10, 
7784             connectDiscontinuousPoints:true, 
7785             discontinuousType:"solid", 
7786             discontinuousDashLength:10, 
7787             discontinuousGapSpace:10
7788         };
7789     }
7790 };
7791 Y.augment(Lines, Y.Attribute);
7792 Y.Lines = Lines;
7793 /**
7794  * Utility class used for drawing area fills.
7795  *
7796  * @class Fills
7797  * @constructor
7798  */
7799 function Fills(cfg)
7800 {
7801     var attrs = {
7802         area: {
7803             getter: function()
7804             {
7805                 return this._defaults || this._getAreaDefaults();
7806             },
7807
7808             setter: function(val)
7809             {
7810                 var defaults = this._defaults || this._getAreaDefaults();
7811                 this._defaults = Y.merge(defaults, val);
7812             }
7813         }
7814     };
7815     this.addAttrs(attrs, cfg);
7816     this.get("styles");
7817 }
7818
7819 Fills.prototype = {
7820     /**
7821      * Draws fill
7822      *
7823      * @method drawFill
7824      * @protected
7825      */
7826     drawFill: function(xcoords, ycoords)
7827     {
7828         if(xcoords.length < 1) 
7829         {
7830             return;
7831         }
7832         var len = xcoords.length,
7833             firstX = xcoords[0],
7834             firstY = ycoords[0],
7835             lastValidX = firstX,
7836             lastValidY = firstY,
7837             nextX,
7838             nextY,
7839             i = 1,
7840             styles = this.get("styles").area,
7841             graphic = this.get("graphic"),
7842             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
7843         graphic.clear();
7844         graphic.beginFill(color, styles.alpha);
7845         graphic.moveTo(firstX, firstY);
7846         for(; i < len; i = ++i)
7847         {
7848             nextX = xcoords[i];
7849             nextY = ycoords[i];
7850             if(isNaN(nextY))
7851             {
7852                 lastValidX = nextX;
7853                 lastValidY = nextY;
7854                 continue;
7855             }
7856             graphic.lineTo(nextX, nextY);
7857             lastValidX = nextX;
7858             lastValidY = nextY;
7859         }
7860         graphic.end();
7861     },
7862         
7863     /**
7864      * Draws a fill for a spline
7865      *
7866      * @method drawAreaSpline
7867      * @protected
7868      */
7869     drawAreaSpline: function()
7870     {
7871         if(this.get("xcoords").length < 1) 
7872         {
7873             return;
7874         }
7875         var xcoords = this.get("xcoords"),
7876             ycoords = this.get("ycoords"),
7877             curvecoords = this.getCurveControlPoints(xcoords, ycoords),
7878             len = curvecoords.length,
7879             cx1,
7880             cx2,
7881             cy1,
7882             cy2,
7883             x,
7884             y,
7885             i = 0,
7886             firstX = xcoords[0],
7887             firstY = ycoords[0],
7888             styles = this.get("styles").area,
7889             graphic = this.get("graphic"),
7890             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
7891         graphic.beginFill(color, styles.alpha);
7892         graphic.moveTo(firstX, firstY);
7893         for(; i < len; i = ++i)
7894         {
7895             x = curvecoords[i].endx;
7896             y = curvecoords[i].endy;
7897             cx1 = curvecoords[i].ctrlx1;
7898             cx2 = curvecoords[i].ctrlx2;
7899             cy1 = curvecoords[i].ctrly1;
7900             cy2 = curvecoords[i].ctrly2;
7901             graphic.curveTo(cx1, cy1, cx2, cy2, x, y);
7902         }
7903         if(this.get("direction") === "vertical")
7904         {
7905             graphic.lineTo(this._leftOrigin, y);
7906             graphic.lineTo(this._leftOrigin, firstY);
7907         }
7908         else
7909         {
7910             graphic.lineTo(x, this._bottomOrigin);
7911             graphic.lineTo(firstX, this._bottomOrigin);
7912         }
7913         graphic.lineTo(firstX, firstY);
7914         graphic.end();
7915     },
7916     
7917     /**
7918      * Draws a a stacked area spline
7919      *
7920      * @method drawStackedAreaSpline
7921      * @protected
7922      */
7923     drawStackedAreaSpline: function()
7924     {
7925         if(this.get("xcoords").length < 1) 
7926         {
7927             return;
7928         }
7929         var xcoords = this.get("xcoords"),
7930             ycoords = this.get("ycoords"),
7931             curvecoords,
7932             order = this.get("order"),
7933             type = this.get("type"),
7934             graph = this.get("graph"),
7935             seriesCollection = graph.seriesTypes[type],
7936             prevXCoords,
7937             prevYCoords,
7938             len,
7939             cx1,
7940             cx2,
7941             cy1,
7942             cy2,
7943             x,
7944             y,
7945             i = 0,
7946             firstX,
7947             firstY,
7948             styles = this.get("styles").area,
7949             graphic = this.get("graphic"),
7950             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
7951         firstX = xcoords[0];
7952         firstY = ycoords[0];
7953         curvecoords = this.getCurveControlPoints(xcoords, ycoords);
7954         len = curvecoords.length;
7955         graphic.beginFill(color, styles.alpha);
7956         graphic.moveTo(firstX, firstY);
7957         for(; i < len; i = ++i)
7958         {
7959             x = curvecoords[i].endx;
7960             y = curvecoords[i].endy;
7961             cx1 = curvecoords[i].ctrlx1;
7962             cx2 = curvecoords[i].ctrlx2;
7963             cy1 = curvecoords[i].ctrly1;
7964             cy2 = curvecoords[i].ctrly2;
7965             graphic.curveTo(cx1, cy1, cx2, cy2, x, y);
7966         }
7967         if(order > 0)
7968         {
7969             prevXCoords = seriesCollection[order - 1].get("xcoords").concat().reverse();
7970             prevYCoords = seriesCollection[order - 1].get("ycoords").concat().reverse();
7971             curvecoords = this.getCurveControlPoints(prevXCoords, prevYCoords);
7972             i = 0;
7973             len = curvecoords.length;
7974             graphic.lineTo(prevXCoords[0], prevYCoords[0]);
7975             for(; i < len; i = ++i)
7976             {
7977                 x = curvecoords[i].endx;
7978                 y = curvecoords[i].endy;
7979                 cx1 = curvecoords[i].ctrlx1;
7980                 cx2 = curvecoords[i].ctrlx2;
7981                 cy1 = curvecoords[i].ctrly1;
7982                 cy2 = curvecoords[i].ctrly2;
7983                 graphic.curveTo(cx1, cy1, cx2, cy2, x, y);
7984             }
7985         }
7986         else
7987         {
7988             if(this.get("direction") === "vertical")
7989             {
7990                 graphic.lineTo(this._leftOrigin, ycoords[ycoords.length-1]);
7991                 graphic.lineTo(this._leftOrigin, firstY);
7992             }
7993             else
7994             {
7995                 graphic.lineTo(xcoords[xcoords.length-1], this._bottomOrigin);
7996                 graphic.lineTo(firstX, this._bottomOrigin);
7997             }
7998
7999         }
8000         graphic.lineTo(firstX, firstY);
8001         graphic.end();
8002     },
8003     
8004     /**
8005      * @private
8006      */
8007     _defaults: null,
8008
8009     /**
8010      * Concatanates coordinate array with correct coordinates for closing an area fill.
8011      *
8012      * @method _getClosingPoints
8013      * @return Array
8014      * @protected
8015      */
8016     _getClosingPoints: function()
8017     {
8018         var xcoords = this.get("xcoords").concat(),
8019             ycoords = this.get("ycoords").concat();
8020         if(this.get("direction") === "vertical")
8021         {
8022             xcoords.push(this._leftOrigin);
8023             xcoords.push(this._leftOrigin);
8024             ycoords.push(ycoords[ycoords.length - 1]);
8025             ycoords.push(ycoords[0]);
8026         }
8027         else
8028         {
8029             xcoords.push(xcoords[xcoords.length - 1]);
8030             xcoords.push(xcoords[0]);
8031             ycoords.push(this._bottomOrigin);
8032             ycoords.push(this._bottomOrigin);
8033         }
8034         xcoords.push(xcoords[0]);
8035         ycoords.push(ycoords[0]);
8036         return [xcoords, ycoords];
8037     },
8038
8039     /**
8040      * Concatenates coordinate array with the correct coordinates for closing an area stack.
8041      *
8042      * @method _getStackedClosingPoints
8043      * @return Array
8044      * @protected
8045      */
8046     _getStackedClosingPoints: function()
8047     {
8048         var order = this.get("order"),
8049             type = this.get("type"),
8050             graph = this.get("graph"),
8051             direction = this.get("direction"),
8052             seriesCollection = graph.seriesTypes[type],
8053             prevXCoords,
8054             prevYCoords,
8055             allXCoords = this.get("xcoords").concat(),
8056             allYCoords = this.get("ycoords").concat(),
8057             firstX = allXCoords[0],
8058             firstY = allYCoords[0];
8059         
8060         if(order > 0)
8061         {
8062             prevXCoords = seriesCollection[order - 1].get("xcoords").concat();
8063             prevYCoords = seriesCollection[order - 1].get("ycoords").concat();
8064             allXCoords = allXCoords.concat(prevXCoords.concat().reverse());
8065             allYCoords = allYCoords.concat(prevYCoords.concat().reverse());
8066             allXCoords.push(allXCoords[0]);
8067             allYCoords.push(allYCoords[0]);
8068         }
8069         else
8070         {
8071             if(direction === "vertical")
8072             {
8073                 allXCoords.push(this._leftOrigin);
8074                 allXCoords.push(this._leftOrigin);
8075                 allYCoords.push(allYCoords[allYCoords.length-1]);
8076                 allYCoords.push(firstY);
8077             }
8078             else
8079             {
8080                 allXCoords.push(allXCoords[allXCoords.length-1]);
8081                 allXCoords.push(firstX);
8082                 allYCoords.push(this._bottomOrigin);
8083                 allYCoords.push(this._bottomOrigin);
8084             }
8085         }
8086         return [allXCoords, allYCoords];
8087     },
8088
8089     /**
8090      * @private
8091      */
8092     _getAreaDefaults: function()
8093     {
8094         return {
8095         };
8096     }
8097 };
8098 Y.augment(Fills, Y.Attribute);
8099 Y.Fills = Fills;
8100 /**
8101  * Utility class used for drawing markers.
8102  *
8103  * @class Plots
8104  * @constructor
8105  */
8106 function Plots(cfg)
8107 {
8108     var attrs = { 
8109         markers: {
8110             getter: function()
8111             {
8112                 return this._markers;
8113             }
8114         }
8115     };
8116     this.addAttrs(attrs, cfg);
8117 }
8118
8119 Plots.prototype = {
8120     /**
8121      * @private
8122      */
8123     _plotDefaults: null,
8124
8125     /**
8126      * Draws the markers
8127      *
8128      * @method drawPlots
8129      * @protected
8130      */
8131     drawPlots: function()
8132     {
8133         if(!this.get("xcoords") || this.get("xcoords").length < 1) 
8134                 {
8135                         return;
8136                 }
8137         var style = Y.clone(this.get("styles").marker),
8138             w = style.width,
8139             h = style.height,
8140             xcoords = this.get("xcoords"),
8141             ycoords = this.get("ycoords"),
8142             i = 0,
8143             len = xcoords.length,
8144             top = ycoords[0],
8145             left,
8146             marker,
8147             offsetWidth = w/2,
8148             offsetHeight = h/2,
8149             fillColors = null,
8150             borderColors = null,
8151             graphOrder = this.get("graphOrder"),
8152             hotspot,
8153             isChrome = ISCHROME;
8154         if(Y.Lang.isArray(style.fill.color))
8155         {
8156             fillColors = style.fill.color.concat(); 
8157         }
8158         if(Y.Lang.isArray(style.border.color))
8159         {
8160             borderColors = style.border.colors.concat();
8161         }
8162         this._createMarkerCache();
8163         if(isChrome)
8164         {
8165             this._createHotspotCache();
8166         }
8167         for(; i < len; ++i)
8168         {
8169             top = (ycoords[i] - offsetHeight);
8170             left = (xcoords[i] - offsetWidth);            
8171             if(!top || !left || top === undefined || left === undefined || top == "undefined" || left == "undefined" || isNaN(top) || isNaN(left))
8172             {
8173                 this._markers.push(null);
8174                 this._graphicNodes.push(null);
8175                 continue;
8176             }
8177             if(fillColors)
8178             {
8179                 style.fill.color = fillColors[i % fillColors.length];
8180             }
8181             if(borderColors)
8182             {
8183                 style.border.colors = borderColors[i % borderColors.length];
8184             }
8185             marker = this.getMarker(style, graphOrder, i);
8186             marker.setPosition(left, top);
8187             if(isChrome)
8188             {
8189                 hotspot = this.getHotspot(style, graphOrder, i);
8190                 hotspot.setPosition(left, top);
8191                 hotspot.parentNode.style.zIndex = 5;
8192             }
8193         }
8194         this._clearMarkerCache();
8195         if(isChrome)
8196         {
8197             this._clearHotspotCache();
8198         }
8199     },
8200
8201     /**
8202      * Gets the default values for series that use the utility. This method is used by
8203      * the class' <code>styles</code> attribute's getter to get build default values.
8204      *
8205      * @method _getPlotDefaults
8206      * @return Object
8207      * @protected
8208      */
8209     _getPlotDefaults: function()
8210     {
8211         var defs = {
8212             fill:{
8213                 type: "solid",
8214                 alpha: 1,
8215                 colors:null,
8216                 alphas: null,
8217                 ratios: null
8218             },
8219             border:{
8220                 weight: 1,
8221                 alpha: 1
8222             },
8223             width: 10,
8224             height: 10,
8225             shape: "circle"
8226         };
8227         defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
8228         defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
8229         return defs;
8230     },
8231
8232     /**
8233      * Collection of markers to be used in the series.
8234      *
8235      * @private
8236      */
8237     _markers: null,
8238
8239     /**
8240      * Collection of markers to be re-used on a series redraw.
8241      *
8242      * @private
8243      */
8244     _markerCache: null,
8245     
8246     /**
8247      * Gets and styles a marker. If there is a marker in cache, it will use it. Otherwise
8248      * it will create one.
8249      *
8250      * @method getMarker
8251      * @param {Object} styles Hash of style properties.
8252      * @param {Number} order Order of the series.
8253      * @param {Number} index Index within the series associated with the marker.
8254      * @return Shape
8255      * @protected
8256      */
8257     getMarker: function(styles, order, index)
8258     {
8259         var marker;
8260         if(this._markerCache.length > 0)
8261         {
8262             while(!marker)
8263             {
8264                 if(this._markerCache.length < 1)
8265                 {
8266                     marker = this._createMarker(styles, order, index);
8267                     break;
8268                 }
8269                 marker = this._markerCache.shift();
8270
8271             }
8272             marker.update(styles);
8273         }
8274         else
8275         {
8276             marker = this._createMarker(styles, order, index);
8277         }
8278         this._markers.push(marker);
8279         this._graphicNodes.push(marker.parentNode);
8280         return marker;
8281     },   
8282     
8283     /**
8284      * Creates a shape to be used as a marker.
8285      *
8286      * @method _createMarker
8287      * @param {Object} styles Hash of style properties.
8288      * @param {Number} order Order of the series.
8289      * @param {Number} index Index within the series associated with the marker.
8290      * @return Shape
8291      * @private
8292      */
8293     _createMarker: function(styles, order, index)
8294     {
8295         var graphic = new Y.Graphic(),
8296             marker,
8297             cfg = Y.clone(styles);
8298         graphic.render(this.get("graph").get("contentBox"));
8299         graphic.node.setAttribute("id", "markerParent_" + order + "_" + index);
8300         cfg.graphic = graphic;
8301         marker = new Y.Shape(cfg); 
8302         marker.addClass("yui3-seriesmarker");
8303         marker.node.setAttribute("id", "series_" + order + "_" + index);
8304         return marker;
8305     },
8306     
8307     /**
8308      * Creates a cache of markers for reuse.
8309      *
8310      * @method _createMarkerCache
8311      * @private
8312      */
8313     _createMarkerCache: function()
8314     {
8315         if(this._markers && this._markers.length > 0)
8316         {
8317             this._markerCache = this._markers.concat();
8318         }
8319         else
8320         {
8321             this._markerCache = [];
8322         }
8323         this._markers = [];
8324         this._graphicNodes = [];
8325     },
8326     
8327     /**
8328      * Removes unused markers from the marker cache
8329      *
8330      * @method _clearMarkerCache
8331      * @private
8332      */
8333     _clearMarkerCache: function()
8334     {
8335         var len = this._markerCache.length,
8336             i = 0,
8337             graphic,
8338             marker;
8339         for(; i < len; ++i)
8340         {
8341             marker = this._markerCache[i];
8342             if(marker)
8343             {
8344                 graphic = marker.graphics;
8345                 graphic.destroy();
8346             }
8347         }
8348         this._markerCache = [];
8349     },
8350
8351     /**
8352      * Resizes and positions markers based on a mouse interaction.
8353      *
8354      * @method updateMarkerState
8355      * @param {String} type state of the marker
8356      * @param {Number} i index of the marker
8357      * @protected
8358      */
8359     updateMarkerState: function(type, i)
8360     {
8361         if(this._markers[i])
8362         {
8363             var w,
8364                 h,
8365                 markerStyles,
8366                 styles = Y.clone(this.get("styles").marker),
8367                 state = this._getState(type),
8368                 xcoords = this.get("xcoords"),
8369                 ycoords = this.get("ycoords"),
8370                 marker = this._markers[i],
8371                 graphicNode = marker.parentNode;
8372                 markerStyles = state == "off" || !styles[state] ? styles : styles[state]; 
8373                 markerStyles.fill.color = this._getItemColor(markerStyles.fill.color, i);
8374                 markerStyles.border.color = this._getItemColor(markerStyles.border.color, i);
8375                 marker.update(markerStyles);
8376                 w = markerStyles.width;
8377                 h = markerStyles.height;
8378                 graphicNode.style.left = (xcoords[i] - w/2) + "px";
8379                 graphicNode.style.top = (ycoords[i] - h/2) + "px";
8380                 marker.toggleVisible(this.get("visible"));
8381         }
8382     },
8383
8384     /**
8385      * Parses a color from an array.
8386      *
8387      * @method _getItemColor
8388      * @param {Array} val collection of colors
8389      * @param {Number} i index of the item
8390      * @return String
8391      * @protected
8392      */
8393     _getItemColor: function(val, i)
8394     {
8395         if(Y.Lang.isArray(val))
8396         {
8397             return val[i % val.length];
8398         }
8399         return val;
8400     },
8401
8402     /**
8403      * Method used by <code>styles</code> setter. Overrides base implementation.
8404      *
8405      * @method _setStyles
8406      * @param {Object} newStyles Hash of properties to update.
8407      * @return Object
8408      * @protected
8409      */
8410     _setStyles: function(val)
8411     {
8412         val = this._parseMarkerStyles(val);
8413         return Y.Renderer.prototype._setStyles.apply(this, [val]);
8414     },
8415
8416     /**
8417      * Combines new styles with existing styles.
8418      *
8419      * @method _parseMarkerStyles
8420      * @private
8421      */
8422     _parseMarkerStyles: function(val)
8423     {
8424         if(val.marker)
8425         {
8426             var defs = this._getPlotDefaults();
8427             val.marker = this._mergeStyles(val.marker, defs);
8428             if(val.marker.over)
8429             {
8430                 val.marker.over = this._mergeStyles(val.marker.over, val.marker);
8431             }
8432             if(val.marker.down)
8433             {
8434                 val.marker.down = this._mergeStyles(val.marker.down, val.marker);
8435             }
8436         }
8437         return val;
8438     },
8439
8440     /**
8441      * Returns marker state based on event type
8442      *
8443      * @method _getState
8444      * @param {String} type event type
8445      * @return String
8446      * @protected
8447      */
8448     _getState: function(type)
8449     {
8450         var state;
8451         switch(type)
8452         {
8453             case "mouseout" :
8454                 state = "off";
8455             break;
8456             case "mouseover" :
8457                 state = "over";
8458             break;
8459             case "mouseup" :
8460                 state = "over";
8461             break;
8462             case "mousedown" :
8463                 state = "down";
8464             break;
8465         }
8466         return state;
8467     },
8468     
8469     /**
8470      * @private
8471      */
8472     _stateSyles: null,
8473
8474     /**
8475      * Collection of hotspots to be used in the series.
8476      *
8477      * @private
8478      */
8479     _hotspots: null,
8480
8481     /**
8482      * Collection of hotspots to be re-used on a series redraw.
8483      *
8484      * @private
8485      */
8486     _hotspotCache: null,
8487     
8488     /**
8489      * Gets and styles a hotspot. If there is a hotspot in cache, it will use it. Otherwise
8490      * it will create one.
8491      *
8492      * @method getHotspot
8493      * @param {Object} styles Hash of style properties.
8494      * @param {Number} order Order of the series.
8495      * @param {Number} index Index within the series associated with the hotspot.
8496      * @return Shape
8497      * @protected
8498      */
8499     getHotspot: function(hotspotStyles, order, index)
8500     {
8501         var hotspot,
8502             styles = Y.clone(hotspotStyles);
8503         styles.fill = {
8504             type: "solid",
8505             color: "#000",
8506             alpha: 0
8507         };
8508         styles.border = {
8509             weight: 0
8510         };
8511         if(this._hotspotCache.length > 0)
8512         {
8513             while(!hotspot)
8514             {
8515                 if(this._hotspotCache.length < 1)
8516                 {
8517                     hotspot = this._createHotspot(styles, order, index);
8518                     break;
8519                 }
8520                 hotspot = this._hotspotCache.shift();
8521
8522             }
8523             hotspot.update(styles);
8524         }
8525         else
8526         {
8527             hotspot = this._createHotspot(styles, order, index);
8528         }
8529         this._hotspots.push(hotspot);
8530         return hotspot;
8531     },   
8532     
8533     /**
8534      * Creates a shape to be used as a hotspot.
8535      *
8536      * @method _createHotspot
8537      * @param {Object} styles Hash of style properties.
8538      * @param {Number} order Order of the series.
8539      * @param {Number} index Index within the series associated with the hotspot.
8540      * @return Shape
8541      * @private
8542      */
8543     _createHotspot: function(styles, order, index)
8544     {
8545         var graphic = new Y.Graphic(),
8546             hotspot,
8547             cfg = Y.clone(styles);
8548         graphic.render(this.get("graph").get("contentBox"));
8549         graphic.node.setAttribute("id", "hotspotParent_" + order + "_" + index);
8550         cfg.graphic = graphic;
8551         hotspot = new Y.Shape(cfg); 
8552         hotspot.addClass("yui3-seriesmarker");
8553         hotspot.node.setAttribute("id", "hotspot_" + order + "_" + index);
8554         return hotspot;
8555     },
8556     
8557     /**
8558      * Creates a cache of hotspots for reuse.
8559      *
8560      * @method _createHotspotCache
8561      * @private
8562      */
8563     _createHotspotCache: function()
8564     {
8565         if(this._hotspots && this._hotspots.length > 0)
8566         {
8567             this._hotspotCache = this._hotspots.concat();
8568         }
8569         else
8570         {
8571             this._hotspotCache = [];
8572         }
8573         this._hotspots = [];
8574     },
8575     
8576     /**
8577      * Removes unused hotspots from the hotspot cache
8578      *
8579      * @method _clearHotspotCache
8580      * @private
8581      */
8582     _clearHotspotCache: function()
8583     {
8584         var len = this._hotspotCache.length,
8585             i = 0,
8586             graphic,
8587             hotspot;
8588         for(; i < len; ++i)
8589         {
8590             hotspot = this._hotspotCache[i];
8591             if(hotspot)
8592             {
8593                 graphic = hotspot.graphics;
8594                 graphic.destroy();
8595             }
8596         }
8597         this._hotspotCache = [];
8598     }
8599 };
8600
8601 Y.augment(Plots, Y.Attribute);
8602 Y.Plots = Plots;
8603 /**
8604  * Histogram is the base class for Column and Bar series.
8605  *
8606  * @class Histogram
8607  * @constructor
8608  */
8609 function Histogram(){}
8610
8611 Histogram.prototype = {
8612     /**
8613      * @protected
8614      *
8615      * Draws the series.
8616      *
8617      * @method drawSeries
8618      */
8619     drawSeries: function()
8620     {
8621         if(this.get("xcoords").length < 1) 
8622         {
8623             return;
8624         }
8625         var style = Y.clone(this.get("styles").marker),
8626             setSize,
8627             calculatedSize,
8628             xcoords = this.get("xcoords"),
8629             ycoords = this.get("ycoords"),
8630             i = 0,
8631             len = xcoords.length,
8632             top = ycoords[0],
8633             type = this.get("type"),
8634             graph = this.get("graph"),
8635             seriesCollection = graph.seriesTypes[type],
8636             seriesLen = seriesCollection.length,
8637             seriesSize = 0,
8638             totalSize = 0,
8639             offset = 0,
8640             ratio,
8641             renderer,
8642             order = this.get("order"),
8643             graphOrder = this.get("graphOrder"),
8644             left,
8645             marker,
8646             setSizeKey,
8647             calculatedSizeKey,
8648             config,
8649             fillColors = null,
8650             borderColors = null,
8651             hotspot,
8652             isChrome = ISCHROME;
8653         if(Y.Lang.isArray(style.fill.color))
8654         {
8655             fillColors = style.fill.color.concat(); 
8656         }
8657         if(Y.Lang.isArray(style.border.color))
8658         {
8659             borderColors = style.border.colors.concat();
8660         }
8661         if(this.get("direction") == "vertical")
8662         {
8663             setSizeKey = "height";
8664             calculatedSizeKey = "width";
8665         }
8666         else
8667         {
8668             setSizeKey = "width";
8669             calculatedSizeKey = "height";
8670         }
8671         setSize = style[setSizeKey];
8672         calculatedSize = style[calculatedSizeKey];
8673         this._createMarkerCache();
8674         if(isChrome)
8675         {
8676             this._createHotspotCache();
8677         }
8678         for(; i < seriesLen; ++i)
8679         {
8680             renderer = seriesCollection[i];
8681             seriesSize += renderer.get("styles").marker[setSizeKey];
8682             if(order > i) 
8683             {
8684                 offset = seriesSize;
8685             }
8686         }
8687         totalSize = len * seriesSize;
8688         if(totalSize > graph.get(setSizeKey))
8689         {
8690             ratio = graph.get(setSizeKey)/totalSize;
8691             seriesSize *= ratio;
8692             offset *= ratio;
8693             setSize *= ratio;
8694             setSize = Math.max(setSize, 1);
8695         }
8696         offset -= seriesSize/2;
8697         for(i = 0; i < len; ++i)
8698         {
8699             config = this._getMarkerDimensions(xcoords[i], ycoords[i], calculatedSize, offset);
8700             top = config.top;
8701             calculatedSize = config.calculatedSize;
8702             left = config.left;
8703             style[setSizeKey] = setSize;
8704             style[calculatedSizeKey] = calculatedSize;
8705             if(fillColors)
8706             {
8707                 style.fill.color = fillColors[i % fillColors.length];
8708             }
8709             if(borderColors)
8710             {
8711                 style.border.colors = borderColors[i % borderColors.length];
8712             }
8713             marker = this.getMarker(style, graphOrder, i);
8714             marker.setPosition(left, top);
8715             if(isChrome)
8716             {
8717                 hotspot = this.getHotspot(style, graphOrder, i);
8718                 hotspot.setPosition(left, top);
8719                 hotspot.parentNode.style.zIndex = 5;
8720             }
8721         }
8722         this._clearMarkerCache();
8723         if(isChrome)
8724         {
8725             this._clearHotspotCache();
8726         }
8727     },
8728     
8729     /**
8730      * @private
8731      */
8732     _defaultFillColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],
8733     
8734     /**
8735      * @private
8736      */
8737     _getPlotDefaults: function()
8738     {
8739         var defs = {
8740             fill:{
8741                 type: "solid",
8742                 alpha: 1,
8743                 colors:null,
8744                 alphas: null,
8745                 ratios: null
8746             },
8747             border:{
8748                 weight: 0,
8749                 alpha: 1
8750             },
8751             width: 12,
8752             height: 12,
8753             shape: "rect",
8754
8755             padding:{
8756                 top: 0,
8757                 left: 0,
8758                 right: 0,
8759                 bottom: 0
8760             }
8761         };
8762         defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
8763         defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
8764         return defs;
8765     }
8766 };
8767
8768 Y.Histogram = Histogram;
8769 /**
8770  * The CartesianSeries class creates a chart with horizontal and vertical axes.
8771  *
8772  * @class CartesianSeries
8773  * @extends Base
8774  * @uses Renderer
8775  * @constructor
8776  */
8777 Y.CartesianSeries = Y.Base.create("cartesianSeries", Y.Base, [Y.Renderer], {
8778     /**
8779      * @private
8780      */
8781     _xDisplayName: null,
8782
8783     /**
8784      * @private
8785      */
8786     _yDisplayName: null,
8787     
8788     /**
8789      * @private
8790      */
8791     _leftOrigin: null,
8792
8793     /**
8794      * @private
8795      */
8796     _bottomOrigin: null,
8797
8798     /**
8799      * @private
8800      */
8801     render: function()
8802     {
8803         this._setCanvas();
8804         this.addListeners();
8805         this.set("rendered", true);
8806         this.validate();
8807     },
8808
8809     /**
8810      * @private
8811      */
8812     addListeners: function()
8813     {
8814         var xAxis = this.get("xAxis"),
8815             yAxis = this.get("yAxis");
8816         if(xAxis)
8817         {
8818             xAxis.after("dataReady", Y.bind(this._xDataChangeHandler, this));
8819             xAxis.after("dataUpdate", Y.bind(this._xDataChangeHandler, this));
8820         }
8821         if(yAxis)
8822         {
8823             yAxis.after("dataReady", Y.bind(this._yDataChangeHandler, this));
8824             yAxis.after("dataUpdate", Y.bind(this._yDataChangeHandler, this));
8825         }
8826         this.after("xAxisChange", this._xAxisChangeHandler);
8827         this.after("yAxisChange", this._yAxisChangeHandler);
8828         this.after("stylesChange", function(e) {
8829             var axesReady = this._updateAxisData();
8830             if(axesReady)
8831             {
8832                 this.draw();
8833             }
8834         });
8835         this.after("widthChange", function(e) {
8836             var axesReady = this._updateAxisData();
8837             if(axesReady)
8838             {
8839                 this.draw();
8840             }
8841         });
8842         this.after("heightChange", function(e) {
8843             var axesReady = this._updateAxisData();
8844             if(axesReady)
8845             {
8846                 this.draw();
8847             }
8848         });
8849         this.after("visibleChange", this._toggleVisible);
8850     },
8851   
8852     /**
8853      * @private
8854      */
8855     _xAxisChangeHandler: function(e)
8856     {
8857         var xAxis = this.get("xAxis");
8858         xAxis.after("dataReady", Y.bind(this._xDataChangeHandler, this));
8859         xAxis.after("dataUpdate", Y.bind(this._xDataChangeHandler, this));
8860     },
8861     
8862     /**
8863      * @private
8864      */
8865     _yAxisChangeHandler: function(e)
8866     {
8867         var yAxis = this.get("yAxis");
8868         yAxis.after("dataReady", Y.bind(this._yDataChangeHandler, this));
8869         yAxis.after("dataUpdate", Y.bind(this._yDataChangeHandler, this));
8870     },
8871
8872     /**
8873      * @private
8874      */
8875     GUID: "yuicartesianseries",
8876
8877     /**
8878      * @private (protected)
8879      */
8880     _xDataChangeHandler: function(event)
8881     {
8882         var axesReady = this._updateAxisData();
8883         if(axesReady)
8884         {
8885             this.draw();
8886         }
8887     },
8888
8889     /**
8890      * @private (protected)
8891      */
8892     _yDataChangeHandler: function(event)
8893     {
8894         var axesReady = this._updateAxisData();
8895         if(axesReady)
8896         {
8897             this.draw();
8898         }
8899     },
8900
8901     /**
8902      * @private 
8903      */
8904     _updateAxisData: function()
8905     {
8906         var xAxis = this.get("xAxis"),
8907             yAxis = this.get("yAxis"),
8908             xKey = this.get("xKey"),
8909             yKey = this.get("yKey"),
8910             yData,
8911             xData;
8912         if(!xAxis || !yAxis || !xKey || !yKey)
8913         {
8914             return false;
8915         }
8916         xData = xAxis.getDataByKey(xKey);
8917         yData = yAxis.getDataByKey(yKey);
8918         if(!xData || !yData)
8919         {
8920             return false;
8921         }
8922         this.set("xData", xData.concat());
8923         this.set("yData", yData.concat());
8924         return true;
8925     },
8926
8927     /**
8928      * @private
8929      */
8930     validate: function()
8931     {
8932         if((this.get("xData") && this.get("yData")) || this._updateAxisData())
8933         {
8934             this.draw();
8935         }
8936     },
8937
8938     /**
8939      * @protected
8940      *
8941      * Creates a <code>Graphic</code> instance.
8942      *
8943      * @method _setCanvas
8944      */
8945     _setCanvas: function()
8946     {
8947         this.set("graphic", new Y.Graphic());
8948         this.get("graphic").render(this.get("graph").get("contentBox"));
8949     },
8950
8951     /**
8952      * @protected
8953      *
8954      * Calculates the coordinates for the series.
8955      *
8956      * @method setAreaData
8957      */
8958     setAreaData: function()
8959     {
8960         var nextX, nextY,
8961             graph = this.get("graph"),
8962             w = graph.get("width"),
8963             h = graph.get("height"),
8964             xAxis = this.get("xAxis"),
8965             yAxis = this.get("yAxis"),
8966             xData = this.get("xData").concat(),
8967             yData = this.get("yData").concat(),
8968             xOffset = xAxis.getEdgeOffset(xData.length, w),
8969             yOffset = yAxis.getEdgeOffset(yData.length, h),
8970             padding = this.get("styles").padding,
8971                         leftPadding = padding.left,
8972                         topPadding = padding.top,
8973                         dataWidth = w - (leftPadding + padding.right + xOffset),
8974                         dataHeight = h - (topPadding + padding.bottom + yOffset),
8975                         xcoords = [],
8976                         ycoords = [],
8977                         xMax = xAxis.get("maximum"),
8978                         xMin = xAxis.get("minimum"),
8979                         yMax = yAxis.get("maximum"),
8980                         yMin = yAxis.get("minimum"),
8981             xScaleFactor = dataWidth / (xMax - xMin),
8982                         yScaleFactor = dataHeight / (yMax - yMin),
8983             dataLength,
8984             direction = this.get("direction"),
8985             i = 0,
8986             xMarkerPlane = [],
8987             yMarkerPlane = [],
8988             xMarkerPlaneOffset = this.get("xMarkerPlaneOffset"),
8989             yMarkerPlaneOffset = this.get("yMarkerPlaneOffset"),
8990             graphic = this.get("graphic");
8991         dataLength = xData.length;
8992         xOffset *= 0.5;
8993         yOffset *= 0.5;
8994         //Assuming a vertical graph has a range/category for its vertical axis.    
8995         if(direction === "vertical")
8996         {
8997             yData = yData.reverse();
8998         }
8999         if(graphic)
9000         {
9001             graphic.setSize(w, h);
9002         }
9003         this._leftOrigin = Math.round(((0 - xMin) * xScaleFactor) + leftPadding + xOffset);
9004         this._bottomOrigin =  Math.round((dataHeight + topPadding + yOffset) - (0 - yMin) * yScaleFactor);
9005         for (; i < dataLength; ++i) 
9006                 {
9007             nextX = Math.round((((xData[i] - xMin) * xScaleFactor) + leftPadding + xOffset));
9008                         nextY = Math.round(((dataHeight + topPadding + yOffset) - (yData[i] - yMin) * yScaleFactor));
9009             xcoords.push(nextX);
9010             ycoords.push(nextY);
9011             xMarkerPlane.push({start:nextX - xMarkerPlaneOffset, end: nextX + xMarkerPlaneOffset});
9012             yMarkerPlane.push({start:nextY - yMarkerPlaneOffset, end: nextY + yMarkerPlaneOffset});
9013         }
9014         this.set("xcoords", xcoords);
9015                 this.set("ycoords", ycoords);
9016         this.set("xMarkerPlane", xMarkerPlane);
9017         this.set("yMarkerPlane", yMarkerPlane);
9018     },
9019
9020     /**
9021      * @protected
9022      *
9023      * Draws the series.
9024      *
9025      * @method draw
9026      */
9027     draw: function()
9028     {
9029         var graph = this.get("graph"),
9030             w = graph.get("width"),
9031             h = graph.get("height");
9032
9033         if(this.get("rendered"))
9034         {
9035             if((isFinite(w) && isFinite(h) && w > 0 && h > 0) && ((this.get("xData") && this.get("yData")) || this._updateAxisData()))
9036             {
9037                 if(this._drawing)
9038                 {
9039                     this._callLater = true;
9040                     return;
9041                 }
9042                 this._drawing = true;
9043                 this._callLater = false;
9044                 this.setAreaData();
9045                 if(this.get("xcoords") && this.get("ycoords"))
9046                 {
9047                     this.drawSeries();
9048                 }
9049                 this._drawing = false;
9050                 if(this._callLater)
9051                 {
9052                     this.draw();
9053                 }
9054                 else
9055                 {
9056                     this._toggleVisible(this.get("visible"));
9057                     this.fire("drawingComplete");
9058                 }
9059             }
9060         }
9061     },
9062     
9063     /**
9064      * @private
9065      */
9066     _defaultPlaneOffset: 4,
9067     
9068     /**
9069      * @protected
9070      *
9071      * Gets the default value for the <code>styles</code> attribute. Overrides
9072      * base implementation.
9073      *
9074      * @method _getDefaultStyles
9075      * @return Object
9076      */
9077     _getDefaultStyles: function()
9078     {
9079         return {padding:{
9080                 top: 0,
9081                 left: 0,
9082                 right: 0,
9083                 bottom: 0
9084             }};
9085     },
9086
9087     /**
9088      * @protected
9089      *
9090      * Collection of default colors used for lines in a series when not specified by user.
9091      *
9092      * @property _defaultLineColors
9093      * @type Array
9094      */
9095     _defaultLineColors:["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"],
9096
9097     /**
9098      * @protected
9099      *
9100      * Collection of default colors used for marker fills in a series when not specified by user.
9101      *
9102      * @property _defaultFillColors
9103      * @type Array
9104      */
9105     _defaultFillColors:["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"],
9106     
9107     /**
9108      * @protected
9109      *
9110      * Collection of default colors used for marker borders in a series when not specified by user.
9111      *
9112      * @property _defaultBorderColors
9113      * @type Array
9114      */
9115     _defaultBorderColors:["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"],
9116     
9117     /**
9118      * @protected
9119      *
9120      * Collection of default colors used for area fills, histogram fills and pie fills in a series when not specified by user.
9121      *
9122      * @property _defaultSliceColors
9123      * @type Array
9124      */
9125     _defaultSliceColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],
9126
9127     /**
9128      * @protected
9129      *
9130      * Parses a color based on a series order and type.
9131      *
9132      * @method _getDefaultColor
9133      * @param {Number} index Index indicating the series order.
9134      * @param {String} type Indicates which type of object needs the color.
9135      * @return String
9136      */
9137     _getDefaultColor: function(index, type)
9138     {
9139         var colors = {
9140                 line: this._defaultLineColors,
9141                 fill: this._defaultFillColors,
9142                 border: this._defaultBorderColors,
9143                 slice: this._defaultSliceColors
9144             },
9145             col = colors[type],
9146             l = col.length;
9147         index = index || 0;
9148         if(index >= l)
9149         {
9150             index = index % l;
9151         }
9152         type = type || "fill";
9153         return colors[type][index];
9154     },
9155     
9156     /**
9157      * @protected
9158      *
9159      * Shows/hides contents of the series.
9160      *
9161      * @method _toggleVisible
9162      */
9163     _toggleVisible: function(e) 
9164     {
9165         var graphic = this.get("graphic"),
9166             markers = this.get("markers"),
9167             i = 0,
9168             len,
9169             visible = this.get("visible"),
9170             marker;
9171         if(graphic)
9172         {
9173             graphic.toggleVisible(visible);
9174         }
9175         if(markers)
9176         {
9177             len = markers.length;
9178             for(; i < len; ++i)
9179             {
9180                 marker = markers[i];
9181                 if(marker)
9182                 {
9183                     marker.toggleVisible(visible);
9184                 }
9185             }
9186
9187         }
9188         if(this._lineGraphic)
9189         {
9190             this._lineGraphic.toggleVisible(visible);
9191         }
9192     }
9193 }, {
9194     ATTRS: {
9195         /**
9196          * Name used for for displaying data related to the x-coordinate.
9197          *
9198          * @attribute xDisplayName
9199          * @type String
9200          */
9201         xDisplayName: {
9202             getter: function()
9203             {
9204                 return this._xDisplayName || this.get("xKey");
9205             },
9206
9207             setter: function(val)
9208             {
9209                 this._xDisplayName = val;
9210                 return val;
9211             }
9212         },
9213
9214         /**
9215          * Name used for for displaying data related to the y-coordinate.
9216          *
9217          * @attribute yDisplayName
9218          * @type String
9219          */
9220         yDisplayName: {
9221             getter: function()
9222             {
9223                 return this._yDisplayName || this.get("yKey");
9224             },
9225
9226             setter: function(val)
9227             {
9228                 this._yDisplayName = val;
9229                 return val;
9230             }
9231         },
9232         
9233         /**
9234          * Name used for for displaying category data
9235          *
9236          * @attribute categoryDisplayName
9237          * @type String
9238          */
9239         categoryDisplayName: {
9240             readOnly: true,
9241
9242             getter: function()
9243             {
9244                 return this.get("direction") == "vertical" ? this.get("yDisplayName") : this.get("xDisplayName");
9245             }
9246         },
9247
9248         /**
9249          * Name used for for displaying value data
9250          *
9251          * @attribute valueDisplayName
9252          * @type String
9253          */
9254         valueDisplayName: {
9255             readOnly: true,
9256
9257             getter: function()
9258             {
9259                 return this.get("direction") == "vertical" ? this.get("xDisplayName") : this.get("yDisplayName");
9260             }
9261         },
9262         
9263         /**
9264          * Read-only attribute indicating the type of series.
9265          *
9266          * @attribute type
9267          * @type String
9268          * @default cartesian
9269          */
9270         type: {         
9271             value: "cartesian"
9272         },
9273
9274         /**
9275          * Order of this instance of this <code>type</code>.
9276          *
9277          * @attribute order
9278          * @type Number
9279          */
9280         order: {},
9281
9282         /**
9283          * Order of the instance
9284          *
9285          * @attribute graphOrder
9286          * @type Number
9287          */
9288         graphOrder: {},
9289
9290         /**
9291          * x coordinates for the series.
9292          *
9293          * @attribute xcoords
9294          * @type Array
9295          */
9296         xcoords: {},
9297         
9298         /**
9299          * y coordinates for the series
9300          *
9301          * @attribute ycoords
9302          * @type Array
9303          */
9304         ycoords: {},
9305         
9306         /**
9307          * Reference to the <code>Graph</code> in which the series is drawn into.
9308          *
9309          * @attribute graph
9310          * @type Graph
9311          */
9312         graph: {},
9313
9314         /**
9315          * Reference to the <code>Axis</code> instance used for assigning 
9316          * x-values to the graph.
9317          *
9318          * @attribute xAxis
9319          * @type Axis
9320          */
9321         xAxis: {},
9322         
9323         /**
9324          * Reference to the <code>Axis</code> instance used for assigning 
9325          * y-values to the graph.
9326          *
9327          * @attribute yAxis
9328          * @type Axis
9329          */
9330         yAxis: {},
9331         
9332         /**
9333          * Indicates which array to from the hash of value arrays in 
9334          * the x-axis <code>Axis</code> instance.
9335          *
9336          * @attribute xKey
9337          * @type String
9338          */
9339         xKey: {},
9340
9341         /**
9342          * Indicates which array to from the hash of value arrays in 
9343          * the y-axis <code>Axis</code> instance.
9344          *
9345          * @attribute yKey
9346          * @type String
9347          */
9348         yKey: {},
9349
9350         /**
9351          * Array of x values for the series.
9352          *
9353          * @attribute xData
9354          * @type Array
9355          */
9356         xData: {},
9357
9358         /**
9359          * Array of y values for the series.
9360          *
9361          * @attribute yData
9362          * @type Array
9363          */
9364         yData: {},
9365        
9366         /**
9367          * Indicates whether the Series has been through its initial set up.
9368          *
9369          * @attribute rendered
9370          * @type Boolean
9371          */
9372         rendered: {
9373             value: false
9374         },
9375
9376         /*
9377          * Returns the width of the parent graph
9378          *
9379          * @attribute width
9380          * @type Number
9381          */
9382         width: {
9383             readOnly: true,
9384             
9385             getter: function()
9386             {
9387                 this.get("graph").get("width");
9388             }
9389         },
9390
9391         /**
9392          * Returns the height of the parent graph
9393          *
9394          * @attribute height
9395          * @type Number
9396          */
9397         height: {
9398             readOnly: true,
9399             
9400             getter: function()
9401             {
9402                 this.get("graph").get("height");
9403             }
9404         },
9405
9406         /**
9407          * Indicates whether to show the series
9408          *
9409          * @attribute visible
9410          * @type Boolean
9411          * @default true
9412          */
9413         visible: {
9414             value: true
9415         },
9416
9417         /**
9418          * Collection of area maps along the xAxis. Used to determine mouseover for multiple
9419          * series.
9420          *
9421          * @attribute xMarkerPlane
9422          * @type Array
9423          */
9424         xMarkerPlane: {},
9425         
9426         /**
9427          * Collection of area maps along the yAxis. Used to determine mouseover for multiple
9428          * series.
9429          *
9430          * @attribute yMarkerPlane
9431          * @type Array
9432          */
9433         yMarkerPlane: {},
9434
9435         /**
9436          * Distance from a data coordinate to the left/right for setting a hotspot.
9437          *
9438          * @attribute xMarkerPlaneOffset
9439          * @type Number
9440          */
9441         xMarkerPlaneOffset: {
9442             getter: function() {
9443                 var marker = this.get("styles").marker;
9444                 if(marker && marker.width && isFinite(marker.width))
9445                 {
9446                     return marker.width * 0.5;
9447                 }
9448                 return this._defaultPlaneOffset;
9449             }
9450         },
9451
9452         /**
9453          * Distance from a data coordinate to the top/bottom for setting a hotspot.
9454          *
9455          * @attribute yMarkerPlaneOffset
9456          * @type Number
9457          */
9458         yMarkerPlaneOffset: {
9459             getter: function() {
9460                 var marker = this.get("styles").marker;
9461                 if(marker && marker.height && isFinite(marker.height))
9462                 {
9463                     return marker.height * 0.5;
9464                 }
9465                 return this._defaultPlaneOffset;
9466             }
9467         },
9468
9469         /**
9470          * Direction of the series
9471          *
9472          * @attribute direction
9473          * @type String
9474          */
9475         direction: {
9476             value: "horizontal"
9477         }
9478     }
9479 });
9480 /**
9481  * The MarkerSeries class renders quantitative data by plotting relevant data points 
9482  * on a graph.
9483  *
9484  * @class MarkerSeries
9485  * @extends CartesianSeries
9486  * @uses Plots
9487  * @constructor
9488  */
9489 Y.MarkerSeries = Y.Base.create("markerSeries", Y.CartesianSeries, [Y.Plots], {
9490     /**
9491      * @private
9492      */
9493     renderUI: function()
9494     {
9495         this._setNode();
9496     },
9497     
9498     /**
9499      * @protected
9500      *
9501      * Draws the series.
9502      *
9503      * @method drawSeries
9504      */
9505     drawSeries: function()
9506     {
9507         this.drawPlots();
9508     },
9509     
9510     /**
9511      * @protected
9512      *
9513      * Method used by <code>styles</code> setter. Overrides base implementation.
9514      *
9515      * @method _setStyles
9516      * @param {Object} newStyles Hash of properties to update.
9517      * @return Object
9518      */
9519     _setStyles: function(val)
9520     {
9521         if(!val.marker)
9522         {
9523             val = {marker:val};
9524         }
9525         val = this._parseMarkerStyles(val);
9526         return Y.MarkerSeries.superclass._mergeStyles.apply(this, [val, this._getDefaultStyles()]);
9527     },
9528     
9529     /**
9530      * @protected
9531      *
9532      * Gets the default value for the <code>styles</code> attribute. Overrides
9533      * base implementation.
9534      *
9535      * @method _getDefaultStyles
9536      * @return Object
9537      */
9538     _getDefaultStyles: function()
9539     {
9540         var styles = this._mergeStyles({marker:this._getPlotDefaults()}, Y.MarkerSeries.superclass._getDefaultStyles());
9541         return styles;
9542     }
9543 },{
9544     ATTRS : {
9545         /**
9546          * Read-only attribute indicating the type of series.
9547          *
9548          * @attribute type
9549          * @type String
9550          * @default marker
9551          */
9552         type: {
9553             value:"marker"
9554         }
9555         
9556         /**
9557          * Style properties used for drawing markers. This attribute is inherited from <code>Renderer</code>. Below are the default values:
9558          *  <dl>
9559          *      <dt>fill</dt><dd>A hash containing the following values:
9560          *          <dl>
9561          *              <dt>color</dt><dd>Color of the fill. The default value is determined by the order of the series on the graph. The color
9562          *              will be retrieved from the below array:<br/>
9563          *              <code>["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"]</code>
9564          *              </dd>
9565          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
9566          *          </dl>
9567          *      </dd>
9568          *      <dt>border</dt><dd>A hash containing the following values:
9569          *          <dl>
9570          *              <dt>color</dt><dd>Color of the border. The default value is determined by the order of the series on the graph. The color
9571          *              will be retrieved from the below array:<br/>
9572          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
9573          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
9574          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
9575          *          </dl>
9576          *      </dd>
9577          *      <dt>width</dt><dd>indicates the width of the marker. The default value is 10.</dd>
9578          *      <dt>height</dt><dd>indicates the height of the marker The default value is 10.</dd>
9579          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
9580          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
9581          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
9582          *  </dl>
9583          *
9584          * @attribute styles
9585          * @type Object
9586          */
9587     }
9588 });
9589
9590 /**
9591  * The LineSeries class renders quantitative data on a graph by connecting relevant data points.
9592  *
9593  * @class LineSeries
9594  * @extends CartesianSeries
9595  * @uses Lines
9596  * @constructor
9597  */
9598 Y.LineSeries = Y.Base.create("lineSeries", Y.CartesianSeries, [Y.Lines], {
9599     /**
9600      * @protected
9601      *
9602      * @method drawSeries
9603      */
9604     drawSeries: function()
9605     {
9606         this.get("graphic").clear();
9607         this.drawLines();
9608     },
9609
9610     /**
9611      * @protected
9612      *
9613      * Method used by <code>styles</code> setter. Overrides base implementation.
9614      *
9615      * @method _setStyles
9616      * @param {Object} newStyles Hash of properties to update.
9617      * @return Object
9618      */
9619     _setStyles: function(val)
9620     {
9621         if(!val.line)
9622         {
9623             val = {line:val};
9624         }
9625         return Y.LineSeries.superclass._setStyles.apply(this, [val]);
9626     },
9627
9628     /**
9629      * @protected
9630      *
9631      * Gets the default value for the <code>styles</code> attribute. Overrides
9632      * base implementation.
9633      *
9634      * @method _getDefaultStyles
9635      * @return Object
9636      */
9637     _getDefaultStyles: function()
9638     {
9639         var styles = this._mergeStyles({line:this._getLineDefaults()}, Y.LineSeries.superclass._getDefaultStyles());
9640         return styles;
9641     }
9642 },
9643 {
9644     ATTRS: {
9645         /**
9646          * Read-only attribute indicating the type of series.
9647          *
9648          * @attribute type
9649          * @type String
9650          * @default line
9651          */
9652         type: {
9653             value:"line"
9654         }
9655
9656         /**
9657          * Style properties used for drawing lines. This attribute is inherited from <code>Renderer</code>. Below are the default values:
9658          *  <dl>
9659          *      <dt>color</dt><dd>The color of the line. The default value is determined by the order of the series on the graph. The color will be
9660          *      retrieved from the following array: 
9661          *      <code>["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"]</code>
9662          *      <dt>weight</dt><dd>Number that indicates the width of the line. The default value is 6.</dd>
9663          *      <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the line. The default value is 1.</dd>
9664          *      <dt>lineType</dt><dd>Indicates whether the line is solid or dashed. The default value is solid.</dd> 
9665          *      <dt>dashLength</dt><dd>When the <code>lineType</code> is dashed, indicates the length of the dash. The default value is 10.</dd>
9666          *      <dt>gapSpace</dt><dd>When the <code>lineType</code> is dashed, indicates the distance between dashes. The default value is 10.</dd>
9667          *      <dt>connectDiscontinuousPoints</dt><dd>Indicates whether or not to connect lines when there is a missing or null value between points. The default value is true.</dd> 
9668          *      <dt>discontinuousType</dt><dd>Indicates whether the line between discontinuous points is solid or dashed. The default value is solid.</dd>
9669          *      <dt>discontinuousDashLength</dt><dd>When the <code>discontinuousType</code> is dashed, indicates the length of the dash. The default value is 10.</dd>
9670          *      <dt>discontinuousGapSpace</dt><dd>When the <code>discontinuousType</code> is dashed, indicates the distance between dashes. The default value is 10.</dd>
9671          *  </dl>
9672          *
9673          * @attribute styles
9674          * @type Object
9675          */
9676     }
9677 });
9678
9679
9680
9681                 
9682
9683                 
9684 /**
9685  * SplineSeries renders a graph with data points connected by a curve.
9686  *
9687  * @class SplineSeries
9688  * @constructor
9689  * @extends CartesianSeries
9690  * @uses CurveUtil
9691  * @uses Lines
9692  */
9693 Y.SplineSeries = Y.Base.create("splineSeries",  Y.CartesianSeries, [Y.CurveUtil, Y.Lines], {
9694     /**
9695      * @protected
9696      *
9697      * Draws the series.
9698      *
9699      * @method drawSeries
9700      */
9701     drawSeries: function()
9702     {
9703         this.get("graphic").clear();
9704         this.drawSpline();
9705     }
9706 }, {
9707         ATTRS : {
9708         /**
9709          * Read-only attribute indicating the type of series.
9710          *
9711          * @attribute type
9712          * @type String
9713          * @default spline
9714          */
9715         type : {
9716             value:"spline"
9717         }
9718
9719         /**
9720          * Style properties used for drawing lines. This attribute is inherited from <code>Renderer</code>. Below are the default values:
9721          *  <dl>
9722          *      <dt>color</dt><dd>The color of the line. The default value is determined by the order of the series on the graph. The color will be
9723          *      retrieved from the following array: 
9724          *      <code>["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"]</code>
9725          *      <dt>weight</dt><dd>Number that indicates the width of the line. The default value is 6.</dd>
9726          *      <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the line. The default value is 1.</dd>
9727          *      <dt>lineType</dt><dd>Indicates whether the line is solid or dashed. The default value is solid.</dd> 
9728          *      <dt>dashLength</dt><dd>When the <code>lineType</code> is dashed, indicates the length of the dash. The default value is 10.</dd>
9729          *      <dt>gapSpace</dt><dd>When the <code>lineType</code> is dashed, indicates the distance between dashes. The default value is 10.</dd>
9730          *      <dt>connectDiscontinuousPoints</dt><dd>Indicates whether or not to connect lines when there is a missing or null value between points. The default value is true.</dd> 
9731          *      <dt>discontinuousType</dt><dd>Indicates whether the line between discontinuous points is solid or dashed. The default value is solid.</dd>
9732          *      <dt>discontinuousDashLength</dt><dd>When the <code>discontinuousType</code> is dashed, indicates the length of the dash. The default value is 10.</dd>
9733          *      <dt>discontinuousGapSpace</dt><dd>When the <code>discontinuousType</code> is dashed, indicates the distance between dashes. The default value is 10.</dd>
9734          *  </dl>
9735          *
9736          * @attribute styles
9737          * @type Object
9738          */
9739     }
9740 });
9741
9742
9743
9744                 
9745
9746                 
9747 /**
9748  * AreaSplineSeries renders an area graph with data points connected by a curve.
9749  *
9750  * @class AreaSplineSeries
9751  * @constructor
9752  * @extends CartesianSeries
9753  * @uses Fills
9754  * @uses CurveUtil
9755  */
9756 Y.AreaSplineSeries = Y.Base.create("areaSplineSeries", Y.CartesianSeries, [Y.Fills, Y.CurveUtil], {
9757     /**
9758      * @protected
9759      *
9760      * Draws the series.
9761      *
9762      * @method drawSeries
9763      */
9764     drawSeries: function()
9765     {
9766         this.get("graphic").clear();
9767         this.drawAreaSpline();
9768     }
9769 }, {
9770         ATTRS : {
9771         /**
9772          * Read-only attribute indicating the type of series.
9773          *
9774          * @attribute type
9775          * @type String
9776          * @default areaSpline
9777          */
9778         type: {
9779             value:"areaSpline"
9780         }
9781         
9782         /**
9783          * Style properties used for drawing area fills. This attribute is inherited from <code>Renderer</code>. Below are the default values:
9784          *
9785          *  <dl>
9786          *      <dt>color</dt><dd>The color of the fill. The default value is determined by the order of the series on the graph. The color will be 
9787          *      retrieved from the following array:
9788          *      <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
9789          *      </dd>
9790          *      <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1</dd>
9791          *  </dl>
9792          *
9793          * @attribute styles
9794          * @type Object
9795          */
9796     }
9797 });
9798
9799 /**
9800  * StackedSplineSeries creates spline graphs in which the different series are stacked along a value axis
9801  * to indicate their contribution to a cumulative total.
9802  *
9803  * @class StackedSplineSeries
9804  * @constructor
9805  * @extends SplineSeries
9806  * @extends StackingUtil
9807  */
9808 Y.StackedSplineSeries = Y.Base.create("stackedSplineSeries", Y.SplineSeries, [Y.StackingUtil], {
9809     /**
9810      * @protected
9811      *
9812      * Calculates the coordinates for the series. Overrides base implementation.
9813      *
9814      * @method setAreaData
9815      */
9816     setAreaData: function()
9817     {   
9818         Y.StackedSplineSeries.superclass.setAreaData.apply(this);
9819         this._stackCoordinates.apply(this);
9820     }
9821 }, {
9822     ATTRS: {
9823         /**
9824          * Read-only attribute indicating the type of series.
9825          *
9826          * @attribute type
9827          * @type String
9828          * @default stackedSpline
9829          */
9830         type: {
9831             value:"stackedSpline"
9832         }
9833     }
9834 });
9835
9836 /**
9837  * StackedMarkerSeries plots markers with different series stacked along the value axis to indicate each
9838  * series' contribution to a cumulative total.
9839  *
9840  * @class StackedMarkerSeries
9841  * @constructor
9842  * @extends MarkerSeries
9843  * @extends StackingUtil
9844  */
9845 Y.StackedMarkerSeries = Y.Base.create("stackedMarkerSeries", Y.MarkerSeries, [Y.StackingUtil], {
9846     /**
9847      * @protected
9848      *
9849      * Calculates the coordinates for the series. Overrides base implementation.
9850      *
9851      * @method setAreaData
9852      */
9853     setAreaData: function()
9854     {   
9855         Y.StackedMarkerSeries.superclass.setAreaData.apply(this);
9856         this._stackCoordinates.apply(this);
9857     }
9858 }, {
9859     ATTRS: {
9860         /**
9861          * Read-only attribute indicating the type of series.
9862          *
9863          * @attribute type
9864          * @type String
9865          * @default stackedMarker
9866          */
9867         type: {
9868             value:"stackedMarker"
9869         }
9870     }
9871 });
9872
9873 /**
9874  * The ColumnSeries class renders columns positioned horizontally along a category or time axis. The columns'
9875  * lengths are proportional to the values they represent along a vertical axis.
9876  * and the relevant data points.
9877  *
9878  * @class ColumnSeries
9879  * @extends MarkerSeries
9880  * @uses Histogram
9881  * @constructor
9882  */
9883 Y.ColumnSeries = Y.Base.create("columnSeries", Y.MarkerSeries, [Y.Histogram], {
9884     /**
9885      * @private
9886      */
9887     _getMarkerDimensions: function(xcoord, ycoord, calculatedSize, offset)
9888     {
9889         var config = {
9890             top: ycoord,
9891             left: xcoord + offset
9892         };
9893         config.calculatedSize = this._bottomOrigin - config.top;
9894         return config;
9895     },
9896
9897     /**
9898      * @protected
9899      *
9900      * Resizes and positions markers based on a mouse interaction.
9901      *
9902      * @method updateMarkerState
9903      * @param {String} type state of the marker
9904      * @param {Number} i index of the marker
9905      */
9906     updateMarkerState: function(type, i)
9907     {
9908         if(this._markers[i])
9909         {
9910             var styles = Y.clone(this.get("styles").marker),
9911                 markerStyles,
9912                 state = this._getState(type),
9913                 xcoords = this.get("xcoords"),
9914                 ycoords = this.get("ycoords"),
9915                 marker = this._markers[i],
9916                 graph = this.get("graph"),
9917                 seriesCollection = graph.seriesTypes[this.get("type")],
9918                 seriesLen = seriesCollection.length,
9919                 seriesSize = 0,
9920                 offset = 0,
9921                 renderer,
9922                 n = 0,
9923                 xs = [],
9924                 order = this.get("order");
9925             markerStyles = state == "off" || !styles[state] ? styles : styles[state]; 
9926             markerStyles.fill.color = this._getItemColor(markerStyles.fill.color, i);
9927             markerStyles.border.color = this._getItemColor(markerStyles.border.color, i);
9928             markerStyles.height = this._bottomOrigin - ycoords[i];
9929             marker.update(markerStyles);
9930             for(; n < seriesLen; ++n)
9931             {
9932                 renderer = seriesCollection[n].get("markers")[i];
9933                 xs[n] = xcoords[i] + seriesSize;
9934                 seriesSize += renderer.width;
9935                 if(order > n)
9936                 {
9937                     offset = seriesSize;
9938                 }
9939                 offset -= seriesSize/2;
9940             }
9941             for(n = 0; n < seriesLen; ++n)
9942             {
9943                 renderer = Y.one(seriesCollection[n]._graphicNodes[i]);
9944                 renderer.setStyle("left", (xs[n] - seriesSize/2) + "px");
9945             }
9946         }
9947     }
9948 }, {
9949     ATTRS: {
9950         /**
9951          * Read-only attribute indicating the type of series.
9952          *
9953          * @attribute type
9954          * @type String
9955          * @default column
9956          */
9957         type: {
9958             value: "column"
9959         }
9960         
9961         /**
9962          * Style properties used for drawing markers. This attribute is inherited from <code>MarkerSeries</code>. Below are the default values:
9963          *  <dl>
9964          *      <dt>fill</dt><dd>A hash containing the following values:
9965          *          <dl>
9966          *              <dt>color</dt><dd>Color of the fill. The default value is determined by the order of the series on the graph. The color
9967          *              will be retrieved from the below array:<br/>
9968          *              <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
9969          *              </dd>
9970          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
9971          *          </dl>
9972          *      </dd>
9973          *      <dt>border</dt><dd>A hash containing the following values:
9974          *          <dl>
9975          *              <dt>color</dt><dd>Color of the border. The default value is determined by the order of the series on the graph. The color
9976          *              will be retrieved from the below array:<br/>
9977          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
9978          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
9979          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
9980          *          </dl>
9981          *      </dd>
9982          *      <dt>width</dt><dd>indicates the width of the marker. The default value is 12.</dd>
9983          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
9984          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
9985          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
9986          *  </dl>
9987          *
9988          * @attribute styles
9989          * @type Object
9990          */
9991     }
9992 });
9993 /**
9994  * The BarSeries class renders bars positioned vertically along a category or time axis. The bars'
9995  * lengths are proportional to the values they represent along a horizontal axis.
9996  * and the relevant data points.
9997  *
9998  * @class BarSeries
9999  * @extends MarkerSeries
10000  * @uses Histogram
10001  * @constructor
10002  */
10003 Y.BarSeries = Y.Base.create("barSeries", Y.MarkerSeries, [Y.Histogram], {
10004     /**
10005      * @private
10006      */
10007     renderUI: function()
10008     {
10009         this._setNode();
10010     },
10011
10012     /**
10013      * @private
10014      */
10015     _getMarkerDimensions: function(xcoord, ycoord, calculatedSize, offset)
10016     {
10017         var config = {
10018             top: ycoord + offset,
10019             left: this._leftOrigin
10020         };
10021         config.calculatedSize = xcoord - config.left;
10022         return config;
10023     },
10024     
10025     /**
10026      * @protected
10027      *
10028      * Resizes and positions markers based on a mouse interaction.
10029      *
10030      * @method updateMarkerState
10031      * @param {String} type state of the marker
10032      * @param {Number} i index of the marker
10033      */
10034     updateMarkerState: function(type, i)
10035     {
10036         if(this._markers[i])
10037         {
10038             var styles = Y.clone(this.get("styles").marker),
10039                 markerStyles,
10040                 state = this._getState(type),
10041                 xcoords = this.get("xcoords"),
10042                 ycoords = this.get("ycoords"),
10043                 marker = this._markers[i],
10044                 graph = this.get("graph"),
10045                 seriesCollection = graph.seriesTypes[this.get("type")],
10046                 seriesLen = seriesCollection.length,
10047                 seriesSize = 0,
10048                 offset = 0,
10049                 renderer,
10050                 n = 0,
10051                 ys = [],
10052                 order = this.get("order");
10053             markerStyles = state == "off" || !styles[state] ? styles : styles[state]; 
10054             markerStyles.fill.color = this._getItemColor(markerStyles.fill.color, i);
10055             markerStyles.border.color = this._getItemColor(markerStyles.border.color, i);
10056             markerStyles.width = (xcoords[i] - this._leftOrigin);
10057             marker.update(markerStyles);
10058             for(; n < seriesLen; ++n)
10059             {
10060                 renderer = seriesCollection[n].get("markers")[i];
10061                 ys[n] = ycoords[i] + seriesSize;
10062                 seriesSize += renderer.height;
10063                 if(order > n)
10064                 {
10065                     offset = seriesSize;
10066                 }
10067                 offset -= seriesSize/2;
10068             }
10069             for(n = 0; n < seriesLen; ++n)
10070             {
10071                 renderer = Y.one(seriesCollection[n]._graphicNodes[i]);
10072                 renderer.setStyle("top", (ys[n] - seriesSize/2));
10073             }
10074         }
10075     }
10076 }, {
10077     ATTRS: {
10078         /**
10079          * Read-only attribute indicating the type of series.
10080          *
10081          * @attribute type
10082          * @type String
10083          * @default bar
10084          */
10085         type: {
10086             value: "bar"
10087         },
10088
10089         /**
10090          * Indicates the direction of the category axis that the bars are plotted against.
10091          *
10092          * @attribute direction
10093          * @type String
10094          */
10095         direction: {
10096             value: "vertical"
10097         }
10098         
10099         /**
10100          * Style properties used for drawing markers. This attribute is inherited from <code>MarkerSeries</code>. Below are the default values:
10101          *  <dl>
10102          *      <dt>fill</dt><dd>A hash containing the following values:
10103          *          <dl>
10104          *              <dt>color</dt><dd>Color of the fill. The default value is determined by the order of the series on the graph. The color
10105          *              will be retrieved from the below array:<br/>
10106          *              <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
10107          *              </dd>
10108          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
10109          *          </dl>
10110          *      </dd>
10111          *      <dt>border</dt><dd>A hash containing the following values:
10112          *          <dl>
10113          *              <dt>color</dt><dd>Color of the border. The default value is determined by the order of the series on the graph. The color
10114          *              will be retrieved from the below array:<br/>
10115          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
10116          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
10117          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
10118          *          </dl>
10119          *      </dd>
10120          *      <dt>height</dt><dd>indicates the width of the marker. The default value is 12.</dd>
10121          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
10122          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
10123          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
10124          *  </dl>
10125          *
10126          * @attribute styles
10127          * @type Object
10128          */
10129     }
10130 });
10131 /**
10132  * The AreaSeries class renders quantitative data on a graph by creating a fill between 0
10133  * and the relevant data points.
10134  *
10135  * @class AreaSeries
10136  * @extends CartesianSeries
10137  * @uses Fills
10138  * @constructor
10139  */
10140 Y.AreaSeries = Y.Base.create("areaSeries", Y.CartesianSeries, [Y.Fills], {
10141     /**
10142      * @protected
10143      *
10144      * Renders the series. 
10145      *
10146      * @method drawSeries
10147      */
10148     drawSeries: function()
10149     {
10150         this.get("graphic").clear();
10151         this.drawFill.apply(this, this._getClosingPoints());
10152     },
10153     
10154     /**
10155      * @protected
10156      *
10157      * Method used by <code>styles</code> setter. Overrides base implementation.
10158      *
10159      * @method _setStyles
10160      * @param {Object} newStyles Hash of properties to update.
10161      * @return Object
10162      */
10163     _setStyles: function(val)
10164     {
10165         if(!val.area)
10166         {
10167             val = {area:val};
10168         }
10169         return Y.AreaSeries.superclass._setStyles.apply(this, [val]);
10170     },
10171
10172     /**
10173      * @protected
10174      *
10175      * Gets the default value for the <code>styles</code> attribute. Overrides
10176      * base implementation.
10177      *
10178      * @method _getDefaultStyles
10179      * @return Object
10180      */
10181     _getDefaultStyles: function()
10182     {
10183         var styles = this._mergeStyles({area:this._getAreaDefaults()}, Y.AreaSeries.superclass._getDefaultStyles());
10184         return styles;
10185     }
10186 },
10187 {
10188     ATTRS: {
10189         /**
10190          * Read-only attribute indicating the type of series.
10191          *
10192          * @attribute type
10193          * @type String
10194          * @default area
10195          */
10196         type: {
10197             value:"area"
10198         }
10199         
10200         /**
10201          * Style properties used for drawing area fills. This attribute is inherited from <code>Renderer</code>. Below are the default values:
10202          *
10203          *  <dl>
10204          *      <dt>color</dt><dd>The color of the fill. The default value is determined by the order of the series on the graph. The color will be 
10205          *      retrieved from the following array:
10206          *      <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
10207          *      </dd>
10208          *      <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1</dd>
10209          *  </dl>
10210          *
10211          * @attribute styles
10212          * @type Object
10213          */
10214     }
10215 });
10216
10217
10218
10219                 
10220
10221                 
10222 /**
10223  * StackedAreaSplineSeries creates a stacked area chart with points data points connected by a curve.
10224  *
10225  * @class StackedAreaSplineSeries
10226  * @constructor
10227  * @extends AreaSeries
10228  * @uses CurveUtil
10229  * @uses StackingUtil
10230  */
10231 Y.StackedAreaSplineSeries = Y.Base.create("stackedAreaSplineSeries", Y.AreaSeries, [Y.CurveUtil, Y.StackingUtil], {
10232     /**
10233      * @protected
10234      *
10235      * Draws the series.
10236      *
10237      * @method drawSeries
10238      */
10239     drawSeries: function()
10240     {
10241         this.get("graphic").clear();
10242         this._stackCoordinates();
10243         this.drawStackedAreaSpline();
10244     }
10245 }, {
10246     ATTRS : {
10247         /**
10248          * Read-only attribute indicating the type of series.
10249          *
10250          * @attribute type
10251          * @type String
10252          * @default stackedAreaSpline
10253          */
10254         type: {
10255             value:"stackedAreaSpline"
10256         }
10257     }
10258 });
10259
10260 /**
10261  * The ComboSeries class renders a combination of lines, plots and area fills in a single series. Each
10262  * series type has a corresponding boolean attribute indicating if it is rendered. By default, lines and plots 
10263  * are rendered and area is not. 
10264  *
10265  * @class ComboSeries
10266  * @extends CartesianSeries 
10267  * @uses Fills
10268  * @uses Lines
10269  * @uses Plots
10270  * @constructor
10271  */
10272 Y.ComboSeries = Y.Base.create("comboSeries", Y.CartesianSeries, [Y.Fills, Y.Lines, Y.Plots], {
10273         /**
10274      * @protected
10275      * 
10276      * Draws the series.
10277      *
10278      * @method drawSeries
10279      */
10280     drawSeries: function()
10281     {
10282         this.get("graphic").clear();
10283         if(this.get("showAreaFill"))
10284         {
10285             this.drawFill.apply(this, this._getClosingPoints());
10286         }
10287         if(this.get("showLines")) 
10288         {
10289             this.drawLines();
10290         }
10291         if(this.get("showMarkers"))
10292         {
10293             this.drawPlots();
10294         }   
10295     },
10296
10297     /**
10298      * @protected
10299      *
10300      * Returns the default hash for the <code>styles</code> attribute.
10301      *
10302      * @method _getDefaultStyles
10303      * @return Object
10304      */
10305     _getDefaultStyles: function()
10306     {
10307         var styles = Y.ComboSeries.superclass._getDefaultStyles();
10308         styles.line = this._getLineDefaults();
10309         styles.marker = this._getPlotDefaults();
10310         styles.area = this._getAreaDefaults();
10311         return styles;
10312     }
10313 },
10314 {
10315     ATTRS: {
10316         /**
10317          * Read-only attribute indicating the type of series.
10318          *
10319          * @attribute type
10320          * @type String
10321          * @default combo
10322          */
10323         type: {
10324             value:"combo"
10325         },
10326
10327         /**
10328          * Indicates whether a fill is displayed.
10329          *
10330          * @attribute showAreaFill
10331          * @type Boolean
10332          * @default false
10333          */
10334         showAreaFill: {
10335             value: false
10336         },
10337
10338         /**
10339          * Indicates whether lines are displayed.
10340          *
10341          * @attribute showLines
10342          * @type Boolean
10343          * @default true
10344          */
10345         showLines: {
10346             value: true
10347         },
10348
10349         /**
10350          * Indicates whether markers are displayed.
10351          *
10352          * @attribute showMarkers
10353          * @type Boolean
10354          * @default true
10355          */
10356         showMarkers: {
10357             value: true
10358         },
10359
10360         /**
10361          * Reference to the styles of the markers. These styles can also
10362          * be accessed through the <code>styles</code> attribute. Below are default
10363          * values:
10364          *  <dl>
10365          *      <dt>fill</dt><dd>A hash containing the following values:
10366          *          <dl>
10367          *              <dt>color</dt><dd>Color of the fill. The default value is determined by the order of the series on the graph. The color
10368          *              will be retrieved from the below array:<br/>
10369          *              <code>["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"]</code>
10370          *              </dd>
10371          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
10372          *          </dl>
10373          *      </dd>
10374          *      <dt>border</dt><dd>A hash containing the following values:
10375          *          <dl>
10376          *              <dt>color</dt><dd>Color of the border. The default value is determined by the order of the series on the graph. The color
10377          *              will be retrieved from the below array:<br/>
10378          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
10379          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
10380          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
10381          *          </dl>
10382          *      </dd>
10383          *      <dt>width</dt><dd>indicates the width of the marker. The default value is 10.</dd>
10384          *      <dt>height</dt><dd>indicates the height of the marker The default value is 10.</dd>
10385          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
10386          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
10387          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
10388          *  </dl>
10389          *
10390          * @attribute marker
10391          * @type Object
10392          */
10393         marker: {
10394             lazyAdd: false,
10395             getter: function()
10396             {
10397                 return this.get("styles").marker;
10398             },
10399             setter: function(val)
10400             {
10401                 this.set("styles", {marker:val});
10402             }
10403         },
10404         
10405         /**
10406          * Reference to the styles of the lines. These styles can also be accessed through the <code>styles</code> attribute.
10407          * Below are the default values:
10408          *  <dl>
10409          *      <dt>color</dt><dd>The color of the line. The default value is determined by the order of the series on the graph. The color will be
10410          *      retrieved from the following array: 
10411          *      <code>["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"]</code>
10412          *      <dt>weight</dt><dd>Number that indicates the width of the line. The default value is 6.</dd>
10413          *      <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the line. The default value is 1.</dd>
10414          *      <dt>lineType</dt><dd>Indicates whether the line is solid or dashed. The default value is solid.</dd> 
10415          *      <dt>dashLength</dt><dd>When the <code>lineType</code> is dashed, indicates the length of the dash. The default value is 10.</dd>
10416          *      <dt>gapSpace</dt><dd>When the <code>lineType</code> is dashed, indicates the distance between dashes. The default value is 10.</dd>
10417          *      <dt>connectDiscontinuousPoints</dt><dd>Indicates whether or not to connect lines when there is a missing or null value between points. The default value is true.</dd> 
10418          *      <dt>discontinuousType</dt><dd>Indicates whether the line between discontinuous points is solid or dashed. The default value is solid.</dd>
10419          *      <dt>discontinuousDashLength</dt><dd>When the <code>discontinuousType</code> is dashed, indicates the length of the dash. The default value is 10.</dd>
10420          *      <dt>discontinuousGapSpace</dt><dd>When the <code>discontinuousType</code> is dashed, indicates the distance between dashes. The default value is 10.</dd>
10421          *  </dl>
10422          *
10423          * @attribute line
10424          * @type Object
10425          */
10426         line: {
10427             lazyAdd: false,
10428             getter: function()
10429             {
10430                 return this.get("styles").line;
10431             },
10432             setter: function(val)
10433             {
10434                 this.set("styles", {line:val});
10435             }
10436         },
10437         
10438         /**
10439          * Reference to the styles of the area fills. These styles can also be accessed through the <code>styles</code> attribute.
10440          * Below are the default values:
10441          *
10442          *  <dl>
10443          *      <dt>color</dt><dd>The color of the fill. The default value is determined by the order of the series on the graph. The color will be 
10444          *      retrieved from the following array:
10445          *      <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
10446          *      </dd>
10447          *      <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1</dd>
10448          *  </dl>
10449          *
10450          * @attribute area
10451          * @type Object
10452          */
10453         area: {
10454             lazyAdd: false,
10455             getter: function()
10456             {
10457                 return this.get("styles").area;
10458             },
10459             setter: function(val)
10460             {
10461                 this.set("styles", {area:val});
10462             }
10463         }
10464
10465         /**
10466          * Style properties for the series. Contains a key indexed hash of the following:
10467          *  <dl>
10468          *      <dt>marker</dt><dd>Style properties for the markers in the series. Specific style attributes are listed
10469          *      <a href="#config_marker">here</a>.</dd>
10470          *      <dt>line</dt><dd>Style properties for the lines in the series. Specific
10471          *      style attributes are listed <a href="#config_line">here</a>.</dd>
10472          *      <dt>area</dt><dd>Style properties for the area fills in the series. Specific style attributes are listed
10473          *      <a href="#config_area">here</a>.</dd>
10474          *  </dl>
10475          *
10476          * @attribute styles
10477          * @type Object
10478          */
10479     }
10480 });
10481
10482
10483
10484                 
10485
10486                 
10487 /**
10488  * The StackedComboSeries class renders a combination of lines, plots and area fills in a single series. Series
10489  * are stacked along the value axis to indicate each series contribution to a cumulative total. Each
10490  * series type has a corresponding boolean attribute indicating if it is rendered. By default, all three types are
10491  * rendered.  
10492  *
10493  * @class StackedComboSeries
10494  * @extends ComboSeries
10495  * @uses StackingUtil
10496  * @constructor
10497  */
10498 Y.StackedComboSeries = Y.Base.create("stackedComboSeries", Y.ComboSeries, [Y.StackingUtil], {
10499     /**
10500      * @protected
10501      *
10502      * Calculates the coordinates for the series. Overrides base implementation.
10503      *
10504      * @method setAreaData
10505      */
10506     setAreaData: function()
10507     {   
10508         Y.StackedComboSeries.superclass.setAreaData.apply(this);
10509         this._stackCoordinates.apply(this);
10510     },
10511         
10512     /**
10513      * @protected
10514      *
10515      * Draws the series.
10516      *
10517      * @method drawSeries
10518      */
10519     drawSeries: function()
10520     {
10521         this.get("graphic").clear();
10522         if(this.get("showAreaFill"))
10523         {
10524             this.drawFill.apply(this, this._getStackedClosingPoints());
10525         }
10526         if(this.get("showLines")) 
10527         {
10528             this.drawLines();
10529         }
10530         if(this.get("showMarkers"))
10531         {
10532             this.drawPlots();
10533         }   
10534     }
10535     
10536 }, {
10537     ATTRS : {
10538         /**
10539          * Read-only attribute indicating the type of series.
10540          *
10541          * @attribute type
10542          * @type String
10543          * @default stackedCombo
10544          */
10545         type: {
10546             value: "stackedCombo"
10547         },
10548
10549         /**
10550          * Indicates whether a fill is displayed.
10551          *
10552          * @attribute showAreaFill
10553          * @type Boolean
10554          * @default true
10555          */
10556         showAreaFill: {
10557             value: true
10558         }
10559     }
10560 });
10561 /**
10562  * The ComboSplineSeries class renders a combination of splines, plots and areaspline fills in a single series. Each
10563  * series type has a corresponding boolean attribute indicating if it is rendered. By default, splines and plots 
10564  * are rendered and areaspline is not. 
10565  *
10566  * @class ComboSplineSeries
10567  * @extends ComboSeries
10568  * @extends CurveUtil
10569  * @constructor
10570  */
10571 Y.ComboSplineSeries = Y.Base.create("comboSplineSeries", Y.ComboSeries, [Y.CurveUtil], {
10572     /**
10573      * @protected
10574      * 
10575      * Draws the series.
10576      *
10577      * @method drawSeries
10578      */
10579     drawSeries: function()
10580     {
10581         this.get("graphic").clear();
10582         if(this.get("showAreaFill"))
10583         {
10584             this.drawAreaSpline();
10585         }
10586         if(this.get("showLines")) 
10587         {
10588             this.drawSpline();
10589         }
10590         if(this.get("showMarkers"))
10591         {
10592             this.drawPlots();
10593         }   
10594     }
10595 }, {
10596     ATTRS: {
10597         /**
10598          * Read-only attribute indicating the type of series.
10599          *
10600          * @attribute type
10601          * @type String
10602          * @default comboSpline
10603          */
10604         type: {
10605             value : "comboSpline"
10606         }
10607     }
10608 });
10609 /**
10610  * The StackedComboSplineSeries class renders a combination of splines, plots and areaspline fills in a single series. Series
10611  * are stacked along the value axis to indicate each series contribution to a cumulative total. Each
10612  * series type has a corresponding boolean attribute indicating if it is rendered. By default, all three types are
10613  * rendered.  
10614  *
10615  * @class StackedComboSplineSeries
10616  * @extends StackedComboSeries
10617  * @uses CurveUtil
10618  * @constructor
10619  */
10620 Y.StackedComboSplineSeries = Y.Base.create("stackedComboSplineSeries", Y.StackedComboSeries, [Y.CurveUtil], {
10621     /**
10622          * @protected
10623      *
10624      * Draws the series.
10625      *
10626      * @method drawSeries
10627          */
10628         drawSeries: function()
10629     {
10630         this.get("graphic").clear();
10631         if(this.get("showAreaFill"))
10632         {
10633             this.drawStackedAreaSpline();
10634         }
10635         if(this.get("showLines")) 
10636         {
10637             this.drawSpline();
10638         }
10639         if(this.get("showMarkers"))
10640         {
10641             this.drawPlots();
10642         }   
10643     }
10644 }, {
10645     ATTRS: {
10646         /**
10647          * Read-only attribute indicating the type of series.
10648          *
10649          * @attribute type
10650          * @type String
10651          * @default stackedComboSpline
10652          */
10653         type : {
10654             value : "stackedComboSpline"
10655         },
10656
10657         /**
10658          * Indicates whether a fill is displayed.
10659          *
10660          * @attribute showAreaFill
10661          * @type Boolean
10662          * @default true
10663          */
10664         showAreaFill: {
10665             value: true
10666         }
10667     }
10668 });
10669 /**
10670  * StackedLineSeries creates line graphs in which the different series are stacked along a value axis
10671  * to indicate their contribution to a cumulative total.
10672  *
10673  * @class StackedLineSeries
10674  * @constructor
10675  * @extends  LineSeries
10676  * @uses StackingUtil
10677  */
10678 Y.StackedLineSeries = Y.Base.create("stackedLineSeries", Y.LineSeries, [Y.StackingUtil], {
10679     /**
10680      * @protected
10681      *
10682      * Calculates the coordinates for the series. Overrides base implementation.
10683      *
10684      * @method setAreaData
10685      */
10686     setAreaData: function()
10687     {   
10688         Y.StackedLineSeries.superclass.setAreaData.apply(this);
10689         this._stackCoordinates.apply(this);
10690     }
10691 }, {
10692     ATTRS: {
10693         /**
10694          * Read-only attribute indicating the type of series.
10695          *
10696          * @attribute type
10697          * @type String
10698          * @default stackedLine
10699          */
10700         type: {
10701             value:"stackedLine"
10702         }
10703     }
10704 });
10705 /**
10706  * StackedAreaSeries area fills to display data showing its contribution to a whole.
10707  *
10708  * @param {Object} config (optional) Configuration parameters for the Chart.
10709  * @class StackedAreaSeries
10710  * @constructor
10711  * @extends AreaSeries
10712  * @uses StackingUtil
10713  */
10714 Y.StackedAreaSeries = Y.Base.create("stackedAreaSeries", Y.AreaSeries, [Y.StackingUtil], {
10715     /**
10716      * @protected
10717      *
10718      * Calculates the coordinates for the series. Overrides base implementation.
10719      *
10720      * @method setAreaData
10721      */
10722     setAreaData: function()
10723     {   
10724         Y.StackedAreaSeries.superclass.setAreaData.apply(this);
10725         this._stackCoordinates.apply(this);
10726     },
10727
10728     /**
10729      * @protected
10730      *
10731      * Draws the series
10732      *
10733      * @method drawSeries
10734      */
10735         drawSeries: function()
10736     {
10737         this.get("graphic").clear();
10738         this.drawFill.apply(this, this._getStackedClosingPoints());
10739     }
10740 }, {
10741     ATTRS: {
10742         /**
10743          * Read-only attribute indicating the type of series.
10744          *
10745          * @attribute type
10746          * @type String
10747          * @default stackedArea
10748          */
10749         type: {
10750             value:"stackedArea"
10751         }
10752     }
10753 });
10754 /**
10755  * The StackedColumnSeries renders column chart in which series are stacked vertically to show
10756  * their contribution to the cumulative total.
10757  *
10758  * @class StackedColumnSeries
10759  * @extends ColumnSeries
10760  * @uses StackingUtil
10761  * @constructor
10762  */
10763 Y.StackedColumnSeries = Y.Base.create("stackedColumnSeries", Y.ColumnSeries, [Y.StackingUtil], {
10764     /**
10765          * @protected
10766      *
10767      * Draws the series.
10768      *
10769      * @method drawSeries
10770          */
10771         drawSeries: function()
10772         {
10773             if(this.get("xcoords").length < 1) 
10774                 {
10775                         return;
10776                 }
10777         var style = this.get("styles").marker, 
10778             w = style.width,
10779             h = style.height,
10780             xcoords = this.get("xcoords"),
10781             ycoords = this.get("ycoords"),
10782             i = 0,
10783             len = xcoords.length,
10784             top = ycoords[0],
10785             type = this.get("type"),
10786             graph = this.get("graph"),
10787             seriesCollection = graph.seriesTypes[type],
10788             ratio,
10789             order = this.get("order"),
10790             graphOrder = this.get("graphOrder"),
10791             left,
10792             marker,
10793             lastCollection,
10794             negativeBaseValues,
10795             positiveBaseValues,
10796             useOrigin = order === 0,
10797             totalWidth = len * w,
10798             hotspot,
10799             isChrome = ISCHROME;
10800         this._createMarkerCache();
10801         if(isChrome)
10802         {
10803             this._createHotspotCache();
10804         }
10805         if(totalWidth > this.get("width"))
10806         {
10807             ratio = this.width/totalWidth;
10808             w *= ratio;
10809             w = Math.max(w, 1);
10810         }
10811         if(!useOrigin)
10812         {
10813             lastCollection = seriesCollection[order - 1];
10814             negativeBaseValues = lastCollection.get("negativeBaseValues");
10815             positiveBaseValues = lastCollection.get("positiveBaseValues");
10816         }
10817         else
10818         {
10819             negativeBaseValues = [];
10820             positiveBaseValues = [];
10821         }
10822         this.set("negativeBaseValues", negativeBaseValues);
10823         this.set("positiveBaseValues", positiveBaseValues);
10824         for(i = 0; i < len; ++i)
10825         {
10826             top = ycoords[i];
10827             if(useOrigin)
10828             {
10829                 h = this._bottomOrigin - top;
10830                 if(top < this._bottomOrigin)
10831                 {
10832                     positiveBaseValues[i] = top;
10833                     negativeBaseValues[i] = this._bottomOrigin;
10834                 }
10835                 else if(top > this._bottomOrigin)
10836                 {
10837                     positiveBaseValues[i] = this._bottomOrigin;
10838                     negativeBaseValues[i] = top;
10839                 }
10840                 else
10841                 {
10842                     positiveBaseValues[i] = top;
10843                     negativeBaseValues[i] = top;
10844                 }
10845             }
10846             else 
10847             {
10848                 if(top > this._bottomOrigin)
10849                 {
10850                     top += (negativeBaseValues[i] - this._bottomOrigin);
10851                     h = negativeBaseValues[i] - top;
10852                     negativeBaseValues[i] = top;
10853                 }
10854                 else if(top < this._bottomOrigin)
10855                 {
10856                     top = positiveBaseValues[i] - (this._bottomOrigin - ycoords[i]);
10857                     h = positiveBaseValues[i] - top;
10858                     positiveBaseValues[i] = top;
10859                 }
10860             }
10861             left = xcoords[i] - w/2;
10862             style.width = w;
10863             style.height = h;
10864             marker = this.getMarker(style, graphOrder, i);
10865             marker.setPosition(left, top);
10866             if(isChrome)
10867             {
10868                 hotspot = this.getHotspot(style, graphOrder, i);
10869                 hotspot.setPosition(left, top);
10870                 hotspot.parentNode.style.zIndex = 5;
10871             }
10872         }
10873         this._clearMarkerCache();
10874         if(isChrome)
10875         {
10876             this._clearHotspotCache();
10877         }
10878     },
10879
10880     /**
10881      * @protected
10882      *
10883      * Resizes and positions markers based on a mouse interaction.
10884      *
10885      * @method updateMarkerState
10886      * @param {String} type state of the marker
10887      * @param {Number} i index of the marker
10888      */
10889     updateMarkerState: function(type, i)
10890     {
10891         if(this._markers[i])
10892         {
10893             var styles,
10894                 markerStyles,
10895                 state = this._getState(type),
10896                 xcoords = this.get("xcoords"),
10897                 marker = this._markers[i],
10898                 offset = 0;        
10899             styles = this.get("styles").marker;
10900             markerStyles = state == "off" || !styles[state] ? styles : styles[state]; 
10901             markerStyles.height = marker.height;
10902             marker.update(markerStyles);
10903             offset = styles.width * 0.5;
10904             if(marker.parentNode)
10905             {
10906                 Y.one(marker.parentNode).setStyle("left", (xcoords[i] - offset));
10907             }
10908         }
10909     },
10910         
10911         /**
10912          * @private
10913          */
10914     _getPlotDefaults: function()
10915     {
10916         var defs = {
10917             fill:{
10918                 type: "solid",
10919                 alpha: 1,
10920                 colors:null,
10921                 alphas: null,
10922                 ratios: null
10923             },
10924             border:{
10925                 weight: 0,
10926                 alpha: 1
10927             },
10928             width: 24,
10929             height: 24,
10930             shape: "rect",
10931
10932             padding:{
10933                 top: 0,
10934                 left: 0,
10935                 right: 0,
10936                 bottom: 0
10937             }
10938         };
10939         defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
10940         defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
10941         return defs;
10942     }
10943 }, {
10944     ATTRS: {
10945         /**
10946          * Read-only attribute indicating the type of series.
10947          *
10948          * @attribute type
10949          * @type String
10950          * @default stackedColumn
10951          */
10952         type: {
10953             value: "stackedColumn"
10954         },
10955
10956         /**
10957          * @private
10958          *
10959          * @attribute negativeBaseValues
10960          * @type Array
10961          * @default null
10962          */
10963         negativeBaseValues: {
10964             value: null
10965         },
10966
10967         /**
10968          * @private
10969          *
10970          * @attribute positiveBaseValues
10971          * @type Array
10972          * @default null
10973          */
10974         positiveBaseValues: {
10975             value: null
10976         }
10977         
10978         /**
10979          * Style properties used for drawing markers. This attribute is inherited from <code>ColumnSeries</code>. Below are the default values:
10980          *  <dl>
10981          *      <dt>fill</dt><dd>A hash containing the following values:
10982          *          <dl>
10983          *              <dt>color</dt><dd>Color of the fill. The default value is determined by the order of the series on the graph. The color
10984          *              will be retrieved from the below array:<br/>
10985          *              <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
10986          *              </dd>
10987          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
10988          *          </dl>
10989          *      </dd>
10990          *      <dt>border</dt><dd>A hash containing the following values:
10991          *          <dl>
10992          *              <dt>color</dt><dd>Color of the border. The default value is determined by the order of the series on the graph. The color
10993          *              will be retrieved from the below array:<br/>
10994          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
10995          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
10996          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
10997          *          </dl>
10998          *      </dd>
10999          *      <dt>width</dt><dd>indicates the width of the marker. The default value is 24.</dd>
11000          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
11001          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
11002          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
11003          *  </dl>
11004          *
11005          * @attribute styles
11006          * @type Object
11007          */
11008     }
11009 });
11010
11011 /**
11012  * The StackedBarSeries renders bar chart in which series are stacked horizontally to show
11013  * their contribution to the cumulative total.
11014  *
11015  * @class StackedBarSeries
11016  * @extends BarSeries
11017  * @uses StackingUtil
11018  * @constructor
11019  */
11020 Y.StackedBarSeries = Y.Base.create("stackedBarSeries", Y.BarSeries, [Y.StackingUtil], {
11021     /**
11022      * @protected
11023      *
11024      * Draws the series.
11025      *
11026      * @method drawSeries
11027      */
11028     drawSeries: function()
11029         {
11030             if(this.get("xcoords").length < 1) 
11031                 {
11032                         return;
11033                 }
11034
11035         var style = this.get("styles").marker,
11036             w = style.width,
11037             h = style.height,
11038             xcoords = this.get("xcoords"),
11039             ycoords = this.get("ycoords"),
11040             i = 0,
11041             len = xcoords.length,
11042             top = ycoords[0],
11043             type = this.get("type"),
11044             graph = this.get("graph"),
11045             seriesCollection = graph.seriesTypes[type],
11046             ratio,
11047             order = this.get("order"),
11048             graphOrder = this.get("graphOrder"),
11049             left,
11050             marker,
11051             lastCollection,
11052             negativeBaseValues,
11053             positiveBaseValues,
11054             useOrigin = order === 0,
11055             totalHeight = len * h,
11056             hotspot,
11057             isChrome = ISCHROME;
11058         this._createMarkerCache();
11059         if(isChrome)
11060         {
11061             this._createHotspotCache();
11062         }
11063         if(totalHeight > this.get("height"))
11064         {
11065             ratio = this.height/totalHeight;
11066             h *= ratio;
11067             h = Math.max(h, 1);
11068         }
11069         if(!useOrigin)
11070         {
11071             lastCollection = seriesCollection[order - 1];
11072             negativeBaseValues = lastCollection.get("negativeBaseValues");
11073             positiveBaseValues = lastCollection.get("positiveBaseValues");
11074         }
11075         else
11076         {
11077             negativeBaseValues = [];
11078             positiveBaseValues = [];
11079         }
11080         this.set("negativeBaseValues", negativeBaseValues);
11081         this.set("positiveBaseValues", positiveBaseValues);
11082         for(i = 0; i < len; ++i)
11083         {
11084             top = ycoords[i];
11085             left = xcoords[i];
11086             
11087             if(useOrigin)
11088             {
11089                 w = left - this._leftOrigin;
11090                 if(left > this._leftOrigin)
11091                 {
11092                     positiveBaseValues[i] = left;
11093                     negativeBaseValues[i] = this._leftOrigin;
11094                 }
11095                 else if(left < this._leftOrigin)
11096                 {   
11097                     positiveBaseValues[i] = this._leftOrigin;
11098                     negativeBaseValues[i] = left;
11099                 }
11100                 else
11101                 {
11102                     positiveBaseValues[i] = left;
11103                     negativeBaseValues[i] = this._leftOrigin;
11104                 }
11105                 left -= w;
11106             }
11107             else
11108             {
11109                 if(left < this._leftOrigin)
11110                 {
11111                     left = negativeBaseValues[i] - (this._leftOrigin - xcoords[i]);
11112                     w = negativeBaseValues[i] - left;
11113                     negativeBaseValues[i] = left;
11114                 }
11115                 else if(left > this._leftOrigin)
11116                 {
11117                     left += (positiveBaseValues[i] - this._leftOrigin);
11118                     w = left - positiveBaseValues[i];
11119                     positiveBaseValues[i] = left;
11120                     left -= w;
11121                 }
11122             }
11123             top -= h/2;        
11124             style.width = w;
11125             style.height = h;
11126             marker = this.getMarker(style, graphOrder, i);
11127             marker.setPosition(left, top);
11128             if(isChrome)
11129             {
11130                 hotspot = this.getHotspot(style, graphOrder, i);
11131                 hotspot.setPosition(left, top);
11132                 hotspot.parentNode.style.zIndex = 5;
11133             }
11134         }
11135         this._clearMarkerCache();
11136         if(isChrome)
11137         {
11138             this._clearHotspotCache();
11139         }
11140     },
11141
11142     /**
11143      * @protected
11144      *
11145      * Resizes and positions markers based on a mouse interaction.
11146      *
11147      * @method updateMarkerState
11148      * @param {String} type state of the marker
11149      * @param {Number} i index of the marker
11150      */
11151     updateMarkerState: function(type, i)
11152     {
11153         if(this._markers[i])
11154         {
11155             var state = this._getState(type),
11156                 ycoords = this.get("ycoords"),
11157                 marker = this._markers[i],
11158                 styles = this.get("styles").marker,
11159                 h = styles.height,
11160                 markerStyles = state == "off" || !styles[state] ? styles : styles[state]; 
11161             markerStyles.width = marker.width;
11162             marker.update(markerStyles);
11163             if(marker.parentNode)
11164             {
11165                 Y.one(marker.parentNode).setStyle("top", (ycoords[i] - h/2));
11166             }
11167         }
11168     },
11169         
11170     /**
11171      * @protected
11172      *
11173      * Returns default values for the <code>styles</code> attribute.
11174      * 
11175      * @method _getPlotDefaults
11176      * @return Object
11177      */
11178     _getPlotDefaults: function()
11179     {
11180         var defs = {
11181             fill:{
11182                 type: "solid",
11183                 alpha: 1,
11184                 colors:null,
11185                 alphas: null,
11186                 ratios: null
11187             },
11188             border:{
11189                 weight: 0,
11190                 alpha: 1
11191             },
11192             width: 24,
11193             height: 24,
11194             shape: "rect",
11195
11196             padding:{
11197                 top: 0,
11198                 left: 0,
11199                 right: 0,
11200                 bottom: 0
11201             }
11202         };
11203         defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
11204         defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
11205         return defs;
11206     }
11207 }, {
11208     ATTRS: {
11209         /**
11210          * Read-only attribute indicating the type of series.
11211          *
11212          * @attribute type
11213          * @type String
11214          * @default stackedBar
11215          */
11216         type: {
11217             value: "stackedBar"
11218         },
11219
11220         /**
11221          * Direction of the series
11222          *
11223          * @attribute direction
11224          * @type String
11225          * @default vertical
11226          */
11227         direction: {
11228             value: "vertical"
11229         },
11230
11231         /**
11232          * @private
11233          *
11234          * @attribute negativeBaseValues
11235          * @type Array
11236          * @default null
11237          */
11238         negativeBaseValues: {
11239             value: null
11240         },
11241
11242         /**
11243          * @private
11244          *
11245          * @attribute positiveBaseValues
11246          * @type Array
11247          * @default null
11248          */
11249         positiveBaseValues: {
11250             value: null
11251         }
11252         
11253         /**
11254          * Style properties used for drawing markers. This attribute is inherited from <code>BarSeries</code>. Below are the default values:
11255          *  <dl>
11256          *      <dt>fill</dt><dd>A hash containing the following values:
11257          *          <dl>
11258          *              <dt>color</dt><dd>Color of the fill. The default value is determined by the order of the series on the graph. The color
11259          *              will be retrieved from the below array:<br/>
11260          *              <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
11261          *              </dd>
11262          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
11263          *          </dl>
11264          *      </dd>
11265          *      <dt>border</dt><dd>A hash containing the following values:
11266          *          <dl>
11267          *              <dt>color</dt><dd>Color of the border. The default value is determined by the order of the series on the graph. The color
11268          *              will be retrieved from the below array:<br/>
11269          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
11270          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
11271          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
11272          *          </dl>
11273          *      </dd>
11274          *      <dt>height</dt><dd>indicates the width of the marker. The default value is 24.</dd>
11275          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
11276          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
11277          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
11278          *  </dl>
11279          *
11280          * @attribute styles
11281          * @type Object
11282          */
11283     }
11284 });
11285
11286 /**
11287  * PieSeries visualizes data as a circular chart divided into wedges which represent data as a 
11288  * percentage of a whole.
11289  *
11290  * @class PieSeries
11291  * @constructor
11292  * @extends MarkerSeries
11293  */
11294 Y.PieSeries = Y.Base.create("pieSeries", Y.MarkerSeries, [], { 
11295     /**
11296      * @private
11297      */
11298     _map: null,
11299
11300     /**
11301      * @private
11302      */
11303     _image: null,
11304
11305     /**
11306      * @private
11307      */
11308     _setMap: function()
11309     {
11310         var id = "pieHotSpotMapi_" + Math.round(100000 * Math.random()),
11311             cb = this.get("graph").get("contentBox"),
11312             areaNode;
11313         if(this._image)
11314         {
11315             cb.removeChild(this._image);
11316             while(this._areaNodes && this._areaNodes.length > 0)
11317             {
11318                 areaNode = this._areaNodes.shift();
11319                 this._map.removeChild(areaNode);
11320             }
11321             cb.removeChild(this._map);
11322         }
11323         this._image = document.createElement("img"); 
11324         this._image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==";
11325         cb.appendChild(this._image);
11326         this._image.setAttribute("usemap", "#" + id);
11327         this._image.style.zIndex = 3;
11328         this._image.style.opacity = 0;
11329         this._image.setAttribute("alt", "imagemap");
11330         this._map = document.createElement("map");
11331         this._map.style.zIndex = 5;
11332         cb.appendChild(this._map);
11333         this._map.setAttribute("name", id);
11334         this._map.setAttribute("id", id);
11335         this._areaNodes = [];
11336     },
11337
11338     /**
11339      * @private
11340      */
11341     _categoryDisplayName: null,
11342     
11343     /**
11344      * @private
11345      */
11346     _valueDisplayName: null,
11347
11348     /**
11349      * @private
11350      */
11351     addListeners: function()
11352     {
11353         var categoryAxis = this.get("categoryAxis"),
11354             valueAxis = this.get("valueAxis");
11355         if(categoryAxis)
11356         {
11357             categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
11358             categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
11359         }
11360         if(valueAxis)
11361         {
11362             valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
11363             valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
11364         }
11365         this.after("categoryAxisChange", this.categoryAxisChangeHandler);
11366         this.after("valueAxisChange", this.valueAxisChangeHandler);
11367         this.after("stylesChange", this._updateHandler);
11368     },
11369     
11370     /**
11371      * @private
11372      */
11373     validate: function()
11374     {
11375         this.draw();
11376         this._renderered = true;
11377     },
11378
11379     /**
11380      * @private
11381      */
11382     _categoryAxisChangeHandler: function(e)
11383     {
11384         var categoryAxis = this.get("categoryAxis");
11385         categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
11386         categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
11387     },
11388     
11389     /**
11390      * @private
11391      */
11392     _valueAxisChangeHandler: function(e)
11393     {
11394         var valueAxis = this.get("valueAxis");
11395         valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
11396         valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
11397     },
11398         
11399     /**
11400      * Constant used to generate unique id.
11401      *
11402      * @private
11403      */
11404     GUID: "pieseries",
11405         
11406     /**
11407      * @private (protected)
11408      * Handles updating the graph when the x < code>Axis</code> values
11409      * change.
11410      */
11411     _categoryDataChangeHandler: function(event)
11412     {
11413        if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
11414         {
11415             this.draw();
11416         }
11417     },
11418
11419     /**
11420      * @private (protected)
11421      * Handles updating the chart when the y <code>Axis</code> values
11422      * change.
11423      */
11424     _valueDataChangeHandler: function(event)
11425     {
11426         if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
11427         {
11428             this.draw();
11429         }
11430     },
11431    
11432     /**
11433      * @protected
11434      *
11435      * Draws the series. Overrides the base implementation.
11436      *
11437      * @method draw
11438      */
11439     draw: function()
11440     {
11441         var graph = this.get("graph"),
11442             w = graph.get("width"),
11443             h = graph.get("height");
11444         if(isFinite(w) && isFinite(h) && w > 0 && h > 0)
11445         {   
11446             this._rendered = true;
11447             this.drawSeries();
11448             this.fire("drawingComplete");
11449         }
11450     },
11451
11452     /**
11453      * @private
11454      */
11455     drawPlots: function()
11456     {
11457         var values = this.get("valueAxis").getDataByKey(this.get("valueKey")).concat(),
11458             catValues = this.get("categoryAxis").getDataByKey(this.get("categoryKey")).concat(),
11459             totalValue = 0,
11460             itemCount = values.length,
11461             styles = this.get("styles").marker,
11462             fillColors = styles.fill.colors,
11463             fillAlphas = styles.fill.alphas || ["1"],
11464             borderColors = styles.border.colors,
11465             borderWeights = [styles.border.weight],
11466             borderAlphas = [styles.border.alpha],
11467             tbw = borderWeights.concat(),
11468             tbc = borderColors.concat(),
11469             tba = borderAlphas.concat(),
11470             tfc,
11471             tfa,
11472             padding = styles.padding,
11473             graph = this.get("graph"),
11474             w = graph.get("width") - (padding.left + padding.right),
11475             h = graph.get("height") - (padding.top + padding.bottom),
11476             startAngle = -90,
11477             halfWidth = w / 2,
11478             halfHeight = h / 2,
11479             radius = Math.min(halfWidth, halfHeight),
11480             i = 0,
11481             value,
11482             angle = 0,
11483             lc,
11484             la,
11485             lw,
11486             wedgeStyle,
11487             marker,
11488             graphOrder = this.get("graphOrder"),
11489             isCanvas = DRAWINGAPI == "canvas";
11490
11491         for(; i < itemCount; ++i)
11492         {
11493             value = values[i];
11494             
11495             values.push(value);
11496             if(!isNaN(value))
11497             {
11498                 totalValue += value;
11499             }
11500         }
11501         
11502         tfc = fillColors ? fillColors.concat() : null;
11503         tfa = fillAlphas ? fillAlphas.concat() : null;
11504         this._createMarkerCache();
11505         if(isCanvas)
11506         {
11507             this._setMap();
11508             this._image.width = w;
11509             this._image.height = h;
11510         }
11511         for(i = 0; i < itemCount; i++)
11512         {
11513             value = values[i];
11514             if(totalValue === 0)
11515             {
11516                 angle = 360 / values.length;
11517             }
11518             else
11519             {
11520                 angle = 360 * (value / totalValue);
11521             }
11522             angle = Math.round(angle);
11523             if(tfc && tfc.length < 1)
11524             {
11525                 tfc = fillColors.concat();
11526             }
11527             if(tfa && tfa.length < 1)
11528             {
11529                 tfa = fillAlphas.concat();
11530             }
11531             if(tbw && tbw.length < 1)
11532             {
11533                 tbw = borderWeights.concat();
11534             }
11535             if(tbw && tbc.length < 1)
11536             {
11537                 tbc = borderColors.concat();
11538             }
11539             if(tba && tba.length < 1)
11540             {
11541                 tba = borderAlphas.concat();
11542             }
11543             lw = tbw ? tbw.shift() : null;
11544             lc = tbc ? tbc.shift() : null;
11545             la = tba ? tba.shift() : null;
11546             startAngle += angle;
11547             wedgeStyle = {
11548                 border: {
11549                     color:lc,
11550                     weight:lw,
11551                     alpha:la
11552                 },
11553                 fill: {
11554                     color:tfc ? tfc.shift() : this._getDefaultColor(i, "slice"),
11555                     alpha:tfa ? tfa.shift() : null
11556                 },
11557                 shape: "wedge",
11558                 props: {
11559                     arc: angle,
11560                     radius: radius,
11561                     startAngle: startAngle,
11562                     x: halfWidth,
11563                     y: halfHeight
11564                 },
11565                 width: w,
11566                 height: h
11567             };
11568             marker = this.getMarker(wedgeStyle, graphOrder, i);
11569             if(isCanvas)
11570             {
11571                 this._addHotspot(wedgeStyle.props, graphOrder, i);
11572             }
11573         }
11574         this._clearMarkerCache();
11575     },
11576
11577     _addHotspot: function(cfg, seriesIndex, index)
11578     {
11579         var areaNode = document.createElement("area"),
11580             i = 1,
11581             x = cfg.x,
11582             y = cfg.y, 
11583             arc = cfg.arc,
11584             startAngle = cfg.startAngle - arc, 
11585             endAngle = cfg.startAngle,
11586             radius = cfg.radius, 
11587             ax = x + Math.cos(startAngle / 180 * Math.PI) * radius,
11588             ay = y + Math.sin(startAngle / 180 * Math.PI) * radius,
11589             bx = x + Math.cos(endAngle / 180 * Math.PI) * radius,
11590             by = y + Math.sin(endAngle / 180 * Math.PI) * radius,
11591             numPoints = Math.floor(arc/10) - 1,
11592             divAngle = (arc/(Math.floor(arc/10)) / 180) * Math.PI,
11593             angleCoord = Math.atan((ay - y)/(ax - x)),
11594             pts = x + ", " + y + ", " + ax + ", " + ay,
11595             cosAng,
11596             sinAng,
11597             multDivAng;
11598         for(i = 1; i <= numPoints; ++i)
11599         {
11600             multDivAng = divAngle * i;
11601             cosAng = Math.cos(angleCoord + multDivAng);
11602             sinAng = Math.sin(angleCoord + multDivAng);
11603             if(startAngle <= 90)
11604             {
11605                 pts += ", " + (x + (radius * Math.cos(angleCoord + (divAngle * i))));
11606                 pts += ", " + (y + (radius * Math.sin(angleCoord + (divAngle * i))));
11607             }
11608             else
11609             {
11610                 pts += ", " + (x - (radius * Math.cos(angleCoord + (divAngle * i))));
11611                 pts += ", " + (y - (radius * Math.sin(angleCoord + (divAngle * i))));
11612             }
11613         }
11614         pts += ", " + bx + ", " + by;
11615         pts += ", " + x + ", " + y;
11616         this._map.appendChild(areaNode);
11617         areaNode.setAttribute("class", "yui3-seriesmarker");
11618         areaNode.setAttribute("id", "hotSpot_" + seriesIndex + "_" + index);
11619         areaNode.setAttribute("shape", "polygon");
11620         areaNode.setAttribute("coords", pts);
11621         this._areaNodes.push(areaNode);
11622
11623     },
11624
11625     /**
11626      * Resizes and positions markers based on a mouse interaction.
11627      *
11628      * @protected
11629      * @method updateMarkerState
11630      * @param {String} type state of the marker
11631      * @param {Number} i index of the marker
11632      */
11633     updateMarkerState: function(type, i)
11634     {
11635         if(this._markers[i])
11636         {
11637             var state = this._getState(type),
11638                 markerStyles,
11639                 indexStyles,
11640                 marker = this._markers[i],
11641                 styles = this.get("styles").marker; 
11642             markerStyles = state == "off" || !styles[state] ? styles : styles[state]; 
11643             indexStyles = this._mergeStyles(markerStyles, {});
11644             indexStyles.fill.color = indexStyles.fill.colors[i % indexStyles.fill.colors.length];
11645             indexStyles.fill.alpha = indexStyles.fill.alphas[i % indexStyles.fill.alphas.length];
11646             marker.update(indexStyles);
11647         }
11648     },
11649     
11650     /**
11651      * @private
11652      */
11653     _createMarker: function(styles, order, index)
11654     {
11655         var cfg = Y.clone(styles),
11656             marker;
11657         cfg.graphic = this.get("graphic");
11658         marker = new Y.Shape(cfg);
11659         marker.addClass("yui3-seriesmarker");
11660         marker.node.setAttribute("id", "series_" + order + "_" + index);
11661         return marker;
11662     },
11663     
11664     /**
11665      * @private
11666      */
11667     _clearMarkerCache: function()
11668     {
11669         var len = this._markerCache.length,
11670             i = 0,
11671             marker;
11672         for(; i < len; ++i)
11673         {
11674             marker = this._markerCache[i];
11675             if(marker && marker.node && marker.parentNode)
11676             {
11677                 marker.parentNode.removeChild(marker.node);
11678             }
11679         }
11680         this._markerCache = [];
11681     },
11682
11683     /**
11684      * @private
11685      */
11686     _getPlotDefaults: function()
11687     {
11688          var defs = {
11689             padding:{
11690                 top: 0,
11691                 left: 0,
11692                 right: 0,
11693                 bottom: 0
11694             },
11695             fill:{
11696                 alphas:["1"]
11697             },
11698             border: {
11699                 weight: 0,
11700                 alpha: 1
11701             }
11702         };
11703         defs.fill.colors = this._defaultSliceColors;
11704         defs.border.colors = this._defaultBorderColors;
11705         return defs;
11706     },
11707
11708     /**
11709      * @private
11710      */
11711     _defaultLineColors:["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"],
11712
11713     /**
11714      * @private
11715      */
11716     _defaultFillColors:["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"],
11717     
11718     /**
11719      * @private
11720      */
11721     _defaultBorderColors:["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"],
11722     
11723     /**
11724      * @private
11725      */
11726     _defaultSliceColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],
11727
11728     /**
11729      * @private
11730      * @description Colors used if style colors are not specified
11731      */
11732     _getDefaultColor: function(index, type)
11733     {
11734         var colors = {
11735                 line: this._defaultLineColors,
11736                 fill: this._defaultFillColors,
11737                 border: this._defaultBorderColors,
11738                 slice: this._defaultSliceColors
11739             },
11740             col = colors[type],
11741             l = col.length;
11742         index = index || 0;
11743         if(index >= l)
11744         {
11745             index = index % l;
11746         }
11747         type = type || "fill";
11748         return colors[type][index];
11749     }
11750 }, {
11751     ATTRS: {
11752         /**
11753          * Read-only attribute indicating the type of series.
11754          *
11755          * @attribute type
11756          * @type String
11757          * @default pie
11758          */
11759         type: {         
11760             value: "pie"
11761         },
11762         
11763         /**
11764          * Order of this instance of this <code>type</code>.
11765          *
11766          * @attribute order
11767          * @type Number
11768          */
11769         order: {},
11770
11771         /**
11772          * Reference to the <code>Graph</code> in which the series is drawn into.
11773          *
11774          * @attribute graph
11775          * @type Graph
11776          */
11777         graph: {},
11778         
11779         /**
11780          * Reference to the <code>Axis</code> instance used for assigning 
11781          * category values to the graph.
11782          *
11783          * @attribute categoryAxis
11784          * @type Axis
11785          */
11786         categoryAxis: {
11787             value: null,
11788
11789             validator: function(value)
11790             {
11791                 return value !== this.get("categoryAxis");
11792             }
11793         },
11794         
11795         /**
11796          * Reference to the <code>Axis</code> instance used for assigning 
11797          * series values to the graph.
11798          *
11799          * @attribute categoryAxis
11800          * @type Axis
11801          */
11802         valueAxis: {
11803             value: null,
11804
11805             validator: function(value)
11806             {
11807                 return value !== this.get("valueAxis");
11808             }
11809         },
11810
11811         /**
11812          * Indicates which array to from the hash of value arrays in 
11813          * the category <code>Axis</code> instance.
11814          */
11815         categoryKey: {
11816             value: null,
11817
11818             validator: function(value)
11819             {
11820                 return value !== this.get("categoryKey");
11821             }
11822         },
11823         /**
11824          * Indicates which array to from the hash of value arrays in 
11825          * the value <code>Axis</code> instance.
11826          */
11827         valueKey: {
11828             value: null,
11829
11830             validator: function(value)
11831             {
11832                 return value !== this.get("valueKey");
11833             }
11834         },
11835
11836         /**
11837          * Name used for for displaying category data
11838          *
11839          * @attribute categoryDisplayName
11840          * @type String
11841          */
11842         categoryDisplayName: {
11843             setter: function(val)
11844             {
11845                 this._categoryDisplayName = val;
11846                 return val;
11847             },
11848
11849             getter: function()
11850             {
11851                 return this._categoryDisplayName || this.get("categoryKey");
11852             }
11853         },
11854
11855         /**
11856          * Name used for for displaying value data
11857          *
11858          * @attribute valueDisplayName
11859          * @type String
11860          */
11861         valueDisplayName: {
11862             setter: function(val)
11863             {
11864                 this._valueDisplayName = val;
11865                 return val;
11866             },
11867
11868             getter: function()
11869             {
11870                 return this._valueDisplayName || this.get("valueKey");
11871             }
11872         },
11873         
11874         /**
11875          * @private
11876          */
11877         slices: null
11878         
11879         /**
11880          * Style properties used for drawing markers. This attribute is inherited from <code>MarkerSeries</code>. Below are the default values:
11881          *  <dl>
11882          *      <dt>fill</dt><dd>A hash containing the following values:
11883          *          <dl>
11884          *              <dt>colors</dt><dd>An array of colors to be used for the marker fills. The color for each marker is retrieved from the 
11885          *              array below:<br/>
11886          *              <code>["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]</code>
11887          *              </dd>
11888          *              <dt>alphas</dt><dd>An array of alpha references (Number from 0 to 1) indicating the opacity of each marker fill. The default value is [1].</dd>
11889          *          </dl>
11890          *      </dd>
11891          *      <dt>border</dt><dd>A hash containing the following values:
11892          *          <dl>
11893          *              <dt>color</dt><dd>An array of colors to be used for the marker borders. The color for each marker is retrieved from the
11894          *              array below:<br/>
11895          *              <code>["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]</code>
11896          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
11897          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
11898          *          </dl>
11899          *      </dd>
11900          *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a <code>mouseover</code> event. The default 
11901          *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
11902          *      the default value for <code>marker.over.fill.color</code> is equivalent to <code>marker.fill.color</code>.</dd>
11903          *  </dl>
11904          *
11905          * @attribute styles
11906          * @type Object
11907          */
11908     }
11909 });
11910 /**
11911  * Gridlines draws gridlines on a Graph.
11912  *
11913  * @class Gridlines
11914  * @constructor
11915  * @extends Base
11916  * @uses Renderer
11917  */
11918 Y.Gridlines = Y.Base.create("gridlines", Y.Base, [Y.Renderer], {
11919     /**
11920      * @private
11921      */
11922     render: function()
11923     {
11924         this._setCanvas();
11925     },
11926
11927     /**
11928      * @private
11929      */
11930     remove: function()
11931     {
11932         var graphic = this.get("graphic"),
11933             gNode;
11934         if(graphic)
11935         {
11936             gNode = graphic.node;
11937             if(gNode)
11938             {
11939                 Y.one(gNode).remove();
11940             }
11941         }
11942     },
11943
11944     /**
11945      * @protected
11946      *
11947      * Draws the gridlines
11948      *
11949      * @method draw
11950      */
11951     draw: function()
11952     {
11953         if(this.get("axis") && this.get("graph"))
11954         {
11955             this._drawGridlines();
11956         }
11957     },
11958
11959     /**
11960      * @private
11961      */
11962     _drawGridlines: function()
11963     {
11964         var graphic = this.get("graphic"),
11965             axis = this.get("axis"),
11966             axisPosition = axis.get("position"),
11967             points,
11968             i = 0,
11969             l,
11970             direction = this.get("direction"),
11971             graph = this.get("graph"),
11972             w = graph.get("width"),
11973             h = graph.get("height"),
11974             line = this.get("styles").line,
11975             color = line.color,
11976             weight = line.weight,
11977             alpha = line.alpha,
11978             lineFunction = direction == "vertical" ? this._verticalLine : this._horizontalLine;
11979         if(axisPosition == "none")
11980         {
11981             points = [];
11982             l = axis.get("styles").majorUnit.count;
11983             for(; i < l; ++i)
11984             {
11985                 points[i] = {
11986                     x: w * (i/(l-1)),
11987                     y: h * (i/(l-1))
11988                 };
11989             }
11990             i = 0;
11991         }
11992         else
11993         {
11994             points = axis.get("tickPoints");
11995             l = points.length;
11996         }
11997         if(!graphic)
11998         {
11999             this._setCanvas();
12000             graphic = this.get("graphic");
12001         }
12002         graphic.clear();
12003         graphic.setSize(w, h);
12004         graphic.lineStyle(weight, color, alpha);
12005         for(; i < l; ++i)
12006         {
12007             lineFunction(graphic, points[i], w, h);
12008         }
12009         graphic.end();
12010     },
12011
12012     /**
12013      * @private
12014      */
12015     _horizontalLine: function(graphic, pt, w, h)
12016     {
12017         graphic.moveTo(0, pt.y);
12018         graphic.lineTo(w, pt.y);
12019     },
12020
12021     /**
12022      * @private
12023      */
12024     _verticalLine: function(graphic, pt, w, h)
12025     {
12026         graphic.moveTo(pt.x, 0);
12027         graphic.lineTo(pt.x, h);
12028     },
12029
12030     /**
12031      * @private
12032      * Creates a <code>Graphic</code> instance.
12033      */
12034     _setCanvas: function()
12035     {
12036         this.set("graphic", new Y.Graphic());
12037         this.get("graphic").render(this.get("graph").get("contentBox"));
12038     },
12039     
12040     /**
12041      * @protected
12042      *
12043      * Gets the default value for the <code>styles</code> attribute. Overrides
12044      * base implementation.
12045      *
12046      * @method _getDefaultStyles
12047      * @return Object
12048      */
12049     _getDefaultStyles: function()
12050     {
12051         var defs = {
12052             line: {
12053                 color:"#f0efe9",
12054                 weight: 1,
12055                 alpha: 1
12056             }
12057         };
12058         return defs;
12059     }
12060
12061 },
12062 {
12063     ATTRS: {
12064         /**
12065          * Indicates the direction of the gridline.
12066          *
12067          * @attribute direction
12068          * @type String
12069          */
12070         direction: {},
12071         
12072         /**
12073          * Indicate the <code>Axis</code> in which to bind
12074          * the gridlines.
12075          *
12076          * @attribute axis
12077          * @type Axis
12078          */
12079         axis: {},
12080         
12081         /**
12082          * Indicates the <code>Graph</code> in which the gridlines 
12083          * are drawn.
12084          *
12085          * @attribute graph
12086          * @type Graph
12087          */
12088         graph: {}
12089     }
12090 });
12091 /**
12092  * Graph manages and contains series instances for a <code>CartesianChart</code>
12093  * instance.
12094  *
12095  * @class Graph
12096  * @constructor
12097  * @extends Widget
12098  * @uses Renderer
12099  */
12100 Y.Graph = Y.Base.create("graph", Y.Widget, [Y.Renderer], {
12101     bindUI: function()
12102     {
12103         var bb = this.get("boundingBox");
12104         bb.setStyle("position", "absolute");
12105         this.after("widthChange", this._sizeChangeHandler);
12106         this.after("heightChange", this._sizeChangeHandler);
12107         this.after("stylesChange", this._updateStyles);
12108     },
12109
12110     /**
12111      * @private
12112      */
12113     syncUI: function()
12114     {
12115         if(this.get("showBackground"))
12116         {
12117             var graphic = new Y.Graphic(),
12118                 graphicNode,
12119                 cb = this.get("contentBox"),
12120                 bg = this.get("styles").background,
12121                 border = bg.border,
12122                 weight = border.weight || 0,
12123                 w = this.get("width"),
12124                 h = this.get("height");
12125             if(w)
12126             {
12127                 w += weight * 2;
12128                 bg.width = w;
12129             }   
12130             if(h)
12131             {
12132                 h += weight * 2;
12133                 bg.height = h;
12134             }
12135             graphic.render(cb);
12136             this._background = graphic.getShape(bg);
12137             graphicNode = Y.one(graphic.node);
12138             graphicNode.setStyle("left", 0 - weight);
12139             graphicNode.setStyle("top", 0 - weight);
12140             graphicNode.setStyle("zIndex", -1);
12141         }
12142     },
12143    
12144     /**
12145      * @private
12146      */
12147     renderUI: function()
12148     {
12149         var sc = this.get("seriesCollection"),
12150             series,
12151             i = 0,
12152             len = sc.length,
12153             hgl = this.get("horizontalGridlines"),
12154             vgl = this.get("verticalGridlines");
12155         for(; i < len; ++i)
12156         {
12157             series = sc[i];
12158             if(series instanceof Y.CartesianSeries)
12159             {
12160                 series.render();
12161             }
12162         }
12163         if(hgl && hgl instanceof Y.Gridlines)
12164         {
12165             hgl.draw();
12166         }
12167         if(vgl && vgl instanceof Y.Gridlines)
12168         {
12169             vgl.draw();
12170         }
12171     },
12172
12173     /**
12174      * @private
12175      * Hash of arrays containing series mapped to a series type.
12176      */
12177     seriesTypes: null,
12178
12179     /**
12180      * Returns a series instance based on an index.
12181      * 
12182      * @method getSeriesByIndex
12183      * @param {Number} val index of the series
12184      * @return CartesianSeries
12185      */
12186     getSeriesByIndex: function(val)
12187     {
12188         var col = this.get("seriesCollection"),
12189             series;
12190         if(col && col.length > val)
12191         {
12192             series = col[val];
12193         }
12194         return series;
12195     },
12196
12197     /**
12198      * Returns a series instance based on a key value.
12199      * 
12200      * @method getSeriesByKey
12201      * @param {String} val key value of the series
12202      * @return CartesianSeries
12203      */
12204     getSeriesByKey: function(val)
12205     {
12206         var obj = this._seriesDictionary,
12207             series;
12208         if(obj && obj.hasOwnProperty(val))
12209         {
12210             series = obj[val];
12211         }
12212         return series;
12213     },
12214
12215     /**
12216      * @protected
12217      * Adds dispatcher to a <code>_dispatcher</code> used to
12218      * to ensure all series have redrawn before for firing event.
12219      *
12220      * @method addDispatcher
12221      * @param {CartesianSeries} val series instance to add
12222      */
12223     addDispatcher: function(val)
12224     {
12225         if(!this._dispatchers)
12226         {
12227             this._dispatchers = [];
12228         }
12229         this._dispatchers.push(val);
12230     },
12231
12232     /**
12233      * @private 
12234      * @description Collection of series to be displayed in the graph.
12235      */
12236     _seriesCollection: null,
12237     
12238     /**
12239      * @private
12240      */
12241     _seriesDictionary: null,
12242
12243     /**
12244      * @private
12245      * Parses series instances to be displayed in the graph.
12246      */
12247     _parseSeriesCollection: function(val)
12248     {
12249         if(!val)
12250         {
12251             return;
12252         }       
12253         var len = val.length,
12254             i = 0,
12255             series,
12256             seriesKey;
12257         if(!this.get("seriesCollection"))
12258         {
12259             this._seriesCollection = [];
12260         }
12261         if(!this._seriesDictionary)
12262         {
12263             this._seriesDictionary = {};
12264         }
12265         if(!this.seriesTypes)
12266         {
12267             this.seriesTypes = [];
12268         }
12269         for(; i < len; ++i)
12270         {       
12271             series = val[i];
12272             if(!(series instanceof Y.CartesianSeries) && !(series instanceof Y.PieSeries))
12273             {
12274                 this._createSeries(series);
12275                 continue;
12276             }
12277             this._addSeries(series);
12278         }
12279         len = this.get("seriesCollection").length;
12280         for(i = 0; i < len; ++i)
12281         {
12282             series = this.get("seriesCollection")[i];
12283             seriesKey = series.get("direction") == "horizontal" ? "yKey" : "xKey";
12284             this._seriesDictionary[series.get(seriesKey)] = series;
12285         }
12286     },
12287
12288     /**
12289      * @private
12290      * Adds a series to the graph.
12291      */
12292     _addSeries: function(series)
12293     {
12294         var type = series.get("type"),
12295             seriesCollection = this.get("seriesCollection"),
12296             graphSeriesLength = seriesCollection.length,
12297             seriesTypes = this.seriesTypes,
12298             typeSeriesCollection;       
12299         if(!series.get("graph")) 
12300         {
12301             series.set("graph", this);
12302         }
12303         seriesCollection.push(series);
12304         if(!seriesTypes.hasOwnProperty(type))
12305         {
12306             this.seriesTypes[type] = [];
12307         }
12308         typeSeriesCollection = this.seriesTypes[type];
12309         series.set("graphOrder", graphSeriesLength);
12310         series.set("order", typeSeriesCollection.length);
12311         typeSeriesCollection.push(series);
12312         this.addDispatcher(series);
12313         series.after("drawingComplete", Y.bind(this._drawingCompleteHandler, this));
12314         this.fire("seriesAdded", series);
12315     },
12316
12317     /**
12318      * @private
12319      */
12320     _createSeries: function(seriesData)
12321     {
12322         var type = seriesData.type,
12323             seriesCollection = this.get("seriesCollection"),
12324             seriesTypes = this.seriesTypes,
12325             typeSeriesCollection,
12326             seriesType,
12327             series;
12328             seriesData.graph = this;
12329         if(!seriesTypes.hasOwnProperty(type))
12330         {
12331             seriesTypes[type] = [];
12332         }
12333         typeSeriesCollection = seriesTypes[type];
12334         seriesData.graph = this;
12335         seriesData.order = typeSeriesCollection.length;
12336         seriesData.graphOrder = seriesCollection.length;
12337         seriesType = this._getSeries(seriesData.type);
12338         series = new seriesType(seriesData);
12339         this.addDispatcher(series);
12340         series.after("drawingComplete", Y.bind(this._drawingCompleteHandler, this));
12341         typeSeriesCollection.push(series);
12342         seriesCollection.push(series);
12343     },
12344
12345     /**
12346      * @private
12347      */
12348     _getSeries: function(type)
12349     {
12350         var seriesClass;
12351         switch(type)
12352         {
12353             case "line" :
12354                 seriesClass = Y.LineSeries;
12355             break;
12356             case "column" :
12357                 seriesClass = Y.ColumnSeries;
12358             break;
12359             case "bar" :
12360                 seriesClass = Y.BarSeries;
12361             break;
12362             case "area" : 
12363                 seriesClass = Y.AreaSeries;
12364             break;
12365             case "candlestick" :
12366                 seriesClass = Y.CandlestickSeries;
12367             break;
12368             case "ohlc" :
12369                 seriesClass = Y.OHLCSeries;
12370             break;
12371             case "stackedarea" :
12372                 seriesClass = Y.StackedAreaSeries;
12373             break;
12374             case "stackedline" :
12375                 seriesClass = Y.StackedLineSeries;
12376             break;
12377             case "stackedcolumn" :
12378                 seriesClass = Y.StackedColumnSeries;
12379             break;
12380             case "stackedbar" :
12381                 seriesClass = Y.StackedBarSeries;
12382             break;
12383             case "markerseries" :
12384                 seriesClass = Y.MarkerSeries;
12385             break;
12386             case "spline" :
12387                 seriesClass = Y.SplineSeries;
12388             break;
12389             case "areaspline" :
12390                 seriesClass = Y.AreaSplineSeries;
12391             break;
12392             case "stackedspline" :
12393                 seriesClass = Y.StackedSplineSeries;
12394             break;
12395             case "stackedareaspline" :
12396                 seriesClass = Y.StackedAreaSplineSeries;
12397             break;
12398             case "stackedmarkerseries" :
12399                 seriesClass = Y.StackedMarkerSeries;
12400             break;
12401             case "pie" :
12402                 seriesClass = Y.PieSeries;
12403             break;
12404             case "combo" :
12405                 seriesClass = Y.ComboSeries;
12406             break;
12407             case "stackedcombo" :
12408                 seriesClass = Y.StackedComboSeries;
12409             break;
12410             case "combospline" :
12411                 seriesClass = Y.ComboSplineSeries;
12412             break;
12413             case "stackedcombospline" :
12414                 seriesClass = Y.StackedComboSplineSeries;
12415             break;
12416             default:
12417                 seriesClass = Y.CartesianSeries;
12418             break;
12419         }
12420         return seriesClass;
12421     },
12422
12423     /**
12424      * @private
12425      */
12426     _markerEventHandler: function(e)
12427     {
12428         var type = e.type,
12429             markerNode = e.currentTarget,
12430             strArr = markerNode.getAttribute("id").split("_"),
12431             series = this.getSeriesByIndex(strArr[1]),
12432             index = strArr[2];
12433         series.updateMarkerState(type, index);
12434     },
12435
12436     /**
12437      * @private
12438      */
12439     _dispatchers: null,
12440
12441     /**
12442      * @private
12443      */
12444     _updateStyles: function()
12445     {
12446         this._background.update(this.get("styles").background);
12447         this._sizeChangeHandler();
12448     },
12449
12450     /**
12451      * @private
12452      */
12453     _sizeChangeHandler: function(e)
12454     {
12455         var hgl = this.get("horizontalGridlines"),
12456             vgl = this.get("verticalGridlines"),
12457             w = this.get("width"),
12458             h = this.get("height"),
12459             graphicNode,
12460             x = 0,
12461             y = 0,
12462             bg = this.get("styles").background,
12463             weight;
12464         if(bg && bg.border)
12465         {
12466             weight = bg.border.weight || 0;
12467         }
12468         if(this._background)
12469         {
12470             graphicNode = Y.one(this._background.parentNode);
12471             if(w && h)
12472             {
12473                 if(weight)
12474                 {
12475                     w += weight * 2;
12476                     h += weight * 2;
12477                     x -= weight;
12478                     y -= weight;
12479                 }
12480                 graphicNode.setStyle("width", w);
12481                 graphicNode.setStyle("height", h);
12482                 graphicNode.setStyle("left", x);
12483                 graphicNode.setStyle("top", y);
12484                 this._background.update({width:w, height:h});
12485             }
12486         }
12487         if(hgl && hgl instanceof Y.Gridlines)
12488         {
12489             hgl.draw();
12490         }
12491         if(vgl && vgl instanceof Y.Gridlines)
12492         {
12493             vgl.draw();
12494         }
12495         this._drawSeries();
12496     },
12497
12498     /**
12499      * @private
12500      */
12501     _drawSeries: function()
12502     {
12503         if(this._drawing)
12504         {
12505             this._callLater = true;
12506             return;
12507         }
12508         this._callLater = false;
12509         this._drawing = true;
12510         var sc = this.get("seriesCollection"),
12511             i = 0,
12512             len = sc.length;
12513         for(; i < len; ++i)
12514         {
12515             sc[i].draw();
12516             if(!sc[i].get("xcoords") || !sc[i].get("ycoords"))
12517             {
12518                 this._callLater = true;
12519                 break;
12520             }
12521         }
12522         this._drawing = false;
12523         if(this._callLater)
12524         {
12525             this._drawSeries();
12526         }
12527     },
12528
12529     /**
12530      * @private
12531      */
12532     _drawingCompleteHandler: function(e)
12533     {
12534         var series = e.currentTarget,
12535             index = Y.Array.indexOf(this._dispatchers, series);
12536         if(index > -1)
12537         {
12538             this._dispatchers.splice(index, 1);
12539         }
12540         if(this._dispatchers.length < 1)
12541         {
12542             this.fire("chartRendered");
12543         }
12544     },
12545
12546     /**
12547      * @protected
12548      *
12549      * Gets the default value for the <code>styles</code> attribute. Overrides
12550      * base implementation.
12551      *
12552      * @method _getDefaultStyles
12553      * @return Object
12554      */
12555     _getDefaultStyles: function()
12556     {
12557         var defs = {
12558             background: {
12559                 shape: "rect",
12560                 fill:{
12561                     color:"#faf9f2"
12562                 },
12563                 border: {
12564                     color:"#dad8c9",
12565                     weight: 1
12566                 }
12567             }
12568         };
12569         return defs;
12570     }
12571 }, {
12572     ATTRS: {
12573         /**
12574          * Collection of series. When setting the <code>seriesCollection</code> the array can contain a combination of either
12575          * <code>CartesianSeries</code> instances or object literals with properties that will define a series.
12576          *
12577          * @attribute seriesCollection
12578          * @type CartesianSeries
12579          */
12580         seriesCollection: {
12581             getter: function()
12582             {
12583                 return this._seriesCollection;
12584             },
12585
12586             setter: function(val)
12587             {
12588                 this._parseSeriesCollection(val);
12589                 return this._seriesCollection;
12590             }
12591         },
12592        
12593         /**
12594          * Indicates whether the <code>Graph</code> has a background.
12595          *
12596          * @attribute showBackground
12597          * @type Boolean
12598          * @default true
12599          */
12600         showBackground: {
12601             value: true
12602         },
12603
12604         /**
12605          * Read-only hash lookup for all series on in the <code>Graph</code>.
12606          *
12607          * @attribute seriesDictionary
12608          * @type Object
12609          */
12610         seriesDictionary: {
12611             readOnly: true,
12612
12613             getter: function()
12614             {
12615                 return this._seriesDictionary;
12616             }
12617         },
12618
12619         /**
12620          * Reference to the horizontal <code>Gridlines</code> instance.
12621          *
12622          * @attribute horizontalGridlines
12623          * @type Gridlines
12624          * @default null
12625          */
12626         horizontalGridlines: {
12627             value: null,
12628
12629             setter: function(val)
12630             {
12631                 var gl = this.get("horizontalGridlines");
12632                 if(gl && gl instanceof Y.Gridlines)
12633                 {
12634                     gl.remove();
12635                 }
12636                 if(val instanceof Y.Gridlines)
12637                 {
12638                     gl = val;
12639                     val.set("graph", this);
12640                     val.render();
12641                     return val;
12642                 }
12643                 else if(val && val.axis)
12644                 {
12645                     gl = new Y.Gridlines({direction:"horizontal", axis:val.axis, graph:this, styles:val.styles});
12646                     gl.render();
12647                     return gl;
12648                 }
12649             }
12650         },
12651         
12652         /**
12653          * Reference to the vertical <code>Gridlines</code> instance.
12654          *
12655          * @attribute verticalGridlines
12656          * @type Gridlines
12657          * @default null
12658          */
12659         verticalGridlines: {
12660             value: null,
12661
12662             setter: function(val)
12663             {
12664                 var gl = this.get("verticalGridlines");
12665                 if(gl && gl instanceof Y.Gridlines)
12666                 {
12667                     gl.remove();
12668                 }
12669                 if(val instanceof Y.Gridlines)
12670                 {
12671                     gl = val;
12672                     val.set("graph", this);
12673                     val.render();
12674                     return val;
12675                 }
12676                 else if(val && val.axis)
12677                 {
12678                     gl = new Y.Gridlines({direction:"vertical", axis:val.axis, graph:this, styles:val.styles});
12679                     gl.render();
12680                     return gl;
12681                 }
12682             }
12683         }
12684
12685         /**
12686          * Style properties used for drawing a background. Below are the default values:
12687          *  <dl>
12688          *      <dt>fill</dt><dd>A hash containing the following values:
12689          *          <dl>
12690          *              <dt>color</dt><dd>Color of the fill. The default value is #faf9f2.</dd>
12691          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the background fill. The default value is 1.</dd>
12692          *          </dl>
12693          *      </dd>
12694          *      <dt>border</dt><dd>A hash containing the following values:
12695          *          <dl>
12696          *              <dt>color</dt><dd>Color of the border. The default value is #dad8c9.</dd>
12697          *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the background border. The default value is 1.</dd>
12698          *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
12699          *          </dl>
12700          *      </dd>
12701          *  </dl>
12702          *
12703          * @attribute styles
12704          * @type Object
12705          */
12706     }
12707 });
12708 /**
12709  * The ChartBase class is an abstract class used to create charts.
12710  *
12711  * @class ChartBase
12712  * @constructor
12713  */
12714 function ChartBase() {}
12715
12716 ChartBase.ATTRS = {
12717     /**
12718      * Reference to the default tooltip available for the chart.
12719      * <p>Contains the following properties:</p>
12720      *  <dl>
12721      *      <dt>node</dt><dd>Reference to the actual dom node</dd>
12722      *      <dt>showEvent</dt><dd>Event that should trigger the tooltip</dd>
12723      *      <dt>hideEvent</dt><dd>Event that should trigger the removal of a tooltip (can be an event or an array of events)</dd>
12724      *      <dt>styles</dt><dd>A hash of style properties that will be applied to the tooltip node</dd>
12725      *      <dt>show</dt><dd>Indicates whether or not to show the tooltip</dd>
12726      *      <dt>markerEventHandler</dt><dd>Displays and hides tooltip based on marker events</dd>
12727      *      <dt>planarEventHandler</dt><dd>Displays and hides tooltip based on planar events</dd>
12728      *      <dt>markerLabelFunction</dt><dd>Reference to the function used to format a marker event triggered tooltip's text</dd>
12729      *      <dt>planarLabelFunction</dt><dd>Reference to the function used to format a planar event triggered tooltip's text</dd>
12730      *  </dl>
12731      * @attribute tooltip
12732      * @type Object
12733      */
12734     tooltip: {
12735         valueFn: "_getTooltip",
12736
12737         setter: function(val)
12738         {
12739             return this._updateTooltip(val);
12740         }
12741     },
12742
12743     /** 
12744      * The key value used for the chart's category axis. 
12745      *
12746      * @attribute categoryKey
12747      * @type String
12748      * @default category
12749      */
12750     categoryKey: {
12751         value: "category"
12752     },
12753         
12754     /**
12755      * Indicates the type of axis to use for the category axis.
12756      *
12757      *  <dl>
12758      *      <dt>category</dt><dd>Specifies a <code>CategoryAxis</code>.</dd>
12759      *      <dt>time</dt><dd>Specifies a <code>TimeAxis</dd>
12760      *  </dl>
12761      *
12762      * @attribute categoryType
12763      * @type String
12764      * @default category
12765      */
12766     categoryType:{
12767         value:"category"
12768     },
12769
12770     /**
12771      * Indicates the the type of interactions that will fire events.
12772      *
12773      *  <dl>
12774      *      <dt>marker</dt><dd>Events will be broadcasted when the mouse interacts with individual markers.</dd>
12775      *      <dt>planar</dt><dd>Events will be broadcasted when the mouse intersects the plane of any markers on the chart.</dd>
12776      *      <dt>none</dt><dd>No events will be broadcasted.</dd>
12777      *  </dl>
12778      *
12779      * @attribute interactionType
12780      * @type String
12781      * @default marker
12782      */
12783     interactionType: {
12784         value: "marker"
12785     },
12786
12787     /**
12788      * Data used to generate the chart.
12789      * 
12790      * @attribute dataProvider
12791      * @type Array
12792      */
12793     dataProvider: {
12794         setter: function(val)
12795         {
12796             return this._setDataValues(val);
12797         }
12798     },
12799         
12800     /**
12801      * A collection of keys that map to the series axes. If no keys are set,
12802      * they will be generated automatically depending on the data structure passed into 
12803      * the chart.
12804      *
12805      * @attribute seriesKeys
12806      * @type Array
12807      */
12808     seriesKeys: {},
12809
12810     /**
12811      * Reference to all the axes in the chart.
12812      *
12813      * @attribute axesCollection
12814      * @type Array
12815      */
12816     axesCollection: {},
12817
12818     /**
12819      * Reference to graph instance.
12820      * 
12821      * @attribute graph
12822      * @type Graph 
12823      */
12824     graph: {
12825         valueFn: "_getGraph"
12826    }
12827 };
12828
12829 ChartBase.prototype = {
12830     /**
12831      * @private
12832      * @description Default value function for the <code>graph</code> attribute.
12833      */
12834     _getGraph: function()
12835     {
12836         var graph = new Y.Graph();
12837         graph.after("chartRendered", Y.bind(function(e) {
12838             this.fire("chartRendered");
12839         }, this));
12840         return graph; 
12841     },
12842
12843     /**
12844      * Returns a series instance by index or key value.
12845      *
12846      * @method getSeries
12847      * @param val
12848      * @return CartesianSeries
12849      */
12850     getSeries: function(val)
12851     {
12852         var series = null, 
12853             graph = this.get("graph");
12854         if(graph)
12855         {
12856             if(Y.Lang.isNumber(val))
12857             {
12858                 series = graph.getSeriesByIndex(val);
12859             }
12860             else
12861             {
12862                 series = graph.getSeriesByKey(val);
12863             }
12864         }
12865         return series;
12866     },
12867
12868     /**
12869      * Returns an <code>Axis</code> instance by key reference. If the axis was explicitly set through the <code>axes</code> attribute,
12870      * the key will be the same as the key used in the <code>axes</code> object. For default axes, the key for
12871      * the category axis is the value of the <code>categoryKey</code> (<code>category</code>). For the value axis, the default 
12872      * key is <code>values</code>.
12873      *
12874      * @method getAxisByKey
12875      * @param {String} val Key reference used to look up the axis.
12876      * @return Axis
12877      */
12878     getAxisByKey: function(val)
12879     {
12880         var axis,
12881             axes = this.get("axes");
12882         if(axes.hasOwnProperty(val))
12883         {
12884             axis = axes[val];
12885         }
12886         return axis;
12887     },
12888
12889     /**
12890      * Returns the category axis for the chart.
12891      *
12892      * @method getCategoryAxis
12893      * @return Axis
12894      */
12895     getCategoryAxis: function()
12896     {
12897         var axis,
12898             key = this.get("categoryKey"),
12899             axes = this.get("axes");
12900         if(axes.hasOwnProperty(key))
12901         {
12902             axis = axes[key];
12903         }
12904         return axis;
12905     },
12906
12907     /**
12908      * @private
12909      */
12910     _direction: "horizontal",
12911     
12912     /**
12913      * @private
12914      */
12915     _dataProvider: null,
12916
12917     /**
12918      * @private
12919      */
12920     _setDataValues: function(val)
12921     {
12922         if(Y.Lang.isArray(val[0]))
12923         {
12924             var hash, 
12925                 dp = [], 
12926                 cats = val[0], 
12927                 i = 0, 
12928                 l = cats.length, 
12929                 n, 
12930                 sl = val.length;
12931             for(; i < l; ++i)
12932             {
12933                 hash = {category:cats[i]};
12934                 for(n = 1; n < sl; ++n)
12935                 {
12936                     hash["series" + n] = val[n][i];
12937                 }
12938                 dp[i] = hash; 
12939             }
12940             return dp;
12941         }
12942         return val;
12943     },
12944
12945     /**
12946      * @private 
12947      */
12948     _seriesCollection: null,
12949
12950     /**
12951      * @private
12952      */
12953     _setSeriesCollection: function(val)
12954     {
12955         this._seriesCollection = val;
12956     },
12957     /**
12958      * @private
12959      */
12960     _getAxisClass: function(t)
12961     {
12962         return this._axisClass[t];
12963     },
12964   
12965     /**
12966      * @private
12967      */
12968     _axisClass: {
12969         stacked: Y.StackedAxis,
12970         numeric: Y.NumericAxis,
12971         category: Y.CategoryAxis,
12972         time: Y.TimeAxis
12973     },
12974
12975     /**
12976      * @private
12977      */
12978     _axes: null,
12979
12980     /**
12981      * @private
12982      */
12983     renderUI: function()
12984     {
12985         var tt = this.get("tooltip");
12986         //move the position = absolute logic to a class file
12987         this.get("boundingBox").setStyle("position", "absolute");
12988         this.get("contentBox").setStyle("position", "absolute");
12989         this._addAxes();
12990         this._addSeries();
12991         if(tt && tt.show)
12992         {
12993             this._addTooltip();
12994         }
12995         this._redraw();
12996     },
12997     
12998     /**
12999      * @private
13000      */
13001     bindUI: function()
13002     {
13003         this.after("tooltipChange", Y.bind(this._tooltipChangeHandler, this));
13004         this.after("widthChange", this._sizeChanged);
13005         this.after("heightChange", this._sizeChanged);
13006         this.after("dataProviderChange", this._dataProviderChangeHandler);
13007         var tt = this.get("tooltip"),
13008             hideEvent = "mouseout",
13009             showEvent = "mouseover",
13010             cb = this.get("contentBox"),
13011             interactionType = this.get("interactionType"),
13012             i = 0,
13013             len;
13014         if(interactionType == "marker")
13015         {
13016             hideEvent = tt.hideEvent;
13017             showEvent = tt.showEvent;
13018             Y.delegate("mouseenter", Y.bind(this._markerEventDispatcher, this), cb, ".yui3-seriesmarker");
13019             Y.delegate("mousedown", Y.bind(this._markerEventDispatcher, this), cb, ".yui3-seriesmarker");
13020             Y.delegate("mouseup", Y.bind(this._markerEventDispatcher, this), cb, ".yui3-seriesmarker");
13021             Y.delegate("mouseleave", Y.bind(this._markerEventDispatcher, this), cb, ".yui3-seriesmarker");
13022             Y.delegate("click", Y.bind(this._markerEventDispatcher, this), cb, ".yui3-seriesmarker");
13023             Y.delegate("mousemove", Y.bind(this._positionTooltip, this), cb, ".yui3-seriesmarker");
13024         }
13025         else if(interactionType == "planar")
13026         {
13027             this._overlay.on("mousemove", Y.bind(this._planarEventDispatcher, this));
13028             this.on("mouseout", this.hideTooltip);
13029         }
13030         if(tt)
13031         {
13032             if(hideEvent && showEvent && hideEvent == showEvent)
13033             {
13034                 this.on(interactionType + "Event:" + hideEvent, this.toggleTooltip);
13035             }
13036             else
13037             {
13038                 if(showEvent)
13039                 {
13040                     this.on(interactionType + "Event:" + showEvent, tt[interactionType + "EventHandler"]);
13041                 }
13042                 if(hideEvent)
13043                 {
13044                     if(Y.Lang.isArray(hideEvent))
13045                     {
13046                         len = hideEvent.length;
13047                         for(; i < len; ++i)
13048                         {
13049                             this.on(interactionType + "Event:" + hideEvent[i], this.hideTooltip);
13050                         }
13051                     }
13052                     this.on(interactionType + "Event:" + hideEvent, this.hideTooltip);
13053                 }
13054             }
13055         }
13056     },
13057     
13058     /**
13059      * @private
13060      */
13061     _markerEventDispatcher: function(e)
13062     {
13063         var type = e.type,
13064             cb = this.get("contentBox"),
13065             markerNode = e.currentTarget,
13066             strArr = markerNode.getAttribute("id").split("_"),
13067             seriesIndex = strArr[1],
13068             series = this.getSeries(parseInt(seriesIndex, 10)),
13069             index = strArr[2],
13070             items = this.getSeriesItems(series, index),
13071             x = e.pageX - cb.getX(),
13072             y = e.pageY - cb.getY();
13073         if(type == "mouseenter")
13074         {
13075             type = "mouseover";
13076         }
13077         else if(type == "mouseleave")
13078         {
13079             type = "mouseout";
13080         }
13081         series.updateMarkerState(type, index);
13082         e.halt();
13083         /**
13084          * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mouseover event.
13085          * 
13086          *
13087          * @event markerEvent:mouseover
13088          * @preventable false
13089          * @param {EventFacade} e Event facade with the following additional
13090          *   properties:
13091          *  <dl>
13092          *      <dt>categoryItem</dt><dd>Hash containing information about the category <code>Axis</code>.</dd>
13093          *      <dt>valueItem</dt><dd>Hash containing information about the value <code>Axis</code>.</dd>
13094          *      <dt>node</dt><dd>The dom node of the marker.</dd>
13095          *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
13096          *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
13097          *      <dt>series</dt><dd>Reference to the series of the marker.</dd>
13098          *      <dt>index</dt><dd>Index of the marker in the series.</dd>
13099          *      <dt>seriesIndex</dt><dd>The <code>order</code> of the marker's series.</dd>
13100          *  </dl>
13101          */
13102         /**
13103          * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mouseout event.
13104          *
13105          * @event markerEvent:mouseout
13106          * @preventable false
13107          * @param {EventFacade} e Event facade with the following additional
13108          *   properties:
13109          *  <dl>
13110          *      <dt>categoryItem</dt><dd>Hash containing information about the category <code>Axis</code>.</dd>
13111          *      <dt>valueItem</dt><dd>Hash containing information about the value <code>Axis</code>.</dd>
13112          *      <dt>node</dt><dd>The dom node of the marker.</dd>
13113          *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
13114          *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
13115          *      <dt>series</dt><dd>Reference to the series of the marker.</dd>
13116          *      <dt>index</dt><dd>Index of the marker in the series.</dd>
13117          *      <dt>seriesIndex</dt><dd>The <code>order</code> of the marker's series.</dd>
13118          *  </dl>
13119          */
13120         /**
13121          * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mousedown event.
13122          *
13123          * @event markerEvent:mousedown
13124          * @preventable false
13125          * @param {EventFacade} e Event facade with the following additional
13126          *   properties:
13127          *  <dl>
13128          *      <dt>categoryItem</dt><dd>Hash containing information about the category <code>Axis</code>.</dd>
13129          *      <dt>valueItem</dt><dd>Hash containing information about the value <code>Axis</code>.</dd>
13130          *      <dt>node</dt><dd>The dom node of the marker.</dd>
13131          *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
13132          *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
13133          *      <dt>series</dt><dd>Reference to the series of the marker.</dd>
13134          *      <dt>index</dt><dd>Index of the marker in the series.</dd>
13135          *      <dt>seriesIndex</dt><dd>The <code>order</code> of the marker's series.</dd>
13136          *  </dl>
13137          */
13138         /**
13139          * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mouseup event.
13140          *
13141          * @event markerEvent:mouseup
13142          * @preventable false
13143          * @param {EventFacade} e Event facade with the following additional
13144          *   properties:
13145          *  <dl>
13146          *      <dt>categoryItem</dt><dd>Hash containing information about the category <code>Axis</code>.</dd>
13147          *      <dt>valueItem</dt><dd>Hash containing information about the value <code>Axis</code>.</dd>
13148          *      <dt>node</dt><dd>The dom node of the marker.</dd>
13149          *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
13150          *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
13151          *      <dt>series</dt><dd>Reference to the series of the marker.</dd>
13152          *      <dt>index</dt><dd>Index of the marker in the series.</dd>
13153          *      <dt>seriesIndex</dt><dd>The <code>order</code> of the marker's series.</dd>
13154          *  </dl>
13155          */
13156         /**
13157          * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a click event.
13158          *
13159          * @event markerEvent:click
13160          * @preventable false
13161          * @param {EventFacade} e Event facade with the following additional
13162          *   properties:
13163          *  <dl>
13164          *      <dt>categoryItem</dt><dd>Hash containing information about the category <code>Axis</code>.</dd>
13165          *      <dt>valueItem</dt><dd>Hash containing information about the value <code>Axis</code>.</dd>
13166          *      <dt>node</dt><dd>The dom node of the marker.</dd>
13167          *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
13168          *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
13169          *      <dt>series</dt><dd>Reference to the series of the marker.</dd>
13170          *      <dt>index</dt><dd>Index of the marker in the series.</dd>
13171          *      <dt>seriesIndex</dt><dd>The <code>order</code> of the marker's series.</dd>
13172          *  </dl>
13173          */
13174         this.fire("markerEvent:" + type, {categoryItem:items.category, valueItem:items.value, node:markerNode, x:x, y:y, series:series, index:index, seriesIndex:seriesIndex});
13175     },
13176
13177     /**
13178      * @private
13179      */
13180     _dataProviderChangeHandler: function(e)
13181     {
13182         var dataProvider = this.get("dataProvider"),
13183             axes = this.get("axes"),
13184             i,
13185             axis;
13186         for(i in axes)
13187         {
13188             if(axes.hasOwnProperty(i))
13189             {
13190                 axis = axes[i];
13191                 if(axis instanceof Y.Axis)
13192                 {
13193                     axis.set("dataProvider", dataProvider);
13194                 }
13195             }
13196         }
13197     },
13198     
13199     /**
13200      * Event listener for toggling the tooltip. If a tooltip is visible, hide it. If not, it 
13201      * will create and show a tooltip based on the event object.
13202      * 
13203      * @method toggleTooltip
13204      */
13205     toggleTooltip: function(e)
13206     {
13207         var tt = this.get("tooltip");
13208         if(tt.visible)
13209         {
13210             this.hideTooltip();
13211         }
13212         else
13213         {
13214             tt.markerEventHandler.apply(this, [e]);
13215         }
13216     },
13217
13218     /**
13219      * @private
13220      */
13221     _showTooltip: function(msg, x, y)
13222     {
13223         var tt = this.get("tooltip"),
13224             node = tt.node;
13225         if(msg)
13226         {
13227             tt.visible = true;
13228             node.set("innerHTML", msg);
13229             node.setStyle("top", y + "px");
13230             node.setStyle("left", x + "px");
13231             node.removeClass("yui3-widget-hidden");
13232         }
13233     },
13234
13235     /**
13236      * @private
13237      */
13238     _positionTooltip: function(e)
13239     {
13240         var tt = this.get("tooltip"),
13241             node = tt.node,
13242             cb = this.get("contentBox"),
13243             x = (e.pageX + 10) - cb.getX(),
13244             y = (e.pageY + 10) - cb.getY();
13245         if(node)
13246         {
13247             node.setStyle("left", x + "px");
13248             node.setStyle("top", y + "px");
13249         }
13250     },
13251
13252     /**
13253      * Hides the default tooltip
13254      */
13255     hideTooltip: function()
13256     {
13257         var tt = this.get("tooltip"),
13258             node = tt.node;
13259         tt.visible = false;
13260         node.set("innerHTML", "");
13261         node.setStyle("left", -10000);
13262         node.setStyle("top", -10000);
13263         node.addClass("yui3-widget-hidden");
13264     },
13265
13266     /**
13267      * @private
13268      */
13269     _addTooltip: function()
13270     {
13271         var tt = this.get("tooltip");
13272         this.get("contentBox").appendChild(tt.node);
13273     },
13274
13275     /**
13276      * @private
13277      */
13278     _updateTooltip: function(val)
13279     {
13280         var tt = this._tooltip,
13281             i,
13282             styles = val.styles,
13283             props = {
13284                 markerLabelFunction:"markerLabelFunction",
13285                 planarLabelFunction:"planarLabelFunction",
13286                 showEvent:"showEvent",
13287                 hideEvent:"hideEvent",
13288                 markerEventHandler:"markerEventHandler",
13289                 planarEventHandler:"planarEventHandler"
13290             };
13291         if(styles)
13292         {
13293             for(i in styles)
13294             {
13295                 if(styles.hasOwnProperty(i))
13296                 {
13297                     tt.node.setStyle(i, styles[i]);
13298                 }
13299             }
13300         }
13301         for(i in props)
13302         {
13303             if(val.hasOwnProperty(i))
13304             {
13305                 tt[i] = val[i];
13306             }
13307         }
13308         return tt;
13309     },
13310
13311     /**
13312      * @private
13313      */
13314     _getTooltip: function()
13315     {
13316         var node = document.createElement("div"),
13317             tt = {
13318                 markerLabelFunction: this._tooltipLabelFunction,
13319                 planarLabelFunction: this._planarLabelFunction,
13320                 show: true,
13321                 hideEvent: "mouseout",
13322                 showEvent: "mouseover",
13323                 markerEventHandler: function(e)
13324                 {
13325                     var tt = this.get("tooltip"),
13326                     msg = tt.markerLabelFunction.apply(this, [e.categoryItem, e.valueItem, e.index, e.series, e.seriesIndex]);
13327                     this._showTooltip(msg, e.x + 10, e.y + 10);
13328                 },
13329                 planarEventHandler: function(e)
13330                 {
13331                     var tt = this.get("tooltip"),
13332                         msg ,
13333                         categoryAxis = this.get("categoryAxis");
13334                     msg = tt.planarLabelFunction.apply(this, [categoryAxis, e.valueItem, e.index, e.items, e.seriesIndex]);
13335                     this._showTooltip(msg, e.x + 10, e.y + 10);
13336                 }
13337             };
13338         node.setAttribute("id", this.get("id") + "_tooltip");
13339         node = Y.one(node);
13340         node.setStyle("fontSize", "85%");
13341         node.setStyle("opacity", "0.83");
13342         node.setStyle("position", "absolute");
13343         node.setStyle("paddingTop", "2px");
13344         node.setStyle("paddingRight", "5px");
13345         node.setStyle("paddingBottom", "4px");
13346         node.setStyle("paddingLeft", "2px");
13347         node.setStyle("backgroundColor", "#fff");
13348         node.setStyle("border", "1px solid #dbdccc");
13349         node.setStyle("pointerEvents", "none");
13350         node.setStyle("zIndex", 3);
13351         node.setStyle("whiteSpace", "noWrap");
13352         node.addClass("yui3-widget-hidden");
13353         tt.node = Y.one(node);
13354         this._tooltip = tt;
13355         return tt;
13356     },
13357
13358     /**
13359      * @private
13360      */
13361     _planarLabelFunction: function(categoryAxis, valueItems, index, seriesArray, seriesIndex)
13362     {
13363         var msg = "",
13364             valueItem,
13365             i = 0,
13366             len = seriesArray.length,
13367             axis,
13368             series;
13369         if(categoryAxis)
13370         {
13371             msg += categoryAxis.get("labelFunction").apply(this, [categoryAxis.getKeyValueAt(this.get("categoryKey"), index), categoryAxis.get("labelFormat")]);
13372         }
13373
13374         for(; i < len; ++i)
13375         {
13376             series = seriesArray[i];
13377             if(series.get("visible"))
13378             {
13379                 valueItem = valueItems[i];
13380                 axis = valueItem.axis;
13381                 msg += "<br/><span>" + valueItem.displayName + ": " + axis.get("labelFunction").apply(this, [axis.getKeyValueAt(valueItem.key, index), axis.get("labelFormat")]) + "</span>";
13382             }
13383         }
13384         return msg;
13385     },
13386
13387     /**
13388      * @private
13389      */
13390     _tooltipLabelFunction: function(categoryItem, valueItem, itemIndex, series, seriesIndex)
13391     {
13392         var msg = categoryItem.displayName +
13393         ":&nbsp;" + categoryItem.axis.get("labelFunction").apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) + 
13394         "<br/>" + valueItem.displayName + 
13395         ":&nbsp;" + valueItem.axis.get("labelFunction").apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]);
13396         return msg; 
13397     },
13398
13399     /**
13400      * @private
13401      */
13402     _tooltipChangeHandler: function(e)
13403     {
13404         if(this.get("tooltip"))
13405         {
13406             var tt = this.get("tooltip"),
13407                 node = tt.node,
13408                 show = tt.show,
13409                 cb = this.get("contentBox");
13410             if(node && show)
13411             {
13412                 if(!cb.containes(node))
13413                 {
13414                     this._addTooltip();
13415                 }
13416             }
13417         }
13418     }
13419 };
13420 Y.ChartBase = ChartBase;
13421 /**
13422  * The CartesianChart class creates a chart with horizontal and vertical axes.
13423  *
13424  * @class CartesianChart
13425  * @extends ChartBase
13426  * @constructor
13427  */
13428 Y.CartesianChart = Y.Base.create("cartesianChart", Y.Widget, [Y.ChartBase], {
13429     /**
13430      * @private
13431      */
13432     renderUI: function()
13433     {
13434         var tt = this.get("tooltip"),
13435             overlay;
13436         //move the position = absolute logic to a class file
13437         this.get("boundingBox").setStyle("position", "absolute");
13438         this.get("contentBox").setStyle("position", "absolute");
13439         this._addAxes();
13440         this._addGridlines();
13441         this._addSeries();
13442         if(tt && tt.show)
13443         {
13444             this._addTooltip();
13445         }
13446         //If there is a style definition. Force them to set.
13447         this.get("styles");
13448         if(this.get("interactionType") == "planar")
13449         {
13450             overlay = document.createElement("div");
13451             this.get("contentBox").appendChild(overlay);
13452             this._overlay = Y.one(overlay); 
13453             this._overlay.setStyle("position", "absolute");
13454             this._overlay.setStyle("background", "#fff");
13455             this._overlay.setStyle("opacity", 0);
13456             this._overlay.addClass("yui3-overlay");
13457             this._overlay.setStyle("zIndex", 4);
13458         }
13459         this._redraw();
13460     },
13461
13462     /**
13463      * @private
13464      */
13465     _planarEventDispatcher: function(e)
13466     {
13467         var graph = this.get("graph"),
13468             bb = this.get("boundingBox"),
13469             cb = graph.get("contentBox"),
13470             x = e.pageX,
13471             offsetX = x - cb.getX(),
13472             posX = x - bb.getX(),
13473             y = e.pageY,
13474             offsetY = y - cb.getY(),
13475             posY = y - bb.getY(),
13476             sc = graph.get("seriesCollection"),
13477             series,
13478             i = 0,
13479             index,
13480             oldIndex = this._selectedIndex,
13481             item,
13482             items = [],
13483             categoryItems = [],
13484             valueItems = [],
13485             direction = this.get("direction"),
13486             hasMarkers,
13487             coord = direction == "horizontal" ? offsetX : offsetY,
13488             //data columns and area data could be created on a graph level
13489             markerPlane = direction == "horizontal" ? sc[0].get("xMarkerPlane") : sc[0].get("yMarkerPlane"),
13490             len = markerPlane.length;
13491        for(; i < len; ++i)
13492        {
13493             if(coord <= markerPlane[i].end && coord >= markerPlane[i].start)
13494             {
13495                 index = i;
13496                 break;
13497             }
13498        }
13499        len = sc.length;
13500        for(i = 0; i < len; ++i)
13501        {
13502             series = sc[i];
13503             hasMarkers = series.get("markers");
13504             if(hasMarkers && !isNaN(oldIndex) && oldIndex > -1)
13505             {
13506                 series.updateMarkerState("mouseout", oldIndex);
13507             }
13508             if(series.get("ycoords")[index] > -1)
13509             {
13510                 if(hasMarkers && !isNaN(index) && index > -1)
13511                 {
13512                     series.updateMarkerState("mouseover", index);
13513                 }
13514                 item = this.getSeriesItems(series, index);
13515                 categoryItems.push(item.category);
13516                 valueItems.push(item.value);
13517                 items.push(series);
13518             }
13519                 
13520         }
13521         this._selectedIndex = index;
13522         
13523         /**
13524          * Broadcasts when <code>interactionType</code> is set to <code>planar</code> and a series' marker plane has received a mouseover event.
13525          * 
13526          *
13527          * @event planarEvent:mouseover
13528          * @preventable false
13529          * @param {EventFacade} e Event facade with the following additional
13530          *   properties:
13531          *  <dl>
13532          *      <dt>categoryItem</dt><dd>An array of hashes, each containing information about the category <code>Axis</code> of each marker whose plane has been intersected.</dd>
13533          *      <dt>valueItem</dt><dd>An array of hashes, each containing information about the value <code>Axis</code> of each marker whose plane has been intersected.</dd>
13534          *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
13535          *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
13536          *      <dt>items</dt><dd>An array including all the series which contain a marker whose plane has been intersected.</dd>
13537          *      <dt>index</dt><dd>Index of the markers in their respective series.</dd>
13538          *  </dl>
13539          */
13540         /**
13541          * Broadcasts when <code>interactionType</code> is set to <code>planar</code> and a series' marker plane has received a mouseout event.
13542          *
13543          * @event planarEvent:mouseout
13544          * @preventable false
13545          * @param {EventFacade} e 
13546          */
13547         if(index > -1)
13548         {
13549             this.fire("planarEvent:mouseover", {categoryItem:categoryItems, valueItem:valueItems, x:posX, y:posY, items:items, index:index});
13550         }
13551         else
13552         {
13553             this.fire("planarEvent:mouseout");
13554         }
13555     },
13556
13557     /**
13558      * @private
13559      */
13560     _type: "combo",
13561
13562     /**
13563      * @private
13564      */
13565     _axesRenderQueue: null,
13566
13567     /**
13568      * @private 
13569      */
13570     _addToAxesRenderQueue: function(axis)
13571     {
13572         if(!this._axesRenderQueue)
13573         {
13574             this._axesRenderQueue = [];
13575         }
13576         if(Y.Array.indexOf(this._axesRenderQueue, axis) < 0)
13577         {
13578             this._axesRenderQueue.push(axis);
13579         }
13580     },
13581
13582     /**
13583      * @private
13584      */
13585     _getDefaultSeriesCollection: function(val)
13586     {
13587         var dir = this.get("direction"), 
13588             sc = val || [], 
13589             catAxis,
13590             valAxis,
13591             tempKeys = [],
13592             series,
13593             seriesKeys = this.get("seriesKeys").concat(),
13594             i,
13595             index,
13596             l,
13597             type = this.get("type"),
13598             key,
13599             catKey,
13600             seriesKey,
13601             graph,
13602             categoryKey = this.get("categoryKey"),
13603             showMarkers = this.get("showMarkers"),
13604             showAreaFill = this.get("showAreaFill"),
13605             showLines = this.get("showLines");
13606         if(dir == "vertical")
13607         {
13608             catAxis = "yAxis";
13609             catKey = "yKey";
13610             valAxis = "xAxis";
13611             seriesKey = "xKey";
13612         }
13613         else
13614         {
13615             catAxis = "xAxis";
13616             catKey = "xKey";
13617             valAxis = "yAxis";
13618             seriesKey = "yKey";
13619         }
13620         l = sc.length;
13621         for(i = 0; i < l; ++i)
13622         {
13623             key = this._getBaseAttribute(sc[i], seriesKey);
13624             if(key)
13625             {
13626                 index = Y.Array.indexOf(seriesKeys, key);
13627                 if(index > -1)
13628                 {
13629                     seriesKeys.splice(index, 1);
13630                 }
13631                tempKeys.push(key);
13632             }
13633         }
13634         if(seriesKeys.length > 0)
13635         {
13636             tempKeys = tempKeys.concat(seriesKeys);
13637         }
13638         l = tempKeys.length;
13639         for(i = 0; i < l; ++i)
13640         {
13641             series = sc[i] || {type:type};
13642             if(series instanceof Y.CartesianSeries)
13643             {
13644                 this._parseSeriesAxes(series);
13645                 continue;
13646             }
13647             
13648             series[catKey] = series[catKey] || categoryKey;
13649             series[seriesKey] = series[seriesKey] || seriesKeys.shift();
13650             series[catAxis] = this._getCategoryAxis();
13651             series[valAxis] = this._getSeriesAxis(series[seriesKey]);
13652             
13653             series.type = series.type || type;
13654             
13655             if((series.type == "combo" || series.type == "stackedcombo" || series.type == "combospline" || series.type == "stackedcombospline"))
13656             {
13657                 if(showAreaFill !== null)
13658                 {
13659                     series.showAreaFill = series.showAreaFill || showAreaFill;
13660                 }
13661                 if(showMarkers !== null)
13662                 {
13663                     series.showMarkers = series.showMarkers || showMarkers;
13664                 }
13665                 if(showLines !== null)
13666                 {
13667                     series.showLines = series.showLines || showLines;
13668                 }
13669             }
13670             sc[i] = series;
13671         }
13672         if(val)
13673         {
13674             graph = this.get("graph");
13675             graph.set("seriesCollection", sc);
13676             sc = graph.get("seriesCollection");
13677         }
13678         return sc;
13679     },
13680
13681     /**
13682      * @private
13683      */
13684     _parseSeriesAxes: function(series)
13685     {
13686         var axes = this.get("axes"),
13687             xAxis = series.get("xAxis"),
13688             yAxis = series.get("yAxis"),
13689             YAxis = Y.Axis,
13690             axis;
13691         if(xAxis && !(xAxis instanceof YAxis) && Y.Lang.isString(xAxis) && axes.hasOwnProperty(xAxis))
13692         {
13693             axis = axes[xAxis];
13694             if(axis instanceof YAxis)
13695             {
13696                 series.set("xAxis", axis);
13697             }
13698         }
13699         if(yAxis && !(yAxis instanceof YAxis) && Y.Lang.isString(yAxis) && axes.hasOwnProperty(yAxis))
13700         {   
13701             axis = axes[yAxis];
13702             if(axis instanceof YAxis)
13703             {
13704                 series.set("yAxis", axis);
13705             }
13706         }
13707
13708     },
13709
13710     /**
13711      * @private
13712      */
13713     _getCategoryAxis: function()
13714     {
13715         var axis,
13716             axes = this.get("axes"),
13717             categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey");
13718         axis = axes[categoryAxisName];
13719         return axis;
13720     },
13721
13722     /**
13723      * @private
13724      */
13725     _getSeriesAxis:function(key, axisName)
13726     {
13727         var axes = this.get("axes"),
13728             i,
13729             keys,
13730             axis;
13731         if(axes)
13732         {
13733             if(axisName && axes.hasOwnProperty(axisName))
13734             {
13735                 axis = axes[axisName];
13736             }
13737             else
13738             {
13739                 for(i in axes)
13740                 {
13741                     if(axes.hasOwnProperty(i))
13742                     {
13743                         keys = axes[i].get("keys");
13744                         if(keys && keys.hasOwnProperty(key))
13745                         {
13746                             axis = axes[i];
13747                             break;
13748                         }
13749                     }
13750                 }
13751             }
13752         }
13753         return axis;
13754     },
13755
13756     /**
13757      * @private
13758      * Gets an attribute from an object, using a getter for Base objects and a property for object
13759      * literals. Used for determining attributes from series/axis references which can be an actual class instance
13760      * or a hash of properties that will be used to create a class instance.
13761      */
13762     _getBaseAttribute: function(item, key)
13763     {
13764         if(item instanceof Y.Base)
13765         {
13766             return item.get(key);
13767         }
13768         if(item.hasOwnProperty(key))
13769         {
13770             return item[key];
13771         }
13772         return null;
13773     },
13774
13775     /**
13776      * @private
13777      * Sets an attribute on an object, using a setter of Base objects and a property for object
13778      * literals. Used for setting attributes on a Base class, either directly or to be stored in an object literal
13779      * for use at instantiation.
13780      */
13781     _setBaseAttribute: function(item, key, value)
13782     {
13783         if(item instanceof Y.Base)
13784         {
13785             item.set(key, value);
13786         }
13787         else
13788         {
13789             item[key] = value;
13790         }
13791     },
13792
13793     /**
13794      * @private
13795      * Creates Axis and Axis data classes based on hashes of properties.
13796      */
13797     _parseAxes: function(val)
13798     {
13799         var hash = this._getDefaultAxes(val),
13800             axes = {},
13801             axesAttrs = {
13802                 edgeOffset: "edgeOffset", 
13803                 position: "position",
13804                 overlapGraph:"overlapGraph",
13805                 labelFunction:"labelFunction",
13806                 labelFunctionScope:"labelFunctionScope",
13807                 labelFormat:"labelFormat",
13808                 maximum:"maximum",
13809                 minimum:"minimum", 
13810                 roundingMethod:"roundingMethod",
13811                 alwaysShowZero:"alwaysShowZero"
13812             },
13813             dp = this.get("dataProvider"),
13814             ai,
13815             i, 
13816             pos, 
13817             axis,
13818             dh, 
13819             axisClass, 
13820             config,
13821             axesCollection;
13822         for(i in hash)
13823         {
13824             if(hash.hasOwnProperty(i))
13825             {
13826                 dh = hash[i];
13827                 if(dh instanceof Y.Axis)
13828                 {
13829                     axis = dh;
13830                 }
13831                 else
13832                 {
13833                     axisClass = this._getAxisClass(dh.type);
13834                     config = {};
13835                     config.dataProvider = dh.dataProvider || dp;
13836                     config.keys = dh.keys;
13837                     
13838                     if(dh.hasOwnProperty("roundingUnit"))
13839                     {
13840                         config.roundingUnit = dh.roundingUnit;
13841                     }
13842                     pos = dh.position;
13843                     if(dh.styles)
13844                     {
13845                         config.styles = dh.styles;
13846                     }
13847                     config.position = dh.position;
13848                     for(ai in axesAttrs)
13849                     {
13850                         if(axesAttrs.hasOwnProperty(ai) && dh.hasOwnProperty(ai))
13851                         {
13852                             config[ai] = dh[ai];
13853                         }
13854                     }
13855                     axis = new axisClass(config);
13856                 }
13857
13858                 if(axis)
13859                 {
13860                     axesCollection = this.get(pos + "AxesCollection");
13861                     if(axesCollection && Y.Array.indexOf(axesCollection, axis) > 0)
13862                     {
13863                         axis.set("overlapGraph", false);
13864                     }
13865                     axis.after("axisRendered", Y.bind(this._axisRendered, this));
13866                     axes[i] = axis;
13867                 }
13868             }
13869         }
13870         return axes;
13871     },
13872     
13873     /**
13874      * @private
13875      */
13876     _addAxes: function()
13877     {
13878         var axes = this.get("axes"),
13879             i, 
13880             axis, 
13881             pos,
13882             w = this.get("width"),
13883             h = this.get("height"),
13884             node = Y.Node.one(this._parentNode);
13885         if(!this._axesCollection)
13886         {   
13887             this._axesCollection = [];
13888         }
13889         for(i in axes)
13890         {
13891             if(axes.hasOwnProperty(i))
13892             {
13893                 axis = axes[i];
13894                 if(axis instanceof Y.Axis)
13895                 {
13896                     if(!w)
13897                     {
13898                         this.set("width", node.get("offsetWidth"));
13899                         w = this.get("width");
13900                     }
13901                     if(!h)
13902                     {
13903                         this.set("height", node.get("offsetHeight"));
13904                         h = this.get("height");
13905                     }
13906                     axis.set("width", w);
13907                     axis.set("height", h);
13908                     this._addToAxesRenderQueue(axis);
13909                     pos = axis.get("position");
13910                     if(!this.get(pos + "AxesCollection"))
13911                     {
13912                         this.set(pos + "AxesCollection", [axis]);
13913                     }
13914                     else
13915                     {
13916                         this.get(pos + "AxesCollection").push(axis);
13917                     }
13918                     this._axesCollection.push(axis);
13919                     if(axis.get("keys").hasOwnProperty(this.get("categoryKey")))
13920                     {
13921                         this.set("categoryAxis", axis);
13922                     }
13923                     axis.render(this.get("contentBox"));
13924                 }
13925             }
13926         }
13927     },
13928
13929     /**
13930      * @private
13931      */
13932     _addSeries: function()
13933     {
13934         var graph = this.get("graph"),
13935             sc = this.get("seriesCollection");
13936         graph.render(this.get("contentBox"));
13937
13938     },
13939
13940     /**
13941      * @private
13942      * @description Adds gridlines to the chart.
13943      */
13944     _addGridlines: function()
13945     {
13946         var graph = this.get("graph"),
13947             hgl = this.get("horizontalGridlines"),
13948             vgl = this.get("verticalGridlines"),
13949             direction = this.get("direction"),
13950             leftAxesCollection = this.get("leftAxesCollection"),
13951             rightAxesCollection = this.get("rightAxesCollection"),
13952             bottomAxesCollection = this.get("bottomAxesCollection"),
13953             topAxesCollection = this.get("topAxesCollection"),
13954             seriesAxesCollection,
13955             catAxis = this.get("categoryAxis"),
13956             hAxis,
13957             vAxis;
13958         if(this._axesCollection)
13959         {
13960             seriesAxesCollection = this._axesCollection.concat();
13961             seriesAxesCollection.splice(Y.Array.indexOf(seriesAxesCollection, catAxis), 1);
13962         }
13963         if(hgl)
13964         {
13965             if(leftAxesCollection && leftAxesCollection[0])
13966             {
13967                 hAxis = leftAxesCollection[0];
13968             }
13969             else if(rightAxesCollection && rightAxesCollection[0])
13970             {
13971                 hAxis = rightAxesCollection[0];
13972             }
13973             else 
13974             {
13975                 hAxis = direction == "horizontal" ? catAxis : seriesAxesCollection[0];
13976             }
13977             if(!this._getBaseAttribute(hgl, "axis") && hAxis)
13978             {
13979                 this._setBaseAttribute(hgl, "axis", hAxis);
13980             }
13981             if(this._getBaseAttribute(hgl, "axis"))
13982             {
13983                 graph.set("horizontalGridlines", hgl);
13984             }
13985         }
13986         if(vgl)
13987         {
13988             if(bottomAxesCollection && bottomAxesCollection[0])
13989             {
13990                 vAxis = bottomAxesCollection[0];
13991             }
13992             else if (topAxesCollection && topAxesCollection[0])
13993             {
13994                 vAxis = topAxesCollection[0];
13995             }
13996             else 
13997             {
13998                 vAxis = direction == "vertical" ? catAxis : seriesAxesCollection[0];
13999             }
14000             if(!this._getBaseAttribute(vgl, "axis") && vAxis)
14001             {
14002                 this._setBaseAttribute(vgl, "axis", vAxis);
14003             }
14004             if(this._getBaseAttribute(vgl, "axis"))
14005             {
14006                 graph.set("verticalGridlines", vgl);
14007             }
14008         }
14009     },
14010
14011     /**
14012      * @private
14013      */
14014     _getDefaultAxes: function(axes)
14015     {
14016         var catKey = this.get("categoryKey"),
14017             axis,
14018             attr,
14019             keys,
14020             newAxes = {},
14021             claimedKeys = [],
14022             categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"),
14023             valueAxisName = this.get("valueAxisName"),
14024             seriesKeys = this.get("seriesKeys") || [], 
14025             i, 
14026             l,
14027             ii,
14028             ll,
14029             cIndex,
14030             dv,
14031             dp = this.get("dataProvider"),
14032             direction = this.get("direction"),
14033             seriesPosition,
14034             categoryPosition,
14035             valueAxes = [],
14036             seriesAxis = this.get("stacked") ? "stacked" : "numeric";
14037         dv = dp[0];
14038         if(direction == "vertical")
14039         {
14040             seriesPosition = "bottom";
14041             categoryPosition = "left";
14042         }
14043         else
14044         {
14045             seriesPosition = "left";
14046             categoryPosition = "bottom";
14047         }
14048         if(axes)
14049         {
14050             for(i in axes)
14051             {
14052                 if(axes.hasOwnProperty(i))
14053                 {
14054                     axis = axes[i];
14055                     keys = this._getBaseAttribute(axis, "keys");
14056                     attr = this._getBaseAttribute(axis, "type");
14057                     if(attr == "time" || attr == "category")
14058                     {
14059                         categoryAxisName = i;
14060                         this.set("categoryAxisName", i);
14061                         if(Y.Lang.isArray(keys) && keys.length > 0)
14062                         {
14063                             catKey = keys[0];
14064                             this.set("categoryKey", catKey);
14065                         }
14066                         newAxes[i] = axis;
14067                     }
14068                     else if(i == categoryAxisName)
14069                     {
14070                         newAxes[i] = axis;
14071                     }
14072                     else 
14073                     {
14074                         newAxes[i] = axis;
14075                         if(i != valueAxisName && keys && Y.Lang.isArray(keys))
14076                         {
14077                             ll = keys.length;
14078                             for(ii = 0; ii < ll; ++ii)
14079                             {
14080                                 claimedKeys.push(keys[ii]);
14081                             }
14082                             valueAxes.push(newAxes[i]);
14083                         }
14084                         if(!(this._getBaseAttribute(newAxes[i], "type")))
14085                         {
14086                             this._setBaseAttribute(newAxes[i], "type", seriesAxis);
14087                         }
14088                         if(!(this._getBaseAttribute(newAxes[i], "position")))
14089                         {
14090                             this._setBaseAttribute(newAxes[i], "position", this._getDefaultAxisPosition(newAxes[i], valueAxes, seriesPosition));
14091                         }
14092                     }
14093                 }
14094             }
14095         }
14096         if(seriesKeys.length < 1)
14097         {
14098             for(i in dv)
14099             {
14100                 if(dv.hasOwnProperty(i) && i != catKey && Y.Array.indexOf(claimedKeys, i) == -1)
14101                 {
14102                     seriesKeys.push(i);
14103                 }
14104             }
14105         }
14106         cIndex = Y.Array.indexOf(seriesKeys, catKey);
14107         if(cIndex > -1)
14108         {
14109             seriesKeys.splice(cIndex, 1);
14110         }
14111         l = claimedKeys.length;
14112         for(i = 0; i < l; ++i)
14113         {
14114             cIndex = Y.Array.indexOf(seriesKeys, claimedKeys[i]); 
14115             if(cIndex > -1)
14116             {
14117                 seriesKeys.splice(cIndex, 1);
14118             }
14119         }
14120         if(!newAxes.hasOwnProperty(categoryAxisName))
14121         {
14122             newAxes[categoryAxisName] = {};
14123         }
14124         if(!(this._getBaseAttribute(newAxes[categoryAxisName], "keys")))
14125         {
14126             this._setBaseAttribute(newAxes[categoryAxisName], "keys", [catKey]);
14127         }
14128         
14129         if(!(this._getBaseAttribute(newAxes[categoryAxisName], "position")))
14130         {
14131             this._setBaseAttribute(newAxes[categoryAxisName], "position", categoryPosition);
14132         }
14133          
14134         if(!(this._getBaseAttribute(newAxes[categoryAxisName], "type")))
14135         {
14136             this._setBaseAttribute(newAxes[categoryAxisName], "type", this.get("categoryType"));
14137         }
14138         if(!newAxes.hasOwnProperty(valueAxisName) && seriesKeys && seriesKeys.length > 0)
14139         {
14140             newAxes[valueAxisName] = {keys:seriesKeys};
14141             valueAxes.push(newAxes[valueAxisName]);
14142         }
14143         if(claimedKeys.length > 0)
14144         {
14145             if(seriesKeys.length > 0)
14146             {
14147                 seriesKeys = claimedKeys.concat(seriesKeys);
14148             }
14149             else
14150             {
14151                 seriesKeys = claimedKeys;
14152             }
14153         }
14154         if(newAxes.hasOwnProperty(valueAxisName))
14155         {
14156             if(!(this._getBaseAttribute(newAxes[valueAxisName], "position")))
14157             {
14158                 this._setBaseAttribute(newAxes[valueAxisName], "position", this._getDefaultAxisPosition(newAxes[valueAxisName], valueAxes, seriesPosition));
14159             }
14160             if(!(this._getBaseAttribute(newAxes[valueAxisName], "type")))
14161             {
14162                 this._setBaseAttribute(newAxes[valueAxisName], "type", seriesAxis);
14163             }
14164             if(!(this._getBaseAttribute(newAxes[valueAxisName], "keys")))
14165             {
14166                 this._setBaseAttribute(newAxes[valueAxisName], "keys", seriesKeys);
14167             }
14168         } 
14169         this.set("seriesKeys", seriesKeys);
14170         return newAxes;
14171     },
14172
14173     /**
14174      * @private
14175      * @description Determines the position of an axis when one is not specified.
14176      */
14177     _getDefaultAxisPosition: function(axis, valueAxes, position)
14178     {
14179         var direction = this.get("direction"),
14180             i = Y.Array.indexOf(valueAxes, axis);
14181         
14182         if(valueAxes[i - 1] && valueAxes[i - 1].position)
14183         {
14184             if(direction == "horizontal")
14185             {
14186                 if(valueAxes[i - 1].position == "left")
14187                 {
14188                     position = "right";
14189                 }
14190                 else if(valueAxes[i - 1].position == "right")
14191                 {
14192                     position = "left";
14193                 }
14194             }
14195             else
14196             {
14197                 if (valueAxes[i -1].position == "bottom")
14198                 {
14199                     position = "top";
14200                 }       
14201                 else
14202                 {
14203                     position = "bottom";
14204                 }
14205             }
14206         }
14207         return position;
14208     },
14209
14210    
14211     /**
14212      * Returns an object literal containing a categoryItem and a valueItem for a given series index.
14213      *
14214      * @method getSeriesItem
14215      * @param {CartesianSeries} series Reference to a series.
14216      * @param {Number} index Index of the specified item within a series.
14217      * @return Object
14218      */
14219     getSeriesItems: function(series, index)
14220     {
14221         var xAxis = series.get("xAxis"),
14222             yAxis = series.get("yAxis"),
14223             xKey = series.get("xKey"),
14224             yKey = series.get("yKey"),
14225             categoryItem,
14226             valueItem;
14227         if(this.get("direction") == "vertical")
14228         {
14229             categoryItem = {
14230                 axis:yAxis,
14231                 key:yKey,
14232                 value:yAxis.getKeyValueAt(yKey, index)
14233             };
14234             valueItem = {
14235                 axis:xAxis,
14236                 key:xKey,
14237                 value: xAxis.getKeyValueAt(xKey, index)
14238             };
14239         }
14240         else
14241         {
14242             valueItem = {
14243                 axis:yAxis,
14244                 key:yKey,
14245                 value:yAxis.getKeyValueAt(yKey, index)
14246             };
14247             categoryItem = {
14248                 axis:xAxis,
14249                 key:xKey,
14250                 value: xAxis.getKeyValueAt(xKey, index)
14251             };
14252         }
14253         categoryItem.displayName = series.get("categoryDisplayName");
14254         valueItem.displayName = series.get("valueDisplayName");
14255         categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index);
14256         valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index);
14257         return {category:categoryItem, value:valueItem};
14258     },
14259
14260     /**
14261      * @private
14262      * Listender for axisRendered event.
14263      */
14264     _axisRendered: function(e)
14265     {
14266         this._axesRenderQueue = this._axesRenderQueue.splice(1 + Y.Array.indexOf(this._axesRenderQueue, e.currentTarget), 1);
14267         if(this._axesRenderQueue.length < 1)
14268         {
14269             this._redraw();
14270         }
14271     },
14272
14273     /**
14274      * @private
14275      */
14276     _sizeChanged: function(e)
14277     {
14278         if(this._axesCollection)
14279         {
14280             var ac = this._axesCollection,
14281                 i = 0,
14282                 l = ac.length;
14283             for(; i < l; ++i)
14284             {
14285                 this._addToAxesRenderQueue(ac[i]);
14286             }
14287             this._redraw();
14288         }
14289     },
14290
14291     /**
14292      * @private
14293      */
14294     _redraw: function()
14295     {
14296         if(this._drawing)
14297         {
14298             this._callLater = true;
14299             return;
14300         }
14301         this._drawing = true;
14302         this._callLater = false;
14303         var w = this.get("width"),
14304             h = this.get("height"),
14305             lw = 0,
14306             rw = 0,
14307             th = 0,
14308             bh = 0,
14309             lc = this.get("leftAxesCollection"),
14310             rc = this.get("rightAxesCollection"),
14311             tc = this.get("topAxesCollection"),
14312             bc = this.get("bottomAxesCollection"),
14313             i = 0,
14314             l,
14315             axis,
14316             pos,
14317             pts = [],
14318             graphOverflow = "visible",
14319             graph = this.get("graph"); 
14320         if(lc)
14321         {
14322             l = lc.length;
14323             for(i = l - 1; i > -1; --i)
14324             {
14325                 pts[Y.Array.indexOf(this._axesCollection, lc[i])] = {x:lw + "px"};
14326                 lw += lc[i].get("width");
14327             }
14328         }
14329         if(rc)
14330         {
14331             l = rc.length;
14332             i = 0;
14333             for(i = l - 1; i > -1; --i)
14334             {
14335                 rw += rc[i].get("width");
14336                 pts[Y.Array.indexOf(this._axesCollection, rc[i])] = {x:(w - rw) + "px"};
14337             }
14338         }
14339         if(tc)
14340         {
14341             l = tc.length;
14342             for(i = l - 1; i > -1; --i)
14343             {
14344                 pts[Y.Array.indexOf(this._axesCollection, tc[i])] = {y:th + "px"};
14345                 th += tc[i].get("height");
14346             }
14347         }
14348         if(bc)
14349         {
14350             l = bc.length;
14351             for(i = l - 1; i > -1; --i)
14352             {
14353                 bh += bc[i].get("height");
14354                 pts[Y.Array.indexOf(this._axesCollection, bc[i])] = {y:(h - bh) + "px"};
14355             }
14356         }
14357         l = this._axesCollection.length;
14358         i = 0;
14359         
14360         for(; i < l; ++i)
14361         {
14362             axis = this._axesCollection[i];
14363             pos = axis.get("position");
14364             if(pos == "left" || pos === "right")
14365             {
14366                 axis.get("boundingBox").setStyle("top", th + "px");
14367                 axis.get("boundingBox").setStyle("left", pts[i].x);
14368                 if(axis.get("height") !== h - (bh + th))
14369                 {
14370                     axis.set("height", h - (bh + th));
14371                 }
14372             }
14373             else if(pos == "bottom" || pos == "top")
14374             {
14375                 if(axis.get("width") !== w - (lw + rw))
14376                 {
14377                     axis.set("width", w - (lw + rw));
14378                 }
14379                 axis.get("boundingBox").setStyle("left", lw + "px");
14380                 axis.get("boundingBox").setStyle("top", pts[i].y);
14381             }
14382             if(axis.get("setMax") || axis.get("setMin"))
14383             {
14384                 graphOverflow = "hidden";
14385             }
14386         }
14387         
14388         this._drawing = false;
14389         if(this._callLater)
14390         {
14391             this._redraw();
14392             return;
14393         }
14394         if(graph)
14395         {
14396             graph.get("boundingBox").setStyle("left", lw + "px");
14397             graph.get("boundingBox").setStyle("top", th + "px");
14398             graph.set("width", w - (lw + rw));
14399             graph.set("height", h - (th + bh));
14400             graph.get("boundingBox").setStyle("overflow", graphOverflow);
14401         }
14402
14403         if(this._overlay)
14404         {
14405             this._overlay.setStyle("left", lw + "px");
14406             this._overlay.setStyle("top", th + "px");
14407             this._overlay.setStyle("width", (w - (lw + rw)) + "px");
14408             this._overlay.setStyle("height", (h - (th + bh)) + "px");
14409         }
14410     }
14411 }, {
14412     ATTRS: {
14413         /**
14414          * @private
14415          * Style object for the axes.
14416          *
14417          * @attribute axesStyles
14418          * @type Object
14419          */
14420         axesStyles: {
14421             getter: function()
14422             {
14423                 var axes = this.get("axes"),
14424                     i,
14425                     styles = this._axesStyles;
14426                 if(axes)
14427                 {
14428                     for(i in axes)
14429                     {
14430                         if(axes.hasOwnProperty(i) && axes[i] instanceof Y.Axis)
14431                         {
14432                             if(!styles)
14433                             {
14434                                 styles = {};
14435                             }
14436                             styles[i] = axes[i].get("styles");
14437                         }
14438                     }
14439                 }
14440                 return styles;
14441             },
14442             
14443             setter: function(val)
14444             {
14445                 var axes = this.get("axes"),
14446                     i;
14447                 for(i in val)
14448                 {
14449                     if(val.hasOwnProperty(i) && axes.hasOwnProperty(i))
14450                     {
14451                         this._setBaseAttribute(axes[i], "styles", val[i]);
14452                     }
14453                 }
14454             }
14455         },
14456
14457         /**
14458          * @private
14459          * Style object for the series
14460          *
14461          * @attribute seriesStyles
14462          * @type Object
14463          */
14464         seriesStyles: {
14465             getter: function()
14466             {
14467                 var styles = this._seriesStyles,
14468                     graph = this.get("graph"),
14469                     dict,
14470                     i;
14471                 if(graph)
14472                 {
14473                     dict = graph.get("seriesDictionary");
14474                     if(dict)
14475                     {
14476                         styles = {};
14477                         for(i in dict)
14478                         {
14479                             if(dict.hasOwnProperty(i))
14480                             {
14481                                 styles[i] = dict[i].get("styles");
14482                             }
14483                         }
14484                     }
14485                 }
14486                 return styles;
14487             },
14488             
14489             setter: function(val)
14490             {
14491                 var i,
14492                     l,
14493                     s;
14494     
14495                 if(Y.Lang.isArray(val))
14496                 {
14497                     s = this.get("seriesCollection");
14498                     i = 0;
14499                     l = val.length;
14500
14501                     for(; i < l; ++i)
14502                     {
14503                         this._setBaseAttribute(s[i], "styles", val[i]);
14504                     }
14505                 }
14506                 else
14507                 {
14508                     for(i in val)
14509                     {
14510                         if(val.hasOwnProperty(i))
14511                         {
14512                             s = this.getSeries(i);
14513                             this._setBaseAttribute(s, "styles", val[i]);
14514                         }
14515                     }
14516                 }
14517             }
14518         },
14519
14520         /**
14521          * @private
14522          * Styles for the graph.
14523          *
14524          * @attribute graphStyles
14525          * @type Object
14526          */
14527         graphStyles: {
14528             getter: function()
14529             {
14530                 var graph = this.get("graph");
14531                 if(graph)
14532                 {
14533                     return(graph.get("styles"));
14534                 }
14535                 return this._graphStyles;
14536             },
14537
14538             setter: function(val)
14539             {
14540                 var graph = this.get("graph");
14541                 this._setBaseAttribute(graph, "styles", val);
14542             }
14543
14544         },
14545
14546         /**
14547          * Style properties for the chart. Contains a key indexed hash of the following:
14548          *  <dl>
14549          *      <dt>series</dt><dd>A key indexed hash containing references to the <code>styles</code> attribute for each series in the chart.
14550          *      Specific style attributes vary depending on the series:
14551          *      <ul>
14552          *          <li><a href="AreaSeries.html#config_styles">AreaSeries</a></li>
14553          *          <li><a href="BarSeries.html#config_styles">BarSeries</a></li>
14554          *          <li><a href="ColumnSeries.html#config_styles">ColumnSeries</a></li>
14555          *          <li><a href="ComboSeries.html#config_styles">ComboSeries</a></li>
14556          *          <li><a href="LineSeries.html#config_styles">LineSeries</a></li>
14557          *          <li><a href="MarkerSeries.html#config_styles">MarkerSeries</a></li>
14558          *          <li><a href="SplineSeries.html#config_styles">SplineSeries</a></li>
14559          *      </ul>
14560          *      </dd>
14561          *      <dt>axes</dt><dd>A key indexed hash containing references to the <code>styles</code> attribute for each axes in the chart. Specific
14562          *      style attributes can be found in the <a href="Axis.html#config_styles">Axis</a> class.</dd>
14563          *      <dt>graph</dt><dd>A reference to the <code>styles</code> attribute in the chart. Specific style attributes can be found in the
14564          *      <a href="Graph.html#config_styles">Graph</a> class.</dd>
14565          *  </dl>
14566          *
14567          * @attribute styles
14568          * @type Object
14569          */
14570         styles: {
14571             getter: function()
14572             {
14573                 var styles = { 
14574                     axes: this.get("axesStyles"),
14575                     series: this.get("seriesStyles"),
14576                     graph: this.get("graphStyles")
14577                 };
14578                 return styles;
14579             },
14580             setter: function(val)
14581             {
14582                 if(val.hasOwnProperty("axes"))
14583                 {
14584                     if(this.get("axesStyles"))
14585                     {
14586                         this.set("axesStyles", val.axes);
14587                     }
14588                     else
14589                     {
14590                         this._axesStyles = val.axes;
14591                     }
14592                 }
14593                 if(val.hasOwnProperty("series"))
14594                 {
14595                     if(this.get("seriesStyles"))
14596                     {
14597                         this.set("seriesStyles", val.series);
14598                     }
14599                     else
14600                     {
14601                         this._seriesStyles = val.series;
14602                     }
14603                 }
14604                 if(val.hasOwnProperty("graph"))
14605                 {
14606                     this.set("graphStyles", val.graph);
14607                 }
14608             }
14609         },
14610
14611         /**
14612          * Axes to appear in the chart. This can be a key indexed hash of axis instances or object literals
14613          * used to construct the appropriate axes.
14614          *
14615          * @attribute axes
14616          * @type Object
14617          */
14618         axes: {
14619             valueFn: "_parseAxes",
14620
14621             setter: function(val)
14622             {
14623                 return this._parseAxes(val);
14624             }
14625         },
14626
14627         /**
14628          * Collection of series to appear on the chart. This can be an array of Series instances or object literals
14629          * used to construct the appropriate series.
14630          *
14631          * @attribute seriesCollection
14632          * @type Array
14633          */
14634         seriesCollection: {
14635             valueFn: "_getDefaultSeriesCollection",
14636             
14637             setter: function(val)
14638             {
14639                 return this._getDefaultSeriesCollection(val);
14640             }
14641         },
14642
14643         /**
14644          * Reference to the left-aligned axes for the chart.
14645          *
14646          * @attribute leftAxesCollection
14647          * @type Array
14648          * @private
14649          */
14650         leftAxesCollection: {},
14651
14652         /**
14653          * Reference to the bottom-aligned axes for the chart.
14654          *
14655          * @attribute bottomAxesCollection
14656          * @type Array
14657          * @private
14658          */
14659         bottomAxesCollection: {},
14660
14661         /**
14662          * Reference to the right-aligned axes for the chart.
14663          *
14664          * @attribute rightAxesCollection
14665          * @type Array
14666          * @private
14667          */
14668         rightAxesCollection: {},
14669
14670         /**
14671          * Reference to the top-aligned axes for the chart.
14672          *
14673          * @attribute topAxesCollection
14674          * @type Array
14675          * @private
14676          */
14677         topAxesCollection: {},
14678         
14679         /**
14680          * Indicates whether or not the chart is stacked.
14681          *
14682          * @attribute stacked
14683          * @type Boolean
14684          */
14685         stacked: {
14686             value: false
14687         },
14688
14689         /**
14690          * Direction of chart's category axis when there is no series collection specified. Charts can
14691          * be horizontal or vertical. When the chart type is column, the chart is horizontal.
14692          * When the chart type is bar, the chart is vertical. 
14693          *
14694          * @attribute direction
14695          * @type String
14696          */
14697         direction: {
14698             getter: function()
14699             {
14700                 var type = this.get("type");
14701                 if(type == "bar")
14702                 {   
14703                     return "vertical";
14704                 }
14705                 else if(type == "column")
14706                 {
14707                     return "horizontal";
14708                 }
14709                 return this._direction;
14710             },
14711
14712             setter: function(val)
14713             {
14714                 this._direction = val;
14715                 return this._direction;
14716             }
14717         },
14718
14719         /**
14720          * Indicates whether or not an area is filled in a combo chart.
14721          * 
14722          * @attribute showAreaFill
14723          * @type Boolean
14724          */
14725         showAreaFill: {},
14726
14727         /**
14728          * Indicates whether to display markers in a combo chart.
14729          *
14730          * @attribute showMarkers
14731          * @type Boolean
14732          */
14733         showMarkers:{},
14734
14735         /**
14736          * Indicates whether to display lines in a combo chart.
14737          *
14738          * @attribute showLines
14739          * @type Boolean
14740          */
14741         showLines:{},
14742
14743         /**
14744          * Indicates the key value used to identify a category axis in the <code>axes</code> hash. If
14745          * not specified, the categoryKey attribute value will be used.
14746          * 
14747          * @attribute categoryAxisName
14748          * @type String
14749          */
14750         categoryAxisName: {
14751         },
14752
14753         /**
14754          * Indicates the key value used to identify a the series axis when an axis not generated.
14755          *
14756          * @attribute valueAxisName
14757          * @type String
14758          */
14759         valueAxisName: {
14760             value: "values"
14761         },
14762
14763         /**
14764          * Reference to the horizontalGridlines for the chart.
14765          *
14766          * @attribute horizontalGridlines
14767          * @type Gridlines
14768          */
14769         horizontalGridlines: {
14770             getter: function()
14771             {
14772                 var graph = this.get("graph");
14773                 if(graph)
14774                 {
14775                     return graph.get("horizontalGridlines");
14776                 }
14777                 return this._horizontalGridlines;
14778             },
14779             setter: function(val)
14780             {
14781                 var graph = this.get("graph");
14782                 if(val && !Y.Lang.isObject(val))
14783                 {
14784                     val = {};
14785                 }
14786                 if(graph)
14787                 {
14788                     graph.set("horizontalGridlines", val);
14789                 }
14790                 else
14791                 {
14792                     this._horizontalGridlines = val;
14793                 }
14794             }
14795         },
14796
14797         /**
14798          * Reference to the verticalGridlines for the chart.
14799          *
14800          * @attribute verticalGridlines
14801          * @type Gridlines
14802          */
14803         verticalGridlines: {
14804             getter: function()
14805             {
14806                 var graph = this.get("graph");
14807                 if(graph)
14808                 {
14809                     return graph.get("verticalGridlines");
14810                 }
14811                 return this._verticalGridlines;
14812             },
14813             setter: function(val)
14814             {
14815                 var graph = this.get("graph");
14816                 if(val && !Y.Lang.isObject(val))
14817                 {
14818                     val = {};
14819                 }
14820                 if(graph)
14821                 {
14822                     graph.set("verticalGridlines", val);
14823                 }
14824                 else
14825                 {
14826                     this._verticalGridlines = val;
14827                 }
14828             }
14829         },
14830         
14831         /**
14832          * Type of chart when there is no series collection specified.
14833          *
14834          * @attribute type
14835          * @type String 
14836          */
14837         type: {
14838             getter: function()
14839             {
14840                 if(this.get("stacked"))
14841                 {
14842                     return "stacked" + this._type;
14843                 }
14844                 return this._type;
14845             },
14846
14847             setter: function(val)
14848             {
14849                 if(this._type == "bar")
14850                 {
14851                     if(val != "bar")
14852                     {
14853                         this.set("direction", "horizontal");
14854                     }
14855                 }
14856                 else
14857                 {
14858                     if(val == "bar")
14859                     {
14860                         this.set("direction", "vertical");
14861                     }
14862                 }
14863                 this._type = val;
14864                 return this._type;
14865             }
14866         },
14867         
14868         /**
14869          * Reference to the category axis used by the chart.
14870          *
14871          * @attribute categoryAxis
14872          * @type Axis
14873          */
14874         categoryAxis:{}
14875     }
14876 });
14877 /**
14878  * The PieChart class creates a pie chart
14879  *
14880  * @class PieChart
14881  * @extends ChartBase
14882  * @constructor
14883  */
14884 Y.PieChart = Y.Base.create("pieChart", Y.Widget, [Y.ChartBase], {
14885     /**
14886      * @private
14887      */
14888     _getSeriesCollection: function()
14889     {
14890         if(this._seriesCollection)
14891         {
14892             return this._seriesCollection;
14893         }
14894         var axes = this.get("axes"),
14895             sc = [], 
14896             seriesKeys,
14897             i = 0,
14898             l,
14899             type = this.get("type"),
14900             key,
14901             catAxis = "categoryAxis",
14902             catKey = "categoryKey",
14903             valAxis = "valueAxis",
14904             seriesKey = "valueKey";
14905         if(axes)
14906         {
14907             seriesKeys = axes.values.get("keyCollection");
14908             key = axes.category.get("keyCollection")[0];
14909             l = seriesKeys.length;
14910             for(; i < l; ++i)
14911             {
14912                 sc[i] = {type:type};
14913                 sc[i][catAxis] = "category";
14914                 sc[i][valAxis] = "values";
14915                 sc[i][catKey] = key;
14916                 sc[i][seriesKey] = seriesKeys[i];
14917             }
14918         }
14919         this._seriesCollection = sc;
14920         return sc;
14921     },
14922
14923     /**
14924      * @private
14925      */
14926     _parseAxes: function(hash)
14927     {
14928         if(!this._axes)
14929         {
14930             this._axes = {};
14931         }
14932         var i, pos, axis, dh, config, axisClass,
14933             type = this.get("type"),
14934             w = this.get("width"),
14935             h = this.get("height"),
14936             node = Y.Node.one(this._parentNode);
14937         if(!w)
14938         {
14939             this.set("width", node.get("offsetWidth"));
14940             w = this.get("width");
14941         }
14942         if(!h)
14943         {
14944             this.set("height", node.get("offsetHeight"));
14945             h = this.get("height");
14946         }
14947         for(i in hash)
14948         {
14949             if(hash.hasOwnProperty(i))
14950             {
14951                 dh = hash[i];
14952                 pos = type == "pie" ? "none" : dh.position;
14953                 axisClass = this._getAxisClass(dh.type);
14954                 config = {dataProvider:this.get("dataProvider")};
14955                 if(dh.hasOwnProperty("roundingUnit"))
14956                 {
14957                     config.roundingUnit = dh.roundingUnit;
14958                 }
14959                 config.keys = dh.keys;
14960                 config.width = w;
14961                 config.height = h;
14962                 config.position = pos;
14963                 config.styles = dh.styles;
14964                 axis = new axisClass(config);
14965                 axis.on("axisRendered", Y.bind(this._axisRendered, this));
14966                 this._axes[i] = axis;
14967             }
14968         }
14969     },
14970
14971     /**
14972      * @private
14973      */
14974     _addAxes: function()
14975     {
14976         var axes = this.get("axes"),
14977             i, 
14978             axis, 
14979             p;
14980         if(!axes)
14981         {
14982             this.set("axes", this._getDefaultAxes());
14983             axes = this.get("axes");
14984         }
14985         if(!this._axesCollection)
14986         {   
14987             this._axesCollection = [];
14988         }
14989         for(i in axes)
14990         {
14991             if(axes.hasOwnProperty(i))
14992             {
14993                 axis = axes[i];
14994                 p = axis.get("position");
14995                 if(!this.get(p + "AxesCollection"))
14996                 {
14997                     this.set(p + "AxesCollection", [axis]);
14998                 }
14999                 else
15000                 {
15001                     this.get(p + "AxesCollection").push(axis);
15002                 }
15003                 this._axesCollection.push(axis);
15004             }
15005         }
15006     },
15007
15008     /**
15009      * @private
15010      */
15011     _addSeries: function()
15012     {
15013         var graph = this.get("graph"),
15014             seriesCollection = this.get("seriesCollection");
15015         this._parseSeriesAxes(seriesCollection);
15016         graph.set("showBackground", false);
15017         graph.set("width", this.get("width"));
15018         graph.set("height", this.get("height"));
15019         graph.set("seriesCollection", seriesCollection);
15020         this._seriesCollection = graph.get("seriesCollection");
15021         graph.render(this.get("contentBox"));
15022     },
15023
15024     /**
15025      * @private
15026      */
15027     _parseSeriesAxes: function(c)
15028     {
15029         var i = 0, 
15030             len = c.length, 
15031             s,
15032             axes = this.get("axes"),
15033             axis;
15034         for(; i < len; ++i)
15035         {
15036             s = c[i];
15037             if(s)
15038             {
15039                 //If series is an actual series instance, 
15040                 //replace axes attribute string ids with axes
15041                 if(s instanceof Y.PieSeries)
15042                 {
15043                     axis = s.get("categoryAxis");
15044                     if(axis && !(axis instanceof Y.Axis))
15045                     {
15046                         s.set("categoryAxis", axes[axis]);
15047                     }
15048                     axis = s.get("valueAxis");
15049                     if(axis && !(axis instanceof Y.Axis))
15050                     {
15051                         s.set("valueAxis", axes[axis]);
15052                     }
15053                     continue;
15054                 }
15055                 s.categoryAxis = axes.category;
15056                 s.valueAxis = axes.values;
15057                 if(!s.type)
15058                 {
15059                     s.type = this.get("type");
15060                 }
15061             }
15062         }
15063     },
15064
15065     /**
15066      * @private
15067      */
15068     _getDefaultAxes: function()
15069     {
15070         var catKey = this.get("categoryKey"),
15071             seriesKeys = this.get("seriesKeys") || [], 
15072             seriesAxis = "numeric",
15073             i, 
15074             dv = this.get("dataProvider")[0];
15075         if(seriesKeys.length < 1)
15076         {
15077             for(i in dv)
15078             {
15079                 if(i != catKey)
15080                 {
15081                     seriesKeys.push(i);
15082                 }
15083             }
15084             if(seriesKeys.length > 0)
15085             {
15086                 this.set("seriesKeys", seriesKeys);
15087             }
15088         }
15089         return {
15090             values:{
15091                 keys:seriesKeys,
15092                 type:seriesAxis
15093             },
15094             category:{
15095                 keys:[catKey],
15096                 type:this.get("categoryType")
15097             }
15098         };
15099     },
15100         
15101     /**
15102      * Returns an object literal containing a categoryItem and a valueItem for a given series index.
15103      *
15104      * @method getSeriesItem
15105      * @param series Reference to a series.
15106      * @param index Index of the specified item within a series.
15107      */
15108     getSeriesItems: function(series, index)
15109     {
15110         var categoryItem = {
15111                 axis: series.get("categoryAxis"),
15112                 key: series.get("categoryKey"),
15113                 displayName: series.get("categoryDisplayName")
15114             },
15115             valueItem = {
15116                 axis: series.get("valueAxis"),
15117                 key: series.get("valueKey"),
15118                 displayName: series.get("valueDisplayName")
15119             };
15120         categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index);
15121         valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index);
15122         return {category:categoryItem, value:valueItem};
15123     },
15124
15125     /**
15126      * @private
15127      */
15128     _sizeChanged: function(e)
15129     {
15130         this._redraw();
15131     },
15132
15133     /**
15134      * @private
15135      */
15136     _redraw: function()
15137     {
15138         var graph = this.get("graph");
15139         if(graph)
15140         {
15141             graph.set("width", this.get("width"));
15142             graph.set("height", this.get("height"));
15143         }
15144     }
15145 }, {
15146     ATTRS: {
15147         /**
15148          * Axes to appear in the chart. 
15149          *
15150          * @attribute axes
15151          * @type Object
15152          */
15153         axes: {
15154             getter: function()
15155             {
15156                 return this._axes;
15157             },
15158
15159             setter: function(val)
15160             {
15161                 this._parseAxes(val);
15162             }
15163         },
15164
15165         /**
15166          * Collection of series to appear on the chart. This can be an array of Series instances or object literals
15167          * used to describe a Series instance.
15168          *
15169          * @attribute seriesCollection
15170          * @type Array
15171          */
15172         seriesCollection: {
15173             getter: function()
15174             {
15175                 return this._getSeriesCollection();
15176             },
15177             
15178             setter: function(val)
15179             {
15180                 return this._setSeriesCollection(val);
15181             }
15182         },
15183         
15184         /**
15185          * Type of chart when there is no series collection specified.
15186          *
15187          * @attribute type
15188          * @type String 
15189          */
15190         type: {
15191             value: "pie"
15192         }
15193     }
15194 });
15195 /**
15196  * The Chart class is the basic application used to create a chart.
15197  *
15198  * @class Chart
15199  * @constructor
15200  */
15201 function Chart(cfg)
15202 {
15203     if(cfg.type != "pie")
15204     {
15205         return new Y.CartesianChart(cfg);
15206     }
15207     else
15208     {
15209         return new Y.PieChart(cfg);
15210     }
15211 }
15212 Y.Chart = Chart;
15213
15214
15215 }, '3.3.0' ,{requires:['dom', 'datatype', 'event-custom', 'event-mouseenter', 'widget', 'widget-position', 'widget-stack']});