/** * Provides base functionality for drawing chart axes. * * @module charts * @submodule axis */ var CONFIG = Y.config, DOCUMENT = CONFIG.doc, Y_Lang = Y.Lang, IS_STRING = Y_Lang.isString, Y_DOM = Y.DOM, LeftAxisLayout, RightAxisLayout, BottomAxisLayout, TopAxisLayout; /** * Algorithmic strategy for rendering a left axis. * * @class LeftAxisLayout * @constructor * @submodule axis */ LeftAxisLayout = function() {}; LeftAxisLayout.prototype = { /** * Default margins for text fields. * * @private * @method _getDefaultMargins * @return Object */ _getDefaultMargins: function() { return { top: 0, left: 0, right: 4, bottom: 0 }; }, /** * Sets the length of the tick on either side of the axis line. * * @method setTickOffset * @protected */ setTickOffsets: function() { var host = this, majorTicks = host.get("styles").majorTicks, tickLength = majorTicks.length, halfTick = tickLength * 0.5, display = majorTicks.display; host.set("topTickOffset", 0); host.set("bottomTickOffset", 0); switch(display) { case "inside" : host.set("rightTickOffset", tickLength); host.set("leftTickOffset", 0); break; case "outside" : host.set("rightTickOffset", 0); host.set("leftTickOffset", tickLength); break; case "cross": host.set("rightTickOffset", halfTick); host.set("leftTickOffset", halfTick); break; default: host.set("rightTickOffset", 0); host.set("leftTickOffset", 0); break; } }, /** * Draws a tick * * @method drawTick * @param {Path} path reference to the path `Path` element in which to draw the tick. * @param {Object} pt Point on the axis in which the tick will intersect. * @param {Object} tickStyle Hash of properties to apply to the tick. * @protected */ drawTick: function(path, pt, tickStyles) { var host = this, style = host.get("styles"), padding = style.padding, tickLength = tickStyles.length, start = {x:padding.left, y:pt.y}, end = {x:tickLength + padding.left, y:pt.y}; host.drawLine(path, start, end); }, /** * Calculates the coordinates for the first point on an axis. * * @method getLineStart * @return {Object} * @protected */ getLineStart: function() { var style = this.get("styles"), padding = style.padding, majorTicks = style.majorTicks, tickLength = majorTicks.length, display = majorTicks.display, pt = {x:padding.left, y:0}; if(display === "outside") { pt.x += tickLength; } else if(display === "cross") { pt.x += tickLength/2; } return pt; }, /** * Calculates the point for a label. * * @method getLabelPoint * @param {Object} point Point on the axis in which the tick will intersect. * @return {Object} * @protected */ getLabelPoint: function(point) { return {x:point.x - this.get("leftTickOffset"), y:point.y}; }, /** * Updates the value for the `maxLabelSize` for use in calculating total size. * * @method updateMaxLabelSize * @param {HTMLElement} label to measure * @protected */ updateMaxLabelSize: function(labelWidth, labelHeight) { var host = this, props = this._labelRotationProps, rot = props.rot, absRot = props.absRot, sinRadians = props.sinRadians, cosRadians = props.cosRadians, max; if(rot === 0) { max = labelWidth; } else if(absRot === 90) { max = labelHeight; } else { max = (cosRadians * labelWidth) + (sinRadians * labelHeight); } host._maxLabelSize = Math.max(host._maxLabelSize, max); }, /** * Determines the available label width when the axis width has been explicitly set. * * @method getExplicitlySized * @return Boolean * @protected */ getExplicitlySized: function(styles) { if(this._explicitWidth) { var host = this, w = host._explicitWidth, totalTitleSize = host._totalTitleSize, leftTickOffset = host.get("leftTickOffset"), margin = styles.label.margin.right; host._maxLabelSize = w - (leftTickOffset + margin + totalTitleSize); return true; } return false; }, /** * Rotate and position title. * * @method positionTitle * @param {HTMLElement} label to rotate position * @protected */ positionTitle: function(label) { var host = this, bounds = host._titleBounds, margin = host.get("styles").title.margin, props = host._titleRotationProps, w = bounds.right - bounds.left, labelWidth = label.offsetWidth, labelHeight = label.offsetHeight, x = (labelWidth * -0.5) + (w * 0.5), y = (host.get("height") * 0.5) - (labelHeight * 0.5); props.labelWidth = labelWidth; props.labelHeight = labelHeight; if(margin && margin.left) { x += margin.left; } props.x = x; props.y = y; props.transformOrigin = [0.5, 0.5]; host._rotate(label, props); }, /** * Rotate and position labels. * * @method positionLabel * @param {HTMLElement} label to rotate position * @param {Object} pt hash containing the x and y coordinates in which the label will be positioned * against. * @protected */ positionLabel: function(label, pt, styles, i) { var host = this, offset = parseFloat(styles.label.offset), tickOffset = host.get("leftTickOffset"), totalTitleSize = this._totalTitleSize, leftOffset = pt.x + totalTitleSize - tickOffset, topOffset = pt.y, props = this._labelRotationProps, rot = props.rot, absRot = props.absRot, maxLabelSize = host._maxLabelSize, labelWidth = this._labelWidths[i], labelHeight = this._labelHeights[i]; if(rot === 0) { leftOffset -= labelWidth; topOffset -= labelHeight * offset; } else if(rot === 90) { leftOffset -= labelWidth * 0.5; topOffset = topOffset + labelWidth/2 - (labelWidth * offset); } else if(rot === -90) { leftOffset -= labelWidth * 0.5; topOffset = topOffset - labelHeight + labelWidth/2 - (labelWidth * offset); } else { leftOffset -= labelWidth + (labelHeight * absRot/360); topOffset -= labelHeight * offset; } props.labelWidth = labelWidth; props.labelHeight = labelHeight; props.x = Math.round(maxLabelSize + leftOffset); props.y = Math.round(topOffset); this._rotate(label, props); }, /** * Adjusts the coordinates of an axis label based on the rotation. * * @method _setRotationCoords * @param {Object} props Coordinates, dimension and rotation properties of the label. * @protected */ _setRotationCoords: function(props) { var rot = props.rot, absRot = props.absRot, leftOffset, topOffset, labelWidth = props.labelWidth, labelHeight = props.labelHeight; if(rot === 0) { leftOffset = labelWidth; topOffset = labelHeight * 0.5; } else if(rot === 90) { topOffset = 0; leftOffset = labelWidth * 0.5; } else if(rot === -90) { leftOffset = labelWidth * 0.5; topOffset = labelHeight; } else { leftOffset = labelWidth + (labelHeight * absRot/360); topOffset = labelHeight * 0.5; } props.x -= leftOffset; props.y -= topOffset; }, /** * Returns the transformOrigin to use for an axis label based on the position of the axis * and the rotation of the label. * * @method _getTransformOrigin * @param {Number} rot The rotation (in degrees) of the label. * @return Array * @protected */ _getTransformOrigin: function(rot) { var transformOrigin; if(rot === 0) { transformOrigin = [0, 0]; } else if(rot === 90) { transformOrigin = [0.5, 0]; } else if(rot === -90) { transformOrigin = [0.5, 1]; } else { transformOrigin = [1, 0.5]; } return transformOrigin; }, /** * Adjust the position of the Axis widget's content box for internal axes. * * @method offsetNodeForTick * @param {Node} cb contentBox of the axis * @protected */ offsetNodeForTick: function() { }, /** * Sets the width of the axis based on its contents. * * @method setCalculatedSize * @protected */ setCalculatedSize: function() { var host = this, graphic = this.get("graphic"), style = host.get("styles"), label = style.label, tickOffset = host.get("leftTickOffset"), max = host._maxLabelSize, totalTitleSize = this._totalTitleSize, ttl = Math.round(totalTitleSize + tickOffset + max + label.margin.right); if(this._explicitWidth) { ttl = this._explicitWidth; } this.set("calculatedWidth", ttl); graphic.set("x", ttl - tickOffset); } }; Y.LeftAxisLayout = LeftAxisLayout;