Version 3.18.1
Show:

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;