#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# File: charts/js/CartesianChart.js

    /**
     * The CartesianChart class creates a chart with horizontal and vertical axes.
     *
     * @class CartesianChart
     * @extends ChartBase
     * @constructor
     * @submodule charts-base
     */
    Y.CartesianChart = Y.Base.create("cartesianChart", Y.Widget, [Y.ChartBase, Y.Renderer], {
        /**
         * @method renderUI
         * @private
         */
        renderUI: function()
        {
            var bb = this.get("boundingBox"),
                cb = this.get("contentBox"),
                tt = this.get("tooltip"),
                overlay,
                overlayClass = _getClassName("overlay");
            //move the position = absolute logic to a class file
            bb.setStyle("position", "absolute");
            cb.setStyle("position", "absolute");
            this._addAxes();
            this._addGridlines();
            this._addSeries();
            if(tt && tt.show)
            {
                this._addTooltip();
            }
            if(this.get("interactionType") === "planar")
            {
                overlay = DOCUMENT.createElement("div");
                this.get("contentBox").appendChild(overlay);
                this._overlay = Y.one(overlay);
                this._overlay.set("id", this.get("id") + "_overlay");
                this._overlay.setStyle("position", "absolute");
                this._overlay.setStyle("background", "#fff");
                this._overlay.setStyle("opacity", 0);
                this._overlay.addClass(overlayClass);
                this._overlay.setStyle("zIndex", 4);
            }
            this._setAriaElements(bb, cb);
            this._redraw();
        },

        /**
         * When `interactionType` is set to `planar`, listens for mouse move events and fires `planarEvent:mouseover` or `planarEvent:mouseout`
         * depending on the position of the mouse in relation to data points on the `Chart`.
         *
         * @method _planarEventDispatcher
         * @param {Object} e Event object.
         * @private
         */
        _planarEventDispatcher: function(e)
        {
            var graph = this.get("graph"),
                bb = this.get("boundingBox"),
                cb = graph.get("contentBox"),
                isTouch = e && e.hasOwnProperty("changedTouches"),
                pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
                pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
                posX = pageX - bb.getX(),
                posY = pageY - bb.getY(),
                offset = {
                    x: pageX - cb.getX(),
                    y: pageY - cb.getY()
                },
                sc = graph.get("seriesCollection"),
                series,
                i = 0,
                index,
                oldIndex = this._selectedIndex,
                item,
                items = [],
                categoryItems = [],
                valueItems = [],
                direction = this.get("direction"),
                hasMarkers,
                catAxis,
                valAxis,
                coord,
                //data columns and area data could be created on a graph level
                markerPlane,
                len,
                coords;
            e.halt(true);
            if(direction === "horizontal")
            {
                catAxis = "x";
                valAxis = "y";
            }
            else
            {
                valAxis = "x";
                catAxis = "y";
            }
            coord = offset[catAxis];
            if(sc)
            {
                len = sc.length;
                while(i < len && !markerPlane)
                {
                    if(sc[i])
                    {
                        markerPlane = sc[i].get(catAxis + "MarkerPlane");
                    }
                    i++;
                }
            }
            if(markerPlane)
            {
                len = markerPlane.length;
                for(i = 0; i < len; ++i)
                {
                    if(coord <= markerPlane[i].end && coord >= markerPlane[i].start)
                    {
                        index = i;
                        break;
                    }
                }
                len = sc.length;
                for(i = 0; i < len; ++i)
                {
                    series = sc[i];
                    coords = series.get(valAxis + "coords");
                    hasMarkers = series.get("markers");
                    if(hasMarkers && !isNaN(oldIndex) && oldIndex > -1)
                    {
                        series.updateMarkerState("mouseout", oldIndex);
                    }
                    if(coords && coords[index] > -1)
                    {
                        if(hasMarkers && !isNaN(index) && index > -1)
                        {
                            series.updateMarkerState("mouseover", index);
                        }
                        item = this.getSeriesItems(series, index);
                        categoryItems.push(item.category);
                        valueItems.push(item.value);
                        items.push(series);
                    }

                }
                this._selectedIndex = index;

                /**
                 * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseover event.
                 *
                 *
                 * @event planarEvent:mouseover
                 * @preventable false
                 * @param {EventFacade} e Event facade with the following additional
                 *   properties:
                 *  <dl>
                 *      <dt>categoryItem</dt><dd>An array of hashes, each containing information about the category `Axis` of each marker
                 *      whose plane has been intersected.</dd>
                 *      <dt>valueItem</dt><dd>An array of hashes, each containing information about the value `Axis` of each marker whose
                 *      plane has been intersected.</dd>
                 *      <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
                 *      <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
                 *      <dt>pageX</dt><dd>The x location of the event on the page (including scroll)</dd>
                 *      <dt>pageY</dt><dd>The y location of the event on the page (including scroll)</dd>
                 *      <dt>items</dt><dd>An array including all the series which contain a marker whose plane has been intersected.</dd>
                 *      <dt>index</dt><dd>Index of the markers in their respective series.</dd>
                 *      <dt>originEvent</dt><dd>Underlying dom event.</dd>
                 *  </dl>
                 */
                /**
                 * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseout event.
                 *
                 * @event planarEvent:mouseout
                 * @preventable false
                 * @param {EventFacade} e
                 */
                if(index > -1)
                {
                    this.fire("planarEvent:mouseover", {
                        categoryItem:categoryItems,
                        valueItem:valueItems,
                        x:posX,
                        y:posY,
                        pageX:pageX,
                        pageY:pageY,
                        items:items,
                        index:index,
                        originEvent:e
                    });
                }
                else
                {
                    this.fire("planarEvent:mouseout");
                }
            }
        },

        /**
         * Indicates the default series type for the chart.
         *
         * @property _type
         * @type {String}
         * @private
         */
        _type: "combo",

        /**
         * Queue of axes instances that will be updated. This method is used internally to determine when all axes have been updated.
         *
         * @property _itemRenderQueue
         * @type Array
         * @private
         */
        _itemRenderQueue: null,

        /**
         * Adds an `Axis` instance to the `_itemRenderQueue`.
         *
         * @method _addToAxesRenderQueue
         * @param {Axis} axis An `Axis` instance.
         * @private
         */
        _addToAxesRenderQueue: function(axis)
        {
            if(!this._itemRenderQueue)
            {
                this._itemRenderQueue = [];
            }
            if(Y.Array.indexOf(this._itemRenderQueue, axis) < 0)
            {
                this._itemRenderQueue.push(axis);
            }
        },

        /**
         * Adds axis instance to the appropriate array based on position
         *
         * @method _addToAxesCollection
         * @param {String} position The position of the axis
         * @param {Axis} axis The `Axis` instance
         */
        _addToAxesCollection: function(position, axis)
        {
            var axesCollection = this.get(position + "AxesCollection");
            if(!axesCollection)
            {
                axesCollection = [];
                this.set(position + "AxesCollection", axesCollection);
            }
            axesCollection.push(axis);
        },

        /**
         * Returns the default value for the `seriesCollection` attribute.
         *
         * @method _getDefaultSeriesCollection
         * @param {Array} val Array containing either `CartesianSeries` instances or objects containing data to construct series instances.
         * @return Array
         * @private
         */
        _getDefaultSeriesCollection: function()
        {
            var seriesCollection,
                dataProvider = this.get("dataProvider");
            if(dataProvider)
            {
                seriesCollection = this._parseSeriesCollection();
            }
            return seriesCollection;
        },

        /**
         * Parses and returns a series collection from an object and default properties.
         *
         * @method _parseSeriesCollection
         * @param {Object} val Object contain properties for series being set.
         * @return Object
         * @private
         */
        _parseSeriesCollection: function(val)
        {
            var dir = this.get("direction"),
                seriesStyles = this.get("styles").series,
                stylesAreArray = seriesStyles && Y_Lang.isArray(seriesStyles),
                stylesIndex,
                setStyles,
                globalStyles,
                sc = [],
                catAxis,
                valAxis,
                tempKeys = [],
                series,
                seriesKeys = this.get("seriesKeys").concat(),
                i,
                index,
                l,
                type = this.get("type"),
                key,
                catKey,
                seriesKey,
                graph,
                orphans = [],
                categoryKey = this.get("categoryKey"),
                showMarkers = this.get("showMarkers"),
                showAreaFill = this.get("showAreaFill"),
                showLines = this.get("showLines");
            val = val ? val.concat() : [];
            if(dir === "vertical")
            {
                catAxis = "yAxis";
                catKey = "yKey";
                valAxis = "xAxis";
                seriesKey = "xKey";
            }
            else
            {
                catAxis = "xAxis";
                catKey = "xKey";
                valAxis = "yAxis";
                seriesKey = "yKey";
            }
            l = val.length;
            while(val && val.length > 0)
            {
                series = val.shift();
                key = this._getBaseAttribute(series, seriesKey);
                if(key)
                {
                    index = Y.Array.indexOf(seriesKeys, key);
                    if(index > -1)
                    {
                        seriesKeys.splice(index, 1);
                        tempKeys.push(key);
                        sc.push(series);
                    }
                    else
                    {
                        orphans.push(series);
                    }
                }
                else
                {
                    orphans.push(series);
                }
            }
            while(orphans.length > 0)
            {
                series = orphans.shift();
                if(seriesKeys.length > 0)
                {
                    key = seriesKeys.shift();
                    this._setBaseAttribute(series, seriesKey, key);
                    tempKeys.push(key);
                    sc.push(series);
                }
                else if(series instanceof Y.CartesianSeries)
                {
                    series.destroy(true);
                }
            }
            if(seriesKeys.length > 0)
            {
                tempKeys = tempKeys.concat(seriesKeys);
            }
            l = tempKeys.length;
            for(i = 0; i < l; ++i)
            {
                series = sc[i] || {type:type};
                if(series instanceof Y.CartesianSeries)
                {
                    this._parseSeriesAxes(series);
                }
                else
                {
                    series[catKey] = series[catKey] || categoryKey;
                    series[seriesKey] = series[seriesKey] || seriesKeys.shift();
                    series[catAxis] = this._getCategoryAxis();
                    series[valAxis] = this._getSeriesAxis(series[seriesKey]);

                    series.type = series.type || type;
                    series.direction = series.direction || dir;

                    if(series.type === "combo" ||
                        series.type === "stackedcombo" ||
                        series.type === "combospline" ||
                        series.type === "stackedcombospline")
                    {
                        if(showAreaFill !== null)
                        {
                            series.showAreaFill = (series.showAreaFill !== null && series.showAreaFill !== undefined) ?
                                                   series.showAreaFill : showAreaFill;
                        }
                        if(showMarkers !== null)
                        {
                            series.showMarkers = (series.showMarkers !== null && series.showMarkers !== undefined) ? series.showMarkers : showMarkers;
                        }
                        if(showLines !== null)
                        {
                            series.showLines = (series.showLines !== null && series.showLines !== undefined) ? series.showLines : showLines;
                        }
                    }
                    if(seriesStyles)
                    {
                        stylesIndex = stylesAreArray ? i : series[seriesKey];
                        globalStyles = seriesStyles[stylesIndex];
                        if(globalStyles)
                        {
                            setStyles = series.styles;
                            if(setStyles)
                            {
                                series.styles = this._mergeStyles(setStyles, globalStyles);
                            }
                            else
                            {
                                series.styles = globalStyles;
                            }
                        }
                    }
                    sc[i] = series;
                }
            }
            if(sc)
            {
                graph = this.get("graph");
                graph.set("seriesCollection", sc);
                sc = graph.get("seriesCollection");
            }
            return sc;
        },

        /**
         * Parse and sets the axes for a series instance.
         *
         * @method _parseSeriesAxes
         * @param {CartesianSeries} series A `CartesianSeries` instance.
         * @private
         */
        _parseSeriesAxes: function(series)
        {
            var axes = this.get("axes"),
                xAxis = series.get("xAxis"),
                yAxis = series.get("yAxis"),
                YAxis = Y.Axis,
                axis;
            if(xAxis && !(xAxis instanceof YAxis) && Y_Lang.isString(xAxis) && axes.hasOwnProperty(xAxis))
            {
                axis = axes[xAxis];
                if(axis instanceof YAxis)
                {
                    series.set("xAxis", axis);
                }
            }
            if(yAxis && !(yAxis instanceof YAxis) && Y_Lang.isString(yAxis) && axes.hasOwnProperty(yAxis))
            {
                axis = axes[yAxis];
                if(axis instanceof YAxis)
                {
                    series.set("yAxis", axis);
                }
            }

        },

        /**
         * Returns the category axis instance for the chart.
         *
         * @method _getCategoryAxis
         * @return Axis
         * @private
         */
        _getCategoryAxis: function()
        {
            var axis,
                axes = this.get("axes"),
                categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey");
            axis = axes[categoryAxisName];
            return axis;
        },

        /**
         * Returns the value axis for a series.
         *
         * @method _getSeriesAxis
         * @param {String} key The key value used to determine the axis instance.
         * @return Axis
         * @private
         */
        _getSeriesAxis:function(key, axisName)
        {
            var axes = this.get("axes"),
                i,
                keys,
                axis;
            if(axes)
            {
                if(axisName && axes.hasOwnProperty(axisName))
                {
                    axis = axes[axisName];
                }
                else
                {
                    for(i in axes)
                    {
                        if(axes.hasOwnProperty(i))
                        {
                            keys = axes[i].get("keys");
                            if(keys && keys.hasOwnProperty(key))
                            {
                                axis = axes[i];
                                break;
                            }
                        }
                    }
                }
            }
            return axis;
        },

        /**
         * Gets an attribute from an object, using a getter for Base objects and a property for object
         * literals. Used for determining attributes from series/axis references which can be an actual class instance
         * or a hash of properties that will be used to create a class instance.
         *
         * @method _getBaseAttribute
         * @param {Object} item Object or instance in which the attribute resides.
         * @param {String} key Attribute whose value will be returned.
         * @return Object
         * @private
         */
        _getBaseAttribute: function(item, key)
        {
            if(item instanceof Y.Base)
            {
                return item.get(key);
            }
            if(item.hasOwnProperty(key))
            {
                return item[key];
            }
            return null;
        },

        /**
         * Sets an attribute on an object, using a setter of Base objects and a property for object
         * literals. Used for setting attributes on a Base class, either directly or to be stored in an object literal
         * for use at instantiation.
         *
         * @method _setBaseAttribute
         * @param {Object} item Object or instance in which the attribute resides.
         * @param {String} key Attribute whose value will be assigned.
         * @param {Object} value Value to be assigned to the attribute.
         * @private
         */
        _setBaseAttribute: function(item, key, value)
        {
            if(item instanceof Y.Base)
            {
                item.set(key, value);
            }
            else
            {
                item[key] = value;
            }
        },

        /**
         * Creates `Axis` instances.
         *
         * @method _setAxes
         * @param {Object} val Object containing `Axis` instances or objects in which to construct `Axis` instances.
         * @return Object
         * @private
         */
        _setAxes: function(val)
        {
            var hash = this._parseAxes(val),
                axes = {},
                axesAttrs = {
                    edgeOffset: "edgeOffset",
                    calculateEdgeOffset: "calculateEdgeOffset",
                    position: "position",
                    overlapGraph:"overlapGraph",
                    labelValues: "labelValues",
                    hideFirstMajorUnit: "hideFirstMajorUnit",
                    hideLastMajorUnit: "hideLastMajorUnit",
                    labelFunction:"labelFunction",
                    labelFunctionScope:"labelFunctionScope",
                    labelFormat:"labelFormat",
                    appendLabelFunction: "appendLabelFunction",
                    appendTitleFunction: "appendTitleFunction",
                    maximum:"maximum",
                    minimum:"minimum",
                    roundingMethod:"roundingMethod",
                    alwaysShowZero:"alwaysShowZero",
                    title:"title",
                    width:"width",
                    height:"height"
                },
                dp = this.get("dataProvider"),
                ai,
                i,
                pos,
                axis,
                axisPosition,
                dh,
                AxisClass,
                config,
                axesCollection;
            for(i in hash)
            {
                if(hash.hasOwnProperty(i))
                {
                    dh = hash[i];
                    if(dh instanceof Y.Axis)
                    {
                        axis = dh;
                    }
                    else
                    {
                        axis = null;
                        config = {};
                        config.dataProvider = dh.dataProvider || dp;
                        config.keys = dh.keys;

                        if(dh.hasOwnProperty("roundingUnit"))
                        {
                            config.roundingUnit = dh.roundingUnit;
                        }
                        pos = dh.position;
                        if(dh.styles)
                        {
                            config.styles = dh.styles;
                        }
                        config.position = dh.position;
                        for(ai in axesAttrs)
                        {
                            if(axesAttrs.hasOwnProperty(ai) && dh.hasOwnProperty(ai))
                            {
                                config[ai] = dh[ai];
                            }
                        }

                        //only check for existing axis if we constructed the default axes already
                        if(val)
                        {
                            axis = this.getAxisByKey(i);
                        }

                        if(axis && axis instanceof Y.Axis)
                        {
                            axisPosition = axis.get("position");
                            if(pos !== axisPosition)
                            {
                                if(axisPosition !== "none")
                                {
                                    axesCollection = this.get(axisPosition + "AxesCollection");
                                    axesCollection.splice(Y.Array.indexOf(axesCollection, axis), 1);
                                }
                                if(pos !== "none")
                                {
                                    this._addToAxesCollection(pos, axis);
                                }
                            }
                            axis.setAttrs(config);
                        }
                        else
                        {
                            AxisClass = this._getAxisClass(dh.type);
                            axis = new AxisClass(config);
                            axis.after("axisRendered", Y.bind(this._itemRendered, this));
                        }
                    }

                    if(axis)
                    {
                        axesCollection = this.get(pos + "AxesCollection");
                        if(axesCollection && Y.Array.indexOf(axesCollection, axis) > 0)
                        {
                            axis.set("overlapGraph", false);
                        }
                        axes[i] = axis;
                    }
                }
            }
            return axes;
        },

        /**
         * Adds axes to the chart.
         *
         * @method _addAxes
         * @private
         */
        _addAxes: function()
        {
            var axes = this.get("axes"),
                i,
                axis,
                pos,
                w = this.get("width"),
                h = this.get("height"),
                node = Y.Node.one(this._parentNode);
            if(!this._axesCollection)
            {
                this._axesCollection = [];
            }
            for(i in axes)
            {
                if(axes.hasOwnProperty(i))
                {
                    axis = axes[i];
                    if(axis instanceof Y.Axis)
                    {
                        if(!w)
                        {
                            this.set("width", node.get("offsetWidth"));
                            w = this.get("width");
                        }
                        if(!h)
                        {
                            this.set("height", node.get("offsetHeight"));
                            h = this.get("height");
                        }
                        this._addToAxesRenderQueue(axis);
                        pos = axis.get("position");
                        if(!this.get(pos + "AxesCollection"))
                        {
                            this.set(pos + "AxesCollection", [axis]);
                        }
                        else
                        {
                            this.get(pos + "AxesCollection").push(axis);
                        }
                        this._axesCollection.push(axis);
                        if(axis.get("keys").hasOwnProperty(this.get("categoryKey")))
                        {
                            this.set("categoryAxis", axis);
                        }
                        axis.render(this.get("contentBox"));
                    }
                }
            }
        },

        /**
         * Renders the Graph.
         *
         * @method _addSeries
         * @private
         */
        _addSeries: function()
        {
            var graph = this.get("graph");
            graph.render(this.get("contentBox"));

        },

        /**
         * Adds gridlines to the chart.
         *
         * @method _addGridlines
         * @private
         */
        _addGridlines: function()
        {
            var graph = this.get("graph"),
                hgl = this.get("horizontalGridlines"),
                vgl = this.get("verticalGridlines"),
                direction = this.get("direction"),
                leftAxesCollection = this.get("leftAxesCollection"),
                rightAxesCollection = this.get("rightAxesCollection"),
                bottomAxesCollection = this.get("bottomAxesCollection"),
                topAxesCollection = this.get("topAxesCollection"),
                seriesAxesCollection,
                catAxis = this.get("categoryAxis"),
                hAxis,
                vAxis;
            if(this._axesCollection)
            {
                seriesAxesCollection = this._axesCollection.concat();
                seriesAxesCollection.splice(Y.Array.indexOf(seriesAxesCollection, catAxis), 1);
            }
            if(hgl)
            {
                if(leftAxesCollection && leftAxesCollection[0])
                {
                    hAxis = leftAxesCollection[0];
                }
                else if(rightAxesCollection && rightAxesCollection[0])
                {
                    hAxis = rightAxesCollection[0];
                }
                else
                {
                    hAxis = direction === "horizontal" ? catAxis : seriesAxesCollection[0];
                }
                if(!this._getBaseAttribute(hgl, "axis") && hAxis)
                {
                    this._setBaseAttribute(hgl, "axis", hAxis);
                }
                if(this._getBaseAttribute(hgl, "axis"))
                {
                    graph.set("horizontalGridlines", hgl);
                }
            }
            if(vgl)
            {
                if(bottomAxesCollection && bottomAxesCollection[0])
                {
                    vAxis = bottomAxesCollection[0];
                }
                else if (topAxesCollection && topAxesCollection[0])
                {
                    vAxis = topAxesCollection[0];
                }
                else
                {
                    vAxis = direction === "vertical" ? catAxis : seriesAxesCollection[0];
                }
                if(!this._getBaseAttribute(vgl, "axis") && vAxis)
                {
                    this._setBaseAttribute(vgl, "axis", vAxis);
                }
                if(this._getBaseAttribute(vgl, "axis"))
                {
                    graph.set("verticalGridlines", vgl);
                }
            }
        },

        /**
         * Default Function for the axes attribute.
         *
         * @method _getDefaultAxes
         * @return Object
         * @private
         */
        _getDefaultAxes: function()
        {
            var axes;
            if(this.get("dataProvider"))
            {
                axes = this._parseAxes();
            }
            return axes;
        },

        /**
         * Generates and returns a key-indexed object containing `Axis` instances or objects used to create `Axis` instances.
         *
         * @method _parseAxes
         * @param {Object} axes Object containing `Axis` instances or `Axis` attributes.
         * @return Object
         * @private
         */
        _parseAxes: function(axes)
        {
            var catKey = this.get("categoryKey"),
                axis,
                attr,
                keys,
                newAxes = {},
                claimedKeys = [],
                newKeys = [],
                categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"),
                valueAxisName = this.get("valueAxisName"),
                seriesKeys = this.get("seriesKeys").concat(),
                i,
                l,
                ii,
                ll,
                cIndex,
                direction = this.get("direction"),
                seriesPosition,
                categoryPosition,
                valueAxes = [],
                seriesAxis = this.get("stacked") ? "stacked" : "numeric";
            if(direction === "vertical")
            {
                seriesPosition = "bottom";
                categoryPosition = "left";
            }
            else
            {
                seriesPosition = "left";
                categoryPosition = "bottom";
            }
            if(axes)
            {
                for(i in axes)
                {
                    if(axes.hasOwnProperty(i))
                    {
                        axis = axes[i];
                        keys = this._getBaseAttribute(axis, "keys");
                        attr = this._getBaseAttribute(axis, "type");
                        if(attr === "time" || attr === "category")
                        {
                            categoryAxisName = i;
                            this.set("categoryAxisName", i);
                            if(Y_Lang.isArray(keys) && keys.length > 0)
                            {
                                catKey = keys[0];
                                this.set("categoryKey", catKey);
                            }
                            newAxes[i] = axis;
                        }
                        else if(i === categoryAxisName)
                        {
                            newAxes[i] = axis;
                        }
                        else
                        {
                            newAxes[i] = axis;
                            if(i !== valueAxisName && keys && Y_Lang.isArray(keys))
                            {
                                ll = keys.length;
                                for(ii = 0; ii < ll; ++ii)
                                {
                                    claimedKeys.push(keys[ii]);
                                }
                                valueAxes.push(newAxes[i]);
                            }
                            if(!(this._getBaseAttribute(newAxes[i], "type")))
                            {
                                this._setBaseAttribute(newAxes[i], "type", seriesAxis);
                            }
                            if(!(this._getBaseAttribute(newAxes[i], "position")))
                            {
                                this._setBaseAttribute(
                                    newAxes[i],
                                    "position",
                                    this._getDefaultAxisPosition(newAxes[i], valueAxes, seriesPosition)
                                );
                            }
                        }
                    }
                }
            }
            cIndex = Y.Array.indexOf(seriesKeys, catKey);
            if(cIndex > -1)
            {
                seriesKeys.splice(cIndex, 1);
            }
            l = seriesKeys.length;
            for(i = 0; i < l; ++i)
            {
                cIndex = Y.Array.indexOf(claimedKeys, seriesKeys[i]);
                if(cIndex > -1)
                {
                    newKeys = newKeys.concat(claimedKeys.splice(cIndex, 1));
                }
            }
            claimedKeys = newKeys.concat(claimedKeys);
            l = claimedKeys.length;
            for(i = 0; i < l; i = i + 1)
            {
                cIndex = Y.Array.indexOf(seriesKeys, claimedKeys[i]);
                if(cIndex > -1)
                {
                    seriesKeys.splice(cIndex, 1);
                }
            }
            if(!newAxes.hasOwnProperty(categoryAxisName))
            {
                newAxes[categoryAxisName] = {};
            }
            if(!(this._getBaseAttribute(newAxes[categoryAxisName], "keys")))
            {
                this._setBaseAttribute(newAxes[categoryAxisName], "keys", [catKey]);
            }

            if(!(this._getBaseAttribute(newAxes[categoryAxisName], "position")))
            {
                this._setBaseAttribute(newAxes[categoryAxisName], "position", categoryPosition);
            }

            if(!(this._getBaseAttribute(newAxes[categoryAxisName], "type")))
            {
                this._setBaseAttribute(newAxes[categoryAxisName], "type", this.get("categoryType"));
            }
            if(!newAxes.hasOwnProperty(valueAxisName) && seriesKeys && seriesKeys.length > 0)
            {
                newAxes[valueAxisName] = {keys:seriesKeys};
                valueAxes.push(newAxes[valueAxisName]);
            }
            if(claimedKeys.length > 0)
            {
                if(seriesKeys.length > 0)
                {
                    seriesKeys = claimedKeys.concat(seriesKeys);
                }
                else
                {
                    seriesKeys = claimedKeys;
                }
            }
            if(newAxes.hasOwnProperty(valueAxisName))
            {
                if(!(this._getBaseAttribute(newAxes[valueAxisName], "position")))
                {
                    this._setBaseAttribute(
                        newAxes[valueAxisName],
                        "position",
                        this._getDefaultAxisPosition(newAxes[valueAxisName], valueAxes, seriesPosition)
                    );
                }
                this._setBaseAttribute(newAxes[valueAxisName], "type", seriesAxis);
                this._setBaseAttribute(newAxes[valueAxisName], "keys", seriesKeys);
            }
            if(!this._seriesKeysExplicitlySet)
            {
                this.set("seriesKeys", seriesKeys, {src: "internal"});
            }
            return newAxes;
        },

        /**
         * Determines the position of an axis when one is not specified.
         *
         * @method _getDefaultAxisPosition
         * @param {Axis} axis `Axis` instance.
         * @param {Array} valueAxes Array of `Axis` instances.
         * @param {String} position Default position depending on the direction of the chart and type of axis.
         * @return String
         * @private
         */
        _getDefaultAxisPosition: function(axis, valueAxes, position)
        {
            var direction = this.get("direction"),
                i = Y.Array.indexOf(valueAxes, axis);

            if(valueAxes[i - 1] && valueAxes[i - 1].position)
            {
                if(direction === "horizontal")
                {
                    if(valueAxes[i - 1].position === "left")
                    {
                        position = "right";
                    }
                    else if(valueAxes[i - 1].position === "right")
                    {
                        position = "left";
                    }
                }
                else
                {
                    if (valueAxes[i -1].position === "bottom")
                    {
                        position = "top";
                    }
                    else
                    {
                        position = "bottom";
                    }
                }
            }
            return position;
        },

        /**
         * Returns an object literal containing a categoryItem and a valueItem for a given series index. Below is the structure of each:
         *
         * @method getSeriesItems
         * @param {CartesianSeries} series Reference to a series.
         * @param {Number} index Index of the specified item within a series.
         * @return Object An object literal containing the following:
         *
         *  <dl>
         *      <dt>categoryItem</dt><dd>Object containing the following data related to the category axis of the series.
         *  <dl>
         *      <dt>axis</dt><dd>Reference to the category axis of the series.</dd>
         *      <dt>key</dt><dd>Category key for the series.</dd>
         *      <dt>value</dt><dd>Value on the axis corresponding to the series index.</dd>
         *  </dl>
         *      </dd>
         *      <dt>valueItem</dt><dd>Object containing the following data related to the category axis of the series.
         *  <dl>
         *      <dt>axis</dt><dd>Reference to the value axis of the series.</dd>
         *      <dt>key</dt><dd>Value key for the series.</dd>
         *      <dt>value</dt><dd>Value on the axis corresponding to the series index.</dd>
         *  </dl>
         *      </dd>
         *  </dl>
         */
        getSeriesItems: function(series, index)
        {
            var xAxis = series.get("xAxis"),
                yAxis = series.get("yAxis"),
                xKey = series.get("xKey"),
                yKey = series.get("yKey"),
                categoryItem,
                valueItem;
            if(this.get("direction") === "vertical")
            {
                categoryItem = {
                    axis:yAxis,
                    key:yKey,
                    value:yAxis.getKeyValueAt(yKey, index)
                };
                valueItem = {
                    axis:xAxis,
                    key:xKey,
                    value: xAxis.getKeyValueAt(xKey, index)
                };
            }
            else
            {
                valueItem = {
                    axis:yAxis,
                    key:yKey,
                    value:yAxis.getKeyValueAt(yKey, index)
                };
                categoryItem = {
                    axis:xAxis,
                    key:xKey,
                    value: xAxis.getKeyValueAt(xKey, index)
                };
            }
            categoryItem.displayName = series.get("categoryDisplayName");
            valueItem.displayName = series.get("valueDisplayName");
            categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index);
            valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index);
            return {category:categoryItem, value:valueItem};
        },

        /**
         * Handler for sizeChanged event.
         *
         * @method _sizeChanged
         * @param {Object} e Event object.
         * @private
         */
        _sizeChanged: function()
        {
            if(this._axesCollection)
            {
                var ac = this._axesCollection,
                    i = 0,
                    l = ac.length;
                for(; i < l; ++i)
                {
                    this._addToAxesRenderQueue(ac[i]);
                }
                this._redraw();
            }
        },

        /**
         * Returns the maximum distance in pixels that the extends outside the top bounds of all vertical axes.
         *
         * @method _getTopOverflow
         * @param {Array} set1 Collection of axes to check.
         * @param {Array} set2 Seconf collection of axes to check.
         * @param {Number} width Width of the axes
         * @return Number
         * @private
         */
        _getTopOverflow: function(set1, set2, height)
        {
            var i = 0,
                len,
                overflow = 0,
                axis;
            if(set1)
            {
                len = set1.length;
                for(; i < len; ++i)
                {
                    axis = set1[i];
                    overflow = Math.max(
                        overflow,
                        Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
                    );
                }
            }
            if(set2)
            {
                i = 0;
                len = set2.length;
                for(; i < len; ++i)
                {
                    axis = set2[i];
                    overflow = Math.max(
                        overflow,
                        Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
                    );
                }
            }
            return overflow;
        },

        /**
         * Returns the maximum distance in pixels that the extends outside the right bounds of all horizontal axes.
         *
         * @method _getRightOverflow
         * @param {Array} set1 Collection of axes to check.
         * @param {Array} set2 Seconf collection of axes to check.
         * @param {Number} width Width of the axes
         * @return Number
         * @private
         */
        _getRightOverflow: function(set1, set2, width)
        {
            var i = 0,
                len,
                overflow = 0,
                axis;
            if(set1)
            {
                len = set1.length;
                for(; i < len; ++i)
                {
                    axis = set1[i];
                    overflow = Math.max(
                        overflow,
                        axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
                    );
                }
            }
            if(set2)
            {
                i = 0;
                len = set2.length;
                for(; i < len; ++i)
                {
                    axis = set2[i];
                    overflow = Math.max(
                        overflow,
                        axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
                    );
                }
            }
            return overflow;
        },

        /**
         * Returns the maximum distance in pixels that the extends outside the left bounds of all horizontal axes.
         *
         * @method _getLeftOverflow
         * @param {Array} set1 Collection of axes to check.
         * @param {Array} set2 Seconf collection of axes to check.
         * @param {Number} width Width of the axes
         * @return Number
         * @private
         */
        _getLeftOverflow: function(set1, set2, width)
        {
            var i = 0,
                len,
                overflow = 0,
                axis;
            if(set1)
            {
                len = set1.length;
                for(; i < len; ++i)
                {
                    axis = set1[i];
                    overflow = Math.max(
                        overflow,
                        Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
                    );
                }
            }
            if(set2)
            {
                i = 0;
                len = set2.length;
                for(; i < len; ++i)
                {
                    axis = set2[i];
                    overflow = Math.max(
                        overflow,
                        Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
                    );
                }
            }
            return overflow;
        },

        /**
         * Returns the maximum distance in pixels that the extends outside the bottom bounds of all vertical axes.
         *
         * @method _getBottomOverflow
         * @param {Array} set1 Collection of axes to check.
         * @param {Array} set2 Seconf collection of axes to check.
         * @param {Number} height Height of the axes
         * @return Number
         * @private
         */
        _getBottomOverflow: function(set1, set2, height)
        {
            var i = 0,
                len,
                overflow = 0,
                axis;
            if(set1)
            {
                len = set1.length;
                for(; i < len; ++i)
                {
                    axis = set1[i];
                    overflow = Math.max(
                        overflow,
                        axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
                    );
                }
            }
            if(set2)
            {
                i = 0;
                len = set2.length;
                for(; i < len; ++i)
                {
                    axis = set2[i];
                    overflow = Math.max(
                        overflow,
                        axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
                    );
                }
            }
            return overflow;
        },

        /**
         * Redraws and position all the components of the chart instance.
         *
         * @method _redraw
         * @private
         */
        _redraw: function()
        {
            if(this._drawing)
            {
                this._callLater = true;
                return;
            }
            this._drawing = true;
            this._callLater = false;
            var w = this.get("width"),
                h = this.get("height"),
                leftPaneWidth = 0,
                rightPaneWidth = 0,
                topPaneHeight = 0,
                bottomPaneHeight = 0,
                leftAxesCollection = this.get("leftAxesCollection"),
                rightAxesCollection = this.get("rightAxesCollection"),
                topAxesCollection = this.get("topAxesCollection"),
                bottomAxesCollection = this.get("bottomAxesCollection"),
                i = 0,
                l,
                axis,
                graphOverflow = "visible",
                graph = this.get("graph"),
                topOverflow,
                bottomOverflow,
                leftOverflow,
                rightOverflow,
                graphWidth,
                graphHeight,
                graphX,
                graphY,
                allowContentOverflow = this.get("allowContentOverflow"),
                diff,
                rightAxesXCoords,
                leftAxesXCoords,
                topAxesYCoords,
                bottomAxesYCoords,
                graphRect = {};
            if(leftAxesCollection)
            {
                leftAxesXCoords = [];
                l = leftAxesCollection.length;
                for(i = l - 1; i > -1; --i)
                {
                    leftAxesXCoords.unshift(leftPaneWidth);
                    leftPaneWidth += leftAxesCollection[i].get("width");
                }
            }
            if(rightAxesCollection)
            {
                rightAxesXCoords = [];
                l = rightAxesCollection.length;
                i = 0;
                for(i = l - 1; i > -1; --i)
                {
                    rightPaneWidth += rightAxesCollection[i].get("width");
                    rightAxesXCoords.unshift(w - rightPaneWidth);
                }
            }
            if(topAxesCollection)
            {
                topAxesYCoords = [];
                l = topAxesCollection.length;
                for(i = l - 1; i > -1; --i)
                {
                    topAxesYCoords.unshift(topPaneHeight);
                    topPaneHeight += topAxesCollection[i].get("height");
                }
            }
            if(bottomAxesCollection)
            {
                bottomAxesYCoords = [];
                l = bottomAxesCollection.length;
                for(i = l - 1; i > -1; --i)
                {
                    bottomPaneHeight += bottomAxesCollection[i].get("height");
                    bottomAxesYCoords.unshift(h - bottomPaneHeight);
                }
            }

            graphWidth = w - (leftPaneWidth + rightPaneWidth);
            graphHeight = h - (bottomPaneHeight + topPaneHeight);
            graphRect.left = leftPaneWidth;
            graphRect.top = topPaneHeight;
            graphRect.bottom = h - bottomPaneHeight;
            graphRect.right = w - rightPaneWidth;
            if(!allowContentOverflow)
            {
                topOverflow = this._getTopOverflow(leftAxesCollection, rightAxesCollection);
                bottomOverflow = this._getBottomOverflow(leftAxesCollection, rightAxesCollection);
                leftOverflow = this._getLeftOverflow(bottomAxesCollection, topAxesCollection);
                rightOverflow = this._getRightOverflow(bottomAxesCollection, topAxesCollection);

                diff = topOverflow - topPaneHeight;
                if(diff > 0)
                {
                    graphRect.top = topOverflow;
                    if(topAxesYCoords)
                    {
                        i = 0;
                        l = topAxesYCoords.length;
                        for(; i < l; ++i)
                        {
                            topAxesYCoords[i] += diff;
                        }
                    }
                }

                diff = bottomOverflow - bottomPaneHeight;
                if(diff > 0)
                {
                    graphRect.bottom = h - bottomOverflow;
                    if(bottomAxesYCoords)
                    {
                        i = 0;
                        l = bottomAxesYCoords.length;
                        for(; i < l; ++i)
                        {
                            bottomAxesYCoords[i] -= diff;
                        }
                    }
                }

                diff = leftOverflow - leftPaneWidth;
                if(diff > 0)
                {
                    graphRect.left = leftOverflow;
                    if(leftAxesXCoords)
                    {
                        i = 0;
                        l = leftAxesXCoords.length;
                        for(; i < l; ++i)
                        {
                            leftAxesXCoords[i] += diff;
                        }
                    }
                }

                diff = rightOverflow - rightPaneWidth;
                if(diff > 0)
                {
                    graphRect.right = w - rightOverflow;
                    if(rightAxesXCoords)
                    {
                        i = 0;
                        l = rightAxesXCoords.length;
                        for(; i < l; ++i)
                        {
                            rightAxesXCoords[i] -= diff;
                        }
                    }
                }
            }
            graphWidth = graphRect.right - graphRect.left;
            graphHeight = graphRect.bottom - graphRect.top;
            graphX = graphRect.left;
            graphY = graphRect.top;
            if(topAxesCollection)
            {
                l = topAxesCollection.length;
                i = 0;
                for(; i < l; i++)
                {
                    axis = topAxesCollection[i];
                    if(axis.get("width") !== graphWidth)
                    {
                        axis.set("width", graphWidth);
                    }
                    axis.get("boundingBox").setStyle("left", graphX + "px");
                    axis.get("boundingBox").setStyle("top", topAxesYCoords[i] + "px");
                }
                if(axis._hasDataOverflow())
                {
                    graphOverflow = "hidden";
                }
            }
            if(bottomAxesCollection)
            {
                l = bottomAxesCollection.length;
                i = 0;
                for(; i < l; i++)
                {
                    axis = bottomAxesCollection[i];
                    if(axis.get("width") !== graphWidth)
                    {
                        axis.set("width", graphWidth);
                    }
                    axis.get("boundingBox").setStyle("left", graphX + "px");
                    axis.get("boundingBox").setStyle("top", bottomAxesYCoords[i] + "px");
                }
                if(axis._hasDataOverflow())
                {
                    graphOverflow = "hidden";
                }
            }
            if(leftAxesCollection)
            {
                l = leftAxesCollection.length;
                i = 0;
                for(; i < l; ++i)
                {
                    axis = leftAxesCollection[i];
                    axis.get("boundingBox").setStyle("top", graphY + "px");
                    axis.get("boundingBox").setStyle("left", leftAxesXCoords[i] + "px");
                    if(axis.get("height") !== graphHeight)
                    {
                        axis.set("height", graphHeight);
                    }
                }
                if(axis._hasDataOverflow())
                {
                    graphOverflow = "hidden";
                }
            }
            if(rightAxesCollection)
            {
                l = rightAxesCollection.length;
                i = 0;
                for(; i < l; ++i)
                {
                    axis = rightAxesCollection[i];
                    axis.get("boundingBox").setStyle("top", graphY + "px");
                    axis.get("boundingBox").setStyle("left", rightAxesXCoords[i] + "px");
                    if(axis.get("height") !== graphHeight)
                    {
                        axis.set("height", graphHeight);
                    }
                }
                if(axis._hasDataOverflow())
                {
                    graphOverflow = "hidden";
                }
            }
            this._drawing = false;
            if(this._callLater)
            {
                this._redraw();
                return;
            }
            if(graph)
            {
                graph.get("boundingBox").setStyle("left", graphX + "px");
                graph.get("boundingBox").setStyle("top", graphY + "px");
                graph.set("width", graphWidth);
                graph.set("height", graphHeight);
                graph.get("boundingBox").setStyle("overflow", graphOverflow);
            }

            if(this._overlay)
            {
                this._overlay.setStyle("left", graphX + "px");
                this._overlay.setStyle("top", graphY + "px");
                this._overlay.setStyle("width", graphWidth + "px");
                this._overlay.setStyle("height", graphHeight + "px");
            }
        },

        /**
         * Destructor implementation for the CartesianChart class. Calls destroy on all axes, series and the Graph instance.
         * Removes the tooltip and overlay HTML elements.
         *
         * @method destructor
         * @protected
         */
        destructor: function()
        {
            var graph = this.get("graph"),
                i = 0,
                len,
                seriesCollection = this.get("seriesCollection"),
                axesCollection = this._axesCollection,
                tooltip = this.get("tooltip").node;
            if(this._description)
            {
                this._description.empty();
                this._description.remove(true);
            }
            if(this._liveRegion)
            {
                this._liveRegion.empty();
                this._liveRegion.remove(true);
            }
            len = seriesCollection ? seriesCollection.length : 0;
            for(; i < len; ++i)
            {
                if(seriesCollection[i] instanceof Y.CartesianSeries)
                {
                    seriesCollection[i].destroy(true);
                }
            }
            len = axesCollection ? axesCollection.length : 0;
            for(i = 0; i < len; ++i)
            {
                if(axesCollection[i] instanceof Y.Axis)
                {
                    axesCollection[i].destroy(true);
                }
            }
            if(graph)
            {
                graph.destroy(true);
            }
            if(tooltip)
            {
                tooltip.empty();
                tooltip.remove(true);
            }
            if(this._overlay)
            {
                this._overlay.empty();
                this._overlay.remove(true);
            }
        },

        /**
         * Returns the appropriate message based on the key press.
         *
         * @method _getAriaMessage
         * @param {Number} key The keycode that was pressed.
         * @return String
         */
        _getAriaMessage: function(key)
        {
            var msg = "",
                series,
                items,
                categoryItem,
                valueItem,
                seriesIndex = this._seriesIndex,
                itemIndex = this._itemIndex,
                seriesCollection = this.get("seriesCollection"),
                len = seriesCollection.length,
                dataLength;
            if(key % 2 === 0)
            {
                if(len > 1)
                {
                    if(key === 38)
                    {
                        seriesIndex = seriesIndex < 1 ? len - 1 : seriesIndex - 1;
                    }
                    else if(key === 40)
                    {
                        seriesIndex = seriesIndex >= len - 1 ? 0 : seriesIndex + 1;
                    }
                    this._itemIndex = -1;
                }
                else
                {
                    seriesIndex = 0;
                }
                this._seriesIndex = seriesIndex;
                series = this.getSeries(parseInt(seriesIndex, 10));
                msg = series.get("valueDisplayName") + " series.";
            }
            else
            {
                if(seriesIndex > -1)
                {
                    msg = "";
                    series = this.getSeries(parseInt(seriesIndex, 10));
                }
                else
                {
                    seriesIndex = 0;
                    this._seriesIndex = seriesIndex;
                    series = this.getSeries(parseInt(seriesIndex, 10));
                    msg = series.get("valueDisplayName") + " series.";
                }
                dataLength = series._dataLength ? series._dataLength : 0;
                if(key === 37)
                {
                    itemIndex = itemIndex > 0 ? itemIndex - 1 : dataLength - 1;
                }
                else if(key === 39)
                {
                    itemIndex = itemIndex >= dataLength - 1 ? 0 : itemIndex + 1;
                }
                this._itemIndex = itemIndex;
                items = this.getSeriesItems(series, itemIndex);
                categoryItem = items.category;
                valueItem = items.value;
                if(categoryItem && valueItem && categoryItem.value && valueItem.value)
                {
                    msg += categoryItem.displayName +
                        ": " +
                        categoryItem.axis.formatLabel.apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) +
                        ", ";
                    msg += valueItem.displayName +
                        ": " +
                        valueItem.axis.formatLabel.apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]) +
                        ", ";
                }
               else
                {
                    msg += "No data available.";
                }
                msg += (itemIndex + 1) + " of " + dataLength + ". ";
            }
            return msg;
        }
    }, {
        ATTRS: {
            /**
             * Indicates whether axis labels are allowed to overflow beyond the bounds of the chart's content box.
             *
             * @attribute allowContentOverflow
             * @type Boolean
             */
            allowContentOverflow: {
                value: false
            },

            /**
             * Style object for the axes.
             *
             * @attribute axesStyles
             * @type Object
             * @private
             */
            axesStyles: {
                lazyAdd: false,

                getter: function()
                {
                    var axes = this.get("axes"),
                        i,
                        styles = this._axesStyles;
                    if(axes)
                    {
                        for(i in axes)
                        {
                            if(axes.hasOwnProperty(i) && axes[i] instanceof Y.Axis)
                            {
                                if(!styles)
                                {
                                    styles = {};
                                }
                                styles[i] = axes[i].get("styles");
                            }
                        }
                    }
                    return styles;
                },

                setter: function(val)
                {
                    var axes = this.get("axes"),
                        i;
                    for(i in val)
                    {
                        if(val.hasOwnProperty(i) && axes.hasOwnProperty(i))
                        {
                            this._setBaseAttribute(axes[i], "styles", val[i]);
                        }
                    }
                    return val;
                }
            },

            /**
             * Style object for the series
             *
             * @attribute seriesStyles
             * @type Object
             * @private
             */
            seriesStyles: {
                lazyAdd: false,

                getter: function()
                {
                    var styles = this._seriesStyles,
                        graph = this.get("graph"),
                        dict,
                        i;
                    if(graph)
                    {
                        dict = graph.get("seriesDictionary");
                        if(dict)
                        {
                            styles = {};
                            for(i in dict)
                            {
                                if(dict.hasOwnProperty(i))
                                {
                                    styles[i] = dict[i].get("styles");
                                }
                            }
                        }
                    }
                    return styles;
                },

                setter: function(val)
                {
                    var i,
                        l,
                        s;

                    if(Y_Lang.isArray(val))
                    {
                        s = this.get("seriesCollection");
                        i = 0;
                        l = val.length;

                        for(; i < l; ++i)
                        {
                            this._setBaseAttribute(s[i], "styles", val[i]);
                        }
                    }
                    else
                    {
                        for(i in val)
                        {
                            if(val.hasOwnProperty(i))
                            {
                                s = this.getSeries(i);
                                this._setBaseAttribute(s, "styles", val[i]);
                            }
                        }
                    }
                    return val;
                }
            },

            /**
             * Styles for the graph.
             *
             * @attribute graphStyles
             * @type Object
             * @private
             */
            graphStyles: {
                lazyAdd: false,

                getter: function()
                {
                    var graph = this.get("graph");
                    if(graph)
                    {
                        return(graph.get("styles"));
                    }
                    return this._graphStyles;
                },

                setter: function(val)
                {
                    var graph = this.get("graph");
                    this._setBaseAttribute(graph, "styles", val);
                    return val;
                }

            },

            /**
             * Style properties for the chart. Contains a key indexed hash of the following:
             *  <dl>
             *      <dt>series</dt><dd>A key indexed hash containing references to the `styles` attribute for each series in the chart.
             *      Specific style attributes vary depending on the series:
             *      <ul>
             *          <li><a href="AreaSeries.html#attr_styles">AreaSeries</a></li>
             *          <li><a href="BarSeries.html#attr_styles">BarSeries</a></li>
             *          <li><a href="ColumnSeries.html#attr_styles">ColumnSeries</a></li>
             *          <li><a href="ComboSeries.html#attr_styles">ComboSeries</a></li>
             *          <li><a href="LineSeries.html#attr_styles">LineSeries</a></li>
             *          <li><a href="MarkerSeries.html#attr_styles">MarkerSeries</a></li>
             *          <li><a href="SplineSeries.html#attr_styles">SplineSeries</a></li>
             *      </ul>
             *      </dd>
             *      <dt>axes</dt><dd>A key indexed hash containing references to the `styles` attribute for each axes in the chart. Specific
             *      style attributes can be found in the <a href="Axis.html#attr_styles">Axis</a> class.</dd>
             *      <dt>graph</dt><dd>A reference to the `styles` attribute in the chart. Specific style attributes can be found in the
             *      <a href="Graph.html#attr_styles">Graph</a> class.</dd>
             *  </dl>
             *
             * @attribute styles
             * @type Object
             */
            styles: {
                lazyAdd: false,

                getter: function()
                {
                    var styles = {
                        axes: this.get("axesStyles"),
                        series: this.get("seriesStyles"),
                        graph: this.get("graphStyles")
                    };
                    return styles;
                },
                setter: function(val)
                {
                    if(val.hasOwnProperty("axes"))
                    {
                        if(this.get("axesStyles"))
                        {
                            this.set("axesStyles", val.axes);
                        }
                        else
                        {
                            this._axesStyles = val.axes;
                        }
                    }
                    if(val.hasOwnProperty("series"))
                    {
                        if(this.get("seriesStyles"))
                        {
                            this.set("seriesStyles", val.series);
                        }
                        else
                        {
                            this._seriesStyles = val.series;
                        }
                    }
                    if(val.hasOwnProperty("graph"))
                    {
                        this.set("graphStyles", val.graph);
                    }
                }
            },

            /**
             * Axes to appear in the chart. This can be a key indexed hash of axis instances or object literals
             * used to construct the appropriate axes.
             *
             * @attribute axes
             * @type Object
             */
            axes: {
                lazyAdd: false,

                valueFn: "_getDefaultAxes",

                setter: function(val)
                {
                    if(this.get("dataProvider"))
                    {
                        val = this._setAxes(val);
                    }
                    return val;
                }
            },

            /**
             * Collection of series to appear on the chart. This can be an array of Series instances or object literals
             * used to construct the appropriate series.
             *
             * @attribute seriesCollection
             * @type Array
             */
            seriesCollection: {
                lazyAdd: false,

                valueFn: "_getDefaultSeriesCollection",

                setter: function(val)
                {
                    if(this.get("dataProvider"))
                    {
                        return this._parseSeriesCollection(val);
                    }
                    return val;
                }
            },

            /**
             * Reference to the left-aligned axes for the chart.
             *
             * @attribute leftAxesCollection
             * @type Array
             * @private
             */
            leftAxesCollection: {},

            /**
             * Reference to the bottom-aligned axes for the chart.
             *
             * @attribute bottomAxesCollection
             * @type Array
             * @private
             */
            bottomAxesCollection: {},

            /**
             * Reference to the right-aligned axes for the chart.
             *
             * @attribute rightAxesCollection
             * @type Array
             * @private
             */
            rightAxesCollection: {},

            /**
             * Reference to the top-aligned axes for the chart.
             *
             * @attribute topAxesCollection
             * @type Array
             * @private
             */
            topAxesCollection: {},

            /**
             * Indicates whether or not the chart is stacked.
             *
             * @attribute stacked
             * @type Boolean
             */
            stacked: {
                value: false
            },

            /**
             * Direction of chart's category axis when there is no series collection specified. Charts can
             * be horizontal or vertical. When the chart type is column, the chart is horizontal.
             * When the chart type is bar, the chart is vertical.
             *
             * @attribute direction
             * @type String
             */
            direction: {
                getter: function()
                {
                    var type = this.get("type");
                    if(type === "bar")
                    {
                        return "vertical";
                    }
                    else if(type === "column")
                    {
                        return "horizontal";
                    }
                    return this._direction;
                },

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

            /**
             * Indicates whether or not an area is filled in a combo chart.
             *
             * @attribute showAreaFill
             * @type Boolean
             */
            showAreaFill: {},

            /**
             * Indicates whether to display markers in a combo chart.
             *
             * @attribute showMarkers
             * @type Boolean
             */
            showMarkers:{},

            /**
             * Indicates whether to display lines in a combo chart.
             *
             * @attribute showLines
             * @type Boolean
             */
            showLines:{},

            /**
             * Indicates the key value used to identify a category axis in the `axes` hash. If
             * not specified, the categoryKey attribute value will be used.
             *
             * @attribute categoryAxisName
             * @type String
             */
            categoryAxisName: {
            },

            /**
             * Indicates the key value used to identify a the series axis when an axis not generated.
             *
             * @attribute valueAxisName
             * @type String
             */
            valueAxisName: {
                value: "values"
            },

            /**
             * Reference to the horizontalGridlines for the chart.
             *
             * @attribute horizontalGridlines
             * @type Gridlines
             */
            horizontalGridlines: {
                getter: function()
                {
                    var graph = this.get("graph");
                    if(graph)
                    {
                        return graph.get("horizontalGridlines");
                    }
                    return this._horizontalGridlines;
                },
                setter: function(val)
                {
                    var graph = this.get("graph");
                    if(val && !Y_Lang.isObject(val))
                    {
                        val = {};
                    }
                    if(graph)
                    {
                        graph.set("horizontalGridlines", val);
                    }
                    else
                    {
                        this._horizontalGridlines = val;
                    }
                }
            },

            /**
             * Reference to the verticalGridlines for the chart.
             *
             * @attribute verticalGridlines
             * @type Gridlines
             */
            verticalGridlines: {
                getter: function()
                {
                    var graph = this.get("graph");
                    if(graph)
                    {
                        return graph.get("verticalGridlines");
                    }
                    return this._verticalGridlines;
                },
                setter: function(val)
                {
                    var graph = this.get("graph");
                    if(val && !Y_Lang.isObject(val))
                    {
                        val = {};
                    }
                    if(graph)
                    {
                        graph.set("verticalGridlines", val);
                    }
                    else
                    {
                        this._verticalGridlines = val;
                    }
                }
            },

            /**
             * Type of chart when there is no series collection specified.
             *
             * @attribute type
             * @type String
             */
            type: {
                getter: function()
                {
                    if(this.get("stacked"))
                    {
                        return "stacked" + this._type;
                    }
                    return this._type;
                },

                setter: function(val)
                {
                    if(this._type === "bar")
                    {
                        if(val !== "bar")
                        {
                            this.set("direction", "horizontal");
                        }
                    }
                    else
                    {
                        if(val === "bar")
                        {
                            this.set("direction", "vertical");
                        }
                    }
                    this._type = val;
                    return this._type;
                }
            },

            /**
             * Reference to the category axis used by the chart.
             *
             * @attribute categoryAxis
             * @type Axis
             */
            categoryAxis:{}
        }
    });