#

API Docs for:
Show: Inherited Protected Private Deprecated

# File: input/charts/CartesianSeries.js

    /**
     * The CartesianSeries class creates a chart with horizontal and vertical axes.
     *
     * @module charts
     * @class CartesianSeries
     * @extends Base
     * @uses Renderer
     * @constructor
     */
    Y.CartesianSeries = Y.Base.create("cartesianSeries", Y.Base, [Y.Renderer], {
        /**
         * Storage for `xDisplayName` attribute.
         *
         * @property _xDisplayName
         * @type String
         * @private
         */
        _xDisplayName: null,

        /**
         * Storage for `yDisplayName` attribute.
         *
         * @property _yDisplayName
         * @type String
         * @private
         */
        _yDisplayName: null,

        /**
         * Th x-coordinate for the left edge of the series.
         *
         * @property _leftOrigin
         * @type String
         * @private
         */
        _leftOrigin: null,

        /**
         * The y-coordinate for the bottom edge of the series.
         *
         * @property _bottomOrigin
         * @type String
         * @private
         */
        _bottomOrigin: null,

        /**
         * @method render
         * @private
         */
        render: function()
        {
            this._setCanvas();
            this.addListeners();
            this.set("rendered", true);
            this.validate();
        },

        /**
         * Adds event listeners.
         *
         * @method addListeners
         * @private
         */
        addListeners: function()
        {
            var xAxis = this.get("xAxis"),
                yAxis = this.get("yAxis");
            if(xAxis)
            {
                xAxis.after("dataReady", Y.bind(this._xDataChangeHandler, this));
                xAxis.after("dataUpdate", Y.bind(this._xDataChangeHandler, this));
            }
            if(yAxis)
            {
                yAxis.after("dataReady", Y.bind(this._yDataChangeHandler, this));
                yAxis.after("dataUpdate", Y.bind(this._yDataChangeHandler, this));
            }
            this.after("xAxisChange", this._xAxisChangeHandler);
            this.after("yAxisChange", this._yAxisChangeHandler);
            this.after("stylesChange", function(e) {
                var axesReady = this._updateAxisData();
                if(axesReady)
                {
                    this.draw();
                }
            });
            this.after("widthChange", function(e) {
                var axesReady = this._updateAxisData();
                if(axesReady)
                {
                    this.draw();
                }
            });
            this.after("heightChange", function(e) {
                var axesReady = this._updateAxisData();
                if(axesReady)
                {
                    this.draw();
                }
            });
            this.after("visibleChange", this._handleVisibleChange);
        },

        /**
         * Event handler for the xAxisChange event.
         *
         * @method _xAxisChangeHandler
         * @param {Object} e Event object.
         * @private
         */
        _xAxisChangeHandler: function(e)
        {
            var xAxis = this.get("xAxis");
            xAxis.after("dataReady", Y.bind(this._xDataChangeHandler, this));
            xAxis.after("dataUpdate", Y.bind(this._xDataChangeHandler, this));
        },

        /**
         * Event handler the yAxisChange event.
         *
         * @method _yAxisChangeHandler
         * @param {Object} e Event object.
         * @private
         */
        _yAxisChangeHandler: function(e)
        {
            var yAxis = this.get("yAxis");
            yAxis.after("dataReady", Y.bind(this._yDataChangeHandler, this));
            yAxis.after("dataUpdate", Y.bind(this._yDataChangeHandler, this));
        },

        /**
         * Constant used to generate unique id.
         *
         * @property GUID
         * @type String
         * @private
         */
        GUID: "yuicartesianseries",

        /**
         * Event handler for xDataChange event.
         *
         * @method _xDataChangeHandler
         * @param {Object} event Event object.
         * @private
         */
        _xDataChangeHandler: function(event)
        {
            var axesReady = this._updateAxisData();
            if(axesReady)
            {
                this.draw();
            }
        },

        /**
         * Event handler for yDataChange event.
         *
         * @method _yDataChangeHandler
         * @param {Object} event Event object.
         * @private
         */
        _yDataChangeHandler: function(event)
        {
            var axesReady = this._updateAxisData();
            if(axesReady)
            {
                this.draw();
            }
        },

        /**
         * Checks to ensure that both xAxis and yAxis data are available. If so, set the `xData` and `yData` attributes and return `true`. Otherwise, return `false`.
         *
         * @method _updateAxisData
         * @return Boolean
         * @private
         */
        _updateAxisData: function()
        {
            var xAxis = this.get("xAxis"),
                yAxis = this.get("yAxis"),
                xKey = this.get("xKey"),
                yKey = this.get("yKey"),
                yData,
                xData;
            if(!xAxis || !yAxis || !xKey || !yKey)
            {
                return false;
            }
            xData = xAxis.getDataByKey(xKey);
            yData = yAxis.getDataByKey(yKey);
            if(!xData || !yData)
            {
                return false;
            }
            this.set("xData", xData.concat());
            this.set("yData", yData.concat());
            return true;
        },

        /**
         * Draws the series is the xAxis and yAxis data are both available.
         *
         * @method validate
         * @private
         */
        validate: function()
        {
            if((this.get("xData") && this.get("yData")) || this._updateAxisData())
            {
                this.draw();
            }
            else
            {
                this.fire("drawingComplete");
            }
        },

        /**
         * Creates a `Graphic` instance.
         *
         * @method _setCanvas
         * @protected
         */
        _setCanvas: function()
        {
            var graph = this.get("graph"),
                graphic = graph.get("graphic");
            this.set("graphic", graphic);
        },

        /**
         * Calculates the coordinates for the series.
         *
         * @method setAreaData
         * @protected
         */
        setAreaData: function()
        {
            var isNumber = Y_Lang.isNumber,
                nextX, nextY,
                graph = this.get("graph"),
                w = graph.get("width"),
                h = graph.get("height"),
                xAxis = this.get("xAxis"),
                yAxis = this.get("yAxis"),
                xData = this.get("xData").concat(),
                yData = this.get("yData").concat(),
                xValue,
                yValue,
                xOffset = xAxis.getEdgeOffset(xData.length, w),
                yOffset = yAxis.getEdgeOffset(yData.length, h),
                padding = this.get("styles").padding,
    			leftPadding = padding.left,
    			topPadding = padding.top,
    			dataWidth = w - (leftPadding + padding.right + xOffset),
    			dataHeight = h - (topPadding + padding.bottom + yOffset),
    			xcoords = [],
    			ycoords = [],
    			xMax = xAxis.get("maximum"),
    			xMin = xAxis.get("minimum"),
    			yMax = yAxis.get("maximum"),
    			yMin = yAxis.get("minimum"),
                xScaleFactor = dataWidth / (xMax - xMin),
    			yScaleFactor = dataHeight / (yMax - yMin),
                dataLength,
                direction = this.get("direction"),
                i = 0,
                xMarkerPlane = [],
                yMarkerPlane = [],
                xMarkerPlaneOffset = this.get("xMarkerPlaneOffset"),
                yMarkerPlaneOffset = this.get("yMarkerPlaneOffset"),
                graphic = this.get("graphic");
            graphic.set("width", w);
            graphic.set("height", h);
            dataLength = xData.length;
            xOffset *= 0.5;
            yOffset *= 0.5;
            //Assuming a vertical graph has a range/category for its vertical axis.
            if(direction === "vertical")
            {
                yData = yData.reverse();
            }
            this._leftOrigin = Math.round(((0 - xMin) * xScaleFactor) + leftPadding + xOffset);
            this._bottomOrigin = Math.round((dataHeight + topPadding + yOffset));
            for (; i < dataLength; ++i)
    		{
                xValue = parseFloat(xData[i]);
                yValue = parseFloat(yData[i]);
                if(isNumber(xValue))
                {
                    nextX = Math.round((((xValue - xMin) * xScaleFactor) + leftPadding + xOffset));
                }
                else
                {
                    nextX = NaN;
                }
                if(isNumber(yValue))
                {
    			    nextY = Math.round(((dataHeight + topPadding + yOffset) - (yValue - yMin) * yScaleFactor));
                }
                else
                {
                    nextY = NaN;
                }
                xcoords.push(nextX);
                ycoords.push(nextY);
                xMarkerPlane.push({start:nextX - xMarkerPlaneOffset, end: nextX + xMarkerPlaneOffset});
                yMarkerPlane.push({start:nextY - yMarkerPlaneOffset, end: nextY + yMarkerPlaneOffset});
            }
            this.set("xcoords", xcoords);
    		this.set("ycoords", ycoords);
            this.set("xMarkerPlane", xMarkerPlane);
            this.set("yMarkerPlane", yMarkerPlane);
            this._dataLength = dataLength;
        },

        /**
         * Draws the series.
         *
         * @method draw
         * @protected
         */
        draw: function()
        {
            var graph = this.get("graph"),
                w = graph.get("width"),
                h = graph.get("height");
            if(this.get("rendered"))
            {
                if((isFinite(w) && isFinite(h) && w > 0 && h > 0) && ((this.get("xData") && this.get("yData")) || this._updateAxisData()))
                {
                    if(this._drawing)
                    {
                        this._callLater = true;
                        return;
                    }
                    this._drawing = true;
                    this._callLater = false;
                    this.setAreaData();
                    if(this.get("xcoords") && this.get("ycoords"))
                    {
                        this.drawSeries();
                    }
                    this._drawing = false;
                    if(this._callLater)
                    {
                        this.draw();
                    }
                    else
                    {
                        this._toggleVisible(this.get("visible"));
                        this.fire("drawingComplete");
                    }
                }
            }
        },

        /**
         * Default value for plane offsets when the parent chart's `interactiveType` is `planar`.
         *
         * @property _defaultPlaneOffset
         * @type Number
         * @private
         */
        _defaultPlaneOffset: 4,

        /**
         * Gets the default value for the `styles` attribute. Overrides
         * base implementation.
         *
         * @method _getDefaultStyles
         * @return Object
         * @protected
         */
        _getDefaultStyles: function()
        {
            return {padding:{
                    top: 0,
                    left: 0,
                    right: 0,
                    bottom: 0
                }};
        },

        /**
         * Collection of default colors used for lines in a series when not specified by user.
         *
         * @property _defaultLineColors
         * @type Array
         * @protected
         */
        _defaultLineColors:["#426ab3", "#d09b2c", "#000000", "#b82837", "#b384b5", "#ff7200", "#779de3", "#cbc8ba", "#7ed7a6", "#007a6c"],

        /**
         * Collection of default colors used for marker fills in a series when not specified by user.
         *
         * @property _defaultFillColors
         * @type Array
         * @protected
         */
        _defaultFillColors:["#6084d0", "#eeb647", "#6c6b5f", "#d6484f", "#ce9ed1", "#ff9f3b", "#93b7ff", "#e0ddd0", "#94ecba", "#309687"],

        /**
         * Collection of default colors used for marker borders in a series when not specified by user.
         *
         * @property _defaultBorderColors
         * @type Array
         * @protected
         */
        _defaultBorderColors:["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"],

        /**
         * Collection of default colors used for area fills, histogram fills and pie fills in a series when not specified by user.
         *
         * @property _defaultSliceColors
         * @type Array
         * @protected
         */
        _defaultSliceColors: ["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"],

        /**
         * Parses a color based on a series order and type.
         *
         * @method _getDefaultColor
         * @param {Number} index Index indicating the series order.
         * @param {String} type Indicates which type of object needs the color.
         * @return String
         * @protected
         */
        _getDefaultColor: function(index, type)
        {
            var colors = {
                    line: this._defaultLineColors,
                    fill: this._defaultFillColors,
                    border: this._defaultBorderColors,
                    slice: this._defaultSliceColors
                },
                col = colors[type],
                l = col.length;
            index = index || 0;
            if(index >= l)
            {
                index = index % l;
            }
            type = type || "fill";
            return colors[type][index];
        },

        /**
         * Shows/hides contents of the series.
         *
         * @method _handleVisibleChange
         * @param {Object} e Event object.
         * @protected
         */
        _handleVisibleChange: function(e)
        {
            this._toggleVisible(this.get("visible"));
        },

        /**
         * Returns the sum of all values for the series.
         *
         * @method getTotalValues
         * @return Number
         */
        getTotalValues: function()
        {
            var total = this.get("valueAxis").getTotalByKey(this.get("valueKey"));
            return total;
        },

        /**
         * Destructor implementation for the CartesianSeries class. Calls destroy on all Graphic instances.
         *
         * @method destructor
         * @protected
         */
        destructor: function()
        {
            if(this._path)
            {
                this._path.destroy();
            }
            if(this._lineGraphic)
            {
                this._lineGraphic.destroy();
                this._lineGraphic = null;
            }
            if(this.get("graphic"))
            {
                this.get("graphic").destroy();
            }
        }
    }, {
        ATTRS: {
            /**
             * Name used for for displaying data related to the x-coordinate.
             *
             * @attribute xDisplayName
             * @type String
             */
            xDisplayName: {
                getter: function()
                {
                    return this._xDisplayName || this.get("xKey");
                },

                setter: function(val)
                {
                    this._xDisplayName = val.toString();
                    return val;
                }
            },

            /**
             * Name used for for displaying data related to the y-coordinate.
             *
             * @attribute yDisplayName
             * @type String
             */
            yDisplayName: {
                getter: function()
                {
                    return this._yDisplayName || this.get("yKey");
                },

                setter: function(val)
                {
                    this._yDisplayName = val.toString();
                    return val;
                }
            },

            /**
             * Name used for for displaying category data
             *
             * @attribute categoryDisplayName
             * @type String
             * @readOnly
             */
            categoryDisplayName: {
                readOnly: true,

                getter: function()
                {
                    return this.get("direction") == "vertical" ? this.get("yDisplayName") : this.get("xDisplayName");
                }
            },

            /**
             * Name used for for displaying value data
             *
             * @attribute valueDisplayName
             * @type String
             * @readOnly
             */
            valueDisplayName: {
                readOnly: true,

                getter: function()
                {
                    return this.get("direction") == "vertical" ? this.get("xDisplayName") : this.get("yDisplayName");
                }
            },

            /**
             * Read-only attribute indicating the type of series.
             *
             * @attribute type
             * @type String
             * @default cartesian
             */
            type: {
                value: "cartesian"
            },

            /**
             * Order of this instance of this `type`.
             *
             * @attribute order
             * @type Number
             */
            order: {},

            /**
             * Order of the instance
             *
             * @attribute graphOrder
             * @type Number
             */
            graphOrder: {},

            /**
             * x coordinates for the series.
             *
             * @attribute xcoords
             * @type Array
             */
            xcoords: {},

            /**
             * y coordinates for the series
             *
             * @attribute ycoords
             * @type Array
             */
            ycoords: {},

            /**
             * Reference to the `Chart` application.
             *
             * @attribute chart
             * @type ChartBase
             * @readOnly
             */
            chart: {
                readOnly: true,

                getter: function()
                {
                    return this.get("graph").get("chart");
                }
            },

            /**
             * Reference to the `Graph` in which the series is drawn into.
             *
             * @attribute graph
             * @type Graph
             */
            graph: {},

            /**
             * Reference to the `Axis` instance used for assigning
             * x-values to the graph.
             *
             * @attribute xAxis
             * @type Axis
             */
            xAxis: {},

            /**
             * Reference to the `Axis` instance used for assigning
             * y-values to the graph.
             *
             * @attribute yAxis
             * @type Axis
             */
            yAxis: {},

            /**
             * Indicates which array to from the hash of value arrays in
             * the x-axis `Axis` instance.
             *
             * @attribute xKey
             * @type String
             */
            xKey: {
                setter: function(val)
                {
                    return val.toString();
                }
            },

            /**
             * Indicates which array to from the hash of value arrays in
             * the y-axis `Axis` instance.
             *
             * @attribute yKey
             * @type String
             */
            yKey: {
                setter: function(val)
                {
                    return val.toString();
                }
            },

            /**
             * Array of x values for the series.
             *
             * @attribute xData
             * @type Array
             */
            xData: {},

            /**
             * Array of y values for the series.
             *
             * @attribute yData
             * @type Array
             */
            yData: {},

            /**
             * Indicates whether the Series has been through its initial set up.
             *
             * @attribute rendered
             * @type Boolean
             */
            rendered: {
                value: false
            },

            /*
             * Returns the width of the parent graph
             *
             * @attribute width
             * @type Number
             */
            width: {
                readOnly: true,

                getter: function()
                {
                    this.get("graph").get("width");
                }
            },

            /**
             * Returns the height of the parent graph
             *
             * @attribute height
             * @type Number
             */
            height: {
                readOnly: true,

                getter: function()
                {
                    this.get("graph").get("height");
                }
            },

            /**
             * Indicates whether to show the series
             *
             * @attribute visible
             * @type Boolean
             * @default true
             */
            visible: {
                value: true
            },

            /**
             * Collection of area maps along the xAxis. Used to determine mouseover for multiple
             * series.
             *
             * @attribute xMarkerPlane
             * @type Array
             */
            xMarkerPlane: {},

            /**
             * Collection of area maps along the yAxis. Used to determine mouseover for multiple
             * series.
             *
             * @attribute yMarkerPlane
             * @type Array
             */
            yMarkerPlane: {},

            /**
             * Distance from a data coordinate to the left/right for setting a hotspot.
             *
             * @attribute xMarkerPlaneOffset
             * @type Number
             */
            xMarkerPlaneOffset: {
                getter: function() {
                    var marker = this.get("styles").marker;
                    if(marker && marker.width && isFinite(marker.width))
                    {
                        return marker.width * 0.5;
                    }
                    return this._defaultPlaneOffset;
                }
            },

            /**
             * Distance from a data coordinate to the top/bottom for setting a hotspot.
             *
             * @attribute yMarkerPlaneOffset
             * @type Number
             */
            yMarkerPlaneOffset: {
                getter: function() {
                    var marker = this.get("styles").marker;
                    if(marker && marker.height && isFinite(marker.height))
                    {
                        return marker.height * 0.5;
                    }
                    return this._defaultPlaneOffset;
                }
            },

            /**
             * Direction of the series
             *
             * @attribute direction
             * @type String
             */
            direction: {
                value: "horizontal"
            },

            /**
             * Indicates whether or not markers for a series will be grouped and rendered in a single complex shape instance.
             *
             * @attribute groupMarkers
             * @type Boolean
             */
            groupMarkers: {
                getter: function()
                {
                    if(this._groupMarkers === undefined)
                    {
                        return this.get("graph").get("groupMarkers");
                    }
                    else
                    {
                        return this._groupMarkers;
                    }
                },

                setter: function(val)
                {
                    this._groupMarkers = val;
                    return val;
                }
            }
        }
    });