/**
* AxisType is an abstract class that manages the data for an axis.
*
* @param {Object} config (optional) Configuration parameters for the Chart.
* @class AxisType
* @constructor
* @extends Axis
*/
Y.AxisType = Y.Base.create("baseAxis", Y.Axis, [], {
/**
* @private
*/
bindUI: function()
{
this.after("dataReady", Y.bind(this._dataChangeHandler, this));
this.after("dataUpdate", Y.bind(this._dataChangeHandler, this));
this.after("minimumChange", Y.bind(this._keyChangeHandler, this));
this.after("maximumChange", Y.bind(this._keyChangeHandler, this));
this.after("keysChange", this._keyChangeHandler);
this.after("dataProviderChange", this._dataProviderChangeHandler);
this.after("stylesChange", this._updateHandler);
this.after("positionChange", this._positionChangeHandler);
this.after("overlapGraphChange", this._updateHandler);
this.after("widthChange", this._handleSizeChange);
this.after("heightChange", this._handleSizeChange);
this.after("alwaysShowZeroChange", this._keyChangeHandler);
this.after("roundingMethodChange", this._keyChangeHandler);
},
/**
* @private
*/
_dataProviderChangeHandler: function(e)
{
var keyCollection = this.get("keyCollection").concat(),
keys = this.get("keys"),
i;
if(keys)
{
for(i in keys)
{
if(keys.hasOwnProperty(i))
{
delete keys[i];
}
}
}
if(keyCollection && keyCollection.length)
{
this.set("keys", keyCollection);
}
},
/**
* @private
*/
GUID: "yuibaseaxis",
/**
* @private
*/
_type: null,
/**
* @private
*/
_setMaximum: null,
/**
* @private
*/
_dataMaximum: null,
/**
* @private
*/
_setMinimum: null,
/**
* @private
*/
_data: null,
/**
* @private
*/
_updateTotalDataFlag: true,
/**
* @private
*/
_dataReady: false,
/**
* Adds an array to the key hash.
*
* @param value Indicates what key to use in retrieving
* the array.
*/
addKey: function (value)
{
this.set("keys", value);
},
/**
* @private
*/
_getKeyArray: function(key, data)
{
var i = 0,
obj,
keyArray = [],
len = data.length;
for(; i < len; ++i)
{
obj = data[i];
keyArray[i] = obj[key];
}
return keyArray;
},
/**
* @private
*/
_setDataByKey: function(key, data)
{
var i,
obj,
arr = [],
dv = this._dataClone.concat(),
len = dv.length;
for(i = 0; i < len; ++i)
{
obj = dv[i];
arr[i] = obj[key];
}
this.get("keys")[key] = arr;
this._updateTotalDataFlag = true;
},
/**
* @private
*/
_updateTotalData: function()
{
var keys = this.get("keys"),
i;
this._data = [];
for(i in keys)
{
if(keys.hasOwnProperty(i))
{
this._data = this._data.concat(keys[i]);
}
}
this._updateTotalDataFlag = false;
},
/**
* Removes an array from the key hash.
*
* @method removeKey
* @param {String} value Indicates what key to use in removing from
* the hash.
*/
removeKey: function(value)
{
var keys = this.get("keys");
if(keys.hasOwnProperty(value))
{
delete keys[value];
this._keyChangeHandler();
}
},
/**
* Returns a numeric value based of a key value and an index.
*
* @method getKeyValueAt
* @param {String} key value used to look up the correct array
* @param {Number} index within the array
* @return Object
*/
getKeyValueAt: function(key, index)
{
var value = NaN,
keys = this.get("keys");
if(keys[key] && keys[key][index])
{
value = keys[key][index];
}
return value;
},
/**
* Returns an array of values based on an identifier key.
*
* @method getDataByKey
* @param {String} value value used to identify the array
* @return Object
*/
getDataByKey: function (value)
{
var keys = this.get("keys");
if(keys[value])
{
return keys[value];
}
return null;
},
/**
* @private
*/
_updateMinAndMax: function()
{
var data = this.get("data"),
max = 0,
min = 0,
len,
num,
i;
if(data && data.length && data.length > 0)
{
len = data.length;
max = min = data[0];
if(len > 1)
{
for(i = 1; i < len; i++)
{
num = data[i];
if(isNaN(num))
{
continue;
}
max = Math.max(num, max);
min = Math.min(num, min);
}
}
}
this._dataMaximum = max;
this._dataMinimum = min;
},
/**
* Returns the total number of majorUnits that will appear on an axis.
*
* @method getTotalMajorUnits
* @return Number
*/
getTotalMajorUnits: function()
{
var units,
majorUnit = this.get("styles").majorUnit,
len = this.get("length");
if(majorUnit.determinant === "count")
{
units = majorUnit.count;
}
else if(majorUnit.determinant === "distance")
{
units = (len/majorUnit.distance) + 1;
}
return units;
},
/**
* Returns the distance between major units on an axis.
*
* @method getMajorUnitDistance
* @param {Number} len Number of ticks
* @param {Number} uiLen Size of the axis.
* @param {Object} majorUnit Hash of properties used to determine the majorUnit
* @return Number
*/
getMajorUnitDistance: function(len, uiLen, majorUnit)
{
var dist;
if(majorUnit.determinant === "count")
{
dist = uiLen/(len - 1);
}
else if(majorUnit.determinant === "distance")
{
dist = majorUnit.distance;
}
return dist;
},
/**
* Gets the distance that the first and last ticks are offset from there respective
* edges.
*
* @attribute getEdgeOffset
* @type Method
* @param {Number} ct Number of ticks on the axis.
* @param {Number} l Length (in pixels) of the axis.
* @return Number
*/
getEdgeOffset: function(ct, l)
{
return 0;
},
/**
* Calculates and returns a value based on the number of labels and the index of
* the current label.
*
* @method getLabelByIndex
* @param {Number} i Index of the label.
* @param {Number} l Total number of labels.
* @return String
*/
getLabelByIndex: function(i, l)
{
var min = this.get("minimum"),
max = this.get("maximum"),
increm = (max - min)/(l-1),
label;
l -= 1;
label = min + (i * increm);
return label;
},
/**
* @private
*/
_keyChangeHandler: function(e)
{
this._updateMinAndMax();
this.fire("dataUpdate");
}
}, {
ATTRS: {
/**
* Hash of array identifed by a string value.
*
* @attribute keys
* @type Object
*/
keys: {
value: {},
setter: function(val)
{
var keys = {},
i,
len,
data = this.get("dataProvider");
if(Y.Lang.isArray(val))
{
len = val.length;
for(i = 0; i < len; ++i)
{
keys[val[i]] = this._getKeyArray(val[i], data);
}
}
else if(Y.Lang.isString(val))
{
keys = this.get("keys");
keys[val] = this._getKeyArray(val, data);
}
else
{
for(i in val)
{
if(val.hasOwnProperty(i))
{
keys[i] = this._getKeyArray(i, data);
}
}
}
this._updateTotalDataFlag = true;
return keys;
}
},
/**
*Indicates how to round unit values.
* <dl>
* <dt>niceNumber</dt><dd>Units will be smoothed based on the number of ticks and data range.</dd>
* <dt>auto</dt><dd>If the range is greater than 1, the units will be rounded.</dd>
* <dt>numeric value</dt><dd>Units will be equal to the numeric value.</dd>
* <dt>null</dt><dd>No rounding will occur.</dd>
* </dl>
*
* @attribute roundingMethod
* @type String
* @default niceNumber
*/
roundingMethod: {
value: "niceNumber"
},
/**
*Returns the type of axis data
* <dl>
* <dt>time</dt><dd>Manages time data</dd>
* <dt>stacked</dt><dd>Manages stacked numeric data</dd>
* <dt>numeric</dt><dd>Manages numeric data</dd>
* <dt>category</dt><dd>Manages categorical data</dd>
* </dl>
*
* @attribute type
* @type String
*/
type:
{
readOnly: true,
getter: function ()
{
return this._type;
}
},
/**
* Instance of <code>ChartDataProvider</code> that the class uses
* to build its own data.
*
* @attribute dataProvider
* @type Array
*/
dataProvider:{
setter: function (value)
{
return value;
}
},
/**
* The maximum value contained in the <code>data</code> array. Used for
* <code>maximum</code> when <code>autoMax</code> is true.
*
* @attribute dataMaximum
* @type Number
*/
dataMaximum: {
getter: function ()
{
if(!this._dataMaximum)
{
this._updateMinAndMax();
}
return this._dataMaximum;
}
},
/**
* The maximum value that will appear on an axis.
*
* @attribute maximum
* @type Number
*/
maximum: {
getter: function ()
{
var max = this.get("dataMaximum");
if(this.get("setMax"))
{
max = this._setMaximum;
}
return max;
},
setter: function (value)
{
this._setMaximum = parseFloat(value);
return value;
}
},
/**
* The minimum value contained in the <code>data</code> array. Used for
* <code>minimum</code> when <code>autoMin</code> is true.
*
* @attribute dataMinimum
* @type Number
*/
dataMinimum: {
getter: function ()
{
if(!this._dataMinimum)
{
this._updateMinAndMax();
}
return this._dataMinimum;
}
},
/**
* The minimum value that will appear on an axis.
*
* @attribute minimum
* @type Number
*/
minimum: {
getter: function ()
{
var min = this.get("dataMinimum");
if(this.get("setMin"))
{
min = this._setMinimum;
}
return min;
},
setter: function(val)
{
this._setMinimum = parseFloat(val);
return val;
}
},
/**
* Determines whether the maximum is calculated or explicitly
* set by the user.
*
* @attribute setMax
* @type Boolean
*/
setMax: {
readOnly: true,
getter: function()
{
return Y.Lang.isNumber(this._setMaximum);
}
},
/**
* Determines whether the minimum is calculated or explicitly
* set by the user.
*
* @attribute setMin
* @type Boolean
*/
setMin: {
readOnly: true,
getter: function()
{
return Y.Lang.isNumber(this._setMinimum);
}
},
/**
* Array of axis data
*
* @attribute data
* @type Array
*/
data: {
getter: function ()
{
if(!this._data || this._updateTotalDataFlag)
{
this._updateTotalData();
}
return this._data;
}
},
/**
* Array containing all the keys in the axis.
*
* @attribute keyCollection
* @type Array
*/
keyCollection: {
getter: function()
{
var keys = this.get("keys"),
i,
col = [];
for(i in keys)
{
if(keys.hasOwnProperty(i))
{
col.push(i);
}
}
return col;
},
readOnly: true
},
/**
* Method used for formatting a label.
*
* @attribute labelFunction
* @type Function
* @param {String} val label to be formatted.
* @param {Object} format temlate for formatting a label.
* @return String
*/
labelFunction: {
value: function(val, format)
{
return val;
}
}
}
});