/**
* 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;