/**
* <code>ia.Map</code> defines the basic layout behavior of map.
*
* @author J Clare
* @class ia.ChartBase
* @extends ia.CanvasBase
* @constructor
* @param {String} id The id of the chart.
*/
ia.ChartBase = function(id)
{
ia.ChartBase.baseConstructor.call(this, id);
// This is only here so we can use it to apply styles to chart items and map labels.
this.itemStyle = $j("<div class='ia-chart-items' style='visibility:hidden'>");
this.mapContainer.append(this.itemStyle);
this.orientation = "vertical";
this.showXAxisLabels = true;
this.xAxisTitle = undefined;
this.showYAxisLabels = true;
this.yAxisTitle = undefined;
this.showBox = false;
this.showBottomBorder = false;
this.showTopBorder = false;
this.showLeftBorder = false;
this.showRightBorder = false;
this.showXAxisGrid = true;
this.showYAxisGrid = true;
this.centerXAxisLabels = false;
this.centerYAxisLabels = false;
this.showXAxisTickMarks = false;
this.showYAxisTickMarks = false;
this.useTightLabels = false;
this.formatter = new ia.Formatter();
this.wrapXAxisLabels = false;
// Add event listener for bBoxChange.
var me = this;
var chartReady = false;
me.addEventListener(ia.Event.MAP_READY, function()
{
chartReady = true;
});
me.addEventListener(ia.BBoxEvent.BBOX_TRANSLATE, function(event)
{
if (chartReady) me.render();
});
me.addEventListener(ia.BBoxEvent.BBOX_SCALE, function(event)
{
if (chartReady) me.render();
});
};
ia.extend(ia.CanvasBase, ia.ChartBase);
/**
* The chart orientation.
*
* @property orientation
* @type String
* @default "vertical"
*/
ia.ChartBase.prototype.orientation;
/**
* Show a box around the chart.
*
* @property showBox
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showBox;
/**
* Show the top border.
*
* @property showTopBorder
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showTopBorder;
/**
* Show the bottom border.
*
* @property showBottomBorder
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showBottomBorder;
/**
* Show the left border.
*
* @property showLeftBorder
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showLeftBorder;
/**
* Show the right border.
*
* @property showRightBorder
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showRightBorder;
/**
* X axis labels.
*
* @property xAxisLabels
* @type String[]
*/
ia.ChartBase.prototype.xAxisLabels;
/**
* Y axis labels.
*
* @property yAxisLabels
* @type String[]
*/
ia.ChartBase.prototype.yAxisLabels;
/**
* Show the x axis labels.
*
* @property showXAxisLabels
* @type Boolean
* @default true
*/
ia.ChartBase.prototype.showXAxisLabels;
/**
* Wrap the x axis labels.
*
* @property wrapXAxisLabels
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.wrapXAxisLabels;
/**
* The x axis title.
*
* @property xAxisTitle
* @type String
*/
ia.ChartBase.prototype.xAxisTitle;
/**
* Show x axis tick marks.
*
* @property showXAxisTickMarks
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showXAxisTickMarks;
/**
* Show the y axis labels.
*
* @property showYAxisLabels
* @type Boolean
* @default true
*/
ia.ChartBase.prototype.showYAxisLabels;
/**
* The y axis title.
*
* @property yAxisTitle
* @type String
*/
ia.ChartBase.prototype.yAxisTitle;
/**
* Show the x axis grid.
*
* @property showXAxisGrid
* @type Boolean
* @default true
*/
ia.ChartBase.prototype.showXAxisGrid;
/**
* Show the y axis grid.
*
* @property showYAxisGrid
* @type Boolean
* @default true
*/
ia.ChartBase.prototype.showYAxisGrid;
/**
* Show y axis tick marks.
*
* @property showYAxisTickMarks
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.showYAxisTickMarks;
/**
* Center the x axis labels.
*
* @property centerXAxisLabels
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.centerXAxisLabels;
/**
* Center the y axis labels.
*
* @property centerYAxisLabels
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.centerYAxisLabels;
/**
* Should the chart use tight labels.
*
* @property useTightLabels
* @type Boolean
* @default false
*/
ia.ChartBase.prototype.useTightLabels;
/**
* The formatter.
*
* @property formatter
* @type ia.Formatter
*/
ia.ChartBase.prototype.formatter;
/**
* The min value.
*
* @property fixedMinValue
* @type Number
*/
ia.ChartBase.prototype.fixedMinValue;
/**
* The max value.
*
* @property fixedMaxValue
* @type Number
*/
ia.ChartBase.prototype.fixedMaxValue;
/**
* Renders the grid and adjusts the bounding box.
*
* @method renderAxes
* @param {Number} xMin The min value along the x axis.
* @param {Number} xMax The max value along the x axis.
* @param {Number} yMin The min value along the y axis.
* @param {Number} yMax The max value along the y axis.
*/
ia.ChartBase.prototype.renderAxes = function(xMin, xMax, yMin, yMax)
{
if (ia.isNumber(xMin)
&& ia.isNumber(xMax)
&& ia.isNumber(yMin)
&& ia.isNumber(yMax)
&& this.canvas.width > 0
&& this.canvas.height > 0)
{
// Axis variables.
var vPadding = 10;
var hPadding = 20;
var axisFontStyle = "Verdana";
var axisFontColor = "#AAAAAA";
var axisFontSize = 10;
var textLinePadding = 1;
var labelPadding = 5;
var xAxisNoLabels = 5;
var yAxisNoLabels = 5;
var gridStrokeStyle = "#E5E5E5"
var gridLineWidth = 1;
// Be sneaky and use ia-chart css to set properties of chart
if (this.container.css("font-family")) axisFontStyle = this.container.css("font-family");
if (this.container.css("color")) axisFontColor = ia.Color.toHex(this.container.css("color"));
if (this.container.css("font-size"))
{
axisFontSize = this.container.css("font-size");
if (axisFontSize.indexOf("px") > -1) axisFontSize = axisFontSize.substring(0, axisFontSize.indexOf("px"));
axisFontSize = ia.parseInt(axisFontSize);
}
// * BUG-FIX FF and IE dont recognize border-color but will pick up borderRightColor which is set to be same as border-color in css. *
if (this.container.css("borderRightColor")) gridStrokeStyle = ia.Color.toHex(this.container.css("borderRightColor"));
// Reset canvas dimensions.
this.canvasX = 0;
this.canvasY = 0;
this.canvasWidth = this.canvas.width;
this.canvasHeight = this.canvas.height;
// Grid.
this.context.lineWidth = gridLineWidth;
this.context.strokeStyle = gridStrokeStyle;
// Font.
this.context.font = ""+axisFontSize+"px "+axisFontStyle;
this.context.fillStyle = axisFontColor;
// This is the space a line of text takes up within a label that contains wrapped text.
var textLineHeight = axisFontSize + (textLinePadding * 2);
// Single line label height. Y-axis labels will always be single line.
var singleLineLabelHeight = textLineHeight + (labelPadding * 2);
// Set the initial gutter size. distance from edge of container to grid borders.
var leftGutter = hPadding;
var rightGutter = hPadding;
var bottomGutter = vPadding;
var topGutter = vPadding;
// Calculate gutter sizes.
// Calculate approximate space taken up by y-axis labels.
if (this.showYAxisLabels)
{
var yAxisHeight = this.canvasHeight - (topGutter + bottomGutter);
// Get the y labels.
var yLabels = this._getYAxisLabels(yMin, yMax, yAxisNoLabels, yAxisHeight, singleLineLabelHeight);
// Calculate the left gutter.
leftGutter = leftGutter + this._getMaxLabelWidth(yLabels, labelPadding);
// Calculate the top / bottom gutters.
if (!this.centerYAxisLabels)
{
bottomGutter = bottomGutter + (singleLineLabelHeight / 2);
topGutter = topGutter + (singleLineLabelHeight / 2);
}
}
// Calculate approximate space taken up by x-axis labels.
if (this.showXAxisLabels)
{
var xAxisWidth = this.canvasWidth - (leftGutter + rightGutter);
// Get the x labels.
var xLabels = this._getXAxisLabels(xMin, xMax, xAxisNoLabels, xAxisWidth, labelPadding);
// Calculate the bottom gutter.
var labelHeight = singleLineLabelHeight;
if (this.wrapXAxisLabels)
{
var availableSpace = (xAxisWidth / xLabels.length);
labelHeight = this._getMaxLabelHeight(xLabels, labelPadding, textLineHeight, availableSpace);
}
bottomGutter = vPadding + labelHeight;
// Calculate the left / right gutter.
if (!this.centerXAxisLabels)
{
var labelWidth = this._getLabelWidth(xLabels[0], labelPadding);
if (labelWidth > (leftGutter - hPadding)) leftGutter = (labelWidth / 2) + hPadding;
var labelWidth = this._getLabelWidth(xLabels[xLabels.length-1], labelPadding);
rightGutter = rightGutter + (labelWidth / 2);
}
}
// Calculate approximate space taken up by x-axis title.
if (this.xAxisTitle !== undefined)
{
var availableSpace = this.canvasWidth - (leftGutter + rightGutter);
var labelHeight = this._getLabelHeight(this.xAxisTitle, labelPadding, textLineHeight, availableSpace);
bottomGutter = bottomGutter + labelHeight - labelPadding; // minus labelpadding because dont need padding under label.
}
// Calculate approximate space taken up by y-axis title.
if (this.yAxisTitle !== undefined)
{
var availableSpace = this.canvasHeight - (bottomGutter + topGutter);
var labelHeight = this._getLabelHeight(this.yAxisTitle, labelPadding, textLineHeight, availableSpace);
leftGutter = leftGutter + labelHeight - labelPadding; // minus labelpadding because dont need padding under label.
}
// Calculate the x-axis width.
var xAxisWidth = this.canvasWidth - (leftGutter + rightGutter);
// Draw x labels.
var xLabelsHeight = 0;
if (this.showXAxisLabels)
{
// Get the x labels.
var xLabels = this._getXAxisLabels(xMin, xMax, xAxisNoLabels, xAxisWidth, labelPadding);
// Re-calculate the bottom gutter.
xLabelsHeight = singleLineLabelHeight;
if (this.wrapXAxisLabels)
{
var availableSpace = (xAxisWidth / xLabels.length);
xLabelsHeight = this._getMaxLabelHeight(xLabels, labelPadding, textLineHeight, availableSpace);
}
var titleHeight = 0;
if (this.xAxisTitle !== undefined)
{
var availableSpace = this.canvasWidth - (leftGutter + rightGutter);
titleHeight = this._getLabelHeight(this.xAxisTitle, labelPadding, textLineHeight, availableSpace);
}
bottomGutter = vPadding + xLabelsHeight + titleHeight;
this.context.textAlign = "center";
this.context.textBaseline = "top";
var nLabels = xLabels.length;
var yText = this.canvasHeight - bottomGutter + labelPadding;
var xText;
var xTextGap;
var xGridGap;
var xGrid;
if (this.centerXAxisLabels)
{
xTextGap = xAxisWidth / nLabels;
xText = leftGutter + (xTextGap / 2);
xGridGap = xTextGap;
xGrid = leftGutter;
}
else
{
xTextGap = xAxisWidth / (nLabels - 1);
xText = leftGutter;
xGridGap = xTextGap;
xGrid = xText;
}
for (var i = 0; i < nLabels; i++)
{
var text = xLabels[i];
// Special case - pyramid chart, "-" sign should be removed from x-axis numbers.
if (this.isPyramidChart === true && text.indexOf("-") !== -1) text = text.substring(1);
if (this.wrapXAxisLabels)
this._wrapText(text, xText, yText, (xTextGap - (labelPadding * 2)), textLineHeight);
else
this.context.fillText(text, xText, yText);
if (this.showXAxisTickMarks)
{
var x1 = xText;
var y1 = Math.floor(this.canvasHeight - bottomGutter) + 0.5;
var x2 = xText;
var y2 = y1 + labelPadding;
this.context.beginPath();
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.stroke();
}
if (this.showXAxisGrid)
{
// The grid line
var x1 = Math.floor(xGrid) + 0.5;
var y1 = Math.floor(topGutter) + 0.5;
var x2 = x1;
var y2 = Math.floor(this.canvasHeight - bottomGutter) + 0.5;
this.context.beginPath();
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.stroke();
}
xText = xText + xTextGap;
xGrid = xGrid + xGridGap;
};
if (this.showXAxisGrid && this.centerXAxisLabels)
{
// The grid line
var x1 = Math.floor(xGrid) + 0.5;
var y1 = Math.floor(topGutter) + 0.5;
var x2 = x1;
var y2 = Math.floor(this.canvasHeight - bottomGutter) + 0.5;
this.context.beginPath();
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.stroke();
}
}
// Draw x title.
if (this.xAxisTitle !== undefined)
{
var availableSpace = this.canvasWidth - (leftGutter + rightGutter);
if (!this.showXAxisLabels)
{
// Re-calculate the bottom gutter.
var titleHeight = this._getLabelHeight(this.xAxisTitle, labelPadding, textLineHeight, availableSpace);
bottomGutter = vPadding + titleHeight;
}
this.context.textAlign = "center";
this.context.textBaseline = "top";
var x = leftGutter + (availableSpace / 2);
var y = (this.canvasHeight - bottomGutter) + (xLabelsHeight + labelPadding);
this._wrapText(this.xAxisTitle, x, y, availableSpace, textLineHeight);
}
// Calculate the y-axis height.
var yAxisHeight = this.canvasHeight - (topGutter + bottomGutter);
// Draw y labels.
var yLabelsWidth = 0;
if (this.showYAxisLabels)
{
// Get the y labels.
var yLabels = this._getYAxisLabels(yMin, yMax, yAxisNoLabels, yAxisHeight, singleLineLabelHeight);
yLabelsWidth = this._getMaxLabelWidth(yLabels, labelPadding);
this.context.textAlign = "right";
this.context.textBaseline = "middle";
var nLabels = yLabels.length;
var xText = leftGutter - labelPadding;
var yText;
var yTextGap;
var yGridGap;
var yGrid;
if (this.centerYAxisLabels)
{
yTextGap = yAxisHeight / (nLabels);
yText = topGutter + (yTextGap / 2);
yGridGap = yTextGap;
yGrid = topGutter;
}
else
{
yTextGap = yAxisHeight / (nLabels - 1);
yText = topGutter;
yGridGap = yTextGap;
yGrid = yText;
}
for (var i = 0; i < nLabels; i++)
{
// The label.
var text = yLabels[i];
this.context.fillText(text, xText, yText);
if (this.showYAxisTickMarks)
{
var x1 = Math.floor(leftGutter) + 0.5;
var y1 = yText;
var x2 = x1 - labelPadding;
var y2 = yText;
this.context.beginPath();
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.stroke();
}
if (this.showYAxisGrid)
{
// The grid line
var x1 = Math.floor(leftGutter) + 0.5;
var y1 = Math.floor(yGrid) + 0.5;
var x2 = Math.floor(this.canvasWidth - rightGutter) + 0.5;
var y2 = y1;
this.context.beginPath();
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.stroke();
}
yText = yText + yTextGap;
yGrid = yGrid + yGridGap;
}
// Extra grid line when labels are centred.
if (this.showYAxisGrid && this.centerYAxisLabels)
{
// The grid line
var x1 = Math.floor(leftGutter) + 0.5;
var y1 = Math.floor(yGrid) + 0.5;
var x2 = Math.floor(this.canvasWidth - rightGutter) + 0.5;
var y2 = y1;
this.context.beginPath();
this.context.moveTo(x1, y1);
this.context.lineTo(x2, y2);
this.context.stroke();
}
}
// Draw y title.
if (this.yAxisTitle !== undefined)
{
var availableSpace = this.canvasHeight - (bottomGutter + topGutter);
var labelHeight = this._getLabelHeight(this.yAxisTitle, labelPadding, textLineHeight, availableSpace);
var y = topGutter + (availableSpace / 2);
var x = leftGutter - yLabelsWidth - labelHeight + labelPadding;
this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI/2);
this.context.textAlign = "center";
this.context.textBaseline = "top";
this._wrapText(this.yAxisTitle, 0, 0, availableSpace, textLineHeight);
this.context.restore();
}
this.canvasX = leftGutter;
this.canvasWidth = this.canvasWidth - (leftGutter + rightGutter);
this.canvasY = topGutter;
this.canvasHeight = this.canvasHeight - (topGutter + bottomGutter);
// Corners of chart.
var l = Math.floor(leftGutter) + 0.5;
var r = Math.floor(leftGutter + this.canvasWidth) + 0.5;
var t = Math.floor(topGutter) + 0.5;
var b = Math.floor(topGutter + this.canvasHeight) + 0.5;
// Draws a box around the chart.
if (this.showBox)
{
this.context.beginPath();
this.context.moveTo(l, t);
this.context.lineTo(r, t);
this.context.lineTo(r, b);
this.context.lineTo(l, b);
this.context.closePath();
this.context.stroke();
}
// Draws selected borders.
if (this.showBottomBorder)
{
this.context.beginPath();
this.context.moveTo(l, b);
this.context.lineTo(r, b);
this.context.closePath();
this.context.stroke();
}
if (this.showTopBorder)
{
this.context.beginPath();
this.context.moveTo(l, t);
this.context.lineTo(r, t);
this.context.closePath();
this.context.stroke();
}
if (this.showLeftBorder)
{
this.context.beginPath();
this.context.moveTo(l, t);
this.context.lineTo(l, b);
this.context.closePath();
this.context.stroke();
}
if (this.showRightBorder)
{
this.context.beginPath();
this.context.moveTo(r, t);
this.context.lineTo(r, b);
this.context.closePath();
this.context.stroke();
}
}
};
/**
* Gets the label width for the given text.
*
* @method _getLabelWidth
* @param {String[]} text The text.
* @param {Number} labelPadding The label padding.
* @param {Number} The width.
* @private
*/
ia.ChartBase.prototype._getLabelWidth = function(text, labelPadding)
{
var labelWidth = this.context.measureText(text).width + (labelPadding * 2);
return labelWidth;
};
/**
* Gets the maximum label width from a list of labels.
*
* @method _getMaxLabelWidth
* @param {String[]} labels The labels.
* @param {Number} labelPadding The label padding.
* @param {Number} The width.
* @private
*/
ia.ChartBase.prototype._getMaxLabelWidth = function(labels, labelPadding)
{
var maxLabelWidth = 0;
for (var i = 0; i < labels.length; i++)
{
var text = labels[i];
maxLabelWidth = Math.max(maxLabelWidth, this._getLabelWidth(text, labelPadding));
}
return maxLabelWidth;
};
/**
* Gets the label height for the given text.
*
* @method _getLabelHeight
* @param {String[]} text The text.
* @param {Number} labelPadding The label padding.
* @param {Number} lineHeight The text line height.
* @param {Number} availableSpace The available space for the label.
* @param {Number} The height.
* @private
*/
ia.ChartBase.prototype._getLabelHeight = function(text, labelPadding, lineHeight, availableSpace)
{
var lineWidth = availableSpace - (labelPadding * 2);
var noLines = this._getNoLines(text, lineWidth);
var labelHeight = (noLines * lineHeight) + (labelPadding * 2);
return labelHeight;
};
/**
* Gets the maximum label height from a list of labels.
*
* @method _getMaxLabelHeight
* @param {String[]} labels The labels.
* @param {Number} labelPadding The label padding.
* @param {Number} The width.
* @private
*/
ia.ChartBase.prototype._getMaxLabelHeight = function(labels, labelPadding, lineHeight, availableSpace)
{
var maxLabelHeight = 0;
for (var i = 0; i < labels.length; i++)
{
var text = labels[i];
maxLabelHeight = Math.max(maxLabelHeight, this._getLabelHeight(text, labelPadding, lineHeight, availableSpace));
}
return maxLabelHeight;
};
/**
* Gets the number of lines a piece of text will take up given the line width.
*
* @method _getNoLines
* @param {String} text The text.
* @param {Number} lineWidth The line width.
* @param {Number} The number of lines.
* @private
*/
ia.ChartBase.prototype._getNoLines = function(text, lineWidth)
{
// Split the text.
var words = text.split(" ");
var line = "";
var noLines = 1;
// Loop through each word and add it to the line if it still
// fits the line width, otherwise create a new line.
for(var n = 0; n < words.length; n++)
{
var textLine = line + words[n] + " ";
var metrics = this.context.measureText(textLine);
var textWidth = metrics.width;
if(textWidth > lineWidth)
{
line = words[n] + " ";
noLines++;
}
else
{
line = textLine;
}
}
return noLines;
};
/**
* Wraps text given the space available.
*
* @method _wrapText
* @param {String} text The text.
* @param {Number} x The x position of the text.
* @param {Number} y The y position of the text
* @param {Number} maxWidth The available width.
* @param {Number} lineHeight The line height.
* @private
*/
ia.ChartBase.prototype._wrapText = function(text, x, y, maxWidth, lineHeight)
{
// Split the text.
var words = text.split(" ");
var line = "";
// Loop through each word and add it to the line if it still
// fits the line width, otherwise create a new line.
for(var i = 0; i < words.length; i++)
{
var word = words[i];
var textLine;
if (i === 0) textLine = word;
else textLine = line + " " + word;
var metrics = this.context.measureText(textLine);
var textWidth = metrics.width;
if(textWidth > maxWidth)
{
this.context.fillText(line, x, y);
line = word;
y += lineHeight;
}
else
{
line = textLine;
}
}
this.context.fillText(line, x, y);
};
/**
* Gets the x-axis labels.
*
* @method _getXAxisLabels
* @param {Number} minValue The min value.
* @param {Number} maxValue The max value.
* @param {Number} noLabels The number of labels.
* @param {Number} axisSize The pixel area reserved for all the labels.
* @param {Number} labelPadding The label padding.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._getXAxisLabels = function(minValue, maxValue, noLabels, axisSize, labelPadding)
{
var xLabels;
if (this.xAxisLabels)
{
xLabels = this._dropXLabels(this.xAxisLabels, axisSize, labelPadding);
}
else xLabels = this._calculateXAxisLabels(minValue, maxValue, noLabels, axisSize, labelPadding);
return xLabels;
};
/**
* Calculates the x-axis labels. Keeps reducing the number of labels till they dont overlap.
*
* @method _calculateXAxisLabels
* @param {Number} minValue The min value.
* @param {Number} maxValue The max value.
* @param {Number} noLabels The number of labels.
* @param {Number} axisSize The pixel area reserved for all the labels.
* @param {Number} labelPadding The label padding.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._calculateXAxisLabels = function(minValue, maxValue, noLabels, axisSize, labelPadding)
{
var o;
var labelsOverlap = true;
var labelCount = noLabels;
while(labelsOverlap)
{
if (this.useTightLabels)
o = this._getTightLabels(minValue, maxValue, labelCount);
else
o = this._getLooseLabels(minValue, maxValue, labelCount);
var labels = o.labels;
var values = o.values;
noLabels = labels.length; // no labels can change when this._getLooseLabels() is called.
if (noLabels < 3) // For cases where no labels drops from above 3 to below 3.
{
labels = [this.formatter.format(minValue),this.formatter.format(maxValue)];
values = [minValue,maxValue];
labelsOverlap = false;
}
else if (noLabels === 3) labelsOverlap = false;
else
{
var totalLabelWidth = 0;
for (var i = 0; i < noLabels; i++)
{
var label = labels[i];
var labelWidth = this.context.measureText(label).width + (labelPadding * 2);
totalLabelWidth = totalLabelWidth + labelWidth;
}
if (totalLabelWidth > axisSize) labelCount--;
else labelsOverlap = false;
}
}
var bb = this.getBBox();
bb.setXMin(values[0]);
bb.setXMax(values[values.length-1]);
return labels;
};
/**
* Removes labels from xAxisLabels if they overlap.
*
* @method _dropXLabels
* @param {String[]} labels The labels.
* @param {Number} axisSize The pixel area reserved for all the labels.
* @param {Number} labelPadding The label padding.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._dropXLabels = function(labels, axisSize, labelPadding)
{
// Use the longest word if the label has multiple word.
var xLabels = new Array();
var nLabels = labels.length;
for (var i = 0; i < nLabels; i++)
{
// The label.
var text = labels[i];
// Split the text.
var words = text.split(" ");
var line = "";
// Loop through each and find the longest word.
var nWords = words.length;
var maxWidth = 0;
for(var j = 0; j < nWords; j++)
{
var word = words[j];
var metrics = this.context.measureText(word);
var wordWidth = metrics.width;
if(wordWidth > maxWidth)
{
maxWidth = wordWidth;
xLabels[i] = word;
}
}
}
// Copy the label array.
var adjustedLabels = [];
for (var i = 0; i < xLabels.length; i++)
{
var label = xLabels[i];
adjustedLabels[i] = label;
}
var labelsOverlap = true;
while(labelsOverlap)
{
var count = 0;
for (var i = 0; i < adjustedLabels.length; i++)
{
var label = adjustedLabels[i];
if (label !== '') count++;
}
if (count <= 2)
{
for (var i = 0; i < xLabels.length; i++) {adjustedLabels[i] = '';}
adjustedLabels[0] = xLabels[0];
adjustedLabels[adjustedLabels.length-1] = xLabels[xLabels.length-1];
labelsOverlap = false;
}
else
{
var totalLabelWidth = 0;
for (var i = 0; i < adjustedLabels.length; i++)
{
var label = adjustedLabels[i];
if (label !== '')
{
var labelWidth = this.context.measureText(label).width + (labelPadding * 2);
totalLabelWidth = totalLabelWidth + labelWidth;
}
}
if (totalLabelWidth > axisSize)
{
var index = 0;
for (var i = 0; i < adjustedLabels.length; i++)
{
var label = adjustedLabels[i];
if (label !== '')
{
if (index%2 === 0) adjustedLabels[i] = '';
index++;
}
}
}
else labelsOverlap = false;
}
}
// Need to substitute back in full labels wher multiple worded labesl have been used
var returnedLabels = new Array();
for (var i = 0; i < xLabels.length; i++)
{
var label = labels[i];
var adjustedLabel = adjustedLabels[i];
if (adjustedLabel === '') returnedLabels[i] = '';
else returnedLabels[i] = label;
}
return returnedLabels;
};
/**
* Gets the y-axis labels.
*
* @method _getYAxisLabels
* @param {Number} minValue The min value.
* @param {Number} maxValue The max value.
* @param {Number} noLabels The number of labels.
* @param {Number} axisSize The pixel area reserved for all the labels.
* @param {Number} labelSize The pixel area reserved for each individual label.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._getYAxisLabels = function(minValue, maxValue, noLabels, axisSize, labelSize)
{
var yLabels;
if (this.yAxisLabels)
{
yLabels = this.yAxisLabels.concat();
yLabels = this._dropYLabels(yLabels, axisSize, labelSize);
}
else
{
yLabels = this._calculateYAxisLabels(minValue, maxValue, noLabels, axisSize, labelSize).concat();
yLabels.reverse(); // Reverse y-labels so can draw from top down.
}
return yLabels;
};
/**
* Calculates the y-axis labels. Keeps reducing the number of labels till they dont overlap.
*
* @method _calculateYAxisLabels
* @param {Number} minValue The min value.
* @param {Number} maxValue The max value.
* @param {Number} noLabels The number of labels.
* @param {Number} axisSize The pixel area reserved for all the labels.
* @param {Number} labelSize The pixel area reserved for each individual label.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._calculateYAxisLabels = function(minValue, maxValue, noLabels, axisSize, labelSize)
{
var o;
var labelsOverlap = true;
var labelCount = noLabels;
while(labelsOverlap)
{
if (this.useTightLabels)
o = this._getTightLabels(minValue, maxValue, labelCount);
else
o = this._getLooseLabels(minValue, maxValue, labelCount);
var labels = o.labels;
var values = o.values;
noLabels = labels.length; // no labels can change when this._getLooseLabels() is called.
if (noLabels < 3) // For cases where no labels drops from above 3 to below 3.
{
labels = [this.formatter.format(minValue),this.formatter.format(maxValue)];
values = [minValue,maxValue];
labelsOverlap = false;
}
else if (noLabels === 3) labelsOverlap = false;
else
{
// Check the labels dont overlap.
var h = axisSize / noLabels;
if (h < labelSize) labelCount--;
else labelsOverlap = false;
}
}
var bb = this.getBBox();
bb.setYMin(values[0]);
bb.setYMax(values[values.length-1]);
return labels;
};
/**
* Removes labels from yAxisLabels if they overlap.
*
* @method _dropYLabels
* @param {String[]} labels The labels.
* @param {Number} axisSize The pixel area reserved for all the labels.
* @param {Number} labelHeight The label height.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._dropYLabels = function(labels, axisSize, labelHeight)
{
// Copy the label array.
var adjustedLabels = [];
for (var i = 0; i < labels.length; i++)
{
var label = labels[i];
adjustedLabels[i] = label;
}
var labelsOverlap = true;
while(labelsOverlap)
{
var count = 0;
for (var i = 0; i < adjustedLabels.length; i++)
{
var label = adjustedLabels[i];
if (label !== '') count++;
}
if (count <= 2)
{
for (var i = 0; i < labels.length; i++) {adjustedLabels[i] = "";}
adjustedLabels[0] = labels[0];
adjustedLabels[adjustedLabels.length-1] = labels[labels.length-1];
labelsOverlap = false;
}
else
{
var totalLabelHeight = 0;
for (var i = 0; i < adjustedLabels.length; i++)
{
var label = adjustedLabels[i];
if (label !== '') totalLabelHeight = totalLabelHeight + labelHeight;
}
if (totalLabelHeight > axisSize)
{
var index = 0;
for (var i = 0; i < adjustedLabels.length; i++)
{
var label = adjustedLabels[i];
if (label !== '')
{
if (index%2 === 0) adjustedLabels[i] = '';
index++;
}
}
}
else labelsOverlap = false;
}
}
return adjustedLabels;
};
/**
* Returns the given number of equally spaced values along an axis.
*
* @method getAxisValues
* @param {Number} minValue The min value.
* @param {Number} minValue The max value.
* @param {Number} noValues The number of values.
* @return {Number[]} A list of values.
*/
ia.ChartBase.prototype.getAxisValues = function(minValue, maxValue, noValues)
{
var valueArray = [];
if (noValues <= 2) valueArray = [minValue, maxValue];
else
{
var w = maxValue - minValue;
var incr = w / (noValues - 1);
var v = minValue;
for (var i = 0; i < noValues; i++)
{
if (i === noValues-1) valueArray[i] = maxValue;
else valueArray[i] = v;
v = v + incr
}
}
return valueArray;
};
/**
* Gets loose labels. The min and max value may change
*
* @method _getLooseLabels
* @param {Number} minValue The min value.
* @param {Number} maxValue The max value.
* @param {Number} noLabels The number of labels.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._getLooseLabels = function(minVal, maxVal, noLabels)
{
//var range = ia.getNiceNum(maxVal - minVal, false);
var range = maxVal - minVal;
// label spacing.
var d = ia.getNiceNum((range / (noLabels-1)), true);
// Graph range min and max.
var graphMin = Math.floor(minVal / d) * d;
var graphMax = Math.ceil(maxVal / d) * d;
// Number of fractional digits to show.
var nFrac = Math.max(-1 * Math.floor(ia.log10(d)), 0);
var values = new Array();
var labels = new Array();
for (var i = graphMin; i < (graphMax + (0.5 * d)); i+=d)
{
values[values.length] = ia.round(i, nFrac);
labels[labels.length] = this.formatter.format(ia.round(i, nFrac));
}
return {values:values,labels:labels};
}
/**
* Gets tight labels. The min and max value are maintained.
*
* @method _getTightLabels
* @param {Number} minValue The min value.
* @param {Number} maxValue The max value.
* @param {Number} noLabels The number of labels.
* @return {String[]} The list of labels.
* @private
*/
ia.ChartBase.prototype._getTightLabels = function(minVal, maxVal, noLabels)
{
var range = maxVal - minVal;
var incr = range / (noLabels-1);
var value = parseFloat(minVal);
var values = new Array();
values.push(minVal);
for (var i = 1; i < noLabels-1; i++)
{
value = value + incr;
values.push(value);
}
values.push(maxVal);
var labels = new Array();
var precision = ia.getPrecision(values);
for (var i = 0; i < noLabels; i++)
{
labels.push(this.formatter.format(values[i], precision));
}
return {values:values,labels:labels};
/*var labels = this._getLooseLabels(minVal, maxVal, noLabels);
labels[0] = minVal;
labels[labels.length-1] = maxVal;
return labels;*/
};