#

API Docs for:
Show: Inherited Protected Private Deprecated

# File: input/charts/NumericAxis.js

    /**
     * NumericAxis manages numeric data on an axis.
     *
     * @module charts
     * @class NumericAxis
     * @constructor
     * @param {Object} config (optional) Configuration parameters for the Chart.
     * @extends AxisType
     */
    function NumericAxis(config)
    {
    	NumericAxis.superclass.constructor.apply(this, arguments);
    }

    NumericAxis.NAME = "numericAxis";

    NumericAxis.ATTRS = {
        /**
         * Indicates whether 0 should always be displayed.
         *
         * @attribute alwaysShowZero
         * @type Boolean
         */
    	alwaysShowZero: {
    	    value: true
    	},

        /**
         * Method used for formatting a label. This attribute allows for the default label formatting method to overridden. The method use would need
         * to implement the arguments below and return a `String` or an `HTMLElement`. The default implementation of the method returns a `String`. The output of this method
         * will be rendered to the DOM using `appendChild`. If you override the `labelFunction` method and return an html string, you will also need to override the Axis'
         * `appendLabelFunction` to accept html as a `String`.
         * <dl>
         *      <dt>val</dt><dd>Label to be formatted. (`String`)</dd>
         *      <dt>format</dt><dd>Object containing properties used to format the label. (optional)</dd>
         * </dl>
         *
         * @attribute labelFunction
         * @type Function
         */
        labelFunction: {
            value: function(val, format)
            {
                if(format)
                {
                    return Y.DataType.Number.format(val, format);
                }
                return val;
            }
        },

        /**
         * Object containing properties used by the `labelFunction` to format a
         * label.
         *
         * @attribute labelFormat
         * @type Object
         */
        labelFormat: {
            value: {
                prefix: "",
                thousandsSeparator: "",
                decimalSeparator: "",
                decimalPlaces: "0",
                suffix: ""
            }
        }
    };

    Y.extend(NumericAxis, Y.AxisType,
    {
        /**
         * Formats a label based on the axis type and optionally specified format.
         *
         * @method formatLabel
         * @param {Object} value
         * @param {Object} format Pattern used to format the value.
         * @return String
         */
        formatLabel: function(val, format)
        {
            if(format)
            {
                return Y.DataType.Number.format(val, format);
            }
            return val;
        },

        /**
         * Returns the sum of all values per key.
         *
         * @method getTotalByKey
         * @param {String} key The identifier for the array whose values will be calculated.
         * @return Number
         */
        getTotalByKey: function(key)
        {
            var total = 0,
                values = this.getDataByKey(key),
                i = 0,
                val,
                len = values ? values.length : 0;
            for(; i < len; ++i)
            {
               val = parseFloat(values[i]);
               if(!isNaN(val))
               {
                    total += val;
               }
            }
            return total;
        },

        /**
         * Type of data used in `Axis`.
         *
         * @property _type
         * @readOnly
         * @private
         */
        _type: "numeric",

        /**
         * Helper method for getting a `roundingUnit` when calculating the minimum and maximum values.
         *
         * @method _getMinimumUnit
         * @param {Number} max Maximum number
         * @param {Number} min Minimum number
         * @param {Number} units Number of units on the axis
         * @return Number
         * @private
         */
        _getMinimumUnit:function(max, min, units)
        {
            return this._getNiceNumber(Math.ceil((max - min)/units));
        },

        /**
         * Calculates a nice rounding unit based on the range.
         *
         * @method _getNiceNumber
         * @param {Number} roundingUnit The calculated rounding unit.
         * @return Number
         * @private
         */
        _getNiceNumber: function(roundingUnit)
        {
            var tempMajorUnit = roundingUnit,
                order = Math.ceil(Math.log(tempMajorUnit) * 0.4342944819032518),
                roundedMajorUnit = Math.pow(10, order),
                roundedDiff;

            if (roundedMajorUnit / 2 >= tempMajorUnit)
            {
                roundedDiff = Math.floor((roundedMajorUnit / 2 - tempMajorUnit) / (Math.pow(10,order-1)/2));
                tempMajorUnit = roundedMajorUnit/2 - roundedDiff*Math.pow(10,order-1)/2;
            }
            else
            {
                tempMajorUnit = roundedMajorUnit;
            }
            if(!isNaN(tempMajorUnit))
            {
                return tempMajorUnit;
            }
            return roundingUnit;

        },

        /**
         * Calculates the maximum and minimum values for the `Axis`.
         *
         * @method _updateMinAndMax
         * @private
         */
        _updateMinAndMax: function()
        {
            var data = this.get("data"),
                max,
                min,
                len,
                num,
                i = 0,
                key,
                setMax = this.get("setMax"),
                setMin = this.get("setMin");
            if(!setMax || !setMin)
            {
                if(data && data.length && data.length > 0)
                {
                    len = data.length;
                    for(; i < len; i++)
                    {
                        num = data[i];
                        if(isNaN(num))
                        {
                            if(Y_Lang.isObject(num))
                            {
                                min = max = 0;
                                //hloc values
                                for(key in num)
                                {
                                   if(num.hasOwnProperty(key))
                                   {
                                        max = Math.max(num[key], max);
                                        min = Math.min(num[key], min);
                                   }
                                }
                            }
                            max = setMax ? this._setMaximum : max;
                            min = setMin ? this._setMinimum : min;
                            continue;
                        }

                        if(setMin)
                        {
                            min = this._setMinimum;
                        }
                        else if(min === undefined)
                        {
                            min = num;
                        }
                        else
                        {
                            min = Math.min(num, min);
                        }
                        if(setMax)
                        {
                            max = this._setMaximum;
                        }
                        else if(max === undefined)
                        {
                            max = num;
                        }
                        else
                        {
                            max = Math.max(num, max);
                        }

                        this._actualMaximum = max;
                        this._actualMinimum = min;
                    }
                }
                this._roundMinAndMax(min, max, setMin, setMax);
            }
        },

        /**
         * Rounds the mimimum and maximum values based on the `roundingUnit` attribute.
         *
         * @method _roundMinAndMax
         * @param {Number} min Minimum value
         * @param {Number} max Maximum value
         * @private
         */
        _roundMinAndMax: function(min, max, setMin, setMax)
        {
            var roundingUnit,
                minimumRange,
                minGreaterThanZero = min >= 0,
                maxGreaterThanZero = max > 0,
                dataRangeGreater,
                maxRound,
                minRound,
                topTicks,
                botTicks,
                tempMax,
                tempMin,
                units = this.getTotalMajorUnits() - 1,
                alwaysShowZero = this.get("alwaysShowZero"),
                roundingMethod = this.get("roundingMethod"),
                useIntegers = (max - min)/units >= 1;
            if(roundingMethod)
            {
                if(roundingMethod == "niceNumber")
                {
                    roundingUnit = this._getMinimumUnit(max, min, units);
                    if(minGreaterThanZero && maxGreaterThanZero)
                    {
                        if((alwaysShowZero || min < roundingUnit) && !setMin)
                        {
                            min = 0;
                            roundingUnit = this._getMinimumUnit(max, min, units);
                        }
                        else
                        {
                           min = this._roundDownToNearest(min, roundingUnit);
                        }
                        if(setMax)
                        {
                            if(!alwaysShowZero)
                            {
                                min = max - (roundingUnit * units);
                            }
                        }
                        else if(setMin)
                        {
                            max = min + (roundingUnit * units);
                        }
                        else
                        {
                            max = this._roundUpToNearest(max, roundingUnit);
                        }
                    }
                    else if(maxGreaterThanZero && !minGreaterThanZero)
                    {
                        if(alwaysShowZero)
                        {
                            topTicks = Math.round(units/((-1 * min)/max + 1));
                            topTicks = Math.max(Math.min(topTicks, units - 1), 1);
                            botTicks = units - topTicks;
                            tempMax = Math.ceil( max/topTicks );
                            tempMin = Math.floor( min/botTicks ) * -1;

                            if(setMin)
                            {
                                while(tempMin < tempMax && botTicks >= 0)
                                {
                                    botTicks--;
                                    topTicks++;
                                    tempMax = Math.ceil( max/topTicks );
                                    tempMin = Math.floor( min/botTicks ) * -1;
                                }
                                //if there are any bottom ticks left calcualate the maximum by multiplying by the tempMin value
                                //if not, it's impossible to ensure that a zero is shown. skip it
                                if(botTicks > 0)
                                {
                                    max = tempMin * topTicks;
                                }
                                else
                                {
                                    max = min + (roundingUnit * units);
                                }
                            }
                            else if(setMax)
                            {
                                while(tempMax < tempMin && topTicks >= 0)
                                {
                                    botTicks++;
                                    topTicks--;
                                    tempMin = Math.floor( min/botTicks ) * -1;
                                    tempMax = Math.ceil( max/topTicks );
                                }
                                //if there are any top ticks left calcualate the minimum by multiplying by the tempMax value
                                //if not, it's impossible to ensure that a zero is shown. skip it
                                if(topTicks > 0)
                                {
                                    min = tempMax * botTicks * -1;
                                }
                                else
                                {
                                    min = max - (roundingUnit * units);
                                }
                            }
                            else
                            {
                                roundingUnit = Math.max(tempMax, tempMin);
                                roundingUnit = this._getNiceNumber(roundingUnit);
                                max = roundingUnit * topTicks;
                                min = roundingUnit * botTicks * -1;
                            }
                        }
                        else
                        {
                            if(setMax)
                            {
                                min = max - (roundingUnit * units);
                            }
                            else if(setMin)
                            {
                                max = min + (roundingUnit * units);
                            }
                            else
                            {
                                min = this._roundDownToNearest(min, roundingUnit);
                                max = this._roundUpToNearest(max, roundingUnit);
                            }
                        }
                    }
                    else
                    {
                        if(setMin)
                        {
                            if(alwaysShowZero)
                            {
                                max = 0;
                            }
                            else
                            {
                                max = min + (roundingUnit * units);
                            }
                        }
                        else if(!setMax)
                        {
                            if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
                            {
                                max = 0;
                                roundingUnit = this._getMinimumUnit(max, min, units);
                            }
                            else
                            {
                                max = this._roundUpToNearest(max, roundingUnit);
                            }
                            min = max - (roundingUnit * units);
                        }
                        else
                        {
                            min = max - (roundingUnit * units);
                        }
                    }
                }
                else if(roundingMethod == "auto")
                {
                    if(minGreaterThanZero && maxGreaterThanZero)
                    {
                        if((alwaysShowZero || min < (max-min)/units) && !setMin)
                        {
                            min = 0;
                        }

                        roundingUnit = (max - min)/units;
                        if(useIntegers)
                        {
                            roundingUnit = Math.ceil(roundingUnit);
                        }
                        max = min + (roundingUnit * units);
                    }
                    else if(maxGreaterThanZero && !minGreaterThanZero)
                    {
                        if(alwaysShowZero)
                        {
                            topTicks = Math.round( units / ( (-1 * min) /max + 1) );
                            topTicks = Math.max(Math.min(topTicks, units - 1), 1);
                            botTicks = units - topTicks;

                            if(useIntegers)
                            {
                                tempMax = Math.ceil( max/topTicks );
                                tempMin = Math.floor( min/botTicks ) * -1;
                            }
                            else
                            {
                                tempMax = max/topTicks;
                                tempMin = min/botTicks * -1;
                            }
                            roundingUnit = Math.max(tempMax, tempMin);
                            max = roundingUnit * topTicks;
                            min = roundingUnit * botTicks * -1;
                        }
                        else
                        {
                            roundingUnit = (max - min)/units;
                            if(useIntegers)
                            {
                                roundingUnit = Math.ceil(roundingUnit);
                            }
                            min = this._roundDownToNearest(min, roundingUnit);
                            max = this._roundUpToNearest(max, roundingUnit);
                        }
                    }
                    else
                    {
                        roundingUnit = (max - min)/units;
                        if(useIntegers)
                        {
                            roundingUnit = Math.ceil(roundingUnit);
                        }
                        if(alwaysShowZero || max === 0 || max + roundingUnit > 0)
                        {
                            max = 0;
                            roundingUnit = (max - min)/units;
                            if(useIntegers)
                            {
                                Math.ceil(roundingUnit);
                            }
                        }
                        else
                        {
                            max = this._roundUpToNearest(max, roundingUnit);
                        }
                        min = max - (roundingUnit * units);

                    }
                }
                else if(!isNaN(roundingMethod) && isFinite(roundingMethod))
                {
                    roundingUnit = roundingMethod;
                    minimumRange = roundingUnit * units;
                    dataRangeGreater = (max - min) > minimumRange;
                    minRound = this._roundDownToNearest(min, roundingUnit);
                    maxRound = this._roundUpToNearest(max, roundingUnit);
                    if(setMax)
                    {
                        min = max - minimumRange;
                    }
                    else if(setMin)
                    {
                        max = min + minimumRange;
                    }
                    else if(minGreaterThanZero && maxGreaterThanZero)
                    {
                        if(alwaysShowZero || minRound <= 0)
                        {
                            min = 0;
                        }
                        else
                        {
                            min = minRound;
                        }
                        max = min + minimumRange;
                    }
                    else if(maxGreaterThanZero && !minGreaterThanZero)
                    {
                        min = minRound;
                        max = min + minimumRange;
                    }
                    else
                    {
                        if(alwaysShowZero || maxRound >= 0)
                        {
                            max = 0;
                        }
                        else
                        {
                            max = maxRound;
                        }
                        min = max - minimumRange;
                    }
                }
            }
            this._dataMaximum = max;
            this._dataMinimum = min;
        },

        /**
         * 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,
                roundingMethod = this.get("roundingMethod");
                l -= 1;
            //respect the min and max. calculate all other labels.
            if(i === 0)
            {
                label = min;
            }
            else if(i === l)
            {
                label = max;
            }
            else
            {
                label = (i * increm);
                if(roundingMethod == "niceNumber")
                {
                    label = this._roundToNearest(label, increm);
                }
                label += min;
            }
            return parseFloat(label);
        },

        /**
         * Rounds a Number to the nearest multiple of an input. For example, by rounding
         * 16 to the nearest 10, you will receive 20. Similar to the built-in function Math.round().
         *
         * @method _roundToNearest
         * @param {Number} number Number to round
         * @param {Number} nearest Multiple to round towards.
         * @return Number
         * @private
         */
        _roundToNearest: function(number, nearest)
        {
            nearest = nearest || 1;
            if(nearest === 0)
            {
                return number;
            }
            var roundedNumber = Math.round(this._roundToPrecision(number / nearest, 10)) * nearest;
            return this._roundToPrecision(roundedNumber, 10);
        },

        /**
         * Rounds a Number up to the nearest multiple of an input. For example, by rounding
         * 16 up to the nearest 10, you will receive 20. Similar to the built-in function Math.ceil().
         *
         * @method _roundUpToNearest
         * @param {Number} number Number to round
         * @param {Number} nearest Multiple to round towards.
         * @return Number
         * @private
         */
        _roundUpToNearest: function(number, nearest)
        {
            nearest = nearest || 1;
            if(nearest === 0)
            {
                return number;
            }
            return Math.ceil(this._roundToPrecision(number / nearest, 10)) * nearest;
        },

        /**
         * Rounds a Number down to the nearest multiple of an input. For example, by rounding
         * 16 down to the nearest 10, you will receive 10. Similar to the built-in function Math.floor().
         *
         * @method _roundDownToNearest
         * @param {Number} number Number to round
         * @param {Number} nearest Multiple to round towards.
         * @return Number
         * @private
         */
        _roundDownToNearest: function(number, nearest)
        {
            nearest = nearest || 1;
            if(nearest === 0)
            {
                return number;
            }
            return Math.floor(this._roundToPrecision(number / nearest, 10)) * nearest;
        },

        /**
         * Rounds a number to a certain level of precision. Useful for limiting the number of
         * decimal places on a fractional number.
         *
         * @method _roundToPrecision
         * @param {Number} number Number to round
         * @param {Number} precision Multiple to round towards.
         * @return Number
         * @private
         */
        _roundToPrecision: function(number, precision)
        {
            precision = precision || 0;
            var decimalPlaces = Math.pow(10, precision);
            return Math.round(decimalPlaces * number) / decimalPlaces;
        },

        /**
         * Checks to see if data extends beyond the range of the axis. If so,
         * that data will need to be hidden. This method is internal, temporary and subject
         * to removal in the future.
         *
         * @method _hasDataOverflow
         * @protected
         * @return Boolean
         */
        _hasDataOverflow: function()
        {
            var roundingMethod,
                min,
                max;
            if(this.get("setMin") || this.get("setMax"))
            {
                return true;
            }
            roundingMethod = this.get("roundingMethod");
            min = this._actualMinimum;
            max = this._actualMaximum;
            if(Y_Lang.isNumber(roundingMethod) && ((Y_Lang.isNumber(max) && max > this._dataMaximum) || (Y_Lang.isNumber(min) && min < this._dataMinimum)))
            {
                return true;
            }
            return false;
        }
    });

    Y.NumericAxis = NumericAxis;