#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# File: charts/js/StackingUtil.js

    /**
     * Provides functionality for creating stacked series.
     *
     * @module charts
     * @submodule series-stacked
     */
    var Y_Lang = Y.Lang;

    /**
     * Utility class used for creating stacked series.
     *
     * @module charts
     * @class StackingUtil
     * @constructor
     * @submodule series-stacked
     */
    function StackingUtil(){}

    StackingUtil.prototype = {
        /**
         * Indicates whether the series is stacked.
         *
         * @property _stacked
         * @private
         */
        _stacked: true,

        /**
         * @protected
         *
         * Adjusts coordinate values for stacked series.
         *
         * @method _stackCoordinates
         */
        _stackCoordinates: function()
        {
            if(this.get("direction") === "vertical")
            {
                this._stackXCoords();
            }
            else
            {
                this._stackYCoords();
            }
        },

        /**
         * Stacks coordinates for a stacked vertical series.
         *
         * @method _stackXCoords
         * @protected
         */
        _stackXCoords: function()
        {
            var order = this.get("order"),
                seriesCollection = this.get("seriesTypeCollection"),
                i = 0,
                xcoords = this.get("xcoords"),
                ycoords = this.get("ycoords"),
                len,
                coord,
                prevCoord,
                prevOrder,
                stackedXCoords = xcoords.concat(),
                prevXCoords,
                prevYCoords,
                nullIndices = [],
                nullIndex;
            if(order > 0)
            {
                prevXCoords = seriesCollection[order - 1].get("stackedXCoords");
                prevYCoords = seriesCollection[order - 1].get("stackedYCoords");
                len = prevXCoords.length;
            }
            else
            {
                len = xcoords.length;
            }
            for(; i < len; i = i + 1)
            {
                if(Y_Lang.isNumber(xcoords[i]))
                {
                    if(order > 0)
                    {
                        prevCoord = prevXCoords[i];
                        if(!Y_Lang.isNumber(prevCoord))
                        {
                            prevOrder = order;
                            while(prevOrder >  - 1 && !Y_Lang.isNumber(prevCoord))
                            {
                                prevOrder = prevOrder - 1;
                                if(prevOrder > -1)
                                {
                                    prevCoord = seriesCollection[prevOrder].get("stackedXCoords")[i];
                                }
                                else
                                {
                                    prevCoord = this._leftOrigin;
                                }
                            }
                        }
                        xcoords[i] = xcoords[i] + prevCoord;
                    }
                    stackedXCoords[i] = xcoords[i];
                }
                else
                {
                    nullIndices.push(i);
                }
            }
            this._cleanXNaN(stackedXCoords, ycoords);
            len = nullIndices.length;
            if(len > 0)
            {
                for(i = 0; i < len; i = i + 1)
                {
                    nullIndex = nullIndices[i];
                    coord = order > 0 ? prevXCoords[nullIndex] : this._leftOrigin;
                    stackedXCoords[nullIndex] =  Math.max(stackedXCoords[nullIndex], coord);
                }
            }
            this.set("stackedXCoords", stackedXCoords);
            this.set("stackedYCoords", ycoords);
        },

        /**
         * Stacks coordinates for a stacked horizontal series.
         *
         * @method _stackYCoords
         * @protected
         */
        _stackYCoords: function()
        {
            var order = this.get("order"),
                graphic = this.get("graphic"),
                h = graphic.get("height"),
                seriesCollection = this.get("seriesTypeCollection"),
                i = 0,
                xcoords = this.get("xcoords"),
                ycoords = this.get("ycoords"),
                len,
                coord,
                prevCoord,
                prevOrder,
                stackedYCoords = ycoords.concat(),
                prevXCoords,
                prevYCoords,
                nullIndices = [],
                nullIndex;
            if(order > 0)
            {
                prevXCoords = seriesCollection[order - 1].get("stackedXCoords");
                prevYCoords = seriesCollection[order - 1].get("stackedYCoords");
                len = prevYCoords.length;
            }
            else
            {
                len = ycoords.length;
            }
            for(; i < len; i = i + 1)
            {
                if(Y_Lang.isNumber(ycoords[i]))
                {
                    if(order > 0)
                    {
                        prevCoord = prevYCoords[i];
                        if(!Y_Lang.isNumber(prevCoord))
                        {
                            prevOrder = order;
                            while(prevOrder >  - 1 && !Y_Lang.isNumber(prevCoord))
                            {
                                prevOrder = prevOrder - 1;
                                if(prevOrder > -1)
                                {
                                    prevCoord = seriesCollection[prevOrder].get("stackedYCoords")[i];
                                }
                                else
                                {
                                    prevCoord = this._bottomOrigin;
                                }
                            }
                        }
                        ycoords[i] = prevCoord - (h - ycoords[i]);
                    }
                    stackedYCoords[i] = ycoords[i];
                }
                else
                {
                    nullIndices.push(i);
                }
            }
            this._cleanYNaN(xcoords, stackedYCoords);
            len = nullIndices.length;
            if(len > 0)
            {
                for(i = 0; i < len; i = i + 1)
                {
                    nullIndex = nullIndices[i];
                    coord = order > 0 ? prevYCoords[nullIndex] : h;
                    stackedYCoords[nullIndex] =  Math.min(stackedYCoords[nullIndex], coord);
                }
            }
            this.set("stackedXCoords", xcoords);
            this.set("stackedYCoords", stackedYCoords);
        },

        /**
         * Cleans invalid x-coordinates by calculating their value based on the corresponding y-coordinate, the
         * previous valid x-coordinate with its corresponding y-coordinate and the next valid x-coordinate with
         * its corresponding y-coordinate. If there is no previous or next valid x-coordinate, the value will not
         * be altered.
         *
         * @method _cleanXNaN
         * @param {Array} xcoords An array of x-coordinate values
         * @param {Array} ycoords An arry of y-coordinate values
         * @private
         */
        _cleanXNaN: function(xcoords, ycoords)
        {
            var previousValidIndex,
                nextValidIndex,
                previousValidX,
                previousValidY,
                x,
                y,
                nextValidX,
                nextValidY,
                isNumber = Y_Lang.isNumber,
                m,
                i = 0,
                len = ycoords.length;
            for(; i < len; ++i)
            {
                x = xcoords[i];
                y = ycoords[i];
                //if x is invalid, calculate where it should be
                if(!isNumber(x) && i > 0 && i < len - 1)
                {
                    previousValidY = ycoords[i - 1];
                    //check to see if the previous value is valid
                    previousValidX = this._getPreviousValidCoordValue(xcoords, i);
                    nextValidY = ycoords[i + 1];
                    nextValidX = this._getNextValidCoordValue(xcoords, i);
                    //check to see if the next value is valid
                    if(isNumber(previousValidX) && isNumber(nextValidX))
                    {
                        //calculate slope and solve for x
                        m = (nextValidY - previousValidY) / (nextValidX - previousValidX);
                        xcoords[i] = (y + (m * previousValidX) - previousValidY)/m;
                    }
                    previousValidIndex = NaN;
                    nextValidIndex = NaN;
                }
            }
        },

        /**
         * Returns the previous valid (numeric) value in an array if available.
         *
         * @method _getPreviousValidCoordValue
         * @param {Array} coords Array of values
         * @param {Number} index The index in the array in which to begin searching.
         * @return Number
         * @private
         */
        _getPreviousValidCoordValue: function(coords, index)
        {
            var coord,
                isNumber = Y_Lang.isNumber,
                limit = -1;
            while(!isNumber(coord) && index > limit)
            {
                index = index - 1;
                coord = coords[index];
            }
            return coord;
        },

        /**
         * Returns the next valid (numeric) value in an array if available.
         *
         * @method _getNextValidCoordValue
         * @param {Array} coords Array of values
         * @param {Number} index The index in the array in which to begin searching.
         * @return Number
         * @private
         */
        _getNextValidCoordValue: function(coords, index)
        {
            var coord,
                isNumber = Y_Lang.isNumber,
                limit = coords.length;
            while(!isNumber(coord) && index < limit)
            {
                index = index + 1;
                coord = coords[index];
            }
            return coord;
        },

        /**
         * Cleans invalid y-coordinates by calculating their value based on the corresponding x-coordinate, the
         * previous valid y-coordinate with its corresponding x-coordinate and the next valid y-coordinate with
         * its corresponding x-coordinate. If there is no previous or next valid y-coordinate, the value will not
         * be altered.
         *
         * @method _cleanYNaN
         * @param {Array} xcoords An array of x-coordinate values
         * @param {Array} ycoords An arry of y-coordinate values
         * @private
         */
        _cleanYNaN: function(xcoords, ycoords)
        {
            var previousValidIndex,
                nextValidIndex,
                previousValidX,
                previousValidY,
                x,
                y,
                nextValidX,
                nextValidY,
                isNumber = Y_Lang.isNumber,
                m,
                i = 0,
                len = xcoords.length;
            for(; i < len; ++i)
            {
                x = xcoords[i];
                y = ycoords[i];
                //if y is invalid, calculate where it should be
                if(!isNumber(y) && i > 0 && i < len - 1)
                {
                    //check to see if the previous value is valid
                    previousValidX = xcoords[i - 1];
                    previousValidY = this._getPreviousValidCoordValue(ycoords, i);
                    //check to see if the next value is valid
                    nextValidX = xcoords[i + 1];
                    nextValidY = this._getNextValidCoordValue(ycoords, i);
                    if(isNumber(previousValidY) && isNumber(nextValidY))
                    {
                        //calculate slope and solve for y
                        m = (nextValidY - previousValidY) / (nextValidX - previousValidX);
                        ycoords[i] = previousValidY + ((m * x) - (m * previousValidX));
                    }
                    previousValidIndex = NaN;
                    nextValidIndex = NaN;
                }
            }
        }
    };
    Y.StackingUtil = StackingUtil;