2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('charts', function(Y) {
11 * The Charts widget provides an api for displaying data
16 var ISCHROME = Y.UA.chrome,
18 canvas = document.createElement("canvas");
19 if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
23 else if(canvas && canvas.getContext && canvas.getContext("2d"))
25 DRAWINGAPI = "canvas";
33 * Graphic is a simple drawing api that allows for basic drawing operations.
38 var Graphic = function(config) {
40 this.initializer.apply(this, arguments);
45 * Indicates whether or not the instance will size itself based on its contents.
53 * Initializes the class.
58 initializer: function(config) {
59 config = config || {};
60 var w = config.width || 0,
61 h = config.height || 0;
64 this.node = config.node;
65 this._styleGroup(this.node);
69 this.node = this._createGraphics();
76 * Specifies a bitmap fill used by subsequent calls to other drawing methods.
78 * @method beginBitmapFill
79 * @param {Object} config
81 beginBitmapFill: function(config) {
84 fill.src = config.bitmap.src;
86 this._fillProps = fill;
87 if(!isNaN(config.tx) ||
89 !isNaN(config.width) ||
90 !isNaN(config.height))
101 this._gradientBox = null;
106 * Specifes a solid fill used by subsequent calls to other drawing methods.
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.
112 beginFill: function(color, alpha) {
114 this._fillAlpha = Y.Lang.isNumber(alpha) ? alpha : 1;
115 this._fillColor = color;
116 this._fillType = 'solid';
123 * Specifies a gradient fill used by subsequent calls to other drawing methods.
125 * @method beginGradientFill
126 * @param {Object} config
128 beginGradientFill: function(config) {
129 var alphas = config.alphas || [];
132 this._defs = this._createGraphicNode("defs");
133 this.node.appendChild(this._defs);
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());
155 this._removeChildren(this.node);
156 if(this.node && this.node.parentNode)
158 this.node.parentNode.removeChild(this.node);
163 * Removes all child nodes.
165 * @method _removeChildren
166 * @param {HTMLElement} node
169 _removeChildren: function(node)
171 if(node.hasChildNodes())
174 while(node.firstChild)
176 child = node.firstChild;
177 this._removeChildren(child);
178 node.removeChild(child);
184 * Shows and and hides a the graphic instance.
186 * @method toggleVisible
187 * @param val {Boolean} indicates whether the instance should be visible.
189 toggleVisible: function(val)
191 this._toggleVisible(this.node, val);
197 * @method _toggleVisible
198 * @param {HTMLElement} node element to toggle
199 * @param {Boolean} val indicates visibilitye
202 _toggleVisible: function(node, val)
204 var children = Y.Selector.query(">/*", node),
205 visibility = val ? "visible" : "hidden",
210 len = children.length;
213 this._toggleVisible(children[i], val);
216 node.style.visibility = visibility;
220 * Clears the graphics object.
225 if(this._graphicsList)
227 while(this._graphicsList.length > 0)
229 this.node.removeChild(this._graphicsList.shift());
236 * Draws a bezier curve.
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.
246 curveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
247 this._shapeType = "path";
248 if(this.path.indexOf("C") < 0 || this._pathType !== "C")
250 this._pathType = "C";
253 this.path += Math.round(cp1x) + ", " + Math.round(cp1y) + ", " + Math.round(cp2x) + ", " + Math.round(cp2y) + ", " + x + ", " + y + " ";
254 this._trackSize(x, y);
258 * Draws a quadratic bezier curve.
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.
266 quadraticCurveTo: function(cpx, cpy, x, y) {
267 if(this.path.indexOf("Q") < 0 || this._pathType !== "Q")
269 this._pathType = "Q";
272 this.path += Math.round(cpx) + " " + Math.round(cpy) + " " + Math.round(x) + " " + Math.round(y);
279 * @param {Number} x y-coordinate
280 * @param {Number} y x-coordinate
281 * @param {Number} r radius
283 drawCircle: function(x, y, r) {
290 this._attributes = {cx:x, cy:y, r:r};
291 this._width = this._height = r * 2;
294 this._shapeType = "circle";
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
307 drawEllipse: function(x, y, w, h) {
318 this._shapeType = "ellipse";
326 * @param {Number} x x-coordinate
327 * @param {Number} y y-coordinate
328 * @param {Number} w width
329 * @param {Number} h height
331 drawRect: function(x, y, w, h) {
343 this.lineTo(x + w, y);
344 this.lineTo(x + w, y + h);
345 this.lineTo(x, y + h);
351 * Draws a rectangle with rounded corners.
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
361 drawRoundRect: function(x, y, w, h, ew, eh) {
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);
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.
394 drawWedge: function(x, y, startAngle, arc, radius, yRadius)
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";
406 * Completes a drawing operation.
419 * Specifies a gradient to use for the stroke when drawing lines.
422 * @method lineGradientStyle
425 lineGradientStyle: function() {
429 * Specifies a line style used for subsequent calls to drawing methods.
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.
436 lineStyle: function(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit) {
438 this._strokeWeight = thickness;
440 this._strokeColor = color;
442 this._strokeAlpha = Y.Lang.isNumber(alpha) ? alpha : 1;
446 * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
449 * @param {Number} point1 x-coordinate for the end point.
450 * @param {Number} point2 y-coordinate for the end point.
452 lineTo: function(point1, point2, etc) {
453 var args = arguments,
456 if (typeof point1 === 'string' || typeof point1 === 'number') {
457 args = [[point1, point2]];
460 this._shapeType = "path";
461 if(this.path.indexOf("L") < 0 || this._pathType !== "L")
463 this._pathType = "L";
466 for (i = 0; i < len; ++i) {
467 this.path += args[i][0] + ', ' + args[i][1] + " ";
469 this._trackSize.apply(this, args[i]);
474 * Moves the current drawing position to specified x and y coordinates.
477 * @param {Number} x x-coordinate for the end point.
478 * @param {Number} y y-coordinate for the end point.
480 moveTo: function(x, y) {
481 this._pathType = "M";
482 this.path += ' M' + x + ', ' + y;
486 * Generates a path string for a wedge shape
488 * @method _getWedgePath
489 * @param {Object} config attributes used to create the path
493 _getWedgePath: function(config)
497 startAngle = config.startAngle,
499 radius = config.radius,
500 yRadius = config.yRadius || radius,
513 path = ' M' + x + ', ' + y;
515 // limit sweep to reasonable numbers
516 if(Math.abs(arc) > 360)
521 // First we calculate how many segments are needed
523 segs = Math.ceil(Math.abs(arc) / 45);
525 // Now calculate the sweep of each segment.
526 segAngle = arc / segs;
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;
532 // convert angle startAngle to radians
533 angle = (startAngle / 180) * Math.PI;
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);
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) + " ";
551 path += ' L' + x + ", " + y;
557 * Sets the size of the graphics object.
560 * @param w {Number} width to set for the instance.
561 * @param h {Number} height to set for the instance.
563 setSize: function(w, h) {
566 if(w > this.node.getAttribute("width"))
568 this.node.setAttribute("width", w);
570 if(h > this.node.getAttribute("height"))
572 this.node.setAttribute("height", h);
578 * Updates the size of the graphics object
581 * @param {Number} w width
582 * @param {Number} h height
585 _trackSize: function(w, h) {
586 if (w > this._width) {
589 if (h > this._height) {
596 * Sets the positon of the graphics object.
598 * @method setPosition
599 * @param {Number} x x-coordinate for the object.
600 * @param {Number} y y-coordinate for the object.
602 setPosition: function(x, y)
604 this.node.setAttribute("x", x);
605 this.node.setAttribute("y", y);
609 * Adds the graphics node to the dom.
612 * @param {HTMLElement} parentNode node in which to render the graphics node into.
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);
625 * Clears the properties
630 _initProps: function() {
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;
652 this._stroked = false;
653 this._pathType = null;
654 this._attributes = {};
658 * Clears path properties
663 _clearPath: function()
666 this._shapeType = null;
672 this._pathType = null;
673 this._attributes = {};
684 var shape = this._createGraphicNode(this._shapeType),
693 shape.setAttribute("d", this.path);
697 for(i in this._attributes)
699 if(this._attributes.hasOwnProperty(i))
701 shape.setAttribute(i, this._attributes[i]);
705 shape.setAttribute("stroke-width", this._strokeWeight);
706 if(this._strokeColor)
708 shape.setAttribute("stroke", this._strokeColor);
709 shape.setAttribute("stroke-opacity", this._strokeAlpha);
711 if(!this._fillType || this._fillType === "solid")
715 shape.setAttribute("fill", this._fillColor);
716 shape.setAttribute("fill-opacity", this._fillAlpha);
720 shape.setAttribute("fill", "none");
723 else if(this._fillType === "linear")
725 gradFill = this._getFill();
726 gradFill.setAttribute("id", this._gradientId);
727 this._defs.appendChild(gradFill);
728 shape.setAttribute("fill", "url(#" + this._gradientId + ")");
731 this.node.appendChild(shape);
736 * Returns ths actual fill object to be used in a drawing or shape
741 _getFill: function() {
742 var type = this._fillType,
747 fill = this._getLinearGradient('fill');
750 //fill = this._getRadialGradient('fill');
753 //fill = this._bitmapFill;
760 * Returns a linear gradient fill
762 * @method _getLinearGradient
763 * @param {String} type gradient type
766 _getLinearGradient: function(type) {
767 var fill = this._createGraphicNode("linearGradient"),
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'],
792 else if(r > 90 && r < 180)
795 r = 90 + ((r-90) * w/h);
798 radCon = Math.PI/180;
799 tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
800 if(Math.abs(tanRadians) * w/2 >= h/2)
812 x1 = cx - ((cy - y1)/tanRadians);
813 x2 = cx - ((cy - y2)/tanRadians);
817 if(r > 90 && r < 270)
827 y1 = ((tanRadians * (cx - x1)) - cy) * -1;
828 y2 = ((tanRadians * (cx - x2)) - cy) * -1;
831 fill.setAttribute("spreadMethod", "pad");
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) + "%");
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");
844 for(i = 0; i < l; ++i)
848 ratio = ratios[i] || i/(l - 1);
849 ratio = Math.round(ratio * 100) + "%";
850 alpha = Y.Lang.isNumber(alpha) ? alpha : "1";
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);
862 * Creates a group element
864 * @method _createGraphics
867 _createGraphics: function() {
868 var group = this._createGraphicNode("svg");
869 this._styleGroup(group);
874 * Styles a group element
876 * @method _styleGroup
879 _styleGroup: function(group)
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");
889 * Creates a graphic node
891 * @method _createGraphicNode
892 * @param {String} type node type to create
893 * @param {String} pe specified pointer-events value
894 * @return HTMLElement
897 _createGraphicNode: function(type, pe)
899 var node = document.createElementNS("http://www.w3.org/2000/svg", "svg:" + type),
901 if(type !== "defs" && type !== "stop" && type !== "linearGradient")
903 node.setAttribute("pointer-events", v);
907 if(!this._graphicsList)
909 this._graphicsList = [];
911 this._graphicsList.push(node);
917 * Creates a Shape instance and adds it to the graphics object.
920 * @param {Object} config Object literal of properties used to construct a Shape.
923 getShape: function(config) {
924 config.graphic = this;
925 return new Y.Shape(config);
932 * Set of drawing apis for canvas based classes.
934 * @class CanvasDrawingUtil
937 function CanvasDrawingUtil()
939 this.initializer.apply(this, arguments);
942 CanvasDrawingUtil.prototype = {
944 * Initializes the class.
946 * @method initializer
949 initializer: function(config) {
950 this._dummy = this._createDummy();
951 this._canvas = this._createGraphic();
952 this._context = this._canvas.getContext('2d');
957 * Specifies a bitmap fill used by subsequent calls to other drawing methods.
959 * @method beginBitmapFill
960 * @param {Object} config
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);
976 * Specifes a solid fill used by subsequent calls to other drawing methods.
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.
982 beginFill: function(color, alpha) {
983 var context = this._context;
987 color = this._2RGBA(color, alpha);
989 color = this._2RGB(color);
992 this._fillColor = color;
993 this._fillType = 'solid';
999 * Specifies a gradient fill used by subsequent calls to other drawing methods.
1001 * @method beginGradientFill
1002 * @param {Object} config
1004 beginGradientFill: function(config) {
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;
1025 color = this._2RGBA(color, alpha);
1027 color = this._2RGB(color);
1031 this._context.beginPath();
1036 * Specifies a line style used for subsequent calls to drawing methods.
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.
1043 lineStyle: function(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit) {
1044 color = color || '#000000';
1045 var context = this._context;
1050 context.lineWidth = thickness;
1059 this._strokeStyle = color;
1061 this._strokeStyle = this._2RGBA(this._strokeStyle, alpha);
1067 context.beginPath();
1070 if (caps === 'butt') {
1074 if (context.lineCap) { // FF errors when trying to set
1075 //context.lineCap = caps;
1077 this._drawingComplete = false;
1082 * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
1085 * @param {Number} point1 x-coordinate for the end point.
1086 * @param {Number} point2 y-coordinate for the end point.
1088 lineTo: function(point1, point2, etc) {
1089 var args = arguments,
1090 context = this._context,
1092 if (typeof point1 === 'string' || typeof point1 === 'number') {
1093 args = [[point1, point2]];
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]);
1101 this._drawingComplete = false;
1106 * Moves the current drawing position to specified x and y coordinates.
1109 * @param {Number} x x-coordinate for the end point.
1110 * @param {Number} y y-coordinate for the end point.
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;
1121 * Clears the graphics object.
1127 this._canvas.width = this._canvas.width;
1128 this._canvas.height = this._canvas.height;
1133 * Draws a bezier curve.
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.
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);
1153 * Draws a quadratic bezier curve.
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.
1161 quadraticCurveTo: function(controlX, controlY, anchorX, anchorY) {
1162 this._context.quadraticCurveTo(controlX, controlY, anchorX, anchorY);
1163 this._drawingComplete = false;
1164 this._updateShapeProps(anchorX, anchorY);
1171 * @method drawCircle
1172 * @param {Number} x y-coordinate
1173 * @param {Number} y x-coordinate
1174 * @param {Number} r radius
1176 drawCircle: function(x, y, radius) {
1177 var context = this._context,
1179 endAngle = 2 * Math.PI;
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);
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
1204 drawEllipse: function(x, y, w, h) {
1211 if(this._stroke && this._context.lineWidth > 0)
1213 w -= this._context.lineWidth * 2;
1214 h -= this._context.lineWidth * 2;
1215 x += this._context.lineWidth;
1216 y += this._context.lineWidth;
1218 var context = this._context,
1220 theta = -(45/180) * Math.PI,
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);
1233 context.beginPath();
1234 ax = centerX + Math.cos(0) * radius;
1235 ay = centerY + Math.sin(0) * yRadius;
1236 context.moveTo(ax, ay);
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);
1253 * Draws a rectangle.
1256 * @param {Number} x x-coordinate
1257 * @param {Number} y y-coordinate
1258 * @param {Number} w width
1259 * @param {Number} h height
1261 drawRect: function(x, y, w, h) {
1262 var ctx = this._context;
1269 this._drawingComplete = false;
1272 ctx.lineTo(x + w, y);
1273 ctx.lineTo(x + w, y + h);
1274 ctx.lineTo(x, y + h);
1276 this._trackPos(x, y);
1277 this._trackSize(w, h);
1283 * Draws a rectangle with rounded corners.
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
1293 drawRoundRect: function(x, y, w, h, ew, eh) {
1300 var ctx = this._context;
1301 this._drawingComplete = false;
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);
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.
1329 drawWedge: function(cfg)
1333 startAngle = cfg.startAngle,
1335 radius = cfg.radius,
1336 yRadius = cfg.yRadius,
1350 this._drawingComplete = false;
1351 // move to x,y position
1354 yRadius = yRadius || radius;
1356 // limit sweep to reasonable numbers
1357 if(Math.abs(arc) > 360)
1362 // First we calculate how many segments are needed
1363 // for a smooth arc.
1364 segs = Math.ceil(Math.abs(arc) / 45);
1366 // Now calculate the sweep of each segment.
1367 segAngle = arc / segs;
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;
1373 // convert angle startAngle to radians
1374 angle = (startAngle / 180) * Math.PI;
1376 // draw the curve in segments no larger than 45 degrees.
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)
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);
1394 // close the wedge by drawing a line to the center
1397 this._trackPos(x, y);
1398 this._trackSize(radius, radius);
1403 * Completes a drawing operation.
1416 * Specifies a gradient to use for the stroke when drawing lines.
1418 lineGradientStyle: function() {
1423 * Sets the size of the graphics object.
1426 * @param w {Number} width to set for the instance.
1427 * @param h {Number} height to set for the instance.
1429 setSize: function(w, h)
1431 this._canvas.width = w;
1432 this._canvas.height = h;
1438 * @method _initProps
1441 _initProps: function() {
1442 var context = this._context;
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)';
1453 //this._shape = null;
1456 this._fillType = null;
1457 this._stroke = null;
1458 this._bitmapFill = null;
1459 this._drawingComplete = false;
1463 * Returns ths actual fill object to be used in a drawing or shape
1468 _getFill: function() {
1469 var type = this._fillType,
1474 fill = this._getLinearGradient('fill');
1478 fill = this._getRadialGradient('fill');
1481 fill = this._bitmapFill;
1484 fill = this._fillColor;
1491 * Returns a linear gradient fill
1493 * @method _getLinearGradient
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'],
1515 radCon = Math.PI/180,
1516 tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
1517 if(Math.abs(tanRadians) * w/2 >= h/2)
1529 x1 = cx - ((cy - y1)/tanRadians);
1530 x2 = cx - ((cy - y2)/tanRadians);
1534 if(r > 90 && r < 270)
1544 y1 = ((tanRadians * (cx - x1)) - cy) * -1;
1545 y2 = ((tanRadians * (cx - x2)) - cy) * -1;
1547 grad = ctx.createLinearGradient(x1, y1, x2, y2);
1550 for(i = 0; i < l; ++i)
1553 ratio = ratios[i] || i/(l - 1);
1554 grad.addColorStop(ratio, color);
1562 * Returns a radial gradient fill
1564 * @method _getRadialGradient
1567 _getRadialGradient: function(type) {
1568 var prop = '_' + type,
1569 colors = this[prop + "Colors"],
1570 ratios = this[prop + "Ratios"],
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,
1581 ctx = this._context;
1584 grad = ctx.createRadialGradient(x, y, 1, x, y, w/2);
1587 for(i = 0; i < l; ++i) {
1589 ratio = ratios[i] || i/(l - 1);
1590 grad.addColorStop(ratio, color);
1596 * Completes a shape or drawing
1603 if(this._drawingComplete || !this._shape)
1607 var context = this._context,
1610 if (this._fillType) {
1611 fill = this._getFill();
1613 context.fillStyle = fill;
1615 context.closePath();
1618 if (this._fillType) {
1623 context.strokeStyle = this._strokeStyle;
1626 this._drawingComplete = true;
1632 _drawingComplete: false,
1635 * Regex expression used for converting hex strings to rgb
1640 _reHex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
1643 * Parses hex color string and alpha value to rgba
1648 _2RGBA: function(val, alpha) {
1649 alpha = (alpha !== undefined) ? alpha : 1;
1650 if (this._reHex.exec(val)) {
1652 parseInt(RegExp.$1, 16),
1653 parseInt(RegExp.$2, 16),
1654 parseInt(RegExp.$3, 16)
1655 ].join(',') + ',' + alpha + ')';
1661 * Creates dom element used for converting color string to rgb
1663 * @method _createDummy
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);
1676 * Creates canvas element
1678 * @method _createGraphic
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;
1690 * Converts color to rgb format
1695 _2RGB: function(val) {
1696 this._dummy.style.background = val;
1697 return this._dummy.style.backgroundColor;
1701 * Updates the size of the graphics object
1703 * @method _trackSize
1704 * @param {Number} w width
1705 * @param {Number} h height
1708 _trackSize: function(w, h) {
1709 if (w > this._width) {
1712 if (h > this._height) {
1718 * Updates the position of the current drawing
1721 * @param {Number} x x-coordinate
1722 * @param {Number} y y-coordinate
1725 _trackPos: function(x, y) {
1735 * Updates the position and size of the current drawing
1737 * @method _updateShapeProps
1738 * @param {Number} x x-coordinate
1739 * @param {Number} y y-coordinate
1742 _updateShapeProps: function(x, y)
1755 this._shape.x = Math.min(this._shape.x, x);
1763 this._shape.y = Math.min(this._shape.y, y);
1765 w = Math.abs(x - this._shape.x);
1772 this._shape.w = Math.max(w, this._shape.w);
1774 h = Math.abs(y - this._shape.y);
1781 this._shape.h = Math.max(h, this._shape.h);
1786 * Creates a Shape instance and adds it to the graphics object.
1789 * @param {Object} config Object literal of properties used to construct a Shape.
1792 getShape: function(config) {
1793 config.graphic = this;
1794 return new Y.Shape(config);
1798 Y.CanvasDrawingUtil = CanvasDrawingUtil;
1800 * CanvasGraphics is a fallback drawing api used for basic drawing operations when SVG is not available.
1802 * @class CanvasGraphics
1805 Y.CanvasGraphic = Y.Base.create("graphic", Y.CanvasDrawingUtil, [], {
1809 * Sets the size of the graphics object.
1812 * @param w {Number} width to set for the instance.
1813 * @param h {Number} height to set for the instance.
1815 setSize: function(w, h) {
1818 if(w > this.node.getAttribute("width"))
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);
1825 if(h > this.node.getAttribute("height"))
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);
1836 * Updates the size of the graphics object
1838 * @method _trackSize
1839 * @param {Number} w width
1840 * @param {Number} h height
1843 _trackSize: function(w, h) {
1844 if (w > this._width) {
1847 if (h > this._height) {
1854 * Sets the positon of the graphics object.
1856 * @method setPosition
1857 * @param {Number} x x-coordinate for the object.
1858 * @param {Number} y y-coordinate for the object.
1860 setPosition: function(x, y)
1862 this.node.style.left = x + "px";
1863 this.node.style.top = y + "px";
1867 * Adds the graphics node to the dom.
1870 * @param {HTMLElement} parentNode node in which to render the graphics node into.
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";
1892 * Shows and and hides a the graphic instance.
1894 * @method toggleVisible
1895 * @param val {Boolean} indicates whether the instance should be visible.
1897 toggleVisible: function(val)
1899 this.node.style.visibility = val ? "visible" : "hidden";
1903 * Creates a graphic node
1905 * @method _createGraphicNode
1906 * @param {String} type node type to create
1907 * @param {String} pe specified pointer-events value
1908 * @return HTMLElement
1911 _createGraphicNode: function(pe)
1913 var node = Y.config.doc.createElement('canvas');
1914 node.style.pointerEvents = pe || "none";
1915 if(!this._graphicsList)
1917 this._graphicsList = [];
1919 this._graphicsList.push(node);
1924 * Removes all nodes.
1930 this._removeChildren(this.node);
1931 if(this.node && this.node.parentNode)
1933 this.node.parentNode.removeChild(this.node);
1938 * Removes all child nodes.
1940 * @method _removeChildren
1941 * @param {HTMLElement} node
1944 _removeChildren: function(node)
1946 if(node.hasChildNodes())
1949 while(node.firstChild)
1951 child = node.firstChild;
1952 this._removeChildren(child);
1953 node.removeChild(child);
1960 * Reference to the node for the graphics object
1965 if(DRAWINGAPI == "canvas")
1967 Y.Graphic = Y.CanvasGraphic;
1970 * VMLGraphics is a fallback drawing api used for basic drawing operations when SVG is not available.
1972 * @class VMLGraphics
1975 var VMLGraphics = function(config) {
1977 this.initializer.apply(this, arguments);
1980 VMLGraphics.prototype = {
1982 * Indicates whether or not the instance will size itself based on its contents.
1984 * @property autoSize
1987 initializer: function(config) {
1988 config = config || {};
1989 var w = config.width || 0,
1990 h = config.height || 0;
1991 this.node = this._createGraphics();
1997 * Specifies a bitmap fill used by subsequent calls to other drawing methods.
1999 * @method beginBitmapFill
2000 * @param {Object} config
2002 beginBitmapFill: function(config) {
2005 fill.src = config.bitmap.src;
2007 this._fillProps = fill;
2008 if(!isNaN(config.tx) ||
2009 !isNaN(config.ty) ||
2010 !isNaN(config.width) ||
2011 !isNaN(config.height))
2013 this._gradientBox = {
2017 height:config.height
2022 this._gradientBox = null;
2027 * Specifes a solid fill used by subsequent calls to other drawing methods.
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.
2033 beginFill: function(color, alpha) {
2035 if (Y.Lang.isNumber(alpha)) {
2041 this._fillColor = color;
2048 * Specifies a gradient fill used by subsequent calls to other drawing methods.
2050 * @method beginGradientFill
2051 * @param {Object} config
2053 beginGradientFill: function(config) {
2054 var type = config.type,
2055 colors = config.colors,
2056 alphas = config.alphas || [],
2057 ratios = config.ratios || [],
2062 len = alphas.length,
2066 rotation = config.rotation || 0;
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;
2076 if(type === "linear")
2081 if(rotation > 0 && rotation <= 90)
2083 rotation = 450 - rotation;
2085 else if(rotation <= 270)
2087 rotation = 270 - rotation;
2089 else if(rotation <= 360)
2091 rotation = 630 - rotation;
2097 fill.type = "gradientunscaled";
2098 fill.angle = rotation;
2100 else if(type === "radial")
2102 fill.alignshape = false;
2103 fill.type = "gradientradial";
2104 fill.focus = "100%";
2105 fill.focusposition = "50%,50%";
2107 fill.ratios = ratios || [];
2109 if(!isNaN(config.tx) ||
2110 !isNaN(config.ty) ||
2111 !isNaN(config.width) ||
2112 !isNaN(config.height))
2114 this._gradientBox = {
2118 height:config.height
2123 this._gradientBox = null;
2125 this._fillProps = fill;
2129 * Clears the graphics object.
2135 this._removeChildren(this.node);
2139 * Removes all nodes.
2145 this._removeChildren(this.node);
2146 this.node.parentNode.removeChild(this.node);
2150 * Removes all child nodes.
2152 * @method _removeChildren
2156 _removeChildren: function(node)
2158 if(node.hasChildNodes())
2161 while(node.firstChild)
2163 child = node.firstChild;
2164 this._removeChildren(child);
2165 node.removeChild(child);
2171 * Shows and and hides a the graphic instance.
2173 * @method toggleVisible
2174 * @param val {Boolean} indicates whether the instance should be visible.
2176 toggleVisible: function(val)
2178 this._toggleVisible(this.node, val);
2182 * Toggles visibility
2184 * @method _toggleVisible
2185 * @param {HTMLElement} node element to toggle
2186 * @param {Boolean} val indicates visibilitye
2189 _toggleVisible: function(node, val)
2191 var children = Y.one(node).get("children"),
2192 visibility = val ? "visible" : "hidden",
2197 len = children.length;
2200 this._toggleVisible(children[i], val);
2203 node.style.visibility = visibility;
2207 * Draws a bezier curve.
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.
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);
2224 * Draws a quadratic bezier curve.
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.
2232 quadraticCurveTo: function(cpx, cpy, x, y) {
2233 this._path += ' qb ' + cpx + ", " + cpy + ", " + x + ", " + y;
2239 * @method drawCircle
2240 * @param {Number} x y-coordinate
2241 * @param {Number} y x-coordinate
2242 * @param {Number} r radius
2244 drawCircle: function(x, y, r) {
2245 this._width = this._height = r * 2;
2248 this._shape = "oval";
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
2261 drawEllipse: function(x, y, w, h) {
2266 this._shape = "oval";
2271 * Draws a rectangle.
2274 * @param {Number} x x-coordinate
2275 * @param {Number} y y-coordinate
2276 * @param {Number} w width
2277 * @param {Number} h height
2279 drawRect: function(x, y, w, h) {
2285 this.lineTo(x + w, y);
2286 this.lineTo(x + w, y + h);
2287 this.lineTo(x, y + h);
2293 * Draws a rectangle with rounded corners.
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
2303 drawRoundRect: function(x, y, w, h, ew, eh) {
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);
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.
2330 drawWedge: function(x, y, startAngle, arc, radius, yRadius)
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";
2344 * Generates a path string for a wedge shape
2346 * @method _getWedgePath
2347 * @param {Object} config attributes used to create the path
2351 _getWedgePath: function(config)
2355 startAngle = config.startAngle,
2357 radius = config.radius,
2358 yRadius = config.yRadius || radius,
2360 if(Math.abs(arc) > 360)
2364 startAngle *= -65535;
2366 path = " m " + x + " " + y + " ae " + x + " " + y + " " + radius + " " + yRadius + " " + startAngle + " " + arc;
2371 * Completes a drawing operation.
2384 * Specifies a gradient to use for the stroke when drawing lines.
2387 * @method lineGradientStyle
2390 lineGradientStyle: function() {
2394 * Specifies a line style used for subsequent calls to drawing methods.
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.
2401 lineStyle: function(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit) {
2403 this._strokeWeight = thickness * 0.7;
2404 this._strokeColor = color;
2405 this._strokeOpacity = Y.Lang.isNumber(alpha) ? alpha : 1;
2409 * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
2412 * @param {Number} point1 x-coordinate for the end point.
2413 * @param {Number} point2 y-coordinate for the end point.
2415 lineTo: function(point1, point2, etc) {
2416 var args = arguments,
2419 if (typeof point1 === 'string' || typeof point1 === 'number') {
2420 args = [[point1, point2]];
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]);
2432 * Moves the current drawing position to specified x and y coordinates.
2435 * @param {Number} x x-coordinate for the end point.
2436 * @param {Number} y y-coordinate for the end point.
2438 moveTo: function(x, y) {
2439 this._path += ' m ' + Math.round(x) + ', ' + Math.round(y);
2443 * Sets the size of the graphics object.
2446 * @param w {Number} width to set for the instance.
2447 * @param h {Number} height to set for the instance.
2449 setSize: function(w, 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;
2460 * Sets the positon of the graphics object.
2462 * @method setPosition
2463 * @param {Number} x x-coordinate for the object.
2464 * @param {Number} y y-coordinate for the object.
2466 setPosition: function(x, y)
2470 this.node.style.left = x + "px";
2471 this.node.style.top = y + "px";
2475 * Adds the graphics node to the dom.
2478 * @param {HTMLElement} parentNode node in which to render the graphics node into.
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);
2496 * Updates the size of the graphics object
2498 * @method _trackSize
2499 * @param {Number} w width
2500 * @param {Number} h height
2503 _trackSize: function(w, h) {
2504 if (w > this._width) {
2507 if (h > this._height) {
2513 * Clears the properties
2515 * @method _initProps
2518 _initProps: function() {
2519 this._fillColor = null;
2520 this._strokeColor = null;
2521 this._strokeOpacity = null;
2522 this._strokeWeight = 0;
2523 this._fillProps = null;
2531 this._stroked = false;
2535 * Clears path properties
2537 * @method _clearPath
2540 _clearPath: function()
2558 var shape = this._createGraphicNode(this._shape),
2559 w = Math.round(this._width),
2560 h = Math.round(this._height),
2562 fillProps = this._fillProps;
2566 if(this._fill || this._fillProps)
2574 shape.path = this._path;
2575 shape.coordSize = w + ', ' + h;
2579 shape.style.display = "block";
2580 shape.style.position = "absolute";
2581 shape.style.left = this._x + "px";
2582 shape.style.top = this._y + "px";
2586 shape.fillColor = this._fillColor;
2590 shape.filled = false;
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)
2597 strokeNode = this._createGraphicNode("stroke");
2598 shape.appendChild(strokeNode);
2599 strokeNode.opacity = this._strokeOpacity;
2602 shape.stroked = false;
2604 shape.style.width = w + 'px';
2605 shape.style.height = h + 'px';
2607 shape.filled = true;
2608 shape.appendChild(this._getFill());
2610 this.node.appendChild(shape);
2615 * Returns ths actual fill object to be used in a drawing or shape
2620 _getFill: function() {
2621 var fill = this._createGraphicNode("fill"),
2624 fillProps = this._fillProps,
2632 hyp = Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2)),
2635 if(this._gradientBox)
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;
2641 if(fillProps.colors)
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];
2651 if(parseInt(pct, 10) < 100)
2653 colorstring += ", 100% " + colors[len-1];
2656 for (prop in fillProps) {
2657 if(fillProps.hasOwnProperty(prop)) {
2658 fill.setAttribute(prop, fillProps[prop]);
2661 fill.colors = colorstring.substr(2);
2662 if(fillProps.type === "gradientradial")
2664 fill.focusposition = cx + "%," + cy + "%";
2670 * Creates a group element
2672 * @method _createGraphics
2675 _createGraphics: function() {
2676 var group = this._createGraphicNode("group");
2677 group.style.display = "inline-block";
2678 group.style.position = 'absolute';
2683 * Creates a graphic node
2685 * @method _createGraphicNode
2686 * @param {String} type node type to create
2687 * @param {String} pe specified pointer-events value
2688 * @return HTMLElement
2691 _createGraphicNode: function(type)
2693 return document.createElement('<' + type + ' xmlns="urn:schemas-microsft.com:vml" class="vml' + type + '"/>');
2698 * Converts a shape type to the appropriate vml node type.
2700 * @method _getNodeShapeType
2701 * @param {String} type The shape to convert.
2705 _getNodeShapeType: function(type)
2707 var shape = "shape";
2708 if(this._typeConversionHash.hasOwnProperty(type))
2710 shape = this._typeConversionHash[type];
2716 * Used to convert certain shape types to the appropriate vml node type.
2718 * @property _typeConversionHash
2722 _typeConversionHash: {
2729 * Creates a Shape instance and adds it to the graphics object.
2732 * @param {Object} config Object literal of properties used to construct a Shape.
2735 getShape: function(config) {
2736 config.graphic = this;
2737 return new Y.Shape(config);
2741 * Adds a child to the <code>node</code>.
2744 * @param {HTMLElement} element to add
2747 addChild: function(child)
2749 this.node.appendChild(child);
2753 if(DRAWINGAPI == "vml")
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;
2771 * The Shape class creates a graphic object with editable
2780 this._initialize(cfg);
2784 Y.extend(Shape, Y.Graphic, {
2786 * Indicates the type of shape.
2794 * Indicates whether or not the instance will size itself based on its contents.
2796 * @property autoSize
2802 * Determines whether the instance will receive mouse events.
2804 * @property pointerEvents
2807 pointerEvents: "visiblePainted",
2810 * Initializes the graphic instance.
2812 * @method _initialize
2815 _initialize: function(cfg)
2819 cfg.graphic = new Y.Graphic();
2821 this._setProps(cfg);
2825 * Updates properties for the shape.
2828 * @param {Object} cfg Properties to update.
2831 _setProps: function(cfg)
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;
2849 * Draws the graphic.
2860 parentNode = this.parentNode,
2862 fillWidth = this.width || 0,
2863 fillHeight = this.height || 0;
2866 this.node = this._createGraphicNode(this.nodetype, this.pointerEvents);
2867 parentNode.appendChild(this.node);
2869 if(this.type == "wedge")
2871 this.path = this._getWedgePath(this.props);
2873 if(this.nodetype == "path")
2877 if(this.border && this.border.weight && this.border.weight > 0)
2879 borderWeight = this.border.weight;
2880 fillWidth -= borderWeight * 2;
2881 fillHeight -= borderWeight * 2;
2884 if(this.nodetype === "ellipse")
2892 this.node.setAttribute("cx", cx);
2893 this.node.setAttribute("cy", cy);
2894 this.node.setAttribute("rx", rx);
2895 this.node.setAttribute("ry", ry);
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";
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);
2916 * Adds a path to the shape node.
2921 _setPath: function()
2926 this.node.setAttribute("d", this.path);
2931 * Adds a border to the shape node.
2933 * @method _addBorder
2936 _addBorder: function()
2938 if(this.border && this.border.weight && this.border.weight > 0)
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);
2952 this.node.setAttribute("stroke", "none");
2957 * Adds a fill to the shape node.
2962 _addFill: function()
2965 if(this.fill.type === "linear" || this.fill.type === "radial")
2967 this.beginGradientFill(this.fill);
2968 this.node.appendChild(this._getFill());
2970 else if(this.fill.type === "bitmap")
2972 this.beginBitmapFill(this.fill);
2973 this.node.appendChild(this._getFill());
2977 if(!this.fill.color)
2979 this.node.setAttribute("fill", "none");
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);
2992 * Completes a drawing operation.
3002 * Updates the properties of the shape instance.
3005 * @param {Object} cfg Object literal containing properties to update.
3007 update: function(cfg)
3009 this._setProps(cfg);
3015 * Converts a shape type to the appropriate node attribute.
3018 * @method _getNodeShapeType
3019 * @param {String} type The type of shape.
3022 _getNodeShapeType: function(type)
3024 if(this._typeConversionHash.hasOwnProperty(type))
3026 type = this._typeConversionHash[type];
3032 * Sets the visibility of a shape.
3034 * @method toggleVisible
3035 * @param {Boolean} val indicates whether or not the shape is visible.
3037 toggleVisible: function(val)
3039 var visibility = val ? "visible" : "hidden";
3042 this.node.style.visibility = visibility;
3047 * Adds a class to the shape's node.
3050 * @param {String} className Name of the class to add.
3052 addClass: function(className)
3054 var node = this.node;
3057 if(node.className && node.className.baseVal)
3059 node.className.baseVal = Y.Lang.trim([node.className.baseVal, className].join(' '));
3063 node.setAttribute("class", className);
3069 * Positions the parent node of the shape.
3071 * @method setPosition
3072 * @param {Number}, x The x-coordinate
3073 * @param {Number}, y The y-coordinate
3075 setPosition: function(x, y)
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);
3084 hotspot.setStyle("position", "absolute");
3085 hotspot.setStyle("left", x);
3086 hotspot.setStyle("top", y);
3091 * Used to convert shape declarations to the appropriate node type.
3093 * @property _typeConversionHash
3097 _typeConversionHash: {
3105 * The Shape class creates a graphic object with editable
3108 * @class CanvasShape
3109 * @extends CanvasGraphic
3112 function CanvasShape(cfg)
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);
3122 Y.extend(CanvasShape, Y.CanvasDrawingUtil, {
3124 * Indicates the type of shape.
3132 * Indicates whether or not the instance will size itself based on its contents.
3134 * @property autoSize
3140 * Initializes the graphic instance.
3142 * @method _initialize
3145 _initialize: function(cfg)
3147 this._canvas.style.position = "absolute";
3150 cfg.graphic.node.appendChild(this._canvas);
3152 this._setProps(cfg);
3156 * Updates properties for the shape.
3159 * @param {Object} cfg Properties to update.
3162 _setProps: function(cfg)
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;
3178 * Draws the graphic.
3183 _validate: function()
3187 border = this.border,
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)
3196 border.color = border.color || "#000";
3197 border.alpha = border.alpha || 1;
3198 this.lineStyle(border.weight, border.color, border.alpha);
3200 if(fill.type === "radial" || fill.type === "linear")
3202 this.beginGradientFill(fill);
3204 else if(fill.type === "bitmap")
3206 this.beginBitmapFill(fill);
3210 this.beginFill(fill.color, fill.alpha);
3215 this.drawEllipse(0, 0, w, h);
3218 this.drawRect(0, 0, w, h);
3221 this.drawWedge(this.props);
3228 * Updates the properties of the shape instance.
3231 * @param {Object} cfg Object literal containing properties to update.
3233 update: function(cfg)
3235 this._setProps(cfg);
3241 * Sets the visibility of a shape.
3243 * @method toggleVisible
3244 * @param {Boolean} val indicates whether or not the shape is visible.
3246 toggleVisible: function(val)
3248 var visibility = val ? "visible" : "hidden";
3251 this.node.style.visibility = visibility;
3256 * Positions the parent node of the shape.
3258 * @method setPosition
3259 * @param {Number}, x The x-coordinate
3260 * @param {Number}, y The y-coordinate
3262 setPosition: function(x, y)
3264 var pNode = Y.one(this.parentNode);
3265 pNode.setStyle("position", "absolute");
3266 pNode.setStyle("left", x);
3267 pNode.setStyle("top", y);
3271 * Adds a class to the shape's node.
3274 * @param {String} className Name of the class to add.
3276 addClass: function(val)
3280 this.node.style.pointerEvents = "painted";
3281 this.node.setAttribute("class", val);
3286 Y.CanvasShape = CanvasShape;
3288 if(DRAWINGAPI == "canvas")
3290 Y.Shape = Y.CanvasShape;
3293 * VMLShape is a fallback class for Shape. It creates a graphic object with editable properties when
3294 * SVG is not available.
3299 function VMLShape(cfg)
3301 this._initialize(cfg);
3305 VMLShape.prototype = {
3307 * Indicates the type of shape.
3315 * Initializes the graphic instance.
3317 * @method _initialize
3320 _initialize: function(cfg)
3324 cfg.graphic = new Y.Graphic();
3326 this._setProps(cfg);
3340 * Updates properties for the shape.
3343 * @param {Object} cfg Properties to update.
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;
3359 * Draws the graphic.
3368 fillWidth = this.width || 0,
3369 fillHeight = this.height || 0;
3370 this.graphics.setSize(fillWidth, fillHeight);
3373 this.node.style.visible = "hidden";
3377 this.node = this.graphics._createGraphicNode(this.graphics._getNodeShapeType(this.type));
3378 this.graphics.node.appendChild(this.node);
3380 if(this.type === "wedge")
3382 path = this.graphics._getWedgePath(this.props);
3391 this.node.path = path;
3394 if(this.border && this.border.weight && this.border.weight > 0)
3396 borderWeight = this.border.weight;
3397 fillWidth -= borderWeight;
3398 fillHeight -= borderWeight;
3400 this.node.style.width = Math.max(fillWidth, 0) + "px";
3401 this.node.style.height = Math.max(fillHeight, 0) + "px";
3407 * Adds a border to the shape node.
3409 * @method _addBorder
3412 _addBorder: function()
3414 if(this.border && this.border.weight && this.border.weight > 0)
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;
3424 if(!this._strokeNode)
3426 this._strokeNode = this.graphics._createGraphicNode("stroke");
3427 this.node.appendChild(this._strokeNode);
3429 this._strokeNode.opacity = borderAlpha;
3431 else if(this._strokeNode)
3433 this._strokeNode.opacity = borderAlpha;
3435 this.node.stroked = true;
3439 this.node.stroked = false;
3444 * Adds a fill to the shape node.
3449 _addFill: function()
3452 this.node.filled = true;
3453 if(this.fill.type === "linear" || this.fill.type === "radial")
3455 this.graphics.beginGradientFill(this.fill);
3456 this.node.appendChild(this.graphics._getFill());
3458 else if(this.fill.type === "bitmap")
3460 this.graphics.beginBitmapFill(this.fill);
3461 this.node.appendChild(this.graphics._getFill());
3465 if(!this.fill.color)
3467 this.node.filled = false;
3473 this.graphics._removeChildren(this.fillnode);
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);
3488 * Adds a class to the shape's node.
3491 * @param {String} className Name of the class to add.
3493 addClass: function(val)
3495 var node = this.node;
3498 Y.one(node).addClass(val);
3503 * Sets the visibility of a shape.
3505 * @method toggleVisible
3506 * @param {Boolean} val indicates whether or not the shape is visible.
3508 toggleVisible: function(val)
3510 var visibility = val ? "visible" : "hidden";
3513 Y.one(this.node).setStyle("visibility", visibility);
3518 * Positions the parent node of the shape.
3520 * @method setPosition
3521 * @param {Number}, x The x-coordinate
3522 * @param {Number}, y The y-coordinate
3524 setPosition: function(x, y)
3526 var pNode = Y.one(this.parentNode);
3527 pNode.setStyle("position", "absolute");
3528 pNode.setStyle("left", x);
3529 pNode.setStyle("top", y);
3533 * Updates the properties of the shape instance.
3536 * @param {Object} cfg Object literal containing properties to update.
3538 update: function(cfg)
3540 this._setProps(cfg);
3546 Y.VMLShape = VMLShape;
3548 if (DRAWINGAPI == "vml") {
3552 * The Renderer class is a base class for chart components that use the <code>styles</code>
3558 function Renderer(){}
3562 * Hash of style properties for class
3571 this._styles = this._styles || this._getDefaultStyles();
3572 return this._styles;
3575 setter: function(val)
3577 this._styles = this._setStyles(val);
3582 * The graphic in which drawings will be rendered.
3584 * @attribute graphic
3589 Renderer.NAME = "renderer";
3591 Renderer.prototype = {
3600 * Method used by <code>styles</code> setter.
3602 * @method _setStyles
3603 * @param {Object} newStyles Hash of properties to update.
3606 _setStyles: function(newstyles)
3608 var styles = this.get("styles");
3609 return this._mergeStyles(newstyles, styles);
3615 * Merges to object literals so that only specified properties are
3618 * @method _mergeStyles
3619 * @param {Object} a Hash of new styles
3620 * @param {Object} b Hash of original styles
3623 _mergeStyles: function(a, b)
3629 var newstyles = Y.merge(b, {});
3630 Y.Object.each(a, function(value, key, a)
3632 if(b.hasOwnProperty(key) && Y.Lang.isObject(value) && !Y.Lang.isArray(value))
3634 newstyles[key] = this._mergeStyles(value, b[key]);
3638 newstyles[key] = value;
3647 * Gets the default value for the <code>styles</code> attribute.
3649 * @method _getDefaultStyles
3652 _getDefaultStyles: function()
3663 Y.augment(Renderer, Y.Attribute);
3664 Y.Renderer = Renderer;
3667 * The Axis class. Generates axes for a chart.
3673 Y.Axis = Y.Base.create("axis", Y.Widget, [Y.Renderer], {
3677 _dataChangeHandler: function(e)
3679 if(this.get("rendered"))
3688 _updateHandler: function(e)
3690 if(this.get("rendered"))
3699 _positionChangeHandler: function(e)
3701 var position = this.get("position");
3702 if(position == "none")
3706 this._layout =this.getLayout(this.get("position"));
3707 if(this.get("rendered"))
3716 renderUI: function()
3718 var pos = this.get("position");
3719 if(pos && pos != "none")
3721 this._layout =this.getLayout(pos);
3737 _setCanvas: function()
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")
3750 cb.setStyle("width", w);
3754 cb.setStyle("height", h);
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);
3766 * Gets the default value for the <code>styles</code> attribute. Overrides
3767 * base implementation.
3769 * @method _getDefaultStyles
3772 _getDefaultStyles: function()
3794 determinant:"count",
3814 hideOverlappingLabelTicks: false
3817 return Y.merge(Y.Renderer.prototype._getDefaultStyles(), axisstyles);
3823 _handleSizeChange: function(e)
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"))
3846 getLayout: function(pos)
3852 l = new Y.TopAxisLayout({axisRenderer:this});
3855 l = new Y.BottomAxisLayout({axisRenderer:this});
3858 l = new Y.LeftAxisLayout({axisRenderer:this});
3861 l = new Y.RightAxisLayout({axisRenderer:this});
3870 drawLine: function(startPoint, endPoint, line)
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);
3882 _drawAxis: function ()
3886 this._callLater = true;
3889 this._drawing = true;
3890 this._callLater = false;
3891 if(this.get("position") != "none")
3893 var styles = this.get("styles"),
3894 majorTickStyles = styles.majorTicks,
3895 drawTicks = majorTickStyles.display != "none",
3897 majorUnit = styles.majorUnit,
3905 layout = this._layout,
3906 labelFunction = this.get("labelFunction"),
3907 labelFunctionScope = this.get("labelFunctionScope"),
3908 labelFormat = this.get("labelFormat"),
3909 graphic = this.get("graphic");
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);
3921 layout.drawTick(tickPoint, majorTickStyles);
3925 this._clearLabelCache();
3928 this._createLabelCache();
3929 this._tickPoints = [];
3930 layout.set("maxLabelSize", 0);
3935 layout.drawTick(tickPoint, majorTickStyles);
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);
3942 this._clearLabelCache();
3943 layout.setSizeAndPosition();
3944 if(this.get("overlapGraph"))
3946 layout.offsetNodeForTick(this.get("contentBox"));
3948 layout.setCalculatedSize();
3949 for(i = 0; i < len; ++i)
3951 layout.positionLabel(this.get("labels")[i], this._tickPoints[i]);
3954 this._drawing = false;
3961 this.fire("axisRendered");
3978 getLabel: function(pt, pos)
3983 rotation: "rotation",
3987 cache = this._labelCache,
3988 styles = this.get("styles").label;
3989 if(cache.length > 0)
3991 label = cache.shift();
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);
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);
4007 if(styles.hasOwnProperty(i) && !customStyles.hasOwnProperty(i))
4009 label.style[i] = styles[i];
4018 _createLabelCache: function()
4022 if(this._labelCache)
4024 this._labelCache = this._labels.concat(this._labelCache);
4028 this._labelCache = this._labels.concat();
4033 this._clearLabelCache();
4041 _clearLabelCache: function()
4043 if(this._labelCache)
4045 var len = this._labelCache.length,
4048 labelCache = this._labelCache;
4051 label = labelCache[i];
4052 label.parentNode.removeChild(label);
4055 this._labelCache = [];
4061 _calculateSizeByTickLength: true,
4066 getLineEnd: function(pt)
4068 var w = this.get("width"),
4069 h = this.get("height"),
4070 pos = this.get("position");
4071 if(pos === "top" || pos === "bottom")
4073 return {x:w, y:pt.y};
4077 return {x:pt.x, y:h};
4084 getLength: function()
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")
4094 l = w - (padding.left + padding.right);
4098 l = h - (padding.top + padding.bottom);
4106 getFirstPoint:function(pt)
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")
4114 np.x += padding.left + this.get("edgeOffset");
4118 np.y += this.get("height") - (padding.top + this.get("edgeOffset"));
4126 getNextPoint: function(point, majorUnitDistance)
4128 var pos = this.get("position");
4129 if(pos === "top" || pos === "bottom")
4131 point.x = point.x + majorUnitDistance;
4135 point.y = point.y - majorUnitDistance;
4143 getLastPoint: function()
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")
4151 return {x:w - padding.right, y:padding.top};
4155 return {x:padding.left, y:padding.top};
4162 getPosition: function(point)
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")
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")
4176 p = (h - (padding.top + padding.bottom)) - (point.y - padding.top);
4180 p = point.y - padding.top;
4185 p = point.x - padding.left;
4195 * Difference betweend the first/last tick and edge of axis.
4197 * @attribute edgeOffset
4206 * The graphic in which the axis line and ticks will be rendered.
4208 * @attribute graphic
4214 * Contains the contents of the axis.
4222 * Direction of the axis.
4224 * @attribute position
4232 setter: function(val)
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.
4246 * @attribute topTickOffset
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.
4257 * @attribute bottomTickOffset
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.
4268 * @attribute leftTickOffset
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.
4279 * @attribute rightTickOffset
4287 * Collection of labels used to render the axis.
4296 return this._labels;
4301 * Collection of points used for placement of labels and ticks along the axis.
4303 * @attribute tickPoints
4311 if(this.get("position") == "none")
4313 return this.get("styles").majorUnit.count;
4315 return this._tickPoints;
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.
4323 * @attribute overlapGraph
4329 validator: function(val)
4331 return Y.Lang.isBoolean(val);
4336 * Object which should have by the labelFunction
4338 * @attribute labelFunctionScope
4341 labelFunctionScope: {}
4344 * Style properties used for drawing an axis. This attribute is inherited from <code>Renderer</code>. Below are the default values:
4346 * <dt>majorTicks</dt><dd>Properties used for drawing ticks.
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>
4356 * <dt>line</dt><dd>Properties used for drawing the axis line.
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>
4363 * <dt>majorUnit</dt><dd>Properties used to calculate the <code>majorUnit</code> for the axis.
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>
4372 * <dt>label</dt><dd>Properties and styles applied to the axis labels.
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.
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>
4396 * Algorithmic strategy for rendering a left axis.
4398 * @class LeftAxisLayout
4400 * @param {Object} config
4403 function LeftAxisLayout(config)
4405 LeftAxisLayout.superclass.constructor.apply(this, arguments);
4408 LeftAxisLayout.ATTRS = {
4410 * Reference to the <code>Axis</code> using the strategy.
4412 * @attribute axisRenderer
4428 Y.extend(LeftAxisLayout, Y.Base, {
4430 * Sets the length of the tick on either side of the axis line.
4432 * @method setTickOffset
4435 setTickOffsets: function()
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);
4448 ar.set("rightTickOffset", tickLength);
4449 ar.set("leftTickOffset", 0);
4452 ar.set("rightTickOffset", 0);
4453 ar.set("leftTickOffset", tickLength);
4456 ar.set("rightTickOffset", halfTick);
4457 ar.set("leftTickOffset", halfTick);
4460 ar.set("rightTickOffset", 0);
4461 ar.set("leftTickOffset", 0);
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.
4474 drawTick: function(pt, tickStyles)
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);
4486 * Calculates the coordinates for the first point on an axis.
4488 * @method getLineStart
4492 getLineStart: function()
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")
4505 else if(display === "cross")
4507 pt.x += tickLength/2;
4513 * Calculates the point for a label.
4515 * @method getLabelPoint
4516 * @param {Object} point Point on the axis in which the tick will intersect.
4520 getLabelPoint: function(point)
4522 var ar = this.get("axisRenderer");
4523 return {x:point.x - ar.get("leftTickOffset"), y:point.y};
4527 * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
4529 * @method updateMaxLabelSize
4530 * @param {HTMLElement} label to measure
4533 updateMaxLabelSize: function(label)
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)),
4543 m12 = rot > 0 ? -sinRadians : sinRadians,
4547 if(!document.createElementNS)
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));
4554 label.style.msTransform = "rotate(0deg)";
4557 max = label.offsetWidth;
4559 else if(absRot === 90)
4561 max = label.offsetHeight;
4565 max = (cosRadians * label.offsetWidth) + (sinRadians * label.offsetHeight);
4567 this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), max));
4572 * Rotate and position labels.
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
4580 positionLabel: function(label, pt)
4582 var ar = this.get("axisRenderer"),
4583 tickOffset = ar.get("leftTickOffset"),
4584 style = ar.get("styles").label,
4585 labelAlpha = style.alpha,
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)),
4596 m12 = rot > 0 ? -sinRadians : sinRadians,
4599 maxLabelSize = this.get("maxLabelSize"),
4600 labelWidth = Math.round(label.offsetWidth),
4601 labelHeight = Math.round(label.offsetHeight);
4602 if(style.margin && style.margin.right)
4604 margin = style.margin.right;
4606 if(!document.createElementNS)
4608 label.style.filter = null;
4609 labelWidth = Math.round(label.offsetWidth);
4610 labelHeight = Math.round(label.offsetHeight);
4613 leftOffset = labelWidth;
4614 topOffset -= labelHeight * 0.5;
4616 else if(absRot === 90)
4618 leftOffset = labelHeight;
4619 topOffset -= labelWidth * 0.5;
4623 leftOffset = (cosRadians * labelWidth) + (labelHeight * rot/90);
4624 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight * 0.5));
4628 leftOffset = (cosRadians * labelWidth) + (absRot/90 * labelHeight);
4629 topOffset -= cosRadians * (labelHeight * 0.5);
4631 leftOffset += tickOffset;
4632 label.style.left = ((pt.x + maxLabelSize) - leftOffset) + "px";
4633 label.style.top = topOffset + "px";
4636 filterString += " ";
4638 if(Y.Lang.isNumber(labelAlpha) && labelAlpha < 1 && labelAlpha > -1 && !isNaN(labelAlpha))
4640 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
4646 filterString += " ";
4652 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
4656 label.style.filter = filterString;
4660 label.style.msTransform = "rotate(0deg)";
4661 labelWidth = Math.round(label.offsetWidth);
4662 labelHeight = Math.round(label.offsetHeight);
4665 leftOffset -= labelWidth;
4666 topOffset -= labelHeight * 0.5;
4670 topOffset -= labelWidth * 0.5;
4672 else if(rot === -90)
4674 leftOffset -= labelHeight;
4675 topOffset += labelWidth * 0.5;
4681 leftOffset -= (cosRadians * labelWidth) + (sinRadians * labelHeight);
4682 topOffset += (sinRadians * labelWidth) - (cosRadians * (labelHeight * 0.6));
4686 leftOffset -= (cosRadians * labelWidth);
4687 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight * 0.6));
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)";
4706 * Calculates the size and positions the content elements.
4708 * @method setSizeAndPosition
4711 setSizeAndPosition: function()
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)
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);
4731 * Adjust the position of the Axis widget's content box for internal axes.
4733 * @method offsetNodeForTick
4734 * @param {Node} cb Content box of the Axis.
4737 offsetNodeForTick: function(cb)
4742 * Sets the width of the axis based on its contents.
4744 * @method setCalculatedSize
4747 setCalculatedSize: function()
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);
4760 Y.LeftAxisLayout = LeftAxisLayout;
4762 * RightAxisLayout contains algorithms for rendering a right axis.
4765 * @class RightAxisLayout
4767 * @param {Object} config
4769 function RightAxisLayout(config)
4771 RightAxisLayout.superclass.constructor.apply(this, arguments);
4774 RightAxisLayout.ATTRS = {
4776 * Reference to the <code>Axis</code> using the strategy.
4778 * @attribute axisRenderer
4787 Y.extend(RightAxisLayout, Y.Base, {
4789 * Sets the length of the tick on either side of the axis line.
4791 * @method setTickOffset
4794 setTickOffsets: function()
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);
4807 ar.set("leftTickOffset", tickLength);
4808 ar.set("rightTickOffset", 0);
4811 ar.set("leftTickOffset", 0);
4812 ar.set("rightTickOffset", tickLength);
4815 ar.set("rightTickOffset", halfTick);
4816 ar.set("leftTickOffset", halfTick);
4819 ar.set("leftTickOffset", 0);
4820 ar.set("rightTickOffset", 0);
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.
4833 drawTick: function(pt, tickStyles)
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);
4845 * Calculates the coordinates for the first point on an axis.
4847 * @method getLineStart
4851 getLineStart: function()
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")
4864 else if(display === "cross")
4866 pt.x += tickLength/2;
4872 * Calculates the point for a label.
4874 * @method getLabelPoint
4875 * @param {Object} point Point on the axis in which the tick will intersect.
4879 getLabelPoint: function(point)
4881 var ar = this.get("axisRenderer");
4882 return {x:point.x + ar.get("rightTickOffset"), y:point.y};
4886 * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
4888 * @method updateMaxLabelSize
4889 * @param {HTMLElement} label to measure
4892 updateMaxLabelSize: function(label)
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)),
4902 m12 = rot > 0 ? -sinRadians : sinRadians,
4906 if(!document.createElementNS)
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));
4913 label.style.msTransform = "rotate(0deg)";
4916 max = label.offsetWidth;
4918 else if(absRot === 90)
4920 max = label.offsetHeight;
4924 max = (cosRadians * label.offsetWidth) + (sinRadians * label.offsetHeight);
4926 this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), max));
4931 * Rotate and position labels.
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
4939 positionLabel: function(label, pt)
4941 var ar = this.get("axisRenderer"),
4942 tickOffset = ar.get("rightTickOffset"),
4943 style = ar.get("styles").label,
4944 labelAlpha = style.alpha,
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)),
4955 m12 = rot > 0 ? -sinRadians : sinRadians,
4958 labelWidth = Math.round(label.offsetWidth),
4959 labelHeight = Math.round(label.offsetHeight);
4960 if(style.margin && style.margin.right)
4962 margin = style.margin.right;
4964 if(!document.createElementNS)
4966 label.style.filter = null;
4969 topOffset -= labelHeight * 0.5;
4971 else if(absRot === 90)
4973 topOffset -= labelWidth * 0.5;
4977 topOffset -= (cosRadians * (labelHeight * 0.5));
4981 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight * 0.5));
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))
4989 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
4995 filterString += " ";
5001 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5005 label.style.filter = filterString;
5009 label.style.msTransform = "rotate(0deg)";
5010 labelWidth = Math.round(label.offsetWidth);
5011 labelHeight = Math.round(label.offsetHeight);
5014 topOffset -= labelHeight * 0.5;
5018 leftOffset += labelHeight;
5019 topOffset -= labelWidth * 0.5;
5021 else if(rot === -90)
5023 topOffset += labelWidth * 0.5;
5027 topOffset -= (cosRadians * (labelHeight * 0.6));
5031 topOffset -= cosRadians * (labelHeight * 0.6);
5032 leftOffset += sinRadians * labelHeight;
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)";
5049 * Calculates the size and positions the content elements.
5051 * @method setSizeAndPosition
5054 setSizeAndPosition: function()
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)
5063 sz += label.margin.weight;
5065 ar.set("width", sz);
5066 ar.get("contentBox").setStyle("width", sz);
5070 * Adjusts position for inner ticks.
5072 * @method offsetNodeForTick
5073 * @param {Node} cb contentBox of the axis
5076 offsetNodeForTick: function(cb)
5078 var ar = this.get("axisRenderer"),
5079 tickOffset = ar.get("leftTickOffset"),
5080 offset = 0 - tickOffset;
5081 cb.setStyle("left", offset);
5085 * Assigns a height based on the size of the contents.
5087 * @method setCalculatedSize
5090 setCalculatedSize: function()
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);
5099 Y.RightAxisLayout = RightAxisLayout;
5101 * Contains algorithms for rendering a bottom axis.
5103 * @class BottomAxisLayout
5106 function BottomAxisLayout(config)
5108 BottomAxisLayout.superclass.constructor.apply(this, arguments);
5111 BottomAxisLayout.ATTRS = {
5113 * Reference to the <code>Axis</code> using the strategy.
5115 * @attribute axisRenderer
5124 * Length in pixels of largest text bounding box. Used to calculate the height of the axis.
5126 * @attribute maxLabelSize
5135 Y.extend(BottomAxisLayout, Y.Base, {
5137 * Sets the length of the tick on either side of the axis line.
5139 * @method setTickOffsets
5142 setTickOffsets: function()
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);
5155 ar.set("topTickOffset", tickLength);
5156 ar.set("bottomTickOffset", 0);
5159 ar.set("topTickOffset", 0);
5160 ar.set("bottomTickOffset", tickLength);
5163 ar.set("topTickOffset", halfTick);
5164 ar.set("bottomTickOffset", halfTick);
5167 ar.set("topTickOffset", 0);
5168 ar.set("bottomTickOffset", 0);
5174 * Calculates the coordinates for the first point on an axis.
5176 * @method getLineStart
5179 getLineStart: function()
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")
5192 else if(display === "cross")
5194 pt.y += tickLength/2;
5203 * @param {Object} pt hash containing x and y coordinates
5204 * @param {Object} tickStyles hash of properties used to draw the tick
5207 drawTick: function(pt, tickStyles)
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);
5219 * Calculates the point for a label.
5221 * @method getLabelPoint
5222 * @param {Object} pt hash containing x and y coordinates
5226 getLabelPoint: function(point)
5228 var ar = this.get("axisRenderer");
5229 return {x:point.x, y:point.y + ar.get("bottomTickOffset")};
5233 * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
5235 * @method updateMaxLabelSize
5236 * @param {HTMLElement} label to measure
5239 updateMaxLabelSize: function(label)
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)),
5249 m12 = rot > 0 ? -sinRadians : sinRadians,
5253 if(!document.createElementNS)
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));
5260 label.style.msTransform = "rotate(0deg)";
5263 max = label.offsetHeight;
5265 else if(absRot === 90)
5267 max = label.offsetWidth;
5271 max = (sinRadians * label.offsetWidth) + (cosRadians * label.offsetHeight);
5273 this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), max));
5278 * Rotate and position labels.
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
5286 positionLabel: function(label, pt)
5288 var ar = this.get("axisRenderer"),
5289 tickOffset = ar.get("bottomTickOffset"),
5290 style = ar.get("styles").label,
5291 labelAlpha = style.alpha,
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)),
5302 m12 = rot > 0 ? -sinRadians : sinRadians,
5305 labelWidth = Math.round(label.offsetWidth),
5306 labelHeight = Math.round(label.offsetHeight);
5307 if(style.margin && style.margin.top)
5309 margin = style.margin.top;
5311 if(!document.createElementNS)
5314 m12 = rot > 0 ? -sinRadians : sinRadians;
5317 label.style.filter = null;
5318 labelWidth = Math.round(label.offsetWidth);
5319 labelHeight = Math.round(label.offsetHeight);
5322 leftOffset -= labelHeight * 0.5;
5326 leftOffset -= cosRadians * labelWidth;
5327 leftOffset -= sinRadians * (labelHeight * 0.5);
5331 leftOffset -= sinRadians * (labelHeight * 0.5);
5335 leftOffset -= labelWidth * 0.5;
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))
5343 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
5349 filterString += " ";
5355 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5359 label.style.filter = filterString;
5363 label.style.msTransform = "rotate(0deg)";
5364 labelWidth = Math.round(label.offsetWidth);
5365 labelHeight = Math.round(label.offsetHeight);
5368 leftOffset -= labelWidth * 0.5;
5370 else if(absRot === 90)
5374 leftOffset += labelHeight * 0.5;
5378 topOffset += labelWidth;
5379 leftOffset -= labelHeight * 0.5;
5386 leftOffset -= (cosRadians * labelWidth) + (sinRadians * (labelHeight * 0.6));
5387 topOffset += sinRadians * labelWidth;
5391 leftOffset += Math.round(sinRadians * (labelHeight * 0.6));
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)";
5409 * Calculates the size and positions the content elements.
5411 * @method setSizeAndPosition
5414 setSizeAndPosition: function()
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)
5426 sz = Math.round(sz);
5427 ar.set("height", sz);
5431 * Adjusts position for inner ticks.
5433 * @method offsetNodeForTick
5434 * @param {Node} cb contentBox of the axis
5437 offsetNodeForTick: function(cb)
5439 var ar = this.get("axisRenderer");
5440 ar.get("contentBox").setStyle("top", 0 - ar.get("topTickOffset"));
5444 * Assigns a height based on the size of the contents.
5446 * @method setCalculatedSize
5449 setCalculatedSize: function()
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);
5458 Y.BottomAxisLayout = BottomAxisLayout;
5460 * Contains algorithms for rendering a top axis.
5462 * @class TopAxisLayout
5465 function TopAxisLayout(config)
5467 TopAxisLayout.superclass.constructor.apply(this, arguments);
5470 TopAxisLayout.ATTRS = {
5472 * Reference to the <code>Axis</code> using the strategy.
5474 * @attribute axisRenderer
5483 * Length in pixels of largest text bounding box. Used to calculate the height of the axis.
5485 * @attribute maxLabelSize
5494 Y.extend(TopAxisLayout, Y.Base, {
5496 * Sets the length of the tick on either side of the axis line.
5498 * @method setTickOffsets
5501 setTickOffsets: function()
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);
5513 ar.set("bottomTickOffset", tickLength);
5514 ar.set("topTickOffset", 0);
5517 ar.set("bottomTickOffset", 0);
5518 ar.set("topTickOffset", tickLength);
5521 ar.set("topTickOffset", halfTick);
5522 ar.set("bottomTickOffset", halfTick);
5525 ar.set("topTickOffset", 0);
5526 ar.set("bottomTickOffset", 0);
5532 * Calculates the coordinates for the first point on an axis.
5534 * @method getLineStart
5537 getLineStart: function()
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")
5550 else if(display === "cross")
5552 pt.y += tickLength/2;
5561 * @param {Object} pt hash containing x and y coordinates
5562 * @param {Object} tickStyles hash of properties used to draw the tick
5565 drawTick: function(pt, tickStyles)
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);
5577 * Calculates the point for a label.
5579 * @method getLabelPoint
5580 * @param {Object} pt hash containing x and y coordinates
5584 getLabelPoint: function(pt)
5586 var ar = this.get("axisRenderer");
5587 return {x:pt.x, y:pt.y - ar.get("topTickOffset")};
5591 * Updates the value for the <code>maxLabelSize</code> for use in calculating total size.
5593 * @method updateMaxLabelSize
5594 * @param {HTMLElement} label to measure
5597 updateMaxLabelSize: function(label)
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)),
5607 m12 = rot > 0 ? -sinRadians : sinRadians,
5611 if(!document.createElementNS)
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));
5618 label.style.msTransform = "rotate(0deg)";
5621 max = label.offsetHeight;
5623 else if(absRot === 90)
5625 max = label.offsetWidth;
5629 max = (sinRadians * label.offsetWidth) + (cosRadians * label.offsetHeight);
5631 this.set("maxLabelSize", Math.max(this.get("maxLabelSize"), max));
5636 * Rotate and position labels.
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
5644 positionLabel: function(label, pt)
5646 var ar = this.get("axisRenderer"),
5647 tickOffset = ar.get("topTickOffset"),
5648 style = ar.get("styles").label,
5649 labelAlpha = style.alpha,
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)),
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)
5670 margin = style.margin.bottom;
5672 if(!document.createElementNS)
5674 label.style.filter = null;
5675 labelWidth = Math.round(label.offsetWidth);
5676 labelHeight = Math.round(label.offsetHeight);
5678 m12 = rot > 0 ? -sinRadians : sinRadians;
5683 leftOffset -= labelWidth * 0.5;
5685 else if(absRot === 90)
5687 leftOffset -= labelHeight * 0.5;
5691 leftOffset -= (cosRadians * labelWidth) + Math.min((sinRadians * labelHeight), (rot/180 * labelHeight));
5692 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight));
5693 topOffset += maxLabelSize;
5697 leftOffset -= sinRadians * (labelHeight * 0.5);
5698 topOffset -= (sinRadians * labelWidth) + (cosRadians * (labelHeight));
5699 topOffset += maxLabelSize;
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))
5706 filterString = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + Math.round(labelAlpha * 100) + ")";
5712 filterString += " ";
5718 filterString += 'progid:DXImageTransform.Microsoft.Matrix(M11=' + m11 + ' M12=' + m12 + ' M21=' + m21 + ' M22=' + m22 + ' sizingMethod="auto expand")';
5722 label.style.filter = filterString;
5726 label.style.msTransform = "rotate(0deg)";
5727 labelWidth = Math.round(label.offsetWidth);
5728 labelHeight = Math.round(label.offsetHeight);
5731 leftOffset -= labelWidth * 0.5;
5732 topOffset -= labelHeight;
5736 leftOffset += labelHeight * 0.5;
5737 topOffset -= labelWidth;
5739 else if(rot === -90)
5741 leftOffset -= labelHeight * 0.5;
5747 leftOffset -= (sinRadians * (labelHeight * 0.6));
5748 topOffset -= (cosRadians * labelHeight);
5752 leftOffset -= (cosRadians * labelWidth) - (sinRadians * (labelHeight * 0.6));
5753 topOffset -= (sinRadians * labelWidth) + (cosRadians * labelHeight);
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)";
5769 * Calculates the size and positions the content elements.
5771 * @method setSizeAndPosition
5774 setSizeAndPosition: function()
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)
5785 sz += margin.bottom;
5787 ar.set("height", sz);
5788 Y.one(graphic.node).setStyle("top", labelSize + margin.bottom);
5792 * Adjusts position for inner ticks.
5794 * @method offsetNodeForTick
5795 * @param {Node} cb contentBox of the axis
5798 offsetNodeForTick: function(cb)
5803 * Assigns a height based on the size of the contents.
5805 * @method setCalculatedSize
5808 setCalculatedSize: function()
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);
5817 Y.TopAxisLayout = TopAxisLayout;
5820 * AxisType is an abstract class that manages the data for an axis.
5822 * @param {Object} config (optional) Configuration parameters for the Chart.
5827 Y.AxisType = Y.Base.create("baseAxis", Y.Axis, [], {
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);
5851 _dataProviderChangeHandler: function(e)
5853 var keyCollection = this.get("keyCollection").concat(),
5854 keys = this.get("keys"),
5860 if(keys.hasOwnProperty(i))
5866 if(keyCollection && keyCollection.length)
5868 this.set("keys", keyCollection);
5875 GUID: "yuibaseaxis",
5905 _updateTotalDataFlag: true,
5913 * Adds an array to the key hash.
5915 * @param value Indicates what key to use in retrieving
5918 addKey: function (value)
5920 this.set("keys", value);
5926 _getKeyArray: function(key, data)
5935 keyArray[i] = obj[key];
5943 _setDataByKey: function(key, data)
5948 dv = this._dataClone.concat(),
5950 for(i = 0; i < len; ++i)
5955 this.get("keys")[key] = arr;
5956 this._updateTotalDataFlag = true;
5962 _updateTotalData: function()
5964 var keys = this.get("keys"),
5969 if(keys.hasOwnProperty(i))
5971 this._data = this._data.concat(keys[i]);
5974 this._updateTotalDataFlag = false;
5978 * Removes an array from the key hash.
5981 * @param {String} value Indicates what key to use in removing from
5984 removeKey: function(value)
5986 var keys = this.get("keys");
5987 if(keys.hasOwnProperty(value))
5990 this._keyChangeHandler();
5995 * Returns a numeric value based of a key value and an index.
5997 * @method getKeyValueAt
5998 * @param {String} key value used to look up the correct array
5999 * @param {Number} index within the array
6002 getKeyValueAt: function(key, index)
6005 keys = this.get("keys");
6006 if(keys[key] && keys[key][index])
6008 value = keys[key][index];
6014 * Returns an array of values based on an identifier key.
6016 * @method getDataByKey
6017 * @param {String} value value used to identify the array
6020 getDataByKey: function (value)
6022 var keys = this.get("keys");
6033 _updateMinAndMax: function()
6035 var data = this.get("data"),
6041 if(data && data.length && data.length > 0)
6044 max = min = data[0];
6047 for(i = 1; i < len; i++)
6054 max = Math.max(num, max);
6055 min = Math.min(num, min);
6059 this._dataMaximum = max;
6060 this._dataMinimum = min;
6064 * Returns the total number of majorUnits that will appear on an axis.
6066 * @method getTotalMajorUnits
6069 getTotalMajorUnits: function()
6072 majorUnit = this.get("styles").majorUnit,
6073 len = this.get("length");
6074 if(majorUnit.determinant === "count")
6076 units = majorUnit.count;
6078 else if(majorUnit.determinant === "distance")
6080 units = (len/majorUnit.distance) + 1;
6086 * Returns the distance between major units on an axis.
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
6094 getMajorUnitDistance: function(len, uiLen, majorUnit)
6097 if(majorUnit.determinant === "count")
6099 dist = uiLen/(len - 1);
6101 else if(majorUnit.determinant === "distance")
6103 dist = majorUnit.distance;
6109 * Gets the distance that the first and last ticks are offset from there respective
6112 * @attribute getEdgeOffset
6114 * @param {Number} ct Number of ticks on the axis.
6115 * @param {Number} l Length (in pixels) of the axis.
6118 getEdgeOffset: function(ct, l)
6124 * Calculates and returns a value based on the number of labels and the index of
6125 * the current label.
6127 * @method getLabelByIndex
6128 * @param {Number} i Index of the label.
6129 * @param {Number} l Total number of labels.
6132 getLabelByIndex: function(i, l)
6134 var min = this.get("minimum"),
6135 max = this.get("maximum"),
6136 increm = (max - min)/(l-1),
6139 label = min + (i * increm);
6146 _keyChangeHandler: function(e)
6148 this._updateMinAndMax();
6149 this.fire("dataUpdate");
6154 * Hash of array identifed by a string value.
6162 setter: function(val)
6167 data = this.get("dataProvider");
6168 if(Y.Lang.isArray(val))
6171 for(i = 0; i < len; ++i)
6173 keys[val[i]] = this._getKeyArray(val[i], data);
6177 else if(Y.Lang.isString(val))
6179 keys = this.get("keys");
6180 keys[val] = this._getKeyArray(val, data);
6186 if(val.hasOwnProperty(i))
6188 keys[i] = this._getKeyArray(i, data);
6192 this._updateTotalDataFlag = true;
6198 *Indicates how to round unit values.
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>
6206 * @attribute roundingMethod
6208 * @default niceNumber
6215 *Returns the type of axis data
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>
6237 * Instance of <code>ChartDataProvider</code> that the class uses
6238 * to build its own data.
6240 * @attribute dataProvider
6244 setter: function (value)
6251 * The maximum value contained in the <code>data</code> array. Used for
6252 * <code>maximum</code> when <code>autoMax</code> is true.
6254 * @attribute dataMaximum
6260 if(!this._dataMaximum)
6262 this._updateMinAndMax();
6264 return this._dataMaximum;
6269 * The maximum value that will appear on an axis.
6271 * @attribute maximum
6277 var max = this.get("dataMaximum");
6278 if(this.get("setMax"))
6280 max = this._setMaximum;
6284 setter: function (value)
6286 this._setMaximum = parseFloat(value);
6292 * The minimum value contained in the <code>data</code> array. Used for
6293 * <code>minimum</code> when <code>autoMin</code> is true.
6295 * @attribute dataMinimum
6301 if(!this._dataMinimum)
6303 this._updateMinAndMax();
6305 return this._dataMinimum;
6310 * The minimum value that will appear on an axis.
6312 * @attribute minimum
6318 var min = this.get("dataMinimum");
6319 if(this.get("setMin"))
6321 min = this._setMinimum;
6325 setter: function(val)
6327 this._setMinimum = parseFloat(val);
6333 * Determines whether the maximum is calculated or explicitly
6344 return Y.Lang.isNumber(this._setMaximum);
6349 * Determines whether the minimum is calculated or explicitly
6360 return Y.Lang.isNumber(this._setMinimum);
6365 * Array of axis data
6373 if(!this._data || this._updateTotalDataFlag)
6375 this._updateTotalData();
6382 * Array containing all the keys in the axis.
6384 * @attribute keyCollection
6390 var keys = this.get("keys"),
6395 if(keys.hasOwnProperty(i))
6406 * Method used for formatting a label.
6408 * @attribute labelFunction
6410 * @param {String} val label to be formatted.
6411 * @param {Object} format temlate for formatting a label.
6415 value: function(val, format)
6423 * NumericAxis manages numeric data on an axis.
6425 * @param {Object} config (optional) Configuration parameters for the Chart.
6426 * @class NumericAxis
6430 function NumericAxis(config)
6432 NumericAxis.superclass.constructor.apply(this, arguments);
6435 NumericAxis.NAME = "numericAxis";
6437 NumericAxis.ATTRS = {
6439 * Indicates whether 0 should always be displayed.
6441 * @attribute alwaysShowZero
6451 * @attribute labelFunction
6453 * @param {Object} val Value to be formatted.
6454 * @param {Object} format Hasho of properties used to format the label.
6457 value: function(val, format)
6461 return Y.DataType.Number.format(val, format);
6468 * Hash of properties used by the <code>labelFunction</code> to format a
6471 * @attribute labelFormat
6477 thousandsSeparator: "",
6478 decimalSeparator: "",
6485 Y.extend(NumericAxis, Y.AxisType,
6495 _getMinimumUnit:function(max, min, units)
6497 return this._getNiceNumber(Math.ceil((max - min)/units));
6503 _getNiceNumber: function(roundingUnit)
6505 var tempMajorUnit = roundingUnit,
6506 order = Math.ceil(Math.log(tempMajorUnit) * 0.4342944819032518),
6507 roundedMajorUnit = Math.pow(10, order),
6510 if (roundedMajorUnit / 2 >= tempMajorUnit)
6512 roundedDiff = Math.floor((roundedMajorUnit / 2 - tempMajorUnit) / (Math.pow(10,order-1)/2));
6513 tempMajorUnit = roundedMajorUnit/2 - roundedDiff*Math.pow(10,order-1)/2;
6517 tempMajorUnit = roundedMajorUnit;
6519 if(!isNaN(tempMajorUnit))
6521 return tempMajorUnit;
6523 return roundingUnit;
6530 _updateMinAndMax: function()
6532 var data = this.get("data"),
6539 setMax = this.get("setMax"),
6540 setMin = this.get("setMin");
6541 if(!setMax && !setMin)
6543 if(data && data.length && data.length > 0)
6546 max = min = data[0];
6549 for(i = 1; i < len; i++)
6554 if(Y.Lang.isObject(num))
6559 if(num.hasOwnProperty(key))
6561 max = Math.max(num[key], max);
6562 min = Math.min(num[key], min);
6568 max = setMax ? this._setMaximum : Math.max(num, max);
6569 min = setMin ? this._setMinimum : Math.min(num, min);
6573 this._roundMinAndMax(min, max);
6580 _roundMinAndMax: function(min, max)
6584 minGreaterThanZero = min >= 0,
6585 maxGreaterThanZero = max > 0,
6593 units = this.getTotalMajorUnits() - 1,
6594 alwaysShowZero = this.get("alwaysShowZero"),
6595 roundingMethod = this.get("roundingMethod"),
6596 useIntegers = (max - min)/units >= 1;
6599 if(roundingMethod == "niceNumber")
6601 roundingUnit = this._getMinimumUnit(max, min, units);
6602 if(minGreaterThanZero && maxGreaterThanZero)
6604 if(alwaysShowZero || min < roundingUnit)
6608 roundingUnit = this._getMinimumUnit(max, min, units);
6609 max = this._roundUpToNearest(max, roundingUnit);
6611 else if(maxGreaterThanZero && !minGreaterThanZero)
6613 topTicks = Math.round( units / ((-1 * min)/max + 1) );
6614 botTicks = units - topTicks;
6615 tempMax = Math.ceil( max/topTicks );
6617 tempMin = Math.floor( min/botTicks ) * -1;
6619 roundingUnit = Math.max(tempMax, tempMin);
6620 roundingUnit = this._getNiceNumber(roundingUnit);
6621 max = roundingUnit * topTicks;
6622 min = roundingUnit * botTicks * -1;
6626 if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
6629 roundingUnit = this._getMinimumUnit(max, min, units);
6633 max = this._roundUpToNearest(max, roundingUnit);
6635 min = max - (roundingUnit * units);
6638 else if(roundingMethod == "auto")
6640 if(minGreaterThanZero && maxGreaterThanZero)
6642 if(alwaysShowZero || min < (max-min)/units)
6647 roundingUnit = (max - min)/units;
6650 roundingUnit = Math.ceil(roundingUnit);
6652 max = min + (roundingUnit * units);
6654 else if(maxGreaterThanZero && !minGreaterThanZero)
6658 topTicks = Math.round( units / ( (-1 * min) /max + 1) );
6659 botTicks = units - topTicks;
6663 tempMax = Math.ceil( max/topTicks );
6664 tempMin = Math.floor( min/botTicks ) * -1;
6668 tempMax = max/topTicks;
6669 tempMin = min/botTicks * -1;
6671 roundingUnit = Math.max(tempMax, tempMin);
6672 max = roundingUnit * topTicks;
6673 min = roundingUnit * botTicks * -1;
6677 roundingUnit = (max - min)/units;
6680 roundingUnit = Math.ceil(roundingUnit);
6682 min = this._roundDownToNearest(min, roundingUnit);
6683 max = this._roundUpToNearest(max, roundingUnit);
6688 roundingUnit = (max - min)/units;
6691 roundingUnit = Math.ceil(roundingUnit);
6693 if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
6696 roundingUnit = (max - min)/units;
6699 Math.ceil(roundingUnit);
6704 max = this._roundUpToNearest(max, roundingUnit);
6706 min = max - (roundingUnit * units);
6710 else if(!isNaN(roundingMethod) && isFinite(roundingMethod))
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)
6719 if(alwaysShowZero || minRound <= 0)
6727 if(!dataRangeGreater)
6729 max = min + minimumRange;
6736 else if(maxGreaterThanZero && !minGreaterThanZero)
6739 if(!dataRangeGreater)
6741 max = min + minimumRange;
6750 if(max === 0 || alwaysShowZero)
6758 if(!dataRangeGreater)
6760 min = max - minimumRange;
6769 this._dataMaximum = max;
6770 this._dataMinimum = min;
6774 * Calculates and returns a value based on the number of labels and the index of
6775 * the current label.
6777 * @method getLabelByIndex
6778 * @param {Number} i Index of the label.
6779 * @param {Number} l Total number of labels.
6782 getLabelByIndex: function(i, l)
6784 var min = this.get("minimum"),
6785 max = this.get("maximum"),
6786 increm = (max - min)/(l-1),
6789 label = min + (i * increm);
6792 label = this._roundToNearest(label, increm);
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().
6803 _roundToNearest: function(number, nearest)
6805 nearest = nearest || 1;
6810 var roundedNumber = Math.round(this._roundToPrecision(number / nearest, 10)) * nearest;
6811 return this._roundToPrecision(roundedNumber, 10);
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().
6820 _roundUpToNearest: function(number, nearest)
6822 nearest = nearest || 1;
6827 return Math.ceil(this._roundToPrecision(number / nearest, 10)) * nearest;
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().
6836 _roundDownToNearest: function(number, nearest)
6838 nearest = nearest || 1;
6843 return Math.floor(this._roundToPrecision(number / nearest, 10)) * nearest;
6849 * Rounds a number to a certain level of precision. Useful for limiting the number of
6850 * decimal places on a fractional number.
6852 _roundToPrecision: function(number, precision)
6854 precision = precision || 0;
6855 var decimalPlaces = Math.pow(10, precision);
6856 return Math.round(decimalPlaces * number) / decimalPlaces;
6860 Y.NumericAxis = NumericAxis;
6863 * StackedAxis manages stacked numeric data on an axis.
6865 * @param {Object} config (optional) Configuration parameters for the Chart.
6866 * @class StackedAxis
6868 * @extends NumericAxis
6870 function StackedAxis(config)
6872 StackedAxis.superclass.constructor.apply(this, arguments);
6875 StackedAxis.NAME = "stackedAxis";
6878 Y.extend(StackedAxis, Y.NumericAxis,
6883 _updateMinAndMax: function()
6893 keys = this.get("keys");
6897 if(keys.hasOwnProperty(key))
6899 len = Math.max(len, keys[key].length);
6908 if(keys.hasOwnProperty(key))
6927 max = Math.max(max, pos);
6931 max = Math.max(max, neg);
6935 min = Math.min(min, neg);
6939 min = Math.min(min, pos);
6942 this._roundMinAndMax(min, max);
6946 Y.StackedAxis = StackedAxis;
6949 * TimeAxis manages time data on an axis.
6951 * @param {Object} config (optional) Configuration parameters for the Chart.
6956 function TimeAxis(config)
6958 TimeAxis.superclass.constructor.apply(this, arguments);
6961 TimeAxis.NAME = "timeAxis";
6973 var max = this._getNumber(this._setMaximum);
6974 return (Y.Lang.isNumber(max));
6986 var min = this._getNumber(this._setMinimum);
6987 return (Y.Lang.isNumber(min));
6992 * The maximum value that will appear on an axis.
6994 * @attribute maximum
7000 var max = this._getNumber(this._setMaximum);
7001 if(!Y.Lang.isNumber(max))
7003 max = this._getNumber(this.get("dataMaximum"));
7007 setter: function (value)
7009 this._setMaximum = this._getNumber(value);
7015 * The minimum value that will appear on an axis.
7017 * @attribute minimum
7023 var min = this._getNumber(this._setMinimum);
7024 if(!Y.Lang.isNumber(min))
7026 min = this._getNumber(this.get("dataMinimum"));
7030 setter: function (value)
7032 this._setMinimum = this._getNumber(value);
7040 * @attribute labelFunction
7042 * @param {Object} val Value to be formatted.
7043 * @param {String} format Pattern used to format label.
7046 value: function(val, format)
7048 val = Y.DataType.Date.parse(val);
7051 return Y.DataType.Date.format(val, {format:format});
7058 * Pattern used by the <code>labelFunction</code> to format a label.
7060 * @attribute labelFormat
7068 Y.extend(TimeAxis, Y.AxisType, {
7070 * Constant used to generate unique id.
7076 GUID: "yuitimeaxis",
7084 * Calculates and returns a value based on the number of labels and the index of
7085 * the current label.
7087 * @method getLabelByIndex
7088 * @param {Number} i Index of the label.
7089 * @param {Number} l Total number of labels.
7092 getLabelByIndex: function(i, l)
7094 var min = this.get("minimum"),
7095 max = this.get("maximum"),
7096 position = this.get("position"),
7100 increm = ((max - min)/l) * i;
7101 if(position == "bottom" || position == "top")
7103 label = min + increm;
7107 label = max - increm;
7115 _getKeyArray: function(key, data)
7125 if(Y.Lang.isDate(obj))
7127 val = obj.valueOf();
7129 else if(!Y.Lang.isNumber(obj))
7131 val = new Date(obj.toString()).valueOf();
7143 * @private (override)
7145 _setDataByKey: function(key, data)
7149 dv = this._dataClone.concat(),
7153 for(i = 0; i < len; ++i)
7156 if(Y.Lang.isDate(obj))
7158 val = obj.valueOf();
7160 else if(!Y.Lang.isNumber(obj))
7162 val = new Date(obj.toString()).valueOf();
7170 this.get("keys")[key] = arr;
7171 this._updateTotalDataFlag = true;
7177 _getNumber: function(val)
7179 if(Y.Lang.isDate(val))
7181 val = val.valueOf();
7183 else if(!Y.Lang.isNumber(val) && val)
7185 val = new Date(val.toString()).valueOf();
7192 Y.TimeAxis = TimeAxis;
7195 * CategoryAxis manages category data on an axis.
7197 * @param {Object} config (optional) Configuration parameters for the Chart.
7198 * @class CategoryAxis
7202 function CategoryAxis(config)
7204 CategoryAxis.superclass.constructor.apply(this, arguments);
7207 CategoryAxis.NAME = "categoryAxis";
7209 Y.extend(CategoryAxis, Y.AxisType,
7219 GUID: "yuicategoryaxis",
7229 _updateMinAndMax: function()
7231 this._dataMaximum = Math.max(this.get("data").length - 1, 0);
7232 this._dataMinimum = 0;
7238 _getKeyArray: function(key, data)
7253 labels[i] = obj[key];
7255 this._indices[key] = keyArr;
7262 _setDataByKey: function(key)
7268 dv = this._dataClone.concat(),
7274 for(i = 0; i < len; ++i)
7278 labels[i] = obj[key];
7280 this._indices[key] = arr;
7281 this.get("keys")[key] = labels.concat();
7282 this._updateTotalDataFlag = true;
7286 * Returns an array of values based on an identifier key.
7288 * @method getDataByKey
7289 * @param {String} value value used to identify the array
7292 getDataByKey: function (value)
7298 var keys = this._indices;
7307 * Returns the total number of majorUnits that will appear on an axis.
7309 * @method getTotalMajorUnits
7312 getTotalMajorUnits: function(majorUnit, len)
7314 return this.get("data").length;
7318 * Returns the distance between major units on an axis.
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
7326 getMajorUnitDistance: function(len, uiLen, majorUnit)
7329 if(majorUnit.determinant === "count")
7333 else if(majorUnit.determinant === "distance")
7335 dist = majorUnit.distance;
7341 * Gets the distance that the first and last ticks are offset from there respective
7344 * @method getEdgeOffset
7345 * @param {Number} ct Number of ticks on the axis.
7346 * @param {Number} l Length (in pixels) of the axis.
7349 getEdgeOffset: function(ct, l)
7355 * Calculates and returns a value based on the number of labels and the index of
7356 * the current label.
7358 * @method getLabelByIndex
7359 * @param {Number} i Index of the label.
7360 * @param {Number} l Total number of labels.
7363 getLabelByIndex: function(i, l)
7366 data = this.get("data"),
7367 position = this.get("position");
7368 if(position == "bottom" || position == "top")
7374 label = data[l - (i + 1)];
7380 Y.CategoryAxis = CategoryAxis;
7383 * Utility class used for calculating curve points.
7388 function CurveUtil()
7392 CurveUtil.prototype = {
7394 * Creates an array of start, end and control points for splines.
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
7401 getCurveControlPoints: function(xcoords, ycoords)
7405 l = xcoords.length - 1,
7410 // Too few points, need at least two
7423 // Special case, the Bezier should be a straight line
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];
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];
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());
7447 for (i = 0; i < l; ++i)
7449 outpoints[i].ctrlx1 = Math.round(xvals[i]);
7450 outpoints[i].ctrly1 = Math.round(yvals[i]);
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]);
7459 outpoints[i].ctrlx2 = Math.round((xcoords[l] + xvals[l-1])/2);
7460 outpoints[i].ctrly2 = Math.round((ycoords[l] + yvals[l-1])/2);
7470 getControlPoints: function(vals)
7472 var l = vals.length,
7481 b = (i < l-1 ? 4.0 : 3.5) - tmp[i];
7482 x[i] = (vals[i] - x[i-1]) / b;
7485 for (i = 1; i < l; ++i)
7487 x[l-i-1] -= tmp[l-i] * x[l-i];
7493 Y.CurveUtil = CurveUtil;
7495 * Utility class used for creating stacked series.
7497 * @class StackingUtil
7500 function StackingUtil(){}
7502 StackingUtil.prototype = {
7506 * Adjusts coordinate values for stacked series.
7508 * @method _stackCoordinates
7510 _stackCoordinates: function()
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],
7520 xcoords = this.get("xcoords"),
7521 ycoords = this.get("ycoords"),
7528 prevXCoords = seriesCollection[order - 1].get("xcoords").concat();
7529 prevYCoords = seriesCollection[order - 1].get("ycoords").concat();
7530 if(direction === "vertical")
7532 len = prevXCoords.length;
7535 if(!isNaN(prevXCoords[i]) && !isNaN(xcoords[i]))
7537 xcoords[i] += prevXCoords[i];
7543 len = prevYCoords.length;
7546 if(!isNaN(prevYCoords[i]) && !isNaN(ycoords[i]))
7548 ycoords[i] = prevYCoords[i] - (h - ycoords[i]);
7554 Y.StackingUtil = StackingUtil;
7556 * Utility class used for drawing lines.
7567 _lineDefaults: null,
7570 * Creates a graphic in which to draw a series.
7572 * @method _getGraphic
7576 _getGraphic: function()
7578 var graph = this.get("graph");
7579 if(!this._lineGraphic)
7581 this._lineGraphic = new Y.Graphic();
7582 this._lineGraphic.render(graph.get("contentBox"));
7584 this._lineGraphic.clear();
7585 this._lineGraphic.setSize(graph.get("width"), graph.get("height"));
7586 this.autoSize = false;
7587 return this._lineGraphic;
7591 * Draws lines for the series.
7596 drawLines: function()
7598 if(this.get("xcoords").length < 1)
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,
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)
7638 if(lastValidX == lastX)
7640 if(lineType != "dashed")
7642 graphic.lineTo(nextX, nextY);
7646 this.drawDashedLine(lastValidX, lastValidY, nextX, nextY,
7651 else if(!connectDiscontinuousPoints)
7653 graphic.moveTo(nextX, nextY);
7657 if(discontinuousType != "solid")
7659 this.drawDashedLine(lastValidX, lastValidY, nextX, nextY,
7660 discontinuousDashLength,
7661 discontinuousGapSpace);
7665 graphic.lineTo(nextX, nextY);
7669 lastX = lastValidX = nextX;
7670 lastY = lastValidY = nextY;
7676 * Connects data points with a consistent curve for a series.
7678 * @method drawSpline
7681 drawSpline: function()
7683 if(this.get("xcoords").length < 1)
7687 var xcoords = this.get("xcoords"),
7688 ycoords = this.get("ycoords"),
7689 curvecoords = this.getCurveControlPoints(xcoords, ycoords),
7690 len = curvecoords.length,
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)
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);
7718 * Draws a dashed line between two points.
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
7729 drawDashedLine: function(xStart, yStart, xEnd, yEnd, dashSize, gapSize)
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),
7742 graphic = this._getGraphic();
7743 xDelta = Math.cos(radians) * segmentLength;
7744 yDelta = Math.sin(radians) * segmentLength;
7746 for(i = 0; i < segmentCount; ++i)
7748 graphic.moveTo(xCurrent, yCurrent);
7749 graphic.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize);
7754 graphic.moveTo(xCurrent, yCurrent);
7755 delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent));
7757 if(delta > dashSize)
7759 graphic.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize);
7763 graphic.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta);
7766 graphic.moveTo(xEnd, yEnd);
7770 * Default values for <code>styles</code> attribute.
7772 * @method _getLineDefaults
7776 _getLineDefaults: function()
7784 connectDiscontinuousPoints:true,
7785 discontinuousType:"solid",
7786 discontinuousDashLength:10,
7787 discontinuousGapSpace:10
7791 Y.augment(Lines, Y.Attribute);
7794 * Utility class used for drawing area fills.
7805 return this._defaults || this._getAreaDefaults();
7808 setter: function(val)
7810 var defaults = this._defaults || this._getAreaDefaults();
7811 this._defaults = Y.merge(defaults, val);
7815 this.addAttrs(attrs, cfg);
7826 drawFill: function(xcoords, ycoords)
7828 if(xcoords.length < 1)
7832 var len = xcoords.length,
7833 firstX = xcoords[0],
7834 firstY = ycoords[0],
7835 lastValidX = firstX,
7836 lastValidY = firstY,
7840 styles = this.get("styles").area,
7841 graphic = this.get("graphic"),
7842 color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
7844 graphic.beginFill(color, styles.alpha);
7845 graphic.moveTo(firstX, firstY);
7846 for(; i < len; i = ++i)
7856 graphic.lineTo(nextX, nextY);
7864 * Draws a fill for a spline
7866 * @method drawAreaSpline
7869 drawAreaSpline: function()
7871 if(this.get("xcoords").length < 1)
7875 var xcoords = this.get("xcoords"),
7876 ycoords = this.get("ycoords"),
7877 curvecoords = this.getCurveControlPoints(xcoords, ycoords),
7878 len = curvecoords.length,
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)
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);
7903 if(this.get("direction") === "vertical")
7905 graphic.lineTo(this._leftOrigin, y);
7906 graphic.lineTo(this._leftOrigin, firstY);
7910 graphic.lineTo(x, this._bottomOrigin);
7911 graphic.lineTo(firstX, this._bottomOrigin);
7913 graphic.lineTo(firstX, firstY);
7918 * Draws a a stacked area spline
7920 * @method drawStackedAreaSpline
7923 drawStackedAreaSpline: function()
7925 if(this.get("xcoords").length < 1)
7929 var xcoords = this.get("xcoords"),
7930 ycoords = this.get("ycoords"),
7932 order = this.get("order"),
7933 type = this.get("type"),
7934 graph = this.get("graph"),
7935 seriesCollection = graph.seriesTypes[type],
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)
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);
7969 prevXCoords = seriesCollection[order - 1].get("xcoords").concat().reverse();
7970 prevYCoords = seriesCollection[order - 1].get("ycoords").concat().reverse();
7971 curvecoords = this.getCurveControlPoints(prevXCoords, prevYCoords);
7973 len = curvecoords.length;
7974 graphic.lineTo(prevXCoords[0], prevYCoords[0]);
7975 for(; i < len; i = ++i)
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);
7988 if(this.get("direction") === "vertical")
7990 graphic.lineTo(this._leftOrigin, ycoords[ycoords.length-1]);
7991 graphic.lineTo(this._leftOrigin, firstY);
7995 graphic.lineTo(xcoords[xcoords.length-1], this._bottomOrigin);
7996 graphic.lineTo(firstX, this._bottomOrigin);
8000 graphic.lineTo(firstX, firstY);
8010 * Concatanates coordinate array with correct coordinates for closing an area fill.
8012 * @method _getClosingPoints
8016 _getClosingPoints: function()
8018 var xcoords = this.get("xcoords").concat(),
8019 ycoords = this.get("ycoords").concat();
8020 if(this.get("direction") === "vertical")
8022 xcoords.push(this._leftOrigin);
8023 xcoords.push(this._leftOrigin);
8024 ycoords.push(ycoords[ycoords.length - 1]);
8025 ycoords.push(ycoords[0]);
8029 xcoords.push(xcoords[xcoords.length - 1]);
8030 xcoords.push(xcoords[0]);
8031 ycoords.push(this._bottomOrigin);
8032 ycoords.push(this._bottomOrigin);
8034 xcoords.push(xcoords[0]);
8035 ycoords.push(ycoords[0]);
8036 return [xcoords, ycoords];
8040 * Concatenates coordinate array with the correct coordinates for closing an area stack.
8042 * @method _getStackedClosingPoints
8046 _getStackedClosingPoints: function()
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],
8055 allXCoords = this.get("xcoords").concat(),
8056 allYCoords = this.get("ycoords").concat(),
8057 firstX = allXCoords[0],
8058 firstY = allYCoords[0];
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]);
8071 if(direction === "vertical")
8073 allXCoords.push(this._leftOrigin);
8074 allXCoords.push(this._leftOrigin);
8075 allYCoords.push(allYCoords[allYCoords.length-1]);
8076 allYCoords.push(firstY);
8080 allXCoords.push(allXCoords[allXCoords.length-1]);
8081 allXCoords.push(firstX);
8082 allYCoords.push(this._bottomOrigin);
8083 allYCoords.push(this._bottomOrigin);
8086 return [allXCoords, allYCoords];
8092 _getAreaDefaults: function()
8098 Y.augment(Fills, Y.Attribute);
8101 * Utility class used for drawing markers.
8112 return this._markers;
8116 this.addAttrs(attrs, cfg);
8123 _plotDefaults: null,
8131 drawPlots: function()
8133 if(!this.get("xcoords") || this.get("xcoords").length < 1)
8137 var style = Y.clone(this.get("styles").marker),
8140 xcoords = this.get("xcoords"),
8141 ycoords = this.get("ycoords"),
8143 len = xcoords.length,
8150 borderColors = null,
8151 graphOrder = this.get("graphOrder"),
8153 isChrome = ISCHROME;
8154 if(Y.Lang.isArray(style.fill.color))
8156 fillColors = style.fill.color.concat();
8158 if(Y.Lang.isArray(style.border.color))
8160 borderColors = style.border.colors.concat();
8162 this._createMarkerCache();
8165 this._createHotspotCache();
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))
8173 this._markers.push(null);
8174 this._graphicNodes.push(null);
8179 style.fill.color = fillColors[i % fillColors.length];
8183 style.border.colors = borderColors[i % borderColors.length];
8185 marker = this.getMarker(style, graphOrder, i);
8186 marker.setPosition(left, top);
8189 hotspot = this.getHotspot(style, graphOrder, i);
8190 hotspot.setPosition(left, top);
8191 hotspot.parentNode.style.zIndex = 5;
8194 this._clearMarkerCache();
8197 this._clearHotspotCache();
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.
8205 * @method _getPlotDefaults
8209 _getPlotDefaults: function()
8227 defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
8228 defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
8233 * Collection of markers to be used in the series.
8240 * Collection of markers to be re-used on a series redraw.
8247 * Gets and styles a marker. If there is a marker in cache, it will use it. Otherwise
8248 * it will create one.
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.
8257 getMarker: function(styles, order, index)
8260 if(this._markerCache.length > 0)
8264 if(this._markerCache.length < 1)
8266 marker = this._createMarker(styles, order, index);
8269 marker = this._markerCache.shift();
8272 marker.update(styles);
8276 marker = this._createMarker(styles, order, index);
8278 this._markers.push(marker);
8279 this._graphicNodes.push(marker.parentNode);
8284 * Creates a shape to be used as a marker.
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.
8293 _createMarker: function(styles, order, index)
8295 var graphic = new Y.Graphic(),
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);
8308 * Creates a cache of markers for reuse.
8310 * @method _createMarkerCache
8313 _createMarkerCache: function()
8315 if(this._markers && this._markers.length > 0)
8317 this._markerCache = this._markers.concat();
8321 this._markerCache = [];
8324 this._graphicNodes = [];
8328 * Removes unused markers from the marker cache
8330 * @method _clearMarkerCache
8333 _clearMarkerCache: function()
8335 var len = this._markerCache.length,
8341 marker = this._markerCache[i];
8344 graphic = marker.graphics;
8348 this._markerCache = [];
8352 * Resizes and positions markers based on a mouse interaction.
8354 * @method updateMarkerState
8355 * @param {String} type state of the marker
8356 * @param {Number} i index of the marker
8359 updateMarkerState: function(type, i)
8361 if(this._markers[i])
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"));
8385 * Parses a color from an array.
8387 * @method _getItemColor
8388 * @param {Array} val collection of colors
8389 * @param {Number} i index of the item
8393 _getItemColor: function(val, i)
8395 if(Y.Lang.isArray(val))
8397 return val[i % val.length];
8403 * Method used by <code>styles</code> setter. Overrides base implementation.
8405 * @method _setStyles
8406 * @param {Object} newStyles Hash of properties to update.
8410 _setStyles: function(val)
8412 val = this._parseMarkerStyles(val);
8413 return Y.Renderer.prototype._setStyles.apply(this, [val]);
8417 * Combines new styles with existing styles.
8419 * @method _parseMarkerStyles
8422 _parseMarkerStyles: function(val)
8426 var defs = this._getPlotDefaults();
8427 val.marker = this._mergeStyles(val.marker, defs);
8430 val.marker.over = this._mergeStyles(val.marker.over, val.marker);
8434 val.marker.down = this._mergeStyles(val.marker.down, val.marker);
8441 * Returns marker state based on event type
8444 * @param {String} type event type
8448 _getState: function(type)
8475 * Collection of hotspots to be used in the series.
8482 * Collection of hotspots to be re-used on a series redraw.
8486 _hotspotCache: null,
8489 * Gets and styles a hotspot. If there is a hotspot in cache, it will use it. Otherwise
8490 * it will create one.
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.
8499 getHotspot: function(hotspotStyles, order, index)
8502 styles = Y.clone(hotspotStyles);
8511 if(this._hotspotCache.length > 0)
8515 if(this._hotspotCache.length < 1)
8517 hotspot = this._createHotspot(styles, order, index);
8520 hotspot = this._hotspotCache.shift();
8523 hotspot.update(styles);
8527 hotspot = this._createHotspot(styles, order, index);
8529 this._hotspots.push(hotspot);
8534 * Creates a shape to be used as a hotspot.
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.
8543 _createHotspot: function(styles, order, index)
8545 var graphic = new Y.Graphic(),
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);
8558 * Creates a cache of hotspots for reuse.
8560 * @method _createHotspotCache
8563 _createHotspotCache: function()
8565 if(this._hotspots && this._hotspots.length > 0)
8567 this._hotspotCache = this._hotspots.concat();
8571 this._hotspotCache = [];
8573 this._hotspots = [];
8577 * Removes unused hotspots from the hotspot cache
8579 * @method _clearHotspotCache
8582 _clearHotspotCache: function()
8584 var len = this._hotspotCache.length,
8590 hotspot = this._hotspotCache[i];
8593 graphic = hotspot.graphics;
8597 this._hotspotCache = [];
8601 Y.augment(Plots, Y.Attribute);
8604 * Histogram is the base class for Column and Bar series.
8609 function Histogram(){}
8611 Histogram.prototype = {
8617 * @method drawSeries
8619 drawSeries: function()
8621 if(this.get("xcoords").length < 1)
8625 var style = Y.clone(this.get("styles").marker),
8628 xcoords = this.get("xcoords"),
8629 ycoords = this.get("ycoords"),
8631 len = xcoords.length,
8633 type = this.get("type"),
8634 graph = this.get("graph"),
8635 seriesCollection = graph.seriesTypes[type],
8636 seriesLen = seriesCollection.length,
8642 order = this.get("order"),
8643 graphOrder = this.get("graphOrder"),
8650 borderColors = null,
8652 isChrome = ISCHROME;
8653 if(Y.Lang.isArray(style.fill.color))
8655 fillColors = style.fill.color.concat();
8657 if(Y.Lang.isArray(style.border.color))
8659 borderColors = style.border.colors.concat();
8661 if(this.get("direction") == "vertical")
8663 setSizeKey = "height";
8664 calculatedSizeKey = "width";
8668 setSizeKey = "width";
8669 calculatedSizeKey = "height";
8671 setSize = style[setSizeKey];
8672 calculatedSize = style[calculatedSizeKey];
8673 this._createMarkerCache();
8676 this._createHotspotCache();
8678 for(; i < seriesLen; ++i)
8680 renderer = seriesCollection[i];
8681 seriesSize += renderer.get("styles").marker[setSizeKey];
8684 offset = seriesSize;
8687 totalSize = len * seriesSize;
8688 if(totalSize > graph.get(setSizeKey))
8690 ratio = graph.get(setSizeKey)/totalSize;
8691 seriesSize *= ratio;
8694 setSize = Math.max(setSize, 1);
8696 offset -= seriesSize/2;
8697 for(i = 0; i < len; ++i)
8699 config = this._getMarkerDimensions(xcoords[i], ycoords[i], calculatedSize, offset);
8701 calculatedSize = config.calculatedSize;
8703 style[setSizeKey] = setSize;
8704 style[calculatedSizeKey] = calculatedSize;
8707 style.fill.color = fillColors[i % fillColors.length];
8711 style.border.colors = borderColors[i % borderColors.length];
8713 marker = this.getMarker(style, graphOrder, i);
8714 marker.setPosition(left, top);
8717 hotspot = this.getHotspot(style, graphOrder, i);
8718 hotspot.setPosition(left, top);
8719 hotspot.parentNode.style.zIndex = 5;
8722 this._clearMarkerCache();
8725 this._clearHotspotCache();
8732 _defaultFillColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],
8737 _getPlotDefaults: function()
8762 defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
8763 defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
8768 Y.Histogram = Histogram;
8770 * The CartesianSeries class creates a chart with horizontal and vertical axes.
8772 * @class CartesianSeries
8777 Y.CartesianSeries = Y.Base.create("cartesianSeries", Y.Base, [Y.Renderer], {
8781 _xDisplayName: null,
8786 _yDisplayName: null,
8796 _bottomOrigin: null,
8804 this.addListeners();
8805 this.set("rendered", true);
8812 addListeners: function()
8814 var xAxis = this.get("xAxis"),
8815 yAxis = this.get("yAxis");
8818 xAxis.after("dataReady", Y.bind(this._xDataChangeHandler, this));
8819 xAxis.after("dataUpdate", Y.bind(this._xDataChangeHandler, this));
8823 yAxis.after("dataReady", Y.bind(this._yDataChangeHandler, this));
8824 yAxis.after("dataUpdate", Y.bind(this._yDataChangeHandler, this));
8826 this.after("xAxisChange", this._xAxisChangeHandler);
8827 this.after("yAxisChange", this._yAxisChangeHandler);
8828 this.after("stylesChange", function(e) {
8829 var axesReady = this._updateAxisData();
8835 this.after("widthChange", function(e) {
8836 var axesReady = this._updateAxisData();
8842 this.after("heightChange", function(e) {
8843 var axesReady = this._updateAxisData();
8849 this.after("visibleChange", this._toggleVisible);
8855 _xAxisChangeHandler: function(e)
8857 var xAxis = this.get("xAxis");
8858 xAxis.after("dataReady", Y.bind(this._xDataChangeHandler, this));
8859 xAxis.after("dataUpdate", Y.bind(this._xDataChangeHandler, this));
8865 _yAxisChangeHandler: function(e)
8867 var yAxis = this.get("yAxis");
8868 yAxis.after("dataReady", Y.bind(this._yDataChangeHandler, this));
8869 yAxis.after("dataUpdate", Y.bind(this._yDataChangeHandler, this));
8875 GUID: "yuicartesianseries",
8878 * @private (protected)
8880 _xDataChangeHandler: function(event)
8882 var axesReady = this._updateAxisData();
8890 * @private (protected)
8892 _yDataChangeHandler: function(event)
8894 var axesReady = this._updateAxisData();
8904 _updateAxisData: function()
8906 var xAxis = this.get("xAxis"),
8907 yAxis = this.get("yAxis"),
8908 xKey = this.get("xKey"),
8909 yKey = this.get("yKey"),
8912 if(!xAxis || !yAxis || !xKey || !yKey)
8916 xData = xAxis.getDataByKey(xKey);
8917 yData = yAxis.getDataByKey(yKey);
8918 if(!xData || !yData)
8922 this.set("xData", xData.concat());
8923 this.set("yData", yData.concat());
8930 validate: function()
8932 if((this.get("xData") && this.get("yData")) || this._updateAxisData())
8941 * Creates a <code>Graphic</code> instance.
8943 * @method _setCanvas
8945 _setCanvas: function()
8947 this.set("graphic", new Y.Graphic());
8948 this.get("graphic").render(this.get("graph").get("contentBox"));
8954 * Calculates the coordinates for the series.
8956 * @method setAreaData
8958 setAreaData: function()
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),
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),
8984 direction = this.get("direction"),
8988 xMarkerPlaneOffset = this.get("xMarkerPlaneOffset"),
8989 yMarkerPlaneOffset = this.get("yMarkerPlaneOffset"),
8990 graphic = this.get("graphic");
8991 dataLength = xData.length;
8994 //Assuming a vertical graph has a range/category for its vertical axis.
8995 if(direction === "vertical")
8997 yData = yData.reverse();
9001 graphic.setSize(w, h);
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)
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});
9014 this.set("xcoords", xcoords);
9015 this.set("ycoords", ycoords);
9016 this.set("xMarkerPlane", xMarkerPlane);
9017 this.set("yMarkerPlane", yMarkerPlane);
9029 var graph = this.get("graph"),
9030 w = graph.get("width"),
9031 h = graph.get("height");
9033 if(this.get("rendered"))
9035 if((isFinite(w) && isFinite(h) && w > 0 && h > 0) && ((this.get("xData") && this.get("yData")) || this._updateAxisData()))
9039 this._callLater = true;
9042 this._drawing = true;
9043 this._callLater = false;
9045 if(this.get("xcoords") && this.get("ycoords"))
9049 this._drawing = false;
9056 this._toggleVisible(this.get("visible"));
9057 this.fire("drawingComplete");
9066 _defaultPlaneOffset: 4,
9071 * Gets the default value for the <code>styles</code> attribute. Overrides
9072 * base implementation.
9074 * @method _getDefaultStyles
9077 _getDefaultStyles: function()
9090 * Collection of default colors used for lines in a series when not specified by user.
9092 * @property _defaultLineColors
9095 _defaultLineColors:["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"],
9100 * Collection of default colors used for marker fills in a series when not specified by user.
9102 * @property _defaultFillColors
9105 _defaultFillColors:["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"],
9110 * Collection of default colors used for marker borders in a series when not specified by user.
9112 * @property _defaultBorderColors
9115 _defaultBorderColors:["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"],
9120 * Collection of default colors used for area fills, histogram fills and pie fills in a series when not specified by user.
9122 * @property _defaultSliceColors
9125 _defaultSliceColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],
9130 * Parses a color based on a series order and type.
9132 * @method _getDefaultColor
9133 * @param {Number} index Index indicating the series order.
9134 * @param {String} type Indicates which type of object needs the color.
9137 _getDefaultColor: function(index, type)
9140 line: this._defaultLineColors,
9141 fill: this._defaultFillColors,
9142 border: this._defaultBorderColors,
9143 slice: this._defaultSliceColors
9152 type = type || "fill";
9153 return colors[type][index];
9159 * Shows/hides contents of the series.
9161 * @method _toggleVisible
9163 _toggleVisible: function(e)
9165 var graphic = this.get("graphic"),
9166 markers = this.get("markers"),
9169 visible = this.get("visible"),
9173 graphic.toggleVisible(visible);
9177 len = markers.length;
9180 marker = markers[i];
9183 marker.toggleVisible(visible);
9188 if(this._lineGraphic)
9190 this._lineGraphic.toggleVisible(visible);
9196 * Name used for for displaying data related to the x-coordinate.
9198 * @attribute xDisplayName
9204 return this._xDisplayName || this.get("xKey");
9207 setter: function(val)
9209 this._xDisplayName = val;
9215 * Name used for for displaying data related to the y-coordinate.
9217 * @attribute yDisplayName
9223 return this._yDisplayName || this.get("yKey");
9226 setter: function(val)
9228 this._yDisplayName = val;
9234 * Name used for for displaying category data
9236 * @attribute categoryDisplayName
9239 categoryDisplayName: {
9244 return this.get("direction") == "vertical" ? this.get("yDisplayName") : this.get("xDisplayName");
9249 * Name used for for displaying value data
9251 * @attribute valueDisplayName
9259 return this.get("direction") == "vertical" ? this.get("xDisplayName") : this.get("yDisplayName");
9264 * Read-only attribute indicating the type of series.
9268 * @default cartesian
9275 * Order of this instance of this <code>type</code>.
9283 * Order of the instance
9285 * @attribute graphOrder
9291 * x coordinates for the series.
9293 * @attribute xcoords
9299 * y coordinates for the series
9301 * @attribute ycoords
9307 * Reference to the <code>Graph</code> in which the series is drawn into.
9315 * Reference to the <code>Axis</code> instance used for assigning
9316 * x-values to the graph.
9324 * Reference to the <code>Axis</code> instance used for assigning
9325 * y-values to the graph.
9333 * Indicates which array to from the hash of value arrays in
9334 * the x-axis <code>Axis</code> instance.
9342 * Indicates which array to from the hash of value arrays in
9343 * the y-axis <code>Axis</code> instance.
9351 * Array of x values for the series.
9359 * Array of y values for the series.
9367 * Indicates whether the Series has been through its initial set up.
9369 * @attribute rendered
9377 * Returns the width of the parent graph
9387 this.get("graph").get("width");
9392 * Returns the height of the parent graph
9402 this.get("graph").get("height");
9407 * Indicates whether to show the series
9409 * @attribute visible
9418 * Collection of area maps along the xAxis. Used to determine mouseover for multiple
9421 * @attribute xMarkerPlane
9427 * Collection of area maps along the yAxis. Used to determine mouseover for multiple
9430 * @attribute yMarkerPlane
9436 * Distance from a data coordinate to the left/right for setting a hotspot.
9438 * @attribute xMarkerPlaneOffset
9441 xMarkerPlaneOffset: {
9442 getter: function() {
9443 var marker = this.get("styles").marker;
9444 if(marker && marker.width && isFinite(marker.width))
9446 return marker.width * 0.5;
9448 return this._defaultPlaneOffset;
9453 * Distance from a data coordinate to the top/bottom for setting a hotspot.
9455 * @attribute yMarkerPlaneOffset
9458 yMarkerPlaneOffset: {
9459 getter: function() {
9460 var marker = this.get("styles").marker;
9461 if(marker && marker.height && isFinite(marker.height))
9463 return marker.height * 0.5;
9465 return this._defaultPlaneOffset;
9470 * Direction of the series
9472 * @attribute direction
9481 * The MarkerSeries class renders quantitative data by plotting relevant data points
9484 * @class MarkerSeries
9485 * @extends CartesianSeries
9489 Y.MarkerSeries = Y.Base.create("markerSeries", Y.CartesianSeries, [Y.Plots], {
9493 renderUI: function()
9503 * @method drawSeries
9505 drawSeries: function()
9513 * Method used by <code>styles</code> setter. Overrides base implementation.
9515 * @method _setStyles
9516 * @param {Object} newStyles Hash of properties to update.
9519 _setStyles: function(val)
9525 val = this._parseMarkerStyles(val);
9526 return Y.MarkerSeries.superclass._mergeStyles.apply(this, [val, this._getDefaultStyles()]);
9532 * Gets the default value for the <code>styles</code> attribute. Overrides
9533 * base implementation.
9535 * @method _getDefaultStyles
9538 _getDefaultStyles: function()
9540 var styles = this._mergeStyles({marker:this._getPlotDefaults()}, Y.MarkerSeries.superclass._getDefaultStyles());
9546 * Read-only attribute indicating the type of series.
9557 * Style properties used for drawing markers. This attribute is inherited from <code>Renderer</code>. Below are the default values:
9559 * <dt>fill</dt><dd>A hash containing the following values:
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>
9565 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
9568 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
9591 * The LineSeries class renders quantitative data on a graph by connecting relevant data points.
9594 * @extends CartesianSeries
9598 Y.LineSeries = Y.Base.create("lineSeries", Y.CartesianSeries, [Y.Lines], {
9602 * @method drawSeries
9604 drawSeries: function()
9606 this.get("graphic").clear();
9613 * Method used by <code>styles</code> setter. Overrides base implementation.
9615 * @method _setStyles
9616 * @param {Object} newStyles Hash of properties to update.
9619 _setStyles: function(val)
9625 return Y.LineSeries.superclass._setStyles.apply(this, [val]);
9631 * Gets the default value for the <code>styles</code> attribute. Overrides
9632 * base implementation.
9634 * @method _getDefaultStyles
9637 _getDefaultStyles: function()
9639 var styles = this._mergeStyles({line:this._getLineDefaults()}, Y.LineSeries.superclass._getDefaultStyles());
9646 * Read-only attribute indicating the type of series.
9657 * Style properties used for drawing lines. This attribute is inherited from <code>Renderer</code>. Below are the default values:
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>
9685 * SplineSeries renders a graph with data points connected by a curve.
9687 * @class SplineSeries
9689 * @extends CartesianSeries
9693 Y.SplineSeries = Y.Base.create("splineSeries", Y.CartesianSeries, [Y.CurveUtil, Y.Lines], {
9699 * @method drawSeries
9701 drawSeries: function()
9703 this.get("graphic").clear();
9709 * Read-only attribute indicating the type of series.
9720 * Style properties used for drawing lines. This attribute is inherited from <code>Renderer</code>. Below are the default values:
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>
9748 * AreaSplineSeries renders an area graph with data points connected by a curve.
9750 * @class AreaSplineSeries
9752 * @extends CartesianSeries
9756 Y.AreaSplineSeries = Y.Base.create("areaSplineSeries", Y.CartesianSeries, [Y.Fills, Y.CurveUtil], {
9762 * @method drawSeries
9764 drawSeries: function()
9766 this.get("graphic").clear();
9767 this.drawAreaSpline();
9772 * Read-only attribute indicating the type of series.
9776 * @default areaSpline
9783 * Style properties used for drawing area fills. This attribute is inherited from <code>Renderer</code>. Below are the default values:
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>
9790 * <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1</dd>
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.
9803 * @class StackedSplineSeries
9805 * @extends SplineSeries
9806 * @extends StackingUtil
9808 Y.StackedSplineSeries = Y.Base.create("stackedSplineSeries", Y.SplineSeries, [Y.StackingUtil], {
9812 * Calculates the coordinates for the series. Overrides base implementation.
9814 * @method setAreaData
9816 setAreaData: function()
9818 Y.StackedSplineSeries.superclass.setAreaData.apply(this);
9819 this._stackCoordinates.apply(this);
9824 * Read-only attribute indicating the type of series.
9828 * @default stackedSpline
9831 value:"stackedSpline"
9837 * StackedMarkerSeries plots markers with different series stacked along the value axis to indicate each
9838 * series' contribution to a cumulative total.
9840 * @class StackedMarkerSeries
9842 * @extends MarkerSeries
9843 * @extends StackingUtil
9845 Y.StackedMarkerSeries = Y.Base.create("stackedMarkerSeries", Y.MarkerSeries, [Y.StackingUtil], {
9849 * Calculates the coordinates for the series. Overrides base implementation.
9851 * @method setAreaData
9853 setAreaData: function()
9855 Y.StackedMarkerSeries.superclass.setAreaData.apply(this);
9856 this._stackCoordinates.apply(this);
9861 * Read-only attribute indicating the type of series.
9865 * @default stackedMarker
9868 value:"stackedMarker"
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.
9878 * @class ColumnSeries
9879 * @extends MarkerSeries
9883 Y.ColumnSeries = Y.Base.create("columnSeries", Y.MarkerSeries, [Y.Histogram], {
9887 _getMarkerDimensions: function(xcoord, ycoord, calculatedSize, offset)
9891 left: xcoord + offset
9893 config.calculatedSize = this._bottomOrigin - config.top;
9900 * Resizes and positions markers based on a mouse interaction.
9902 * @method updateMarkerState
9903 * @param {String} type state of the marker
9904 * @param {Number} i index of the marker
9906 updateMarkerState: function(type, i)
9908 if(this._markers[i])
9910 var styles = Y.clone(this.get("styles").marker),
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,
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)
9932 renderer = seriesCollection[n].get("markers")[i];
9933 xs[n] = xcoords[i] + seriesSize;
9934 seriesSize += renderer.width;
9937 offset = seriesSize;
9939 offset -= seriesSize/2;
9941 for(n = 0; n < seriesLen; ++n)
9943 renderer = Y.one(seriesCollection[n]._graphicNodes[i]);
9944 renderer.setStyle("left", (xs[n] - seriesSize/2) + "px");
9951 * Read-only attribute indicating the type of series.
9962 * Style properties used for drawing markers. This attribute is inherited from <code>MarkerSeries</code>. Below are the default values:
9964 * <dt>fill</dt><dd>A hash containing the following values:
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>
9970 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
9973 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
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.
9999 * @extends MarkerSeries
10003 Y.BarSeries = Y.Base.create("barSeries", Y.MarkerSeries, [Y.Histogram], {
10007 renderUI: function()
10015 _getMarkerDimensions: function(xcoord, ycoord, calculatedSize, offset)
10018 top: ycoord + offset,
10019 left: this._leftOrigin
10021 config.calculatedSize = xcoord - config.left;
10028 * Resizes and positions markers based on a mouse interaction.
10030 * @method updateMarkerState
10031 * @param {String} type state of the marker
10032 * @param {Number} i index of the marker
10034 updateMarkerState: function(type, i)
10036 if(this._markers[i])
10038 var styles = Y.clone(this.get("styles").marker),
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,
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)
10060 renderer = seriesCollection[n].get("markers")[i];
10061 ys[n] = ycoords[i] + seriesSize;
10062 seriesSize += renderer.height;
10065 offset = seriesSize;
10067 offset -= seriesSize/2;
10069 for(n = 0; n < seriesLen; ++n)
10071 renderer = Y.one(seriesCollection[n]._graphicNodes[i]);
10072 renderer.setStyle("top", (ys[n] - seriesSize/2));
10079 * Read-only attribute indicating the type of series.
10090 * Indicates the direction of the category axis that the bars are plotted against.
10092 * @attribute direction
10100 * Style properties used for drawing markers. This attribute is inherited from <code>MarkerSeries</code>. Below are the default values:
10102 * <dt>fill</dt><dd>A hash containing the following values:
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>
10108 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
10111 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
10126 * @attribute styles
10132 * The AreaSeries class renders quantitative data on a graph by creating a fill between 0
10133 * and the relevant data points.
10135 * @class AreaSeries
10136 * @extends CartesianSeries
10140 Y.AreaSeries = Y.Base.create("areaSeries", Y.CartesianSeries, [Y.Fills], {
10144 * Renders the series.
10146 * @method drawSeries
10148 drawSeries: function()
10150 this.get("graphic").clear();
10151 this.drawFill.apply(this, this._getClosingPoints());
10157 * Method used by <code>styles</code> setter. Overrides base implementation.
10159 * @method _setStyles
10160 * @param {Object} newStyles Hash of properties to update.
10163 _setStyles: function(val)
10169 return Y.AreaSeries.superclass._setStyles.apply(this, [val]);
10175 * Gets the default value for the <code>styles</code> attribute. Overrides
10176 * base implementation.
10178 * @method _getDefaultStyles
10181 _getDefaultStyles: function()
10183 var styles = this._mergeStyles({area:this._getAreaDefaults()}, Y.AreaSeries.superclass._getDefaultStyles());
10190 * Read-only attribute indicating the type of series.
10201 * Style properties used for drawing area fills. This attribute is inherited from <code>Renderer</code>. Below are the default values:
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>
10208 * <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1</dd>
10211 * @attribute styles
10223 * StackedAreaSplineSeries creates a stacked area chart with points data points connected by a curve.
10225 * @class StackedAreaSplineSeries
10227 * @extends AreaSeries
10229 * @uses StackingUtil
10231 Y.StackedAreaSplineSeries = Y.Base.create("stackedAreaSplineSeries", Y.AreaSeries, [Y.CurveUtil, Y.StackingUtil], {
10235 * Draws the series.
10237 * @method drawSeries
10239 drawSeries: function()
10241 this.get("graphic").clear();
10242 this._stackCoordinates();
10243 this.drawStackedAreaSpline();
10248 * Read-only attribute indicating the type of series.
10252 * @default stackedAreaSpline
10255 value:"stackedAreaSpline"
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.
10265 * @class ComboSeries
10266 * @extends CartesianSeries
10272 Y.ComboSeries = Y.Base.create("comboSeries", Y.CartesianSeries, [Y.Fills, Y.Lines, Y.Plots], {
10276 * Draws the series.
10278 * @method drawSeries
10280 drawSeries: function()
10282 this.get("graphic").clear();
10283 if(this.get("showAreaFill"))
10285 this.drawFill.apply(this, this._getClosingPoints());
10287 if(this.get("showLines"))
10291 if(this.get("showMarkers"))
10300 * Returns the default hash for the <code>styles</code> attribute.
10302 * @method _getDefaultStyles
10305 _getDefaultStyles: function()
10307 var styles = Y.ComboSeries.superclass._getDefaultStyles();
10308 styles.line = this._getLineDefaults();
10309 styles.marker = this._getPlotDefaults();
10310 styles.area = this._getAreaDefaults();
10317 * Read-only attribute indicating the type of series.
10328 * Indicates whether a fill is displayed.
10330 * @attribute showAreaFill
10339 * Indicates whether lines are displayed.
10341 * @attribute showLines
10350 * Indicates whether markers are displayed.
10352 * @attribute showMarkers
10361 * Reference to the styles of the markers. These styles can also
10362 * be accessed through the <code>styles</code> attribute. Below are default
10365 * <dt>fill</dt><dd>A hash containing the following values:
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>
10371 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
10374 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
10390 * @attribute marker
10397 return this.get("styles").marker;
10399 setter: function(val)
10401 this.set("styles", {marker:val});
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:
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>
10430 return this.get("styles").line;
10432 setter: function(val)
10434 this.set("styles", {line:val});
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:
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>
10447 * <dt>alpha</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1</dd>
10457 return this.get("styles").area;
10459 setter: function(val)
10461 this.set("styles", {area:val});
10466 * Style properties for the series. Contains a key indexed hash of the following:
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>
10476 * @attribute styles
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
10493 * @class StackedComboSeries
10494 * @extends ComboSeries
10495 * @uses StackingUtil
10498 Y.StackedComboSeries = Y.Base.create("stackedComboSeries", Y.ComboSeries, [Y.StackingUtil], {
10502 * Calculates the coordinates for the series. Overrides base implementation.
10504 * @method setAreaData
10506 setAreaData: function()
10508 Y.StackedComboSeries.superclass.setAreaData.apply(this);
10509 this._stackCoordinates.apply(this);
10515 * Draws the series.
10517 * @method drawSeries
10519 drawSeries: function()
10521 this.get("graphic").clear();
10522 if(this.get("showAreaFill"))
10524 this.drawFill.apply(this, this._getStackedClosingPoints());
10526 if(this.get("showLines"))
10530 if(this.get("showMarkers"))
10539 * Read-only attribute indicating the type of series.
10543 * @default stackedCombo
10546 value: "stackedCombo"
10550 * Indicates whether a fill is displayed.
10552 * @attribute showAreaFill
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.
10566 * @class ComboSplineSeries
10567 * @extends ComboSeries
10568 * @extends CurveUtil
10571 Y.ComboSplineSeries = Y.Base.create("comboSplineSeries", Y.ComboSeries, [Y.CurveUtil], {
10575 * Draws the series.
10577 * @method drawSeries
10579 drawSeries: function()
10581 this.get("graphic").clear();
10582 if(this.get("showAreaFill"))
10584 this.drawAreaSpline();
10586 if(this.get("showLines"))
10590 if(this.get("showMarkers"))
10598 * Read-only attribute indicating the type of series.
10602 * @default comboSpline
10605 value : "comboSpline"
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
10615 * @class StackedComboSplineSeries
10616 * @extends StackedComboSeries
10620 Y.StackedComboSplineSeries = Y.Base.create("stackedComboSplineSeries", Y.StackedComboSeries, [Y.CurveUtil], {
10624 * Draws the series.
10626 * @method drawSeries
10628 drawSeries: function()
10630 this.get("graphic").clear();
10631 if(this.get("showAreaFill"))
10633 this.drawStackedAreaSpline();
10635 if(this.get("showLines"))
10639 if(this.get("showMarkers"))
10647 * Read-only attribute indicating the type of series.
10651 * @default stackedComboSpline
10654 value : "stackedComboSpline"
10658 * Indicates whether a fill is displayed.
10660 * @attribute showAreaFill
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.
10673 * @class StackedLineSeries
10675 * @extends LineSeries
10676 * @uses StackingUtil
10678 Y.StackedLineSeries = Y.Base.create("stackedLineSeries", Y.LineSeries, [Y.StackingUtil], {
10682 * Calculates the coordinates for the series. Overrides base implementation.
10684 * @method setAreaData
10686 setAreaData: function()
10688 Y.StackedLineSeries.superclass.setAreaData.apply(this);
10689 this._stackCoordinates.apply(this);
10694 * Read-only attribute indicating the type of series.
10698 * @default stackedLine
10701 value:"stackedLine"
10706 * StackedAreaSeries area fills to display data showing its contribution to a whole.
10708 * @param {Object} config (optional) Configuration parameters for the Chart.
10709 * @class StackedAreaSeries
10711 * @extends AreaSeries
10712 * @uses StackingUtil
10714 Y.StackedAreaSeries = Y.Base.create("stackedAreaSeries", Y.AreaSeries, [Y.StackingUtil], {
10718 * Calculates the coordinates for the series. Overrides base implementation.
10720 * @method setAreaData
10722 setAreaData: function()
10724 Y.StackedAreaSeries.superclass.setAreaData.apply(this);
10725 this._stackCoordinates.apply(this);
10733 * @method drawSeries
10735 drawSeries: function()
10737 this.get("graphic").clear();
10738 this.drawFill.apply(this, this._getStackedClosingPoints());
10743 * Read-only attribute indicating the type of series.
10747 * @default stackedArea
10750 value:"stackedArea"
10755 * The StackedColumnSeries renders column chart in which series are stacked vertically to show
10756 * their contribution to the cumulative total.
10758 * @class StackedColumnSeries
10759 * @extends ColumnSeries
10760 * @uses StackingUtil
10763 Y.StackedColumnSeries = Y.Base.create("stackedColumnSeries", Y.ColumnSeries, [Y.StackingUtil], {
10767 * Draws the series.
10769 * @method drawSeries
10771 drawSeries: function()
10773 if(this.get("xcoords").length < 1)
10777 var style = this.get("styles").marker,
10780 xcoords = this.get("xcoords"),
10781 ycoords = this.get("ycoords"),
10783 len = xcoords.length,
10785 type = this.get("type"),
10786 graph = this.get("graph"),
10787 seriesCollection = graph.seriesTypes[type],
10789 order = this.get("order"),
10790 graphOrder = this.get("graphOrder"),
10794 negativeBaseValues,
10795 positiveBaseValues,
10796 useOrigin = order === 0,
10797 totalWidth = len * w,
10799 isChrome = ISCHROME;
10800 this._createMarkerCache();
10803 this._createHotspotCache();
10805 if(totalWidth > this.get("width"))
10807 ratio = this.width/totalWidth;
10809 w = Math.max(w, 1);
10813 lastCollection = seriesCollection[order - 1];
10814 negativeBaseValues = lastCollection.get("negativeBaseValues");
10815 positiveBaseValues = lastCollection.get("positiveBaseValues");
10819 negativeBaseValues = [];
10820 positiveBaseValues = [];
10822 this.set("negativeBaseValues", negativeBaseValues);
10823 this.set("positiveBaseValues", positiveBaseValues);
10824 for(i = 0; i < len; ++i)
10829 h = this._bottomOrigin - top;
10830 if(top < this._bottomOrigin)
10832 positiveBaseValues[i] = top;
10833 negativeBaseValues[i] = this._bottomOrigin;
10835 else if(top > this._bottomOrigin)
10837 positiveBaseValues[i] = this._bottomOrigin;
10838 negativeBaseValues[i] = top;
10842 positiveBaseValues[i] = top;
10843 negativeBaseValues[i] = top;
10848 if(top > this._bottomOrigin)
10850 top += (negativeBaseValues[i] - this._bottomOrigin);
10851 h = negativeBaseValues[i] - top;
10852 negativeBaseValues[i] = top;
10854 else if(top < this._bottomOrigin)
10856 top = positiveBaseValues[i] - (this._bottomOrigin - ycoords[i]);
10857 h = positiveBaseValues[i] - top;
10858 positiveBaseValues[i] = top;
10861 left = xcoords[i] - w/2;
10864 marker = this.getMarker(style, graphOrder, i);
10865 marker.setPosition(left, top);
10868 hotspot = this.getHotspot(style, graphOrder, i);
10869 hotspot.setPosition(left, top);
10870 hotspot.parentNode.style.zIndex = 5;
10873 this._clearMarkerCache();
10876 this._clearHotspotCache();
10883 * Resizes and positions markers based on a mouse interaction.
10885 * @method updateMarkerState
10886 * @param {String} type state of the marker
10887 * @param {Number} i index of the marker
10889 updateMarkerState: function(type, i)
10891 if(this._markers[i])
10895 state = this._getState(type),
10896 xcoords = this.get("xcoords"),
10897 marker = this._markers[i],
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)
10906 Y.one(marker.parentNode).setStyle("left", (xcoords[i] - offset));
10914 _getPlotDefaults: function()
10939 defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
10940 defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
10946 * Read-only attribute indicating the type of series.
10950 * @default stackedColumn
10953 value: "stackedColumn"
10959 * @attribute negativeBaseValues
10963 negativeBaseValues: {
10970 * @attribute positiveBaseValues
10974 positiveBaseValues: {
10979 * Style properties used for drawing markers. This attribute is inherited from <code>ColumnSeries</code>. Below are the default values:
10981 * <dt>fill</dt><dd>A hash containing the following values:
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>
10987 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
10990 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
11005 * @attribute styles
11012 * The StackedBarSeries renders bar chart in which series are stacked horizontally to show
11013 * their contribution to the cumulative total.
11015 * @class StackedBarSeries
11016 * @extends BarSeries
11017 * @uses StackingUtil
11020 Y.StackedBarSeries = Y.Base.create("stackedBarSeries", Y.BarSeries, [Y.StackingUtil], {
11024 * Draws the series.
11026 * @method drawSeries
11028 drawSeries: function()
11030 if(this.get("xcoords").length < 1)
11035 var style = this.get("styles").marker,
11038 xcoords = this.get("xcoords"),
11039 ycoords = this.get("ycoords"),
11041 len = xcoords.length,
11043 type = this.get("type"),
11044 graph = this.get("graph"),
11045 seriesCollection = graph.seriesTypes[type],
11047 order = this.get("order"),
11048 graphOrder = this.get("graphOrder"),
11052 negativeBaseValues,
11053 positiveBaseValues,
11054 useOrigin = order === 0,
11055 totalHeight = len * h,
11057 isChrome = ISCHROME;
11058 this._createMarkerCache();
11061 this._createHotspotCache();
11063 if(totalHeight > this.get("height"))
11065 ratio = this.height/totalHeight;
11067 h = Math.max(h, 1);
11071 lastCollection = seriesCollection[order - 1];
11072 negativeBaseValues = lastCollection.get("negativeBaseValues");
11073 positiveBaseValues = lastCollection.get("positiveBaseValues");
11077 negativeBaseValues = [];
11078 positiveBaseValues = [];
11080 this.set("negativeBaseValues", negativeBaseValues);
11081 this.set("positiveBaseValues", positiveBaseValues);
11082 for(i = 0; i < len; ++i)
11089 w = left - this._leftOrigin;
11090 if(left > this._leftOrigin)
11092 positiveBaseValues[i] = left;
11093 negativeBaseValues[i] = this._leftOrigin;
11095 else if(left < this._leftOrigin)
11097 positiveBaseValues[i] = this._leftOrigin;
11098 negativeBaseValues[i] = left;
11102 positiveBaseValues[i] = left;
11103 negativeBaseValues[i] = this._leftOrigin;
11109 if(left < this._leftOrigin)
11111 left = negativeBaseValues[i] - (this._leftOrigin - xcoords[i]);
11112 w = negativeBaseValues[i] - left;
11113 negativeBaseValues[i] = left;
11115 else if(left > this._leftOrigin)
11117 left += (positiveBaseValues[i] - this._leftOrigin);
11118 w = left - positiveBaseValues[i];
11119 positiveBaseValues[i] = left;
11126 marker = this.getMarker(style, graphOrder, i);
11127 marker.setPosition(left, top);
11130 hotspot = this.getHotspot(style, graphOrder, i);
11131 hotspot.setPosition(left, top);
11132 hotspot.parentNode.style.zIndex = 5;
11135 this._clearMarkerCache();
11138 this._clearHotspotCache();
11145 * Resizes and positions markers based on a mouse interaction.
11147 * @method updateMarkerState
11148 * @param {String} type state of the marker
11149 * @param {Number} i index of the marker
11151 updateMarkerState: function(type, i)
11153 if(this._markers[i])
11155 var state = this._getState(type),
11156 ycoords = this.get("ycoords"),
11157 marker = this._markers[i],
11158 styles = this.get("styles").marker,
11160 markerStyles = state == "off" || !styles[state] ? styles : styles[state];
11161 markerStyles.width = marker.width;
11162 marker.update(markerStyles);
11163 if(marker.parentNode)
11165 Y.one(marker.parentNode).setStyle("top", (ycoords[i] - h/2));
11173 * Returns default values for the <code>styles</code> attribute.
11175 * @method _getPlotDefaults
11178 _getPlotDefaults: function()
11203 defs.fill.color = this._getDefaultColor(this.get("graphOrder"), "fill");
11204 defs.border.color = this._getDefaultColor(this.get("graphOrder"), "border");
11210 * Read-only attribute indicating the type of series.
11214 * @default stackedBar
11217 value: "stackedBar"
11221 * Direction of the series
11223 * @attribute direction
11225 * @default vertical
11234 * @attribute negativeBaseValues
11238 negativeBaseValues: {
11245 * @attribute positiveBaseValues
11249 positiveBaseValues: {
11254 * Style properties used for drawing markers. This attribute is inherited from <code>BarSeries</code>. Below are the default values:
11256 * <dt>fill</dt><dd>A hash containing the following values:
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>
11262 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker fill. The default value is 1.</dd>
11265 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
11280 * @attribute styles
11287 * PieSeries visualizes data as a circular chart divided into wedges which represent data as a
11288 * percentage of a whole.
11292 * @extends MarkerSeries
11294 Y.PieSeries = Y.Base.create("pieSeries", Y.MarkerSeries, [], {
11308 _setMap: function()
11310 var id = "pieHotSpotMapi_" + Math.round(100000 * Math.random()),
11311 cb = this.get("graph").get("contentBox"),
11315 cb.removeChild(this._image);
11316 while(this._areaNodes && this._areaNodes.length > 0)
11318 areaNode = this._areaNodes.shift();
11319 this._map.removeChild(areaNode);
11321 cb.removeChild(this._map);
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 = [];
11341 _categoryDisplayName: null,
11346 _valueDisplayName: null,
11351 addListeners: function()
11353 var categoryAxis = this.get("categoryAxis"),
11354 valueAxis = this.get("valueAxis");
11357 categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
11358 categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
11362 valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
11363 valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
11365 this.after("categoryAxisChange", this.categoryAxisChangeHandler);
11366 this.after("valueAxisChange", this.valueAxisChangeHandler);
11367 this.after("stylesChange", this._updateHandler);
11373 validate: function()
11376 this._renderered = true;
11382 _categoryAxisChangeHandler: function(e)
11384 var categoryAxis = this.get("categoryAxis");
11385 categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
11386 categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
11392 _valueAxisChangeHandler: function(e)
11394 var valueAxis = this.get("valueAxis");
11395 valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
11396 valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
11400 * Constant used to generate unique id.
11407 * @private (protected)
11408 * Handles updating the graph when the x < code>Axis</code> values
11411 _categoryDataChangeHandler: function(event)
11413 if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
11420 * @private (protected)
11421 * Handles updating the chart when the y <code>Axis</code> values
11424 _valueDataChangeHandler: function(event)
11426 if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
11435 * Draws the series. Overrides the base implementation.
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)
11446 this._rendered = true;
11448 this.fire("drawingComplete");
11455 drawPlots: function()
11457 var values = this.get("valueAxis").getDataByKey(this.get("valueKey")).concat(),
11458 catValues = this.get("categoryAxis").getDataByKey(this.get("categoryKey")).concat(),
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(),
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),
11478 halfHeight = h / 2,
11479 radius = Math.min(halfWidth, halfHeight),
11488 graphOrder = this.get("graphOrder"),
11489 isCanvas = DRAWINGAPI == "canvas";
11491 for(; i < itemCount; ++i)
11495 values.push(value);
11498 totalValue += value;
11502 tfc = fillColors ? fillColors.concat() : null;
11503 tfa = fillAlphas ? fillAlphas.concat() : null;
11504 this._createMarkerCache();
11508 this._image.width = w;
11509 this._image.height = h;
11511 for(i = 0; i < itemCount; i++)
11514 if(totalValue === 0)
11516 angle = 360 / values.length;
11520 angle = 360 * (value / totalValue);
11522 angle = Math.round(angle);
11523 if(tfc && tfc.length < 1)
11525 tfc = fillColors.concat();
11527 if(tfa && tfa.length < 1)
11529 tfa = fillAlphas.concat();
11531 if(tbw && tbw.length < 1)
11533 tbw = borderWeights.concat();
11535 if(tbw && tbc.length < 1)
11537 tbc = borderColors.concat();
11539 if(tba && tba.length < 1)
11541 tba = borderAlphas.concat();
11543 lw = tbw ? tbw.shift() : null;
11544 lc = tbc ? tbc.shift() : null;
11545 la = tba ? tba.shift() : null;
11546 startAngle += angle;
11554 color:tfc ? tfc.shift() : this._getDefaultColor(i, "slice"),
11555 alpha:tfa ? tfa.shift() : null
11561 startAngle: startAngle,
11568 marker = this.getMarker(wedgeStyle, graphOrder, i);
11571 this._addHotspot(wedgeStyle.props, graphOrder, i);
11574 this._clearMarkerCache();
11577 _addHotspot: function(cfg, seriesIndex, index)
11579 var areaNode = document.createElement("area"),
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,
11598 for(i = 1; i <= numPoints; ++i)
11600 multDivAng = divAngle * i;
11601 cosAng = Math.cos(angleCoord + multDivAng);
11602 sinAng = Math.sin(angleCoord + multDivAng);
11603 if(startAngle <= 90)
11605 pts += ", " + (x + (radius * Math.cos(angleCoord + (divAngle * i))));
11606 pts += ", " + (y + (radius * Math.sin(angleCoord + (divAngle * i))));
11610 pts += ", " + (x - (radius * Math.cos(angleCoord + (divAngle * i))));
11611 pts += ", " + (y - (radius * Math.sin(angleCoord + (divAngle * i))));
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);
11626 * Resizes and positions markers based on a mouse interaction.
11629 * @method updateMarkerState
11630 * @param {String} type state of the marker
11631 * @param {Number} i index of the marker
11633 updateMarkerState: function(type, i)
11635 if(this._markers[i])
11637 var state = this._getState(type),
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);
11653 _createMarker: function(styles, order, index)
11655 var cfg = Y.clone(styles),
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);
11667 _clearMarkerCache: function()
11669 var len = this._markerCache.length,
11672 for(; i < len; ++i)
11674 marker = this._markerCache[i];
11675 if(marker && marker.node && marker.parentNode)
11677 marker.parentNode.removeChild(marker.node);
11680 this._markerCache = [];
11686 _getPlotDefaults: function()
11703 defs.fill.colors = this._defaultSliceColors;
11704 defs.border.colors = this._defaultBorderColors;
11711 _defaultLineColors:["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"],
11716 _defaultFillColors:["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"],
11721 _defaultBorderColors:["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"],
11726 _defaultSliceColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],
11730 * @description Colors used if style colors are not specified
11732 _getDefaultColor: function(index, type)
11735 line: this._defaultLineColors,
11736 fill: this._defaultFillColors,
11737 border: this._defaultBorderColors,
11738 slice: this._defaultSliceColors
11740 col = colors[type],
11742 index = index || 0;
11747 type = type || "fill";
11748 return colors[type][index];
11753 * Read-only attribute indicating the type of series.
11764 * Order of this instance of this <code>type</code>.
11772 * Reference to the <code>Graph</code> in which the series is drawn into.
11780 * Reference to the <code>Axis</code> instance used for assigning
11781 * category values to the graph.
11783 * @attribute categoryAxis
11789 validator: function(value)
11791 return value !== this.get("categoryAxis");
11796 * Reference to the <code>Axis</code> instance used for assigning
11797 * series values to the graph.
11799 * @attribute categoryAxis
11805 validator: function(value)
11807 return value !== this.get("valueAxis");
11812 * Indicates which array to from the hash of value arrays in
11813 * the category <code>Axis</code> instance.
11818 validator: function(value)
11820 return value !== this.get("categoryKey");
11824 * Indicates which array to from the hash of value arrays in
11825 * the value <code>Axis</code> instance.
11830 validator: function(value)
11832 return value !== this.get("valueKey");
11837 * Name used for for displaying category data
11839 * @attribute categoryDisplayName
11842 categoryDisplayName: {
11843 setter: function(val)
11845 this._categoryDisplayName = val;
11851 return this._categoryDisplayName || this.get("categoryKey");
11856 * Name used for for displaying value data
11858 * @attribute valueDisplayName
11861 valueDisplayName: {
11862 setter: function(val)
11864 this._valueDisplayName = val;
11870 return this._valueDisplayName || this.get("valueKey");
11880 * Style properties used for drawing markers. This attribute is inherited from <code>MarkerSeries</code>. Below are the default values:
11882 * <dt>fill</dt><dd>A hash containing the following values:
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>
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>
11891 * <dt>border</dt><dd>A hash containing the following values:
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>
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>
11905 * @attribute styles
11911 * Gridlines draws gridlines on a Graph.
11918 Y.Gridlines = Y.Base.create("gridlines", Y.Base, [Y.Renderer], {
11932 var graphic = this.get("graphic"),
11936 gNode = graphic.node;
11939 Y.one(gNode).remove();
11947 * Draws the gridlines
11953 if(this.get("axis") && this.get("graph"))
11955 this._drawGridlines();
11962 _drawGridlines: function()
11964 var graphic = this.get("graphic"),
11965 axis = this.get("axis"),
11966 axisPosition = axis.get("position"),
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")
11982 l = axis.get("styles").majorUnit.count;
11994 points = axis.get("tickPoints");
12000 graphic = this.get("graphic");
12003 graphic.setSize(w, h);
12004 graphic.lineStyle(weight, color, alpha);
12007 lineFunction(graphic, points[i], w, h);
12015 _horizontalLine: function(graphic, pt, w, h)
12017 graphic.moveTo(0, pt.y);
12018 graphic.lineTo(w, pt.y);
12024 _verticalLine: function(graphic, pt, w, h)
12026 graphic.moveTo(pt.x, 0);
12027 graphic.lineTo(pt.x, h);
12032 * Creates a <code>Graphic</code> instance.
12034 _setCanvas: function()
12036 this.set("graphic", new Y.Graphic());
12037 this.get("graphic").render(this.get("graph").get("contentBox"));
12043 * Gets the default value for the <code>styles</code> attribute. Overrides
12044 * base implementation.
12046 * @method _getDefaultStyles
12049 _getDefaultStyles: function()
12065 * Indicates the direction of the gridline.
12067 * @attribute direction
12073 * Indicate the <code>Axis</code> in which to bind
12082 * Indicates the <code>Graph</code> in which the gridlines
12092 * Graph manages and contains series instances for a <code>CartesianChart</code>
12100 Y.Graph = Y.Base.create("graph", Y.Widget, [Y.Renderer], {
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);
12115 if(this.get("showBackground"))
12117 var graphic = new Y.Graphic(),
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");
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);
12147 renderUI: function()
12149 var sc = this.get("seriesCollection"),
12153 hgl = this.get("horizontalGridlines"),
12154 vgl = this.get("verticalGridlines");
12155 for(; i < len; ++i)
12158 if(series instanceof Y.CartesianSeries)
12163 if(hgl && hgl instanceof Y.Gridlines)
12167 if(vgl && vgl instanceof Y.Gridlines)
12175 * Hash of arrays containing series mapped to a series type.
12180 * Returns a series instance based on an index.
12182 * @method getSeriesByIndex
12183 * @param {Number} val index of the series
12184 * @return CartesianSeries
12186 getSeriesByIndex: function(val)
12188 var col = this.get("seriesCollection"),
12190 if(col && col.length > val)
12198 * Returns a series instance based on a key value.
12200 * @method getSeriesByKey
12201 * @param {String} val key value of the series
12202 * @return CartesianSeries
12204 getSeriesByKey: function(val)
12206 var obj = this._seriesDictionary,
12208 if(obj && obj.hasOwnProperty(val))
12217 * Adds dispatcher to a <code>_dispatcher</code> used to
12218 * to ensure all series have redrawn before for firing event.
12220 * @method addDispatcher
12221 * @param {CartesianSeries} val series instance to add
12223 addDispatcher: function(val)
12225 if(!this._dispatchers)
12227 this._dispatchers = [];
12229 this._dispatchers.push(val);
12234 * @description Collection of series to be displayed in the graph.
12236 _seriesCollection: null,
12241 _seriesDictionary: null,
12245 * Parses series instances to be displayed in the graph.
12247 _parseSeriesCollection: function(val)
12253 var len = val.length,
12257 if(!this.get("seriesCollection"))
12259 this._seriesCollection = [];
12261 if(!this._seriesDictionary)
12263 this._seriesDictionary = {};
12265 if(!this.seriesTypes)
12267 this.seriesTypes = [];
12269 for(; i < len; ++i)
12272 if(!(series instanceof Y.CartesianSeries) && !(series instanceof Y.PieSeries))
12274 this._createSeries(series);
12277 this._addSeries(series);
12279 len = this.get("seriesCollection").length;
12280 for(i = 0; i < len; ++i)
12282 series = this.get("seriesCollection")[i];
12283 seriesKey = series.get("direction") == "horizontal" ? "yKey" : "xKey";
12284 this._seriesDictionary[series.get(seriesKey)] = series;
12290 * Adds a series to the graph.
12292 _addSeries: function(series)
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"))
12301 series.set("graph", this);
12303 seriesCollection.push(series);
12304 if(!seriesTypes.hasOwnProperty(type))
12306 this.seriesTypes[type] = [];
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);
12320 _createSeries: function(seriesData)
12322 var type = seriesData.type,
12323 seriesCollection = this.get("seriesCollection"),
12324 seriesTypes = this.seriesTypes,
12325 typeSeriesCollection,
12328 seriesData.graph = this;
12329 if(!seriesTypes.hasOwnProperty(type))
12331 seriesTypes[type] = [];
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);
12348 _getSeries: function(type)
12354 seriesClass = Y.LineSeries;
12357 seriesClass = Y.ColumnSeries;
12360 seriesClass = Y.BarSeries;
12363 seriesClass = Y.AreaSeries;
12365 case "candlestick" :
12366 seriesClass = Y.CandlestickSeries;
12369 seriesClass = Y.OHLCSeries;
12371 case "stackedarea" :
12372 seriesClass = Y.StackedAreaSeries;
12374 case "stackedline" :
12375 seriesClass = Y.StackedLineSeries;
12377 case "stackedcolumn" :
12378 seriesClass = Y.StackedColumnSeries;
12380 case "stackedbar" :
12381 seriesClass = Y.StackedBarSeries;
12383 case "markerseries" :
12384 seriesClass = Y.MarkerSeries;
12387 seriesClass = Y.SplineSeries;
12389 case "areaspline" :
12390 seriesClass = Y.AreaSplineSeries;
12392 case "stackedspline" :
12393 seriesClass = Y.StackedSplineSeries;
12395 case "stackedareaspline" :
12396 seriesClass = Y.StackedAreaSplineSeries;
12398 case "stackedmarkerseries" :
12399 seriesClass = Y.StackedMarkerSeries;
12402 seriesClass = Y.PieSeries;
12405 seriesClass = Y.ComboSeries;
12407 case "stackedcombo" :
12408 seriesClass = Y.StackedComboSeries;
12410 case "combospline" :
12411 seriesClass = Y.ComboSplineSeries;
12413 case "stackedcombospline" :
12414 seriesClass = Y.StackedComboSplineSeries;
12417 seriesClass = Y.CartesianSeries;
12420 return seriesClass;
12426 _markerEventHandler: function(e)
12429 markerNode = e.currentTarget,
12430 strArr = markerNode.getAttribute("id").split("_"),
12431 series = this.getSeriesByIndex(strArr[1]),
12433 series.updateMarkerState(type, index);
12439 _dispatchers: null,
12444 _updateStyles: function()
12446 this._background.update(this.get("styles").background);
12447 this._sizeChangeHandler();
12453 _sizeChangeHandler: function(e)
12455 var hgl = this.get("horizontalGridlines"),
12456 vgl = this.get("verticalGridlines"),
12457 w = this.get("width"),
12458 h = this.get("height"),
12462 bg = this.get("styles").background,
12464 if(bg && bg.border)
12466 weight = bg.border.weight || 0;
12468 if(this._background)
12470 graphicNode = Y.one(this._background.parentNode);
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});
12487 if(hgl && hgl instanceof Y.Gridlines)
12491 if(vgl && vgl instanceof Y.Gridlines)
12495 this._drawSeries();
12501 _drawSeries: function()
12505 this._callLater = true;
12508 this._callLater = false;
12509 this._drawing = true;
12510 var sc = this.get("seriesCollection"),
12513 for(; i < len; ++i)
12516 if(!sc[i].get("xcoords") || !sc[i].get("ycoords"))
12518 this._callLater = true;
12522 this._drawing = false;
12523 if(this._callLater)
12525 this._drawSeries();
12532 _drawingCompleteHandler: function(e)
12534 var series = e.currentTarget,
12535 index = Y.Array.indexOf(this._dispatchers, series);
12538 this._dispatchers.splice(index, 1);
12540 if(this._dispatchers.length < 1)
12542 this.fire("chartRendered");
12549 * Gets the default value for the <code>styles</code> attribute. Overrides
12550 * base implementation.
12552 * @method _getDefaultStyles
12555 _getDefaultStyles: function()
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.
12577 * @attribute seriesCollection
12578 * @type CartesianSeries
12580 seriesCollection: {
12583 return this._seriesCollection;
12586 setter: function(val)
12588 this._parseSeriesCollection(val);
12589 return this._seriesCollection;
12594 * Indicates whether the <code>Graph</code> has a background.
12596 * @attribute showBackground
12605 * Read-only hash lookup for all series on in the <code>Graph</code>.
12607 * @attribute seriesDictionary
12610 seriesDictionary: {
12615 return this._seriesDictionary;
12620 * Reference to the horizontal <code>Gridlines</code> instance.
12622 * @attribute horizontalGridlines
12626 horizontalGridlines: {
12629 setter: function(val)
12631 var gl = this.get("horizontalGridlines");
12632 if(gl && gl instanceof Y.Gridlines)
12636 if(val instanceof Y.Gridlines)
12639 val.set("graph", this);
12643 else if(val && val.axis)
12645 gl = new Y.Gridlines({direction:"horizontal", axis:val.axis, graph:this, styles:val.styles});
12653 * Reference to the vertical <code>Gridlines</code> instance.
12655 * @attribute verticalGridlines
12659 verticalGridlines: {
12662 setter: function(val)
12664 var gl = this.get("verticalGridlines");
12665 if(gl && gl instanceof Y.Gridlines)
12669 if(val instanceof Y.Gridlines)
12672 val.set("graph", this);
12676 else if(val && val.axis)
12678 gl = new Y.Gridlines({direction:"vertical", axis:val.axis, graph:this, styles:val.styles});
12686 * Style properties used for drawing a background. Below are the default values:
12688 * <dt>fill</dt><dd>A hash containing the following values:
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>
12694 * <dt>border</dt><dd>A hash containing the following values:
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>
12703 * @attribute styles
12709 * The ChartBase class is an abstract class used to create charts.
12714 function ChartBase() {}
12716 ChartBase.ATTRS = {
12718 * Reference to the default tooltip available for the chart.
12719 * <p>Contains the following properties:</p>
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>
12731 * @attribute tooltip
12735 valueFn: "_getTooltip",
12737 setter: function(val)
12739 return this._updateTooltip(val);
12744 * The key value used for the chart's category axis.
12746 * @attribute categoryKey
12748 * @default category
12755 * Indicates the type of axis to use for the category axis.
12758 * <dt>category</dt><dd>Specifies a <code>CategoryAxis</code>.</dd>
12759 * <dt>time</dt><dd>Specifies a <code>TimeAxis</dd>
12762 * @attribute categoryType
12764 * @default category
12771 * Indicates the the type of interactions that will fire events.
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>
12779 * @attribute interactionType
12788 * Data used to generate the chart.
12790 * @attribute dataProvider
12794 setter: function(val)
12796 return this._setDataValues(val);
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
12805 * @attribute seriesKeys
12811 * Reference to all the axes in the chart.
12813 * @attribute axesCollection
12816 axesCollection: {},
12819 * Reference to graph instance.
12825 valueFn: "_getGraph"
12829 ChartBase.prototype = {
12832 * @description Default value function for the <code>graph</code> attribute.
12834 _getGraph: function()
12836 var graph = new Y.Graph();
12837 graph.after("chartRendered", Y.bind(function(e) {
12838 this.fire("chartRendered");
12844 * Returns a series instance by index or key value.
12846 * @method getSeries
12848 * @return CartesianSeries
12850 getSeries: function(val)
12853 graph = this.get("graph");
12856 if(Y.Lang.isNumber(val))
12858 series = graph.getSeriesByIndex(val);
12862 series = graph.getSeriesByKey(val);
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>.
12874 * @method getAxisByKey
12875 * @param {String} val Key reference used to look up the axis.
12878 getAxisByKey: function(val)
12881 axes = this.get("axes");
12882 if(axes.hasOwnProperty(val))
12890 * Returns the category axis for the chart.
12892 * @method getCategoryAxis
12895 getCategoryAxis: function()
12898 key = this.get("categoryKey"),
12899 axes = this.get("axes");
12900 if(axes.hasOwnProperty(key))
12910 _direction: "horizontal",
12915 _dataProvider: null,
12920 _setDataValues: function(val)
12922 if(Y.Lang.isArray(val[0]))
12933 hash = {category:cats[i]};
12934 for(n = 1; n < sl; ++n)
12936 hash["series" + n] = val[n][i];
12948 _seriesCollection: null,
12953 _setSeriesCollection: function(val)
12955 this._seriesCollection = val;
12960 _getAxisClass: function(t)
12962 return this._axisClass[t];
12969 stacked: Y.StackedAxis,
12970 numeric: Y.NumericAxis,
12971 category: Y.CategoryAxis,
12983 renderUI: function()
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");
12993 this._addTooltip();
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"),
13014 if(interactionType == "marker")
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");
13025 else if(interactionType == "planar")
13027 this._overlay.on("mousemove", Y.bind(this._planarEventDispatcher, this));
13028 this.on("mouseout", this.hideTooltip);
13032 if(hideEvent && showEvent && hideEvent == showEvent)
13034 this.on(interactionType + "Event:" + hideEvent, this.toggleTooltip);
13040 this.on(interactionType + "Event:" + showEvent, tt[interactionType + "EventHandler"]);
13044 if(Y.Lang.isArray(hideEvent))
13046 len = hideEvent.length;
13047 for(; i < len; ++i)
13049 this.on(interactionType + "Event:" + hideEvent[i], this.hideTooltip);
13052 this.on(interactionType + "Event:" + hideEvent, this.hideTooltip);
13061 _markerEventDispatcher: function(e)
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)),
13070 items = this.getSeriesItems(series, index),
13071 x = e.pageX - cb.getX(),
13072 y = e.pageY - cb.getY();
13073 if(type == "mouseenter")
13075 type = "mouseover";
13077 else if(type == "mouseleave")
13081 series.updateMarkerState(type, index);
13084 * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mouseover event.
13087 * @event markerEvent:mouseover
13088 * @preventable false
13089 * @param {EventFacade} e Event facade with the following additional
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>
13103 * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mouseout event.
13105 * @event markerEvent:mouseout
13106 * @preventable false
13107 * @param {EventFacade} e Event facade with the following additional
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>
13121 * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mousedown event.
13123 * @event markerEvent:mousedown
13124 * @preventable false
13125 * @param {EventFacade} e Event facade with the following additional
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>
13139 * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a mouseup event.
13141 * @event markerEvent:mouseup
13142 * @preventable false
13143 * @param {EventFacade} e Event facade with the following additional
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>
13157 * Broadcasts when <code>interactionType</code> is set to <code>marker</code> and a series marker has received a click event.
13159 * @event markerEvent:click
13160 * @preventable false
13161 * @param {EventFacade} e Event facade with the following additional
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>
13174 this.fire("markerEvent:" + type, {categoryItem:items.category, valueItem:items.value, node:markerNode, x:x, y:y, series:series, index:index, seriesIndex:seriesIndex});
13180 _dataProviderChangeHandler: function(e)
13182 var dataProvider = this.get("dataProvider"),
13183 axes = this.get("axes"),
13188 if(axes.hasOwnProperty(i))
13191 if(axis instanceof Y.Axis)
13193 axis.set("dataProvider", dataProvider);
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.
13203 * @method toggleTooltip
13205 toggleTooltip: function(e)
13207 var tt = this.get("tooltip");
13210 this.hideTooltip();
13214 tt.markerEventHandler.apply(this, [e]);
13221 _showTooltip: function(msg, x, y)
13223 var tt = this.get("tooltip"),
13228 node.set("innerHTML", msg);
13229 node.setStyle("top", y + "px");
13230 node.setStyle("left", x + "px");
13231 node.removeClass("yui3-widget-hidden");
13238 _positionTooltip: function(e)
13240 var tt = this.get("tooltip"),
13242 cb = this.get("contentBox"),
13243 x = (e.pageX + 10) - cb.getX(),
13244 y = (e.pageY + 10) - cb.getY();
13247 node.setStyle("left", x + "px");
13248 node.setStyle("top", y + "px");
13253 * Hides the default tooltip
13255 hideTooltip: function()
13257 var tt = this.get("tooltip"),
13259 tt.visible = false;
13260 node.set("innerHTML", "");
13261 node.setStyle("left", -10000);
13262 node.setStyle("top", -10000);
13263 node.addClass("yui3-widget-hidden");
13269 _addTooltip: function()
13271 var tt = this.get("tooltip");
13272 this.get("contentBox").appendChild(tt.node);
13278 _updateTooltip: function(val)
13280 var tt = this._tooltip,
13282 styles = val.styles,
13284 markerLabelFunction:"markerLabelFunction",
13285 planarLabelFunction:"planarLabelFunction",
13286 showEvent:"showEvent",
13287 hideEvent:"hideEvent",
13288 markerEventHandler:"markerEventHandler",
13289 planarEventHandler:"planarEventHandler"
13295 if(styles.hasOwnProperty(i))
13297 tt.node.setStyle(i, styles[i]);
13303 if(val.hasOwnProperty(i))
13314 _getTooltip: function()
13316 var node = document.createElement("div"),
13318 markerLabelFunction: this._tooltipLabelFunction,
13319 planarLabelFunction: this._planarLabelFunction,
13321 hideEvent: "mouseout",
13322 showEvent: "mouseover",
13323 markerEventHandler: function(e)
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);
13329 planarEventHandler: function(e)
13331 var tt = this.get("tooltip"),
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);
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;
13361 _planarLabelFunction: function(categoryAxis, valueItems, index, seriesArray, seriesIndex)
13366 len = seriesArray.length,
13371 msg += categoryAxis.get("labelFunction").apply(this, [categoryAxis.getKeyValueAt(this.get("categoryKey"), index), categoryAxis.get("labelFormat")]);
13374 for(; i < len; ++i)
13376 series = seriesArray[i];
13377 if(series.get("visible"))
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>";
13390 _tooltipLabelFunction: function(categoryItem, valueItem, itemIndex, series, seriesIndex)
13392 var msg = categoryItem.displayName +
13393 ": " + categoryItem.axis.get("labelFunction").apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) +
13394 "<br/>" + valueItem.displayName +
13395 ": " + valueItem.axis.get("labelFunction").apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]);
13402 _tooltipChangeHandler: function(e)
13404 if(this.get("tooltip"))
13406 var tt = this.get("tooltip"),
13409 cb = this.get("contentBox");
13412 if(!cb.containes(node))
13414 this._addTooltip();
13420 Y.ChartBase = ChartBase;
13422 * The CartesianChart class creates a chart with horizontal and vertical axes.
13424 * @class CartesianChart
13425 * @extends ChartBase
13428 Y.CartesianChart = Y.Base.create("cartesianChart", Y.Widget, [Y.ChartBase], {
13432 renderUI: function()
13434 var tt = this.get("tooltip"),
13436 //move the position = absolute logic to a class file
13437 this.get("boundingBox").setStyle("position", "absolute");
13438 this.get("contentBox").setStyle("position", "absolute");
13440 this._addGridlines();
13444 this._addTooltip();
13446 //If there is a style definition. Force them to set.
13447 this.get("styles");
13448 if(this.get("interactionType") == "planar")
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);
13465 _planarEventDispatcher: function(e)
13467 var graph = this.get("graph"),
13468 bb = this.get("boundingBox"),
13469 cb = graph.get("contentBox"),
13471 offsetX = x - cb.getX(),
13472 posX = x - bb.getX(),
13474 offsetY = y - cb.getY(),
13475 posY = y - bb.getY(),
13476 sc = graph.get("seriesCollection"),
13480 oldIndex = this._selectedIndex,
13483 categoryItems = [],
13485 direction = this.get("direction"),
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)
13493 if(coord <= markerPlane[i].end && coord >= markerPlane[i].start)
13500 for(i = 0; i < len; ++i)
13503 hasMarkers = series.get("markers");
13504 if(hasMarkers && !isNaN(oldIndex) && oldIndex > -1)
13506 series.updateMarkerState("mouseout", oldIndex);
13508 if(series.get("ycoords")[index] > -1)
13510 if(hasMarkers && !isNaN(index) && index > -1)
13512 series.updateMarkerState("mouseover", index);
13514 item = this.getSeriesItems(series, index);
13515 categoryItems.push(item.category);
13516 valueItems.push(item.value);
13517 items.push(series);
13521 this._selectedIndex = index;
13524 * Broadcasts when <code>interactionType</code> is set to <code>planar</code> and a series' marker plane has received a mouseover event.
13527 * @event planarEvent:mouseover
13528 * @preventable false
13529 * @param {EventFacade} e Event facade with the following additional
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>
13541 * Broadcasts when <code>interactionType</code> is set to <code>planar</code> and a series' marker plane has received a mouseout event.
13543 * @event planarEvent:mouseout
13544 * @preventable false
13545 * @param {EventFacade} e
13549 this.fire("planarEvent:mouseover", {categoryItem:categoryItems, valueItem:valueItems, x:posX, y:posY, items:items, index:index});
13553 this.fire("planarEvent:mouseout");
13565 _axesRenderQueue: null,
13570 _addToAxesRenderQueue: function(axis)
13572 if(!this._axesRenderQueue)
13574 this._axesRenderQueue = [];
13576 if(Y.Array.indexOf(this._axesRenderQueue, axis) < 0)
13578 this._axesRenderQueue.push(axis);
13585 _getDefaultSeriesCollection: function(val)
13587 var dir = this.get("direction"),
13593 seriesKeys = this.get("seriesKeys").concat(),
13597 type = this.get("type"),
13602 categoryKey = this.get("categoryKey"),
13603 showMarkers = this.get("showMarkers"),
13604 showAreaFill = this.get("showAreaFill"),
13605 showLines = this.get("showLines");
13606 if(dir == "vertical")
13611 seriesKey = "xKey";
13618 seriesKey = "yKey";
13621 for(i = 0; i < l; ++i)
13623 key = this._getBaseAttribute(sc[i], seriesKey);
13626 index = Y.Array.indexOf(seriesKeys, key);
13629 seriesKeys.splice(index, 1);
13631 tempKeys.push(key);
13634 if(seriesKeys.length > 0)
13636 tempKeys = tempKeys.concat(seriesKeys);
13638 l = tempKeys.length;
13639 for(i = 0; i < l; ++i)
13641 series = sc[i] || {type:type};
13642 if(series instanceof Y.CartesianSeries)
13644 this._parseSeriesAxes(series);
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]);
13653 series.type = series.type || type;
13655 if((series.type == "combo" || series.type == "stackedcombo" || series.type == "combospline" || series.type == "stackedcombospline"))
13657 if(showAreaFill !== null)
13659 series.showAreaFill = series.showAreaFill || showAreaFill;
13661 if(showMarkers !== null)
13663 series.showMarkers = series.showMarkers || showMarkers;
13665 if(showLines !== null)
13667 series.showLines = series.showLines || showLines;
13674 graph = this.get("graph");
13675 graph.set("seriesCollection", sc);
13676 sc = graph.get("seriesCollection");
13684 _parseSeriesAxes: function(series)
13686 var axes = this.get("axes"),
13687 xAxis = series.get("xAxis"),
13688 yAxis = series.get("yAxis"),
13691 if(xAxis && !(xAxis instanceof YAxis) && Y.Lang.isString(xAxis) && axes.hasOwnProperty(xAxis))
13693 axis = axes[xAxis];
13694 if(axis instanceof YAxis)
13696 series.set("xAxis", axis);
13699 if(yAxis && !(yAxis instanceof YAxis) && Y.Lang.isString(yAxis) && axes.hasOwnProperty(yAxis))
13701 axis = axes[yAxis];
13702 if(axis instanceof YAxis)
13704 series.set("yAxis", axis);
13713 _getCategoryAxis: function()
13716 axes = this.get("axes"),
13717 categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey");
13718 axis = axes[categoryAxisName];
13725 _getSeriesAxis:function(key, axisName)
13727 var axes = this.get("axes"),
13733 if(axisName && axes.hasOwnProperty(axisName))
13735 axis = axes[axisName];
13741 if(axes.hasOwnProperty(i))
13743 keys = axes[i].get("keys");
13744 if(keys && keys.hasOwnProperty(key))
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.
13762 _getBaseAttribute: function(item, key)
13764 if(item instanceof Y.Base)
13766 return item.get(key);
13768 if(item.hasOwnProperty(key))
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.
13781 _setBaseAttribute: function(item, key, value)
13783 if(item instanceof Y.Base)
13785 item.set(key, value);
13795 * Creates Axis and Axis data classes based on hashes of properties.
13797 _parseAxes: function(val)
13799 var hash = this._getDefaultAxes(val),
13802 edgeOffset: "edgeOffset",
13803 position: "position",
13804 overlapGraph:"overlapGraph",
13805 labelFunction:"labelFunction",
13806 labelFunctionScope:"labelFunctionScope",
13807 labelFormat:"labelFormat",
13810 roundingMethod:"roundingMethod",
13811 alwaysShowZero:"alwaysShowZero"
13813 dp = this.get("dataProvider"),
13824 if(hash.hasOwnProperty(i))
13827 if(dh instanceof Y.Axis)
13833 axisClass = this._getAxisClass(dh.type);
13835 config.dataProvider = dh.dataProvider || dp;
13836 config.keys = dh.keys;
13838 if(dh.hasOwnProperty("roundingUnit"))
13840 config.roundingUnit = dh.roundingUnit;
13845 config.styles = dh.styles;
13847 config.position = dh.position;
13848 for(ai in axesAttrs)
13850 if(axesAttrs.hasOwnProperty(ai) && dh.hasOwnProperty(ai))
13852 config[ai] = dh[ai];
13855 axis = new axisClass(config);
13860 axesCollection = this.get(pos + "AxesCollection");
13861 if(axesCollection && Y.Array.indexOf(axesCollection, axis) > 0)
13863 axis.set("overlapGraph", false);
13865 axis.after("axisRendered", Y.bind(this._axisRendered, this));
13876 _addAxes: function()
13878 var axes = this.get("axes"),
13882 w = this.get("width"),
13883 h = this.get("height"),
13884 node = Y.Node.one(this._parentNode);
13885 if(!this._axesCollection)
13887 this._axesCollection = [];
13891 if(axes.hasOwnProperty(i))
13894 if(axis instanceof Y.Axis)
13898 this.set("width", node.get("offsetWidth"));
13899 w = this.get("width");
13903 this.set("height", node.get("offsetHeight"));
13904 h = this.get("height");
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"))
13912 this.set(pos + "AxesCollection", [axis]);
13916 this.get(pos + "AxesCollection").push(axis);
13918 this._axesCollection.push(axis);
13919 if(axis.get("keys").hasOwnProperty(this.get("categoryKey")))
13921 this.set("categoryAxis", axis);
13923 axis.render(this.get("contentBox"));
13932 _addSeries: function()
13934 var graph = this.get("graph"),
13935 sc = this.get("seriesCollection");
13936 graph.render(this.get("contentBox"));
13942 * @description Adds gridlines to the chart.
13944 _addGridlines: function()
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"),
13958 if(this._axesCollection)
13960 seriesAxesCollection = this._axesCollection.concat();
13961 seriesAxesCollection.splice(Y.Array.indexOf(seriesAxesCollection, catAxis), 1);
13965 if(leftAxesCollection && leftAxesCollection[0])
13967 hAxis = leftAxesCollection[0];
13969 else if(rightAxesCollection && rightAxesCollection[0])
13971 hAxis = rightAxesCollection[0];
13975 hAxis = direction == "horizontal" ? catAxis : seriesAxesCollection[0];
13977 if(!this._getBaseAttribute(hgl, "axis") && hAxis)
13979 this._setBaseAttribute(hgl, "axis", hAxis);
13981 if(this._getBaseAttribute(hgl, "axis"))
13983 graph.set("horizontalGridlines", hgl);
13988 if(bottomAxesCollection && bottomAxesCollection[0])
13990 vAxis = bottomAxesCollection[0];
13992 else if (topAxesCollection && topAxesCollection[0])
13994 vAxis = topAxesCollection[0];
13998 vAxis = direction == "vertical" ? catAxis : seriesAxesCollection[0];
14000 if(!this._getBaseAttribute(vgl, "axis") && vAxis)
14002 this._setBaseAttribute(vgl, "axis", vAxis);
14004 if(this._getBaseAttribute(vgl, "axis"))
14006 graph.set("verticalGridlines", vgl);
14014 _getDefaultAxes: function(axes)
14016 var catKey = this.get("categoryKey"),
14022 categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"),
14023 valueAxisName = this.get("valueAxisName"),
14024 seriesKeys = this.get("seriesKeys") || [],
14031 dp = this.get("dataProvider"),
14032 direction = this.get("direction"),
14036 seriesAxis = this.get("stacked") ? "stacked" : "numeric";
14038 if(direction == "vertical")
14040 seriesPosition = "bottom";
14041 categoryPosition = "left";
14045 seriesPosition = "left";
14046 categoryPosition = "bottom";
14052 if(axes.hasOwnProperty(i))
14055 keys = this._getBaseAttribute(axis, "keys");
14056 attr = this._getBaseAttribute(axis, "type");
14057 if(attr == "time" || attr == "category")
14059 categoryAxisName = i;
14060 this.set("categoryAxisName", i);
14061 if(Y.Lang.isArray(keys) && keys.length > 0)
14064 this.set("categoryKey", catKey);
14068 else if(i == categoryAxisName)
14075 if(i != valueAxisName && keys && Y.Lang.isArray(keys))
14078 for(ii = 0; ii < ll; ++ii)
14080 claimedKeys.push(keys[ii]);
14082 valueAxes.push(newAxes[i]);
14084 if(!(this._getBaseAttribute(newAxes[i], "type")))
14086 this._setBaseAttribute(newAxes[i], "type", seriesAxis);
14088 if(!(this._getBaseAttribute(newAxes[i], "position")))
14090 this._setBaseAttribute(newAxes[i], "position", this._getDefaultAxisPosition(newAxes[i], valueAxes, seriesPosition));
14096 if(seriesKeys.length < 1)
14100 if(dv.hasOwnProperty(i) && i != catKey && Y.Array.indexOf(claimedKeys, i) == -1)
14102 seriesKeys.push(i);
14106 cIndex = Y.Array.indexOf(seriesKeys, catKey);
14109 seriesKeys.splice(cIndex, 1);
14111 l = claimedKeys.length;
14112 for(i = 0; i < l; ++i)
14114 cIndex = Y.Array.indexOf(seriesKeys, claimedKeys[i]);
14117 seriesKeys.splice(cIndex, 1);
14120 if(!newAxes.hasOwnProperty(categoryAxisName))
14122 newAxes[categoryAxisName] = {};
14124 if(!(this._getBaseAttribute(newAxes[categoryAxisName], "keys")))
14126 this._setBaseAttribute(newAxes[categoryAxisName], "keys", [catKey]);
14129 if(!(this._getBaseAttribute(newAxes[categoryAxisName], "position")))
14131 this._setBaseAttribute(newAxes[categoryAxisName], "position", categoryPosition);
14134 if(!(this._getBaseAttribute(newAxes[categoryAxisName], "type")))
14136 this._setBaseAttribute(newAxes[categoryAxisName], "type", this.get("categoryType"));
14138 if(!newAxes.hasOwnProperty(valueAxisName) && seriesKeys && seriesKeys.length > 0)
14140 newAxes[valueAxisName] = {keys:seriesKeys};
14141 valueAxes.push(newAxes[valueAxisName]);
14143 if(claimedKeys.length > 0)
14145 if(seriesKeys.length > 0)
14147 seriesKeys = claimedKeys.concat(seriesKeys);
14151 seriesKeys = claimedKeys;
14154 if(newAxes.hasOwnProperty(valueAxisName))
14156 if(!(this._getBaseAttribute(newAxes[valueAxisName], "position")))
14158 this._setBaseAttribute(newAxes[valueAxisName], "position", this._getDefaultAxisPosition(newAxes[valueAxisName], valueAxes, seriesPosition));
14160 if(!(this._getBaseAttribute(newAxes[valueAxisName], "type")))
14162 this._setBaseAttribute(newAxes[valueAxisName], "type", seriesAxis);
14164 if(!(this._getBaseAttribute(newAxes[valueAxisName], "keys")))
14166 this._setBaseAttribute(newAxes[valueAxisName], "keys", seriesKeys);
14169 this.set("seriesKeys", seriesKeys);
14175 * @description Determines the position of an axis when one is not specified.
14177 _getDefaultAxisPosition: function(axis, valueAxes, position)
14179 var direction = this.get("direction"),
14180 i = Y.Array.indexOf(valueAxes, axis);
14182 if(valueAxes[i - 1] && valueAxes[i - 1].position)
14184 if(direction == "horizontal")
14186 if(valueAxes[i - 1].position == "left")
14188 position = "right";
14190 else if(valueAxes[i - 1].position == "right")
14197 if (valueAxes[i -1].position == "bottom")
14203 position = "bottom";
14212 * Returns an object literal containing a categoryItem and a valueItem for a given series index.
14214 * @method getSeriesItem
14215 * @param {CartesianSeries} series Reference to a series.
14216 * @param {Number} index Index of the specified item within a series.
14219 getSeriesItems: function(series, index)
14221 var xAxis = series.get("xAxis"),
14222 yAxis = series.get("yAxis"),
14223 xKey = series.get("xKey"),
14224 yKey = series.get("yKey"),
14227 if(this.get("direction") == "vertical")
14232 value:yAxis.getKeyValueAt(yKey, index)
14237 value: xAxis.getKeyValueAt(xKey, index)
14245 value:yAxis.getKeyValueAt(yKey, index)
14250 value: xAxis.getKeyValueAt(xKey, index)
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};
14262 * Listender for axisRendered event.
14264 _axisRendered: function(e)
14266 this._axesRenderQueue = this._axesRenderQueue.splice(1 + Y.Array.indexOf(this._axesRenderQueue, e.currentTarget), 1);
14267 if(this._axesRenderQueue.length < 1)
14276 _sizeChanged: function(e)
14278 if(this._axesCollection)
14280 var ac = this._axesCollection,
14285 this._addToAxesRenderQueue(ac[i]);
14294 _redraw: function()
14298 this._callLater = true;
14301 this._drawing = true;
14302 this._callLater = false;
14303 var w = this.get("width"),
14304 h = this.get("height"),
14309 lc = this.get("leftAxesCollection"),
14310 rc = this.get("rightAxesCollection"),
14311 tc = this.get("topAxesCollection"),
14312 bc = this.get("bottomAxesCollection"),
14318 graphOverflow = "visible",
14319 graph = this.get("graph");
14323 for(i = l - 1; i > -1; --i)
14325 pts[Y.Array.indexOf(this._axesCollection, lc[i])] = {x:lw + "px"};
14326 lw += lc[i].get("width");
14333 for(i = l - 1; i > -1; --i)
14335 rw += rc[i].get("width");
14336 pts[Y.Array.indexOf(this._axesCollection, rc[i])] = {x:(w - rw) + "px"};
14342 for(i = l - 1; i > -1; --i)
14344 pts[Y.Array.indexOf(this._axesCollection, tc[i])] = {y:th + "px"};
14345 th += tc[i].get("height");
14351 for(i = l - 1; i > -1; --i)
14353 bh += bc[i].get("height");
14354 pts[Y.Array.indexOf(this._axesCollection, bc[i])] = {y:(h - bh) + "px"};
14357 l = this._axesCollection.length;
14362 axis = this._axesCollection[i];
14363 pos = axis.get("position");
14364 if(pos == "left" || pos === "right")
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))
14370 axis.set("height", h - (bh + th));
14373 else if(pos == "bottom" || pos == "top")
14375 if(axis.get("width") !== w - (lw + rw))
14377 axis.set("width", w - (lw + rw));
14379 axis.get("boundingBox").setStyle("left", lw + "px");
14380 axis.get("boundingBox").setStyle("top", pts[i].y);
14382 if(axis.get("setMax") || axis.get("setMin"))
14384 graphOverflow = "hidden";
14388 this._drawing = false;
14389 if(this._callLater)
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);
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");
14415 * Style object for the axes.
14417 * @attribute axesStyles
14423 var axes = this.get("axes"),
14425 styles = this._axesStyles;
14430 if(axes.hasOwnProperty(i) && axes[i] instanceof Y.Axis)
14436 styles[i] = axes[i].get("styles");
14443 setter: function(val)
14445 var axes = this.get("axes"),
14449 if(val.hasOwnProperty(i) && axes.hasOwnProperty(i))
14451 this._setBaseAttribute(axes[i], "styles", val[i]);
14459 * Style object for the series
14461 * @attribute seriesStyles
14467 var styles = this._seriesStyles,
14468 graph = this.get("graph"),
14473 dict = graph.get("seriesDictionary");
14479 if(dict.hasOwnProperty(i))
14481 styles[i] = dict[i].get("styles");
14489 setter: function(val)
14495 if(Y.Lang.isArray(val))
14497 s = this.get("seriesCollection");
14503 this._setBaseAttribute(s[i], "styles", val[i]);
14510 if(val.hasOwnProperty(i))
14512 s = this.getSeries(i);
14513 this._setBaseAttribute(s, "styles", val[i]);
14522 * Styles for the graph.
14524 * @attribute graphStyles
14530 var graph = this.get("graph");
14533 return(graph.get("styles"));
14535 return this._graphStyles;
14538 setter: function(val)
14540 var graph = this.get("graph");
14541 this._setBaseAttribute(graph, "styles", val);
14547 * Style properties for the chart. Contains a key indexed hash of the following:
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:
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>
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>
14567 * @attribute styles
14574 axes: this.get("axesStyles"),
14575 series: this.get("seriesStyles"),
14576 graph: this.get("graphStyles")
14580 setter: function(val)
14582 if(val.hasOwnProperty("axes"))
14584 if(this.get("axesStyles"))
14586 this.set("axesStyles", val.axes);
14590 this._axesStyles = val.axes;
14593 if(val.hasOwnProperty("series"))
14595 if(this.get("seriesStyles"))
14597 this.set("seriesStyles", val.series);
14601 this._seriesStyles = val.series;
14604 if(val.hasOwnProperty("graph"))
14606 this.set("graphStyles", val.graph);
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.
14619 valueFn: "_parseAxes",
14621 setter: function(val)
14623 return this._parseAxes(val);
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.
14631 * @attribute seriesCollection
14634 seriesCollection: {
14635 valueFn: "_getDefaultSeriesCollection",
14637 setter: function(val)
14639 return this._getDefaultSeriesCollection(val);
14644 * Reference to the left-aligned axes for the chart.
14646 * @attribute leftAxesCollection
14650 leftAxesCollection: {},
14653 * Reference to the bottom-aligned axes for the chart.
14655 * @attribute bottomAxesCollection
14659 bottomAxesCollection: {},
14662 * Reference to the right-aligned axes for the chart.
14664 * @attribute rightAxesCollection
14668 rightAxesCollection: {},
14671 * Reference to the top-aligned axes for the chart.
14673 * @attribute topAxesCollection
14677 topAxesCollection: {},
14680 * Indicates whether or not the chart is stacked.
14682 * @attribute stacked
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.
14694 * @attribute direction
14700 var type = this.get("type");
14705 else if(type == "column")
14707 return "horizontal";
14709 return this._direction;
14712 setter: function(val)
14714 this._direction = val;
14715 return this._direction;
14720 * Indicates whether or not an area is filled in a combo chart.
14722 * @attribute showAreaFill
14728 * Indicates whether to display markers in a combo chart.
14730 * @attribute showMarkers
14736 * Indicates whether to display lines in a combo chart.
14738 * @attribute showLines
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.
14747 * @attribute categoryAxisName
14750 categoryAxisName: {
14754 * Indicates the key value used to identify a the series axis when an axis not generated.
14756 * @attribute valueAxisName
14764 * Reference to the horizontalGridlines for the chart.
14766 * @attribute horizontalGridlines
14769 horizontalGridlines: {
14772 var graph = this.get("graph");
14775 return graph.get("horizontalGridlines");
14777 return this._horizontalGridlines;
14779 setter: function(val)
14781 var graph = this.get("graph");
14782 if(val && !Y.Lang.isObject(val))
14788 graph.set("horizontalGridlines", val);
14792 this._horizontalGridlines = val;
14798 * Reference to the verticalGridlines for the chart.
14800 * @attribute verticalGridlines
14803 verticalGridlines: {
14806 var graph = this.get("graph");
14809 return graph.get("verticalGridlines");
14811 return this._verticalGridlines;
14813 setter: function(val)
14815 var graph = this.get("graph");
14816 if(val && !Y.Lang.isObject(val))
14822 graph.set("verticalGridlines", val);
14826 this._verticalGridlines = val;
14832 * Type of chart when there is no series collection specified.
14840 if(this.get("stacked"))
14842 return "stacked" + this._type;
14847 setter: function(val)
14849 if(this._type == "bar")
14853 this.set("direction", "horizontal");
14860 this.set("direction", "vertical");
14869 * Reference to the category axis used by the chart.
14871 * @attribute categoryAxis
14878 * The PieChart class creates a pie chart
14881 * @extends ChartBase
14884 Y.PieChart = Y.Base.create("pieChart", Y.Widget, [Y.ChartBase], {
14888 _getSeriesCollection: function()
14890 if(this._seriesCollection)
14892 return this._seriesCollection;
14894 var axes = this.get("axes"),
14899 type = this.get("type"),
14901 catAxis = "categoryAxis",
14902 catKey = "categoryKey",
14903 valAxis = "valueAxis",
14904 seriesKey = "valueKey";
14907 seriesKeys = axes.values.get("keyCollection");
14908 key = axes.category.get("keyCollection")[0];
14909 l = seriesKeys.length;
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];
14919 this._seriesCollection = sc;
14926 _parseAxes: function(hash)
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);
14939 this.set("width", node.get("offsetWidth"));
14940 w = this.get("width");
14944 this.set("height", node.get("offsetHeight"));
14945 h = this.get("height");
14949 if(hash.hasOwnProperty(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"))
14957 config.roundingUnit = dh.roundingUnit;
14959 config.keys = dh.keys;
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;
14974 _addAxes: function()
14976 var axes = this.get("axes"),
14982 this.set("axes", this._getDefaultAxes());
14983 axes = this.get("axes");
14985 if(!this._axesCollection)
14987 this._axesCollection = [];
14991 if(axes.hasOwnProperty(i))
14994 p = axis.get("position");
14995 if(!this.get(p + "AxesCollection"))
14997 this.set(p + "AxesCollection", [axis]);
15001 this.get(p + "AxesCollection").push(axis);
15003 this._axesCollection.push(axis);
15011 _addSeries: function()
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"));
15027 _parseSeriesAxes: function(c)
15032 axes = this.get("axes"),
15034 for(; i < len; ++i)
15039 //If series is an actual series instance,
15040 //replace axes attribute string ids with axes
15041 if(s instanceof Y.PieSeries)
15043 axis = s.get("categoryAxis");
15044 if(axis && !(axis instanceof Y.Axis))
15046 s.set("categoryAxis", axes[axis]);
15048 axis = s.get("valueAxis");
15049 if(axis && !(axis instanceof Y.Axis))
15051 s.set("valueAxis", axes[axis]);
15055 s.categoryAxis = axes.category;
15056 s.valueAxis = axes.values;
15059 s.type = this.get("type");
15068 _getDefaultAxes: function()
15070 var catKey = this.get("categoryKey"),
15071 seriesKeys = this.get("seriesKeys") || [],
15072 seriesAxis = "numeric",
15074 dv = this.get("dataProvider")[0];
15075 if(seriesKeys.length < 1)
15081 seriesKeys.push(i);
15084 if(seriesKeys.length > 0)
15086 this.set("seriesKeys", seriesKeys);
15096 type:this.get("categoryType")
15102 * Returns an object literal containing a categoryItem and a valueItem for a given series index.
15104 * @method getSeriesItem
15105 * @param series Reference to a series.
15106 * @param index Index of the specified item within a series.
15108 getSeriesItems: function(series, index)
15110 var categoryItem = {
15111 axis: series.get("categoryAxis"),
15112 key: series.get("categoryKey"),
15113 displayName: series.get("categoryDisplayName")
15116 axis: series.get("valueAxis"),
15117 key: series.get("valueKey"),
15118 displayName: series.get("valueDisplayName")
15120 categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index);
15121 valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index);
15122 return {category:categoryItem, value:valueItem};
15128 _sizeChanged: function(e)
15136 _redraw: function()
15138 var graph = this.get("graph");
15141 graph.set("width", this.get("width"));
15142 graph.set("height", this.get("height"));
15148 * Axes to appear in the chart.
15159 setter: function(val)
15161 this._parseAxes(val);
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.
15169 * @attribute seriesCollection
15172 seriesCollection: {
15175 return this._getSeriesCollection();
15178 setter: function(val)
15180 return this._setSeriesCollection(val);
15185 * Type of chart when there is no series collection specified.
15196 * The Chart class is the basic application used to create a chart.
15201 function Chart(cfg)
15203 if(cfg.type != "pie")
15205 return new Y.CartesianChart(cfg);
15209 return new Y.PieChart(cfg);
15215 }, '3.3.0' ,{requires:['dom', 'datatype', 'event-custom', 'event-mouseenter', 'widget', 'widget-position', 'widget-stack']});