text: YUI.add('charts-base', function (Y, NAME) {
  
  /**
   * Provides functionality for creating charts.
   *
   * @module charts
   * @submodule charts-base
   */
  var CONFIG = Y.config,
      WINDOW = CONFIG.win,
      DOCUMENT = CONFIG.doc,
      Y_Lang = Y.Lang,
      IS_STRING = Y_Lang.isString,
      Y_DOM = Y.DOM,
      _getClassName = Y.ClassNameManager.getClassName,
      SERIES_MARKER = _getClassName("seriesmarker");
  
  /**
   * Gridlines draws gridlines on a Graph.
   *
   * @class Gridlines
   * @constructor
   * @extends Base
   * @uses Renderer
   * @param {Object} config (optional) Configuration parameters.
   * @submodule charts-base
   */
  Y.Gridlines = Y.Base.create("gridlines", Y.Base, [Y.Renderer], {
      /**
       * Reference to the `Path` element used for drawing Gridlines.
       *
       * @property _path
       * @type Path
       * @private
       */
      _path: null,
  
      /**
       * Removes the Gridlines.
       *
       * @method remove
       * @private
       */
      remove: function()
      {
          var path = this._path;
          if(path)
          {
              path.destroy();
          }
      },
  
      /**
       * Draws the gridlines
       *
       * @method draw
       * @protected
       */
      draw: function()
      {
          if(this.get("axis") && this.get("graph"))
          {
              this._drawGridlines();
          }
      },
  
      /**
       * Algorithm for drawing gridlines
       *
       * @method _drawGridlines
       * @private
       */
      _drawGridlines: function()
      {
          var path,
              axis = this.get("axis"),
              axisPosition = axis.get("position"),
              points,
              i = 0,
              l,
              direction = this.get("direction"),
              graph = this.get("graph"),
              w = graph.get("width"),
              h = graph.get("height"),
              line = this.get("styles").line,
              color = line.color,
              weight = line.weight,
              alpha = line.alpha,
              count = this.get("count"),
              lineFunction = direction == "vertical" ? this._verticalLine : this._horizontalLine;
          if(isFinite(w) && isFinite(h) && w > 0 && h > 0)
          {
              if(count && Y.Lang.isNumber(count))
              {
                  points = this._getPoints(count, w, h);
              }
              else if(axisPosition != "none" && axis && axis.get("tickPoints"))
              {
                  points = axis.get("tickPoints");
              }
              else
              {
                  points = this._getPoints(axis.get("styles").majorUnit.count, w, h);
              }
              l = points.length;
              path = graph.get("gridlines");
              path.set("width", w);
              path.set("height", h);
              path.set("stroke", {
                  weight: weight,
                  color: color,
                  opacity: alpha
              });
              for(i = 0; i < l; i = i + 1)
              {
                  lineFunction(path, points[i], w, h);
              }
              path.end();
          }
      },
  
      /**
       * Calculates the coordinates for the gridlines based on a count.
       *
       * @method _getPoints
       * @param {Number} count Number of gridlines
       * @return Array
       * @private
       */
      _getPoints: function(count, w, h)
      {
          var i,
              points = [],
              multiplier,
              divisor = count - 1;
          for(i = 0; i < count; i = i + 1)
          {
              multiplier = i/divisor;
              points[i] = {
                  x: w * multiplier,
                  y: h * multiplier
              };
          }
          return points;
      },
  
      /**
       * Algorithm for horizontal lines.
       *
       * @method _horizontalLine
       * @param {Path} path Reference to path element
       * @param {Object} pt Coordinates corresponding to a major unit of an axis.
       * @param {Number} w Width of the Graph
       * @param {Number} h Height of the Graph
       * @private
       */
      _horizontalLine: function(path, pt, w, h)
      {
          path.moveTo(0, pt.y);
          path.lineTo(w, pt.y);
      },
  
      /**
       * Algorithm for vertical lines.
       *
       * @method _verticalLine
       * @param {Path} path Reference to path element
       * @param {Object} pt Coordinates corresponding to a major unit of an axis.
       * @param {Number} w Width of the Graph
       * @param {Number} h Height of the Graph
       * @private
       */
      _verticalLine: function(path, pt, w, h)
      {
          path.moveTo(pt.x, 0);
          path.lineTo(pt.x, h);
      },
  
      /**
       * Gets the default value for the `styles` attribute. Overrides
       * base implementation.
       *
       * @method _getDefaultStyles
       * @return Object
       * @protected
       */
      _getDefaultStyles: function()
      {
          var defs = {
              line: {
                  color:"#f0efe9",
                  weight: 1,
                  alpha: 1
              }
          };
          return defs;
      }
  
  },
  {
      ATTRS: {
          /**
           * Indicates the direction of the gridline.
           *
           * @attribute direction
           * @type String
           */
          direction: {},
  
          /**
           * Indicate the `Axis` in which to bind
           * the gridlines.
           *
           * @attribute axis
           * @type Axis
           */
          axis: {},
  
          /**
           * Indicates the `Graph` in which the gridlines
           * are drawn.
           *
           * @attribute graph
           * @type Graph
           */
          graph: {},
  
          /**
           * Indicates the number of gridlines to display. If no value is set, gridlines will equal the number of ticks in
           * the corresponding axis.
           *
           * @attribute count
           * @type Number
           */
          count: {}
      }
  });
  /**
   * Graph manages and contains series instances for a `CartesianChart`
   * instance.
   *
   * @class Graph
   * @constructor
   * @extends Widget
   * @uses Renderer
   * @submodule charts-base
   */
  Y.Graph = Y.Base.create("graph", Y.Widget, [Y.Renderer], {
      /**
       * @method bindUI
       * @private
       */
      bindUI: function()
      {
          var bb = this.get("boundingBox");
          bb.setStyle("position", "absolute");
          this.after("widthChange", this._sizeChangeHandler);
          this.after("heightChange", this._sizeChangeHandler);
          this.after("stylesChange", this._updateStyles);
          this.after("groupMarkersChange", this._drawSeries);
      },
  
      /**
       * @method syncUI
       * @private
       */
      syncUI: function()
      {
          var background,
              cb,
              bg,
              sc = this.get("seriesCollection"),
              series,
              i = 0,
              len = sc ? sc.length : 0,
              hgl = this.get("horizontalGridlines"),
              vgl = this.get("verticalGridlines");
          if(this.get("showBackground"))
          {
              background = this.get("background");
              cb = this.get("contentBox");
              bg = this.get("styles").background;
              bg.stroke = bg.border;
              bg.stroke.opacity = bg.stroke.alpha;
              bg.fill.opacity = bg.fill.alpha;
              bg.width = this.get("width");
              bg.height = this.get("height");
              bg.type = bg.shape;
              background.set(bg);
          }
          for(; i < len; ++i)
          {
              series = sc[i];
              if(series instanceof Y.CartesianSeries)
              {
                  series.render();
              }
          }
          if(hgl && hgl instanceof Y.Gridlines)
          {
              hgl.draw();
          }
          if(vgl && vgl instanceof Y.Gridlines)
          {
              vgl.draw();
          }
      },
  
      /**
       * Object of arrays containing series mapped to a series type.
       *
       * @property seriesTypes
       * @type Object
       * @private
       */
      seriesTypes: null,
  
      /**
       * Returns a series instance based on an index.
       *
       * @method getSeriesByIndex
       * @param {Number} val index of the series
       * @return CartesianSeries
       */
      getSeriesByIndex: function(val)
      {
          var col = this.get("seriesCollection"),
              series;
          if(col && col.length > val)
          {
              series = col[val];
          }
          return series;
      },
  
      /**
       * Returns a series instance based on a key value.
       *
       * @method getSeriesByKey
       * @param {String} val key value of the series
       * @return CartesianSeries
       */
      getSeriesByKey: function(val)
      {
          var obj = this._seriesDictionary,
              series;
          if(obj && obj.hasOwnProperty(val))
          {
              series = obj[val];
          }
          return series;
      },
  
      /**
       * Adds dispatcher to a `_dispatcher` used to
       * to ensure all series have redrawn before for firing event.
       *
       * @method addDispatcher
       * @param {CartesianSeries} val series instance to add
       * @protected
       */
      addDispatcher: function(val)
      {
          if(!this._dispatchers)
          {
              this._dispatchers = [];
          }
          this._dispatchers.push(val);
      },
  
      /**
       * Collection of series to be displayed in the graph.
       *
       * @property _seriesCollection
       * @type Array
       * @private
       */
      _seriesCollection: null,
  
      /**
       * Object containing key value pairs of `CartesianSeries` instances.
       *
       * @property _seriesDictionary
       * @type Object
       * @private
       */
      _seriesDictionary: null,
  
      /**
       * Parses series instances to be displayed in the graph.
       *
       * @method _parseSeriesCollection
       * @param {Array} Collection of `CartesianSeries` instances or objects container `CartesianSeries` attributes values.
       * @private
       */
      _parseSeriesCollection: function(val)
      {
          if(!val)
          {
              return;
          }
          var len = val.length,
              i = 0,
              series,
              seriesKey;
          this._seriesCollection = [];
          this._seriesDictionary = {};
          this.seriesTypes = [];
          for(; i < len; ++i)
          {
              series = val[i];
              if(!(series instanceof Y.CartesianSeries) && !(series instanceof Y.PieSeries))
              {
                  this._createSeries(series);
                  continue;
              }
              this._addSeries(series);
          }
          len = this._seriesCollection.length;
          for(i = 0; i < len; ++i)
          {
              series = this.get("seriesCollection")[i];
              seriesKey = series.get("direction") == "horizontal" ? "yKey" : "xKey";
              this._seriesDictionary[series.get(seriesKey)] = series;
          }
      },
  
      /**
       * Adds a series to the graph.
       *
       * @method _addSeries
       * @param {CartesianSeries} series Series to add to the graph.
       * @private
       */
      _addSeries: function(series)
      {
          var type = series.get("type"),
              seriesCollection = this.get("seriesCollection"),
              graphSeriesLength = seriesCollection.length,
              seriesTypes = this.seriesTypes,
              typeSeriesCollection;
          if(!series.get("graph"))
          {
              series.set("graph", this);
          }
          seriesCollection.push(series);
          if(!seriesTypes.hasOwnProperty(type))
          {
              this.seriesTypes[type] = [];
          }
          typeSeriesCollection = this.seriesTypes[type];
          series.set("graphOrder", graphSeriesLength);
          series.set("order", typeSeriesCollection.length);
          typeSeriesCollection.push(series);
          series.set("seriesTypeCollection", typeSeriesCollection);
          this.addDispatcher(series);
          series.after("drawingComplete", Y.bind(this._drawingCompleteHandler, this));
          this.fire("seriesAdded", series);
      },
  
      /**
       * Creates a `CartesianSeries` instance from an object containing attribute key value pairs. The key value pairs include
       * attributes for the specific series and a type value which defines the type of series to be used.
       *
       * @method createSeries
       * @param {Object} seriesData Series attribute key value pairs.
       * @private
       */
      _createSeries: function(seriesData)
      {
          var type = seriesData.type,
              seriesCollection = this.get("seriesCollection"),
              seriesTypes = this.seriesTypes,
              typeSeriesCollection,
              seriesType,
              series;
              seriesData.graph = this;
          if(!seriesTypes.hasOwnProperty(type))
          {
              seriesTypes[type] = [];
          }
          typeSeriesCollection = seriesTypes[type];
          seriesData.graph = this;
          seriesData.order = typeSeriesCollection.length;
          seriesData.graphOrder = seriesCollection.length;
          seriesType = this._getSeries(seriesData.type);
          series = new seriesType(seriesData);
          this.addDispatcher(series);
          series.after("drawingComplete", Y.bind(this._drawingCompleteHandler, this));
          typeSeriesCollection.push(series);
          seriesCollection.push(series);
          series.set("seriesTypeCollection", typeSeriesCollection);
          if(this.get("rendered"))
          {
              series.render();
          }
      },
  
      /**
       * String reference for pre-defined `Series` classes.
       *
       * @property _seriesMap
       * @type Object
       * @private
       */
      _seriesMap: {
          line : Y.LineSeries,
          column : Y.ColumnSeries,
          bar : Y.BarSeries,
          area :  Y.AreaSeries,
          candlestick : Y.CandlestickSeries,
          ohlc : Y.OHLCSeries,
          stackedarea : Y.StackedAreaSeries,
          stackedline : Y.StackedLineSeries,
          stackedcolumn : Y.StackedColumnSeries,
          stackedbar : Y.StackedBarSeries,
          markerseries : Y.MarkerSeries,
          spline : Y.SplineSeries,
          areaspline : Y.AreaSplineSeries,
          stackedspline : Y.StackedSplineSeries,
          stackedareaspline : Y.StackedAreaSplineSeries,
          stackedmarkerseries : Y.StackedMarkerSeries,
          pie : Y.PieSeries,
          combo : Y.ComboSeries,
          stackedcombo : Y.StackedComboSeries,
          combospline : Y.ComboSplineSeries,
          stackedcombospline : Y.StackedComboSplineSeries
      },
  
      /**
       * Returns a specific `CartesianSeries` class based on key value from a look up table of a direct reference to a
       * class. When specifying a key value, the following options are available:
       *
       *  <table>
       *      <tr><th>Key Value</th><th>Class</th></tr>
       *      <tr><td>line</td><td>Y.LineSeries</td></tr>
       *      <tr><td>column</td><td>Y.ColumnSeries</td></tr>
       *      <tr><td>bar</td><td>Y.BarSeries</td></tr>
       *      <tr><td>area</td><td>Y.AreaSeries</td></tr>
       *      <tr><td>stackedarea</td><td>Y.StackedAreaSeries</td></tr>
       *      <tr><td>stackedline</td><td>Y.StackedLineSeries</td></tr>
       *      <tr><td>stackedcolumn</td><td>Y.StackedColumnSeries</td></tr>
       *      <tr><td>stackedbar</td><td>Y.StackedBarSeries</td></tr>
       *      <tr><td>markerseries</td><td>Y.MarkerSeries</td></tr>
       *      <tr><td>spline</td><td>Y.SplineSeries</td></tr>
       *      <tr><td>areaspline</td><td>Y.AreaSplineSeries</td></tr>
       *      <tr><td>stackedspline</td><td>Y.StackedSplineSeries</td></tr>
       *      <tr><td>stackedareaspline</td><td>Y.StackedAreaSplineSeries</td></tr>
       *      <tr><td>stackedmarkerseries</td><td>Y.StackedMarkerSeries</td></tr>
       *      <tr><td>pie</td><td>Y.PieSeries</td></tr>
       *      <tr><td>combo</td><td>Y.ComboSeries</td></tr>
       *      <tr><td>stackedcombo</td><td>Y.StackedComboSeries</td></tr>
       *      <tr><td>combospline</td><td>Y.ComboSplineSeries</td></tr>
       *      <tr><td>stackedcombospline</td><td>Y.StackedComboSplineSeries</td></tr>
       *  </table>
       *
       * When referencing a class directly, you can specify any of the above classes or any custom class that extends
       * `CartesianSeries` or `PieSeries`.
       *
       * @method _getSeries
       * @param {String | Object} type Series type.
       * @return CartesianSeries
       * @private
       */
      _getSeries: function(type)
      {
          var seriesClass;
          if(Y_Lang.isString(type))
          {
              seriesClass = this._seriesMap[type];
          }
          else
          {
              seriesClass = type;
          }
          return seriesClass;
      },
  
      /**
       * Event handler for marker events.
       *
       * @method _markerEventHandler
       * @param {Object} e Event object.
       * @private
       */
      _markerEventHandler: function(e)
      {
          var type = e.type,
              markerNode = e.currentTarget,
              strArr = markerNode.getAttribute("id").split("_"),
              series = this.getSeriesByIndex(strArr[1]),
              index = strArr[2];
          series.updateMarkerState(type, index);
      },
  
      /**
       * Collection of `CartesianSeries` instances to be redrawn.
       *
       * @property _dispatchers
       * @type Array
       * @private
       */
      _dispatchers: null,
  
      /**
       * Updates the `Graph` styles.
       *
       * @method _updateStyles
       * @private
       */
      _updateStyles: function()
      {
          var styles = this.get("styles").background,
              border = styles.border;
              border.opacity = border.alpha;
              styles.stroke = border;
              styles.fill.opacity = styles.fill.alpha;
          this.get("background").set(styles);
          this._sizeChangeHandler();
      },
  
      /**
       * Event handler for size changes.
       *
       * @method _sizeChangeHandler
       * @param {Object} e Event object.
       * @private
       */
      _sizeChangeHandler: function(e)
      {
          var hgl = this.get("horizontalGridlines"),
              vgl = this.get("verticalGridlines"),
              w = this.get("width"),
              h = this.get("height"),
              bg = this.get("styles").background,
              weight,
              background;
          if(bg && bg.border)
          {
              weight = bg.border.weight || 0;
          }
          if(this.get("showBackground"))
          {
              background = this.get("background");
              if(w && h)
              {
                  background.set("width", w);
                  background.set("height", h);
              }
          }
          if(this._gridlines)
          {
              this._gridlines.clear();
          }
          if(hgl && hgl instanceof Y.Gridlines)
          {
              hgl.draw();
          }
          if(vgl && vgl instanceof Y.Gridlines)
          {
              vgl.draw();
          }
          this._drawSeries();
      },
  
      /**
       * Draws each series.
       *
       * @method _drawSeries
       * @private
       */
      _drawSeries: function()
      {
          if(this._drawing)
          {
              this._callLater = true;
              return;
          }
          var sc,
              i,
              len,
              graphic = this.get("graphic");
          graphic.set("autoDraw", false);
          graphic.set("width", this.get("width"));
          graphic.set("height", this.get("height"));
          this._callLater = false;
          this._drawing = true;
          sc = this.get("seriesCollection");
          i = 0;
          len = sc ? sc.length : 0;
          for(; i < len; ++i)
          {
              sc[i].draw();
              if((!sc[i].get("xcoords") || !sc[i].get("ycoords")) && !sc[i] instanceof Y.PieSeries)
              {
                  this._callLater = true;
                  break;
              }
          }
          this._drawing = false;
          if(this._callLater)
          {
              this._drawSeries();
          }
      },
  
      /**
       * Event handler for series drawingComplete event.
       *
       * @method _drawingCompleteHandler
       * @param {Object} e Event object.
       * @private
       */
      _drawingCompleteHandler: function(e)
      {
          var series = e.currentTarget,
              graphic,
              index = Y.Array.indexOf(this._dispatchers, series);
          if(index > -1)
          {
              this._dispatchers.splice(index, 1);
          }
          if(this._dispatchers.length < 1)
          {
              graphic = this.get("graphic");
              if(!graphic.get("autoDraw"))
              {
                  graphic._redraw();
              }
              this.fire("chartRendered");
          }
      },
  
      /**
       * Gets the default value for the `styles` attribute. Overrides
       * base implementation.
       *
       * @method _getDefaultStyles
       * @return Object
       * @protected
       */
      _getDefaultStyles: function()
      {
          var defs = {
              background: {
                  shape: "rect",
                  fill:{
                      color:"#faf9f2"
                  },
                  border: {
                      color:"#dad8c9",
                      weight: 1
                  }
              }
          };
          return defs;
      },
  
      /**
       * Destructor implementation Graph class. Removes all Graphic instances from the widget.
       *
       * @method destructor
       * @protected
       */
      destructor: function()
      {
          if(this._graphic)
          {
              this._graphic.destroy();
              this._graphic = null;
          }
          if(this._background)
          {
              this._background.get("graphic").destroy();
              this._background = null;
          }
          if(this._gridlines)
          {
              this._gridlines.get("graphic").destroy();
              this._gridlines = null;
          }
      }
  }, {
      ATTRS: {
          /**
           * The x-coordinate for the graph.
           *
           * @attribute x
           * @type Number
           * @protected
           */
          x: {
              setter: function(val)
              {
                  this.get("boundingBox").setStyle("left", val + "px");
                  return val;
              }
          },
  
          /**
           * The y-coordinate for the graph.
           *
           * @attribute y
           * @type Number
           * @protected
           */
          y: {
              setter: function(val)
              {
                  this.get("boundingBox").setStyle("top", val + "px");
                  return val;
              }
          },
  
          /**
           * Reference to the chart instance using the graph.
           *
           * @attribute chart
           * @type ChartBase
           * @readOnly
           */
          chart: {
              getter: function() {
                  var chart = this._state.chart || this;
                  return chart;
              }
          },
  
          /**
           * Collection of series. When setting the `seriesCollection` the array can contain a combination of either
           * `CartesianSeries` instances or object literals with properties that will define a series.
           *
           * @attribute seriesCollection
           * @type CartesianSeries
           */
          seriesCollection: {
              getter: function()
              {
                  return this._seriesCollection;
              },
  
              setter: function(val)
              {
                  this._parseSeriesCollection(val);
                  return this._seriesCollection;
              }
          },
  
          /**
           * Indicates whether the `Graph` has a background.
           *
           * @attribute showBackground
           * @type Boolean
           * @default true
           */
          showBackground: {
              value: true
          },
  
          /**
           * Read-only hash lookup for all series on in the `Graph`.
           *
           * @attribute seriesDictionary
           * @type Object
           * @readOnly
           */
          seriesDictionary: {
              readOnly: true,
  
              getter: function()
              {
                  return this._seriesDictionary;
              }
          },
  
          /**
           * Reference to the horizontal `Gridlines` instance.
           *
           * @attribute horizontalGridlines
           * @type Gridlines
           * @default null
           */
          horizontalGridlines: {
              value: null,
  
              setter: function(val)
              {
                  var cfg,
                      key,
                      gl = this.get("horizontalGridlines");
                  if(gl && gl instanceof Y.Gridlines)
                  {
                      gl.remove();
                  }
                  if(val instanceof Y.Gridlines)
                  {
                      gl = val;
                      val.set("graph", this);
                      return val;
                  }
                  else if(val)
                  {
                      cfg = {
                          direction: "horizonal",
                          graph: this
                      };
                      for(key in val)
                      {
                          if(val.hasOwnProperty(key))
                          {
                              cfg[key] = val[key];
                          }
                      }
                      gl = new Y.Gridlines(cfg);
                      return gl;
                  }
              }
          },
  
          /**
           * Reference to the vertical `Gridlines` instance.
           *
           * @attribute verticalGridlines
           * @type Gridlines
           * @default null
           */
          verticalGridlines: {
              value: null,
  
              setter: function(val)
              {
                  var cfg,
                      key,
                      gl = this.get("verticalGridlines");
                  if(gl && gl instanceof Y.Gridlines)
                  {
                      gl.remove();
                  }
                  if(val instanceof Y.Gridlines)
                  {
                      gl = val;
                      val.set("graph", this);
                      return val;
                  }
                  else if(val)
                  {
                      cfg = {
                          direction: "vertical",
                          graph: this
                      };
                      for(key in val)
                      {
                          if(val.hasOwnProperty(key))
                          {
                              cfg[key] = val[key];
                          }
                      }
                      gl = new Y.Gridlines(cfg);
                      return gl;
                  }
              }
          },
  
          /**
           * Reference to graphic instance used for the background.
           *
           * @attribute background
           * @type Graphic
           * @readOnly
           */
          background: {
              getter: function()
              {
                  if(!this._background)
                  {
                      this._backgroundGraphic = new Y.Graphic({render:this.get("contentBox")});
                      this._backgroundGraphic.get("node").style.zIndex = 0;
                      this._background = this._backgroundGraphic.addShape({type: "rect"});
                  }
                  return this._background;
              }
          },
  
          /**
           * Reference to graphic instance used for gridlines.
           *
           * @attribute gridlines
           * @type Graphic
           * @readOnly
           */
          gridlines: {
              readOnly: true,
  
              getter: function()
              {
                  if(!this._gridlines)
                  {
                      this._gridlinesGraphic = new Y.Graphic({render:this.get("contentBox")});
                      this._gridlinesGraphic.get("node").style.zIndex = 1;
                      this._gridlines = this._gridlinesGraphic.addShape({type: "path"});
                  }
                  return this._gridlines;
              }
          },
  
          /**
           * Reference to graphic instance used for series.
           *
           * @attribute graphic
           * @type Graphic
           * @readOnly
           */
          graphic: {
              readOnly: true,
  
              getter: function()
              {
                  if(!this._graphic)
                  {
                      this._graphic = new Y.Graphic({render:this.get("contentBox")});
                      this._graphic.get("node").style.zIndex = 2;
                      this._graphic.set("autoDraw", false);
                  }
                  return this._graphic;
              }
          },
  
          /**
           * Indicates whether or not markers for a series will be grouped and rendered in a single complex shape instance.
           *
           * @attribute groupMarkers
           * @type Boolean
           */
          groupMarkers: {
              value: false
          }
  
          /**
           * Style properties used for drawing a background. Below are the default values:
           *  <dl>
           *      <dt>background</dt><dd>An object containing the following values:
           *          <dl>
           *              <dt>fill</dt><dd>Defines the style properties for the fill. Contains the following values:
           *                  <dl>
           *                      <dt>color</dt><dd>Color of the fill. The default value is #faf9f2.</dd>
           *                      <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the background fill.
           *                      The default value is 1.</dd>
           *                  </dl>
           *              </dd>
           *              <dt>border</dt><dd>Defines the style properties for the border. Contains the following values:
           *                  <dl>
           *                      <dt>color</dt><dd>Color of the border. The default value is #dad8c9.</dd>
           *                      <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the background border.
           *                      The default value is 1.</dd>
           *                      <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
           *                  </dl>
           *              </dd>
           *          </dl>
           *      </dd>
           *  </dl>
           *
           * @attribute styles
           * @type Object
           */
      }
  });
  /**
   * The ChartBase class is an abstract class used to create charts.
   *
   * @class ChartBase
   * @constructor
   * @submodule charts-base
   */
  function ChartBase() {}
  
  ChartBase.ATTRS = {
      /**
       * Data used to generate the chart.
       *
       * @attribute dataProvider
       * @type Array
       */
      dataProvider: {
          lazyAdd: false,
  
          valueFn: function()
          {
              var defDataProvider = [];
              if(!this._seriesKeysExplicitlySet)
              {
                  this._seriesKeys = this._buildSeriesKeys(defDataProvider);
              }
              return defDataProvider;
          },
  
          setter: function(val)
          {
              var dataProvider = this._setDataValues(val);
              if(!this._seriesKeysExplicitlySet)
              {
                  this._seriesKeys = this._buildSeriesKeys(dataProvider);
              }
              return dataProvider;
          }
      },
  
      /**
       * A collection of keys that map to the series axes. If no keys are set,
       * they will be generated automatically depending on the data structure passed into
       * the chart.
       *
       * @attribute seriesKeys
       * @type Array
       */
      seriesKeys: {
          getter: function()
          {
              return this._seriesKeys;
          },
  
          setter: function(val)
          {
              this._seriesKeysExplicitlySet = true;
              this._seriesKeys = val;
              return val;
          }
      },
  
      /**
       * Sets the `aria-label` for the chart.
       *
       * @attribute ariaLabel
       * @type String
       */
      ariaLabel: {
          value: "Chart Application",
  
          setter: function(val)
          {
              var cb = this.get("contentBox");
              if(cb)
              {
                  cb.setAttribute("aria-label", val);
              }
              return val;
          }
      },
  
      /**
       * Sets the aria description for the chart.
       *
       * @attribute ariaDescription
       * @type String
       */
      ariaDescription: {
          value: "Use the up and down keys to navigate between series. Use the left and right keys to navigate through items in a series.",
  
          setter: function(val)
          {
              if(this._description)
              {
                  this._description.setContent("");
                  this._description.appendChild(DOCUMENT.createTextNode(val));
              }
              return val;
          }
      },
  
      /**
       * Reference to the default tooltip available for the chart.
       * <p>Contains the following properties:</p>
       *  <dl>
       *      <dt>node</dt><dd>Reference to the actual dom node</dd>
       *      <dt>showEvent</dt><dd>Event that should trigger the tooltip</dd>
       *      <dt>hideEvent</dt><dd>Event that should trigger the removal of a tooltip (can be an event or an array of events)</dd>
       *      <dt>styles</dt><dd>A hash of style properties that will be applied to the tooltip node</dd>
       *      <dt>show</dt><dd>Indicates whether or not to show the tooltip</dd>
       *      <dt>markerEventHandler</dt><dd>Displays and hides tooltip based on marker events</dd>
       *      <dt>planarEventHandler</dt><dd>Displays and hides tooltip based on planar events</dd>
       *      <dt>markerLabelFunction</dt><dd>Reference to the function used to format a marker event triggered tooltip's text.
       *      The method contains the following arguments:
       *  <dl>
       *      <dt>categoryItem</dt><dd>An object containing the following:
       *  <dl>
       *      <dt>axis</dt><dd>The axis to which the category is bound.</dd>
       *      <dt>displayName</dt><dd>The display name set to the category (defaults to key if not provided).</dd>
       *      <dt>key</dt><dd>The key of the category.</dd>
       *      <dt>value</dt><dd>The value of the category.</dd>
       *  </dl>
       *  </dd>
       *  <dt>valueItem</dt><dd>An object containing the following:
       *      <dl>
       *          <dt>axis</dt><dd>The axis to which the item's series is bound.</dd>
       *          <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd>
       *          <dt>key</dt><dd>The key for the series.</dd>
       *          <dt>value</dt><dd>The value for the series item.</dd>
       *      </dl>
       *  </dd>
       *  <dt>itemIndex</dt><dd>The index of the item within the series.</dd>
       *  <dt>series</dt><dd> The `CartesianSeries` instance of the item.</dd>
       *  <dt>seriesIndex</dt><dd>The index of the series in the `seriesCollection`.</dd>
       *  </dl>
       *  The method returns an `HTMLElement` which is written into the DOM using `appendChild`. If you override this method and choose
       *  to return an html string, you will also need to override the tooltip's `setTextFunction` method to accept an html string.
       *  </dd>
       *  <dt>planarLabelFunction</dt><dd>Reference to the function used to format a planar event triggered tooltip's text
       *  <dl>
       *      <dt>categoryAxis</dt><dd> `CategoryAxis` Reference to the categoryAxis of the chart.
       *      <dt>valueItems</dt><dd>Array of objects for each series that has a data point in the coordinate plane of the event. Each
       *      object contains the following data:
       *  <dl>
       *      <dt>axis</dt><dd>The value axis of the series.</dd>
       *      <dt>key</dt><dd>The key for the series.</dd>
       *      <dt>value</dt><dd>The value for the series item.</dd>
       *      <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd>
       *  </dl>
       *  </dd>
       *      <dt>index</dt><dd>The index of the item within its series.</dd>
       *      <dt>seriesArray</dt><dd>Array of series instances for each value item.</dd>
       *      <dt>seriesIndex</dt><dd>The index of the series in the `seriesCollection`.</dd>
       *  </dl>
       *  </dd>
       *  </dl>
       *  The method returns an `HTMLElement` which is written into the DOM using `appendChild`. If you override this method and choose
       *  to return an html string, you will also need to override the tooltip's `setTextFunction` method to accept an html string.
       *  </dd>
       *  <dt>setTextFunction</dt><dd>Method that writes content returned from `planarLabelFunction` or `markerLabelFunction` into the
       *  the tooltip node. Has the following signature:
       *  <dl>
       *      <dt>label</dt><dd>The `HTMLElement` that the content is to be added.</dd>
       *      <dt>val</dt><dd>The content to be rendered into tooltip. This can be a `String` or `HTMLElement`. If an HTML string is used,
       *      it will be rendered as a string.</dd>
       *  </dl>
       *  </dd>
       *  </dl>
       * @attribute tooltip
       * @type Object
       */
      tooltip: {
          valueFn: "_getTooltip",
  
          setter: function(val)
          {
              return this._updateTooltip(val);
          }
      },
  
      /**
       * The key value used for the chart's category axis.
       *
       * @attribute categoryKey
       * @type String
       * @default category
       */
      categoryKey: {
          value: "category"
      },
  
      /**
       * Indicates the type of axis to use for the category axis.
       *
       *  <dl>
       *      <dt>category</dt><dd>Specifies a `CategoryAxis`.</dd>
       *      <dt>time</dt><dd>Specifies a `TimeAxis</dd>
       *  </dl>
       *
       * @attribute categoryType
       * @type String
       * @default category
       */
      categoryType:{
          value:"category"
      },
  
      /**
       * Indicates the the type of interactions that will fire events.
       *
       *  <dl>
       *      <dt>marker</dt><dd>Events will be broadcasted when the mouse interacts with individual markers.</dd>
       *      <dt>planar</dt><dd>Events will be broadcasted when the mouse intersects the plane of any markers on the chart.</dd>
       *      <dt>none</dt><dd>No events will be broadcasted.</dd>
       *  </dl>
       *
       * @attribute interactionType
       * @type String
       * @default marker
       */
      interactionType: {
          value: "marker"
      },
  
      /**
       * Reference to all the axes in the chart.
       *
       * @attribute axesCollection
       * @type Array
       */
      axesCollection: {},
  
      /**
       * Reference to graph instance.
       *
       * @attribute graph
       * @type Graph
       */
      graph: {
          valueFn: "_getGraph"
      },
  
      /**
       * Indicates whether or not markers for a series will be grouped and rendered in a single complex shape instance.
       *
       * @attribute groupMarkers
       * @type Boolean
       */
      groupMarkers: {
          value: false
      }
  };
  
  ChartBase.prototype = {
      /**
       * Handles groupMarkers change event.
       *
       * @method _groupMarkersChangeHandler
       * @param {Object} e Event object.
       * @private
       */
      _groupMarkersChangeHandler: function(e)
      {
          var graph = this.get("graph"),
              useGroupMarkers = e.newVal;
          if(graph)
          {
              graph.set("groupMarkers", useGroupMarkers);
          }
      },
  
      /**
       * Handler for itemRendered event.
       *
       * @method _itemRendered
       * @param {Object} e Event object.
       * @private
       */
      _itemRendered: function(e)
      {
          this._itemRenderQueue = this._itemRenderQueue.splice(1 + Y.Array.indexOf(this._itemRenderQueue, e.currentTarget), 1);
          if(this._itemRenderQueue.length < 1)
          {
              this._redraw();
          }
      },
  
      /**
       * Default value function for the `Graph` attribute.
       *
       * @method _getGraph
       * @return Graph
       * @private
       */
      _getGraph: function()
      {
          var graph = new Y.Graph({
              chart:this,
              groupMarkers: this.get("groupMarkers")
          });
          graph.after("chartRendered", Y.bind(function(e) {
              this.fire("chartRendered");
          }, this));
          return graph;
      },
  
      /**
       * Returns a series instance by index or key value.
       *
       * @method getSeries
       * @param val
       * @return CartesianSeries
       */
      getSeries: function(val)
      {
          var series = null,
              graph = this.get("graph");
          if(graph)
          {
              if(Y_Lang.isNumber(val))
              {
                  series = graph.getSeriesByIndex(val);
              }
              else
              {
                  series = graph.getSeriesByKey(val);
              }
          }
          return series;
      },
  
      /**
       * Returns an `Axis` instance by key reference. If the axis was explicitly set through the `axes` attribute,
       * the key will be the same as the key used in the `axes` object. For default axes, the key for
       * the category axis is the value of the `categoryKey` (`category`). For the value axis, the default
       * key is `values`.
       *
       * @method getAxisByKey
       * @param {String} val Key reference used to look up the axis.
       * @return Axis
       */
      getAxisByKey: function(val)
      {
          var axis,
              axes = this.get("axes");
          if(axes && axes.hasOwnProperty(val))
          {
              axis = axes[val];
          }
          return axis;
      },
  
      /**
       * Returns the category axis for the chart.
       *
       * @method getCategoryAxis
       * @return Axis
       */
      getCategoryAxis: function()
      {
          var axis,
              key = this.get("categoryKey"),
              axes = this.get("axes");
          if(axes.hasOwnProperty(key))
          {
              axis = axes[key];
          }
          return axis;
      },
  
      /**
       * Default direction of the chart.
       *
       * @property _direction
       * @type String
       * @default horizontal
       * @private
       */
      _direction: "horizontal",
  
      /**
       * Storage for the `dataProvider` attribute.
       *
       * @property _dataProvider
       * @type Array
       * @private
       */
      _dataProvider: null,
  
      /**
       * Setter method for `dataProvider` attribute.
       *
       * @method _setDataValues
       * @param {Array} val Array to be set as `dataProvider`.
       * @return Array
       * @private
       */
      _setDataValues: function(val)
      {
          if(Y_Lang.isArray(val[0]))
          {
              var hash,
                  dp = [],
                  cats = val[0],
                  i = 0,
                  l = cats.length,
                  n,
                  sl = val.length;
              for(; i < l; ++i)
              {
                  hash = {category:cats[i]};
                  for(n = 1; n < sl; ++n)
                  {
                      hash["series" + n] = val[n][i];
                  }
                  dp[i] = hash;
              }
              return dp;
          }
          return val;
      },
  
      /**
       * Storage for `seriesCollection` attribute.
       *
       * @property _seriesCollection
       * @type Array
       * @private
       */
      _seriesCollection: null,
  
      /**
       * Setter method for `seriesCollection` attribute.
       *
       * @property _setSeriesCollection
       * @param {Array} val Array of either `CartesianSeries` instances or objects containing series attribute key value pairs.
       * @private
       */
      _setSeriesCollection: function(val)
      {
          this._seriesCollection = val;
      },
      /**
       * Helper method that returns the axis class that a key references.
       *
       * @method _getAxisClass
       * @param {String} t The type of axis.
       * @return Axis
       * @private
       */
      _getAxisClass: function(t)
      {
          return this._axisClass[t];
      },
  
      /**
       * Key value pairs of axis types.
       *
       * @property _axisClass
       * @type Object
       * @private
       */
      _axisClass: {
          stacked: Y.StackedAxis,
          numeric: Y.NumericAxis,
          category: Y.CategoryAxis,
          time: Y.TimeAxis
      },
  
      /**
       * Collection of axes.
       *
       * @property _axes
       * @type Array
       * @private
       */
      _axes: null,
  
      /**
       * @method initializer
       * @private
       */
      initializer: function()
      {
          this._itemRenderQueue = [];
          this._seriesIndex = -1;
          this._itemIndex = -1;
          this.after("dataProviderChange", this._dataProviderChangeHandler);
      },
  
      /**
       * @method renderUI
       * @private
       */
      renderUI: function()
      {
          var tt = this.get("tooltip"),
              bb = this.get("boundingBox"),
              cb = this.get("contentBox");
          //move the position = absolute logic to a class file
          bb.setStyle("position", "absolute");
          cb.setStyle("position", "absolute");
          this._addAxes();
          this._addSeries();
          if(tt && tt.show)
          {
              this._addTooltip();
          }
          this._setAriaElements(bb, cb);
      },
  
      /**
       * Creates an aria `live-region`, `aria-label` and `aria-describedby` for the Chart.
       *
       * @method _setAriaElements
       * @param {Node} cb Reference to the Chart's `contentBox` attribute.
       * @private
       */
      _setAriaElements: function(bb, cb)
      {
          var description = this._getAriaOffscreenNode(),
              id = this.get("id") + "_description",
              liveRegion = this._getAriaOffscreenNode();
          cb.set("tabIndex", 0);
          cb.set("role", "img");
          cb.setAttribute("aria-label", this.get("ariaLabel"));
          cb.setAttribute("aria-describedby", id);
          description.set("id", id);
          description.set("tabIndex", -1);
          description.appendChild(DOCUMENT.createTextNode(this.get("ariaDescription")));
          liveRegion.set("id", "live-region");
          liveRegion.set("aria-live", "polite");
          liveRegion.set("aria-atomic", "true");
          liveRegion.set("role", "status");
          bb.setAttribute("role", "application");
          bb.appendChild(description);
          bb.appendChild(liveRegion);
          this._description = description;
          this._liveRegion = liveRegion;
      },
  
      /**
       * Sets a node offscreen for use as aria-description or aria-live-regin.
       *
       * @method _setOffscreen
       * @return Node
       * @private
       */
      _getAriaOffscreenNode: function()
      {
          var node = Y.Node.create("<div></div>"),
              ie = Y.UA.ie,
              clipRect = (ie && ie < 8) ? "rect(1px 1px 1px 1px)" : "rect(1px, 1px, 1px, 1px)";
          node.setStyle("position", "absolute");
          node.setStyle("height", "1px");
          node.setStyle("width", "1px");
          node.setStyle("overflow", "hidden");
          node.setStyle("clip", clipRect);
          return node;
      },
  
      /**
       * @method syncUI
       * @private
       */
      syncUI: function()
      {
          this._redraw();
      },
  
      /**
       * @method bindUI
       * @private
       */
      bindUI: function()
      {
          this.after("tooltipChange", Y.bind(this._tooltipChangeHandler, this));
          this.after("widthChange", this._sizeChanged);
          this.after("heightChange", this._sizeChanged);
          this.after("groupMarkersChange", this._groupMarkersChangeHandler);
          var tt = this.get("tooltip"),
              hideEvent = "mouseout",
              showEvent = "mouseover",
              cb = this.get("contentBox"),
              interactionType = this.get("interactionType"),
              i = 0,
              len,
              markerClassName = "." + SERIES_MARKER,
              isTouch = ((WINDOW && ("ontouchstart" in WINDOW)) && !(Y.UA.chrome && Y.UA.chrome < 6));
          Y.on("keydown", Y.bind(function(e) {
              var key = e.keyCode,
                  numKey = parseFloat(key),
                  msg;
              if(numKey > 36 && numKey < 41)
              {
                  e.halt();
                  msg = this._getAriaMessage(numKey);
                  this._liveRegion.setContent("");
                  this._liveRegion.appendChild(DOCUMENT.createTextNode(msg));
              }
          }, this), this.get("contentBox"));
          if(interactionType == "marker")
          {
              //if touch capabilities, toggle tooltip on touchend. otherwise, the tooltip attribute's hideEvent/showEvent types.
              hideEvent = tt.hideEvent;
              showEvent = tt.showEvent;
              if(isTouch)
              {
                  Y.delegate("touchend", Y.bind(this._markerEventDispatcher, this), cb, markerClassName);
                  //hide active tooltip if the chart is touched
                  Y.on("touchend", Y.bind(function(e) {
                      //only halt the event if it originated from the chart
                      if(cb.contains(e.target))
                      {
                          e.halt(true);
                      }
                      if(this._activeMarker)
                      {
                          this._activeMarker = null;
                          this.hideTooltip(e);
                      }
                  }, this));
              }
              else
              {
                  Y.delegate("mouseenter", Y.bind(this._markerEventDispatcher, this), cb, markerClassName);
                  Y.delegate("mousedown", Y.bind(this._markerEventDispatcher, this), cb, markerClassName);
                  Y.delegate("mouseup", Y.bind(this._markerEventDispatcher, this), cb, markerClassName);
                  Y.delegate("mouseleave", Y.bind(this._markerEventDispatcher, this), cb, markerClassName);
                  Y.delegate("click", Y.bind(this._markerEventDispatcher, this), cb, markerClassName);
                  Y.delegate("mousemove", Y.bind(this._positionTooltip, this), cb, markerClassName);
              }
          }
          else if(interactionType == "planar")
          {
              if(isTouch)
              {
                  this._overlay.on("touchend", Y.bind(this._planarEventDispatcher, this));
              }
              else
              {
                  this._overlay.on("mousemove", Y.bind(this._planarEventDispatcher, this));
                  this.on("mouseout", this.hideTooltip);
              }
          }
          if(tt)
          {
              this.on("markerEvent:touchend", Y.bind(function(e) {
                  var marker = e.series.get("markers")[e.index];
                  if(this._activeMarker && marker === this._activeMarker)
                  {
                      this._activeMarker = null;
                      this.hideTooltip(e);
                  }
                  else
                  {
  
                      this._activeMarker = marker;
                      tt.markerEventHandler.apply(this, [e]);
                  }
              }, this));
              if(hideEvent && showEvent && hideEvent == showEvent)
              {
                  this.on(interactionType + "Event:" + hideEvent, this.toggleTooltip);
              }
              else
              {
                  if(showEvent)
                  {
                      this.on(interactionType + "Event:" + showEvent, tt[interactionType + "EventHandler"]);
                  }
                  if(hideEvent)
                  {
                      if(Y_Lang.isArray(hideEvent))
                      {
                          len = hideEvent.length;
                          for(; i < len; ++i)
                          {
                              this.on(interactionType + "Event:" + hideEvent[i], this.hideTooltip);
                          }
                      }
                      this.on(interactionType + "Event:" + hideEvent, this.hideTooltip);
                  }
              }
          }
      },
  
      /**
       * Event handler for marker events.
       *
       * @method _markerEventDispatcher
       * @param {Object} e Event object.
       * @private
       */
      _markerEventDispatcher: function(e)
      {
          var type = e.type,
              cb = this.get("contentBox"),
              markerNode = e.currentTarget,
              strArr = markerNode.getAttribute("id").split("_"),
              index = strArr.pop(),
              seriesIndex = strArr.pop(),
              series = this.getSeries(parseInt(seriesIndex, 10)),
              items = this.getSeriesItems(series, index),
              isTouch = e && e.hasOwnProperty("changedTouches"),
              pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
              pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
              x = pageX - cb.getX(),
              y = pageY - cb.getY();
          if(type == "mouseenter")
          {
              type = "mouseover";
          }
          else if(type == "mouseleave")
          {
              type = "mouseout";
          }
          series.updateMarkerState(type, index);
          e.halt();
          /**
           * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mouseover event.
           *
           *
           * @event markerEvent:mouseover
           * @preventable false
           * @param {EventFacade} e Event facade with the following additional
           *   properties:
           *  <dl>
           *      <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd>
           *      <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd>
           *      <dt>node</dt><dd>The dom node of the marker.</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>series</dt><dd>Reference to the series of the marker.</dd>
           *      <dt>index</dt><dd>Index of the marker in the series.</dd>
           *      <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd>
           *  </dl>
           */
          /**
           * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mouseout event.
           *
           * @event markerEvent:mouseout
           * @preventable false
           * @param {EventFacade} e Event facade with the following additional
           *   properties:
           *  <dl>
           *      <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd>
           *      <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd>
           *      <dt>node</dt><dd>The dom node of the marker.</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>series</dt><dd>Reference to the series of the marker.</dd>
           *      <dt>index</dt><dd>Index of the marker in the series.</dd>
           *      <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd>
           *  </dl>
           */
          /**
           * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mousedown event.
           *
           * @event markerEvent:mousedown
           * @preventable false
           * @param {EventFacade} e Event facade with the following additional
           *   properties:
           *  <dl>
           *      <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd>
           *      <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd>
           *      <dt>node</dt><dd>The dom node of the marker.</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>series</dt><dd>Reference to the series of the marker.</dd>
           *      <dt>index</dt><dd>Index of the marker in the series.</dd>
           *      <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd>
           *  </dl>
           */
          /**
           * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mouseup event.
           *
           * @event markerEvent:mouseup
           * @preventable false
           * @param {EventFacade} e Event facade with the following additional
           *   properties:
           *  <dl>
           *      <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd>
           *      <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd>
           *      <dt>node</dt><dd>The dom node of the marker.</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>series</dt><dd>Reference to the series of the marker.</dd>
           *      <dt>index</dt><dd>Index of the marker in the series.</dd>
           *      <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd>
           *  </dl>
           */
          /**
           * Broadcasts when `interactionType` is set to `marker` and a series marker has received a click event.
           *
           * @event markerEvent:click
           * @preventable false
           * @param {EventFacade} e Event facade with the following additional
           *   properties:
           *  <dl>
           *      <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd>
           *      <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd>
           *      <dt>node</dt><dd>The dom node of the marker.</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>series</dt><dd>Reference to the series of the marker.</dd>
           *      <dt>index</dt><dd>Index of the marker in the series.</dd>
           *      <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd>
           *      <dt>originEvent</dt><dd>Underlying dom event.</dd>
           *  </dl>
           */
          this.fire("markerEvent:" + type, {
              originEvent: e,
              pageX:pageX,
              pageY:pageY,
              categoryItem:items.category,
              valueItem:items.value,
              node:markerNode,
              x:x,
              y:y,
              series:series,
              index:index,
              seriesIndex:seriesIndex
          });
      },
  
      /**
       * Event handler for dataProviderChange.
       *
       * @method _dataProviderChangeHandler
       * @param {Object} e Event object.
       * @private
       */
      _dataProviderChangeHandler: function(e)
      {
          var dataProvider = e.newVal,
              axes,
              i,
              axis;
          this._seriesIndex = -1;
          this._itemIndex = -1;
          if(this instanceof Y.CartesianChart)
          {
              this.set("axes", this.get("axes"));
              this.set("seriesCollection", this.get("seriesCollection"));
          }
          axes = this.get("axes");
          if(axes)
          {
              for(i in axes)
              {
                  if(axes.hasOwnProperty(i))
                  {
                      axis = axes[i];
                      if(axis instanceof Y.Axis)
                      {
                          if(axis.get("position") != "none")
                          {
                              this._addToAxesRenderQueue(axis);
                          }
                          axis.set("dataProvider", dataProvider);
                      }
                  }
              }
          }
      },
  
      /**
       * Event listener for toggling the tooltip. If a tooltip is visible, hide it. If not, it
       * will create and show a tooltip based on the event object.
       *
       * @method toggleTooltip
       * @param {Object} e Event object.
       */
      toggleTooltip: function(e)
      {
          var tt = this.get("tooltip");
          if(tt.visible)
          {
              this.hideTooltip();
          }
          else
          {
              tt.markerEventHandler.apply(this, [e]);
          }
      },
  
      /**
       * Shows a tooltip
       *
       * @method _showTooltip
       * @param {String} msg Message to dispaly in the tooltip.
       * @param {Number} x x-coordinate
       * @param {Number} y y-coordinate
       * @private
       */
      _showTooltip: function(msg, x, y)
      {
          var tt = this.get("tooltip"),
              node = tt.node;
          if(msg)
          {
              tt.visible = true;
              tt.setTextFunction(node, msg);
              node.setStyle("top", y + "px");
              node.setStyle("left", x + "px");
              node.setStyle("visibility", "visible");
          }
      },
  
      /**
       * Positions the tooltip
       *
       * @method _positionTooltip
       * @param {Object} e Event object.
       * @private
       */
      _positionTooltip: function(e)
      {
          var tt = this.get("tooltip"),
              node = tt.node,
              cb = this.get("contentBox"),
              x = (e.pageX + 10) - cb.getX(),
              y = (e.pageY + 10) - cb.getY();
          if(node)
          {
              node.setStyle("left", x + "px");
              node.setStyle("top", y + "px");
          }
      },
  
      /**
       * Hides the default tooltip
       *
       * @method hideTooltip
       */
      hideTooltip: function()
      {
          var tt = this.get("tooltip"),
              node = tt.node;
          tt.visible = false;
          node.set("innerHTML", "");
          node.setStyle("left", -10000);
          node.setStyle("top", -10000);
          node.setStyle("visibility", "hidden");
      },
  
      /**
       * Adds a tooltip to the dom.
       *
       * @method _addTooltip
       * @private
       */
      _addTooltip: function()
      {
          var tt = this.get("tooltip"),
              id = this.get("id") + "_tooltip",
              cb = this.get("contentBox"),
              oldNode = DOCUMENT.getElementById(id);
          if(oldNode)
          {
              cb.removeChild(oldNode);
          }
          tt.node.set("id", id);
          tt.node.setStyle("visibility", "hidden");
          cb.appendChild(tt.node);
      },
  
      /**
       * Updates the tooltip attribute.
       *
       * @method _updateTooltip
       * @param {Object} val Object containing properties for the tooltip.
       * @return Object
       * @private
       */
      _updateTooltip: function(val)
      {
          var tt = this.get("tooltip") || this._getTooltip(),
              i,
              styles,
              node,
              props = {
                  markerLabelFunction:"markerLabelFunction",
                  planarLabelFunction:"planarLabelFunction",
                  setTextFunction:"setTextFunction",
                  showEvent:"showEvent",
                  hideEvent:"hideEvent",
                  markerEventHandler:"markerEventHandler",
                  planarEventHandler:"planarEventHandler",
                  show:"show"
              };
          if(Y_Lang.isObject(val))
          {
              styles = val.styles;
              node = Y.one(val.node) || tt.node;
              if(styles)
              {
                  for(i in styles)
                  {
                      if(styles.hasOwnProperty(i))
                      {
                          node.setStyle(i, styles[i]);
                      }
                  }
              }
              for(i in props)
              {
                  if(val.hasOwnProperty(i))
                  {
                      tt[i] = val[i];
                  }
              }
              tt.node = node;
          }
          return tt;
      },
  
      /**
       * Default getter for `tooltip` attribute.
       *
       * @method _getTooltip
       * @return Object
       * @private
       */
      _getTooltip: function()
      {
          var node = DOCUMENT.createElement("div"),
              tooltipClass = _getClassName("chart-tooltip"),
              tt = {
                  setTextFunction: this._setText,
                  markerLabelFunction: this._tooltipLabelFunction,
                  planarLabelFunction: this._planarLabelFunction,
                  show: true,
                  hideEvent: "mouseout",
                  showEvent: "mouseover",
                  markerEventHandler: function(e)
                  {
                      var tt = this.get("tooltip"),
                      msg = tt.markerLabelFunction.apply(this, [e.categoryItem, e.valueItem, e.index, e.series, e.seriesIndex]);
                      this._showTooltip(msg, e.x + 10, e.y + 10);
                  },
                  planarEventHandler: function(e)
                  {
                      var tt = this.get("tooltip"),
                          msg ,
                          categoryAxis = this.get("categoryAxis");
                      msg = tt.planarLabelFunction.apply(this, [categoryAxis, e.valueItem, e.index, e.items, e.seriesIndex]);
                      this._showTooltip(msg, e.x + 10, e.y + 10);
                  }
              };
          node = Y.one(node);
          node.set("id", this.get("id") + "_tooltip");
          node.setStyle("fontSize", "85%");
          node.setStyle("opacity", "0.83");
          node.setStyle("position", "absolute");
          node.setStyle("paddingTop", "2px");
          node.setStyle("paddingRight", "5px");
          node.setStyle("paddingBottom", "4px");
          node.setStyle("paddingLeft", "2px");
          node.setStyle("backgroundColor", "#fff");
          node.setStyle("border", "1px solid #dbdccc");
          node.setStyle("pointerEvents", "none");
          node.setStyle("zIndex", 3);
          node.setStyle("whiteSpace", "noWrap");
          node.setStyle("visibility", "hidden");
          node.addClass(tooltipClass);
          tt.node = Y.one(node);
          return tt;
      },
  
      /**
       * Formats tooltip text when `interactionType` is `planar`.
       *
       * @method _planarLabelFunction
       * @param {Axis} categoryAxis Reference to the categoryAxis of the chart.
       * @param {Array} valueItems Array of objects for each series that has a data point in the coordinate plane of the event.
       * Each object contains the following data:
       *  <dl>
       *      <dt>axis</dt><dd>The value axis of the series.</dd>
       *      <dt>key</dt><dd>The key for the series.</dd>
       *      <dt>value</dt><dd>The value for the series item.</dd>
       *      <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd>
       *  </dl>
       *  @param {Number} index The index of the item within its series.
       *  @param {Array} seriesArray Array of series instances for each value item.
       *  @param {Number} seriesIndex The index of the series in the `seriesCollection`.
       *  @return {String | HTML}
       * @private
       */
      _planarLabelFunction: function(categoryAxis, valueItems, index, seriesArray, seriesIndex)
      {
          var msg = DOCUMENT.createElement("div"),
              valueItem,
              i = 0,
              len = seriesArray.length,
              axis,
              categoryValue,
              seriesValue,
              series;
          if(categoryAxis)
          {
              categoryValue = categoryAxis.get("labelFunction").apply(this, [categoryAxis.getKeyValueAt(this.get("categoryKey"), index), categoryAxis.get("labelFormat")]);
              if(!Y_Lang.isObject(categoryValue))
              {
                  categoryValue = DOCUMENT.createTextNode(categoryValue);
              }
              msg.appendChild(categoryValue);
          }
  
          for(; i < len; ++i)
          {
              series = seriesArray[i];
              if(series.get("visible"))
              {
                  valueItem = valueItems[i];
                  axis = valueItem.axis;
                  seriesValue =  axis.get("labelFunction").apply(this, [axis.getKeyValueAt(valueItem.key, index), axis.get("labelFormat")]);
                  msg.appendChild(DOCUMENT.createElement("br"));
                  msg.appendChild(DOCUMENT.createTextNode(valueItem.displayName));
                  msg.appendChild(DOCUMENT.createTextNode(": "));
                  if(!Y_Lang.isObject(seriesValue))
                  {
                      seriesValue = DOCUMENT.createTextNode(seriesValue);
                  }
                  msg.appendChild(seriesValue);
              }
          }
          return msg;
      },
  
      /**
       * Formats tooltip text when `interactionType` is `marker`.
       *
       * @method _tooltipLabelFunction
       * @param {Object} categoryItem An object containing the following:
       *  <dl>
       *      <dt>axis</dt><dd>The axis to which the category is bound.</dd>
       *      <dt>displayName</dt><dd>The display name set to the category (defaults to key if not provided)</dd>
       *      <dt>key</dt><dd>The key of the category.</dd>
       *      <dt>value</dt><dd>The value of the category</dd>
       *  </dl>
       * @param {Object} valueItem An object containing the following:
       *  <dl>
       *      <dt>axis</dt><dd>The axis to which the item's series is bound.</dd>
       *      <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd>
       *      <dt>key</dt><dd>The key for the series.</dd>
       *      <dt>value</dt><dd>The value for the series item.</dd>
       *  </dl>
       * @param {Number} itemIndex The index of the item within the series.
       * @param {CartesianSeries} series The `CartesianSeries` instance of the item.
       * @param {Number} seriesIndex The index of the series in the `seriesCollection`.
       * @return {String | HTML}
       * @private
       */
      _tooltipLabelFunction: function(categoryItem, valueItem, itemIndex, series, seriesIndex)
      {
          var msg = DOCUMENT.createElement("div"),
              categoryValue = categoryItem.axis.get("labelFunction").apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]),
              seriesValue = valueItem.axis.get("labelFunction").apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]);
          msg.appendChild(DOCUMENT.createTextNode(categoryItem.displayName));
          msg.appendChild(DOCUMENT.createTextNode(": "));
          if(!Y_Lang.isObject(categoryValue))
          {
              categoryValue = DOCUMENT.createTextNode(categoryValue);
          }
          msg.appendChild(categoryValue);
          msg.appendChild(DOCUMENT.createElement("br"));
          msg.appendChild(DOCUMENT.createTextNode(valueItem.displayName));
          msg.appendChild(DOCUMENT.createTextNode(": "));
          if(!Y_Lang.isObject(seriesValue))
          {
              seriesValue = DOCUMENT.createTextNode(seriesValue);
          }
          msg.appendChild(seriesValue);
          return msg;
      },
  
      /**
       * Event handler for the tooltipChange.
       *
       * @method _tooltipChangeHandler
       * @param {Object} e Event object.
       * @private
       */
      _tooltipChangeHandler: function(e)
      {
          if(this.get("tooltip"))
          {
              var tt = this.get("tooltip"),
                  node = tt.node,
                  show = tt.show,
                  cb = this.get("contentBox");
              if(node && show)
              {
                  if(!cb.contains(node))
                  {
                      this._addTooltip();
                  }
              }
          }
      },
  
      /**
       * Updates the content of text field. This method writes a value into a text field using
       * `appendChild`. If the value is a `String`, it is converted to a `TextNode` first.
       *
       * @method _setText
       * @param label {HTMLElement} label to be updated
       * @param val {String} value with which to update the label
       * @private
       */
      _setText: function(textField, val)
      {
          textField.setContent("");
          if(Y_Lang.isNumber(val))
          {
              val = val + "";
          }
          else if(!val)
          {
              val = "";
          }
          if(IS_STRING(val))
          {
              val = DOCUMENT.createTextNode(val);
          }
          textField.appendChild(val);
      },
  
      /**
       * Returns all the keys contained in a  `dataProvider`.
       *
       * @method _getAllKeys
       * @param {Array} dp Collection of objects to be parsed.
       * @return Object
       */
      _getAllKeys: function(dp)
      {
          var i = 0,
              len = dp.length,
              item,
              key,
              keys = {};
          for(; i < len; ++i)
          {
              item = dp[i];
              for(key in item)
              {
                  if(item.hasOwnProperty(key))
                  {
                      keys[key] = true;
                  }
              }
          }
          return keys;
      },
  
      /**
       * Constructs seriesKeys if not explicitly specified.
       *
       * @method _buildSeriesKeys
       * @param {Array} dataProvider The dataProvider for the chart.
       * @return Array
       * @private
       */
      _buildSeriesKeys: function(dataProvider)
      {
          var allKeys,
              catKey = this.get("categoryKey"),
              keys = [],
              i;
          if(this._seriesKeysExplicitlySet)
          {
              return this._seriesKeys;
          }
          allKeys = this._getAllKeys(dataProvider);
          for(i in allKeys)
          {
              if(allKeys.hasOwnProperty(i) && i != catKey)
              {
                  keys.push(i);
              }
          }
          return keys;
      }
  };
  Y.ChartBase = ChartBase;
  /**
   * 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], {
      /**
       * @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 there is a style definition. Force them to set.
          this.get("styles");
          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"),
              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 || [];
          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);
                  continue;
              }
  
              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;
                  }
              }
              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",
                  position: "position",
                  overlapGraph:"overlapGraph",
                  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"),
              sc = this.get("seriesCollection");
          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 = [],
              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 = claimedKeys.length;
          for(i = 0; i < l; ++i)
          {
              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._seriesKeys = seriesKeys;
          }
          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(e)
      {
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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) * 0.5));
              }
          }
          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: {
              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]);
                      }
                  }
              }
          },
  
          /**
           * Style object for the series
           *
           * @attribute seriesStyles
           * @type Object
           * @private
           */
          seriesStyles: {
              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]);
                          }
                      }
                  }
              }
          },
  
          /**
           * Styles for the graph.
           *
           * @attribute graphStyles
           * @type Object
           * @private
           */
          graphStyles: {
              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);
              }
  
          },
  
          /**
           * 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: {
              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: {
              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: {
              valueFn: "_getDefaultSeriesCollection",
  
              setter: function(val)
              {
                  if(this.get("dataProvider"))
                  {
                      val = 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:{}
      }
  });
  /**
   * The PieChart class creates a pie chart
   *
   * @class PieChart
   * @extends ChartBase
   * @constructor
   * @submodule charts-base
   */
  Y.PieChart = Y.Base.create("pieChart", Y.Widget, [Y.ChartBase], {
      /**
       * Calculates and returns a `seriesCollection`.
       *
       * @method _getSeriesCollection
       * @return Array
       * @private
       */
      _getSeriesCollection: function()
      {
          if(this._seriesCollection)
          {
              return this._seriesCollection;
          }
          var axes = this.get("axes"),
              sc = [],
              seriesKeys,
              i = 0,
              l,
              type = this.get("type"),
              key,
              catAxis = "categoryAxis",
              catKey = "categoryKey",
              valAxis = "valueAxis",
              seriesKey = "valueKey";
          if(axes)
          {
              seriesKeys = axes.values.get("keyCollection");
              key = axes.category.get("keyCollection")[0];
              l = seriesKeys.length;
              for(; i < l; ++i)
              {
                  sc[i] = {type:type};
                  sc[i][catAxis] = "category";
                  sc[i][valAxis] = "values";
                  sc[i][catKey] = key;
                  sc[i][seriesKey] = seriesKeys[i];
              }
          }
          this._seriesCollection = sc;
          return sc;
      },
  
      /**
       * Creates `Axis` instances.
       *
       * @method _parseAxes
       * @param {Object} val Object containing `Axis` instances or objects in which to construct `Axis` instances.
       * @return Object
       * @private
       */
      _parseAxes: function(hash)
      {
          if(!this._axes)
          {
              this._axes = {};
          }
          var i, pos, axis, dh, config, axisClass,
              type = this.get("type"),
              w = this.get("width"),
              h = this.get("height"),
              node = Y.Node.one(this._parentNode);
          if(!w)
          {
              this.set("width", node.get("offsetWidth"));
              w = this.get("width");
          }
          if(!h)
          {
              this.set("height", node.get("offsetHeight"));
              h = this.get("height");
          }
          for(i in hash)
          {
              if(hash.hasOwnProperty(i))
              {
                  dh = hash[i];
                  pos = type == "pie" ? "none" : dh.position;
                  axisClass = this._getAxisClass(dh.type);
                  config = {dataProvider:this.get("dataProvider")};
                  if(dh.hasOwnProperty("roundingUnit"))
                  {
                      config.roundingUnit = dh.roundingUnit;
                  }
                  config.keys = dh.keys;
                  config.width = w;
                  config.height = h;
                  config.position = pos;
                  config.styles = dh.styles;
                  axis = new axisClass(config);
                  axis.on("axisRendered", Y.bind(this._itemRendered, this));
                  this._axes[i] = axis;
              }
          }
      },
  
      /**
       * Adds axes to the chart.
       *
       * @method _addAxes
       * @private
       */
      _addAxes: function()
      {
          var axes = this.get("axes"),
              i,
              axis,
              p;
          if(!axes)
          {
              this.set("axes", this._getDefaultAxes());
              axes = this.get("axes");
          }
          if(!this._axesCollection)
          {
              this._axesCollection = [];
          }
          for(i in axes)
          {
              if(axes.hasOwnProperty(i))
              {
                  axis = axes[i];
                  p = axis.get("position");
                  if(!this.get(p + "AxesCollection"))
                  {
                      this.set(p + "AxesCollection", [axis]);
                  }
                  else
                  {
                      this.get(p + "AxesCollection").push(axis);
                  }
                  this._axesCollection.push(axis);
              }
          }
      },
  
      /**
       * Renders the Graph.
       *
       * @method _addSeries
       * @private
       */
      _addSeries: function()
      {
          var graph = this.get("graph"),
              seriesCollection = this.get("seriesCollection");
          this._parseSeriesAxes(seriesCollection);
          graph.set("showBackground", false);
          graph.set("width", this.get("width"));
          graph.set("height", this.get("height"));
          graph.set("seriesCollection", seriesCollection);
          this._seriesCollection = graph.get("seriesCollection");
          graph.render(this.get("contentBox"));
      },
  
      /**
       * Parse and sets the axes for the chart.
       *
       * @method _parseSeriesAxes
       * @param {Array} c A collection `PieSeries` instance.
       * @private
       */
      _parseSeriesAxes: function(c)
      {
          var i = 0,
              len = c.length,
              s,
              axes = this.get("axes"),
              axis;
          for(; i < len; ++i)
          {
              s = c[i];
              if(s)
              {
                  //If series is an actual series instance,
                  //replace axes attribute string ids with axes
                  if(s instanceof Y.PieSeries)
                  {
                      axis = s.get("categoryAxis");
                      if(axis && !(axis instanceof Y.Axis))
                      {
                          s.set("categoryAxis", axes[axis]);
                      }
                      axis = s.get("valueAxis");
                      if(axis && !(axis instanceof Y.Axis))
                      {
                          s.set("valueAxis", axes[axis]);
                      }
                      continue;
                  }
                  s.categoryAxis = axes.category;
                  s.valueAxis = axes.values;
                  if(!s.type)
                  {
                      s.type = this.get("type");
                  }
              }
          }
      },
  
      /**
       * Generates and returns a key-indexed object containing `Axis` instances or objects used to create `Axis` instances.
       *
       * @method _getDefaultAxes
       * @return Object
       * @private
       */
      _getDefaultAxes: function()
      {
          var catKey = this.get("categoryKey"),
              seriesKeys = this.get("seriesKeys").concat(),
              seriesAxis = "numeric";
          return {
              values:{
                  keys:seriesKeys,
                  type:seriesAxis
              },
              category:{
                  keys:[catKey],
                  type:this.get("categoryType")
              }
          };
      },
  
      /**
       * Returns an object literal containing a categoryItem and a valueItem for a given series index.
       *
       * @method getSeriesItem
       * @param series Reference to a series.
       * @param index Index of the specified item within a series.
       * @return Object
       */
      getSeriesItems: function(series, index)
      {
          var categoryItem = {
                  axis: series.get("categoryAxis"),
                  key: series.get("categoryKey"),
                  displayName: series.get("categoryDisplayName")
              },
              valueItem = {
                  axis: series.get("valueAxis"),
                  key: series.get("valueKey"),
                  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(e)
      {
          this._redraw();
      },
  
      /**
       * Redraws the chart instance.
       *
       * @method _redraw
       * @private
       */
      _redraw: function()
      {
          var graph = this.get("graph"),
              w = this.get("width"),
              h = this.get("height"),
              dimension;
          if(graph)
          {
              dimension = Math.min(w, h);
              graph.set("width", dimension);
              graph.set("height", dimension);
          }
      },
  
      /**
       * Formats tooltip text for a pie chart.
       *
       * @method _tooltipLabelFunction
       * @param {Object} categoryItem An object containing the following:
       *  <dl>
       *      <dt>axis</dt><dd>The axis to which the category is bound.</dd>
       *      <dt>displayName</dt><dd>The display name set to the category (defaults to key if not provided)</dd>
       *      <dt>key</dt><dd>The key of the category.</dd>
       *      <dt>value</dt><dd>The value of the category</dd>
       *  </dl>
       * @param {Object} valueItem An object containing the following:
       *  <dl>
       *      <dt>axis</dt><dd>The axis to which the item's series is bound.</dd>
       *      <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd>
       *      <dt>key</dt><dd>The key for the series.</dd>
       *      <dt>value</dt><dd>The value for the series item.</dd>
       *  </dl>
       * @param {Number} itemIndex The index of the item within the series.
       * @param {CartesianSeries} series The `PieSeries` instance of the item.
       * @param {Number} seriesIndex The index of the series in the `seriesCollection`.
       * @return {HTML}
       * @private
       */
      _tooltipLabelFunction: function(categoryItem, valueItem, itemIndex, series, seriesIndex)
      {
          var msg = DOCUMENT.createElement("div"),
              total = series.getTotalValues(),
              pct = Math.round((valueItem.value / total) * 10000)/100;
          msg.appendChild(DOCUMENT.createTextNode(categoryItem.displayName +
          ": " + categoryItem.axis.get("labelFunction").apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")])));
          msg.appendChild(DOCUMENT.createElement("br"));
          msg.appendChild(DOCUMENT.createTextNode(valueItem.displayName +
          ": " + valueItem.axis.get("labelFunction").apply(this, [valueItem.value, valueItem.axis.get("labelFormat")])));
          msg.appendChild(DOCUMENT.createElement("br"));
          msg.appendChild(DOCUMENT.createTextNode(pct + "%"));
          return msg;
      },
  
      /**
       * 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 = "",
              categoryItem,
              items,
              series,
              valueItem,
              seriesIndex = 0,
              itemIndex = this._itemIndex,
              seriesCollection = this.get("seriesCollection"),
              len,
              total,
              pct,
              markers;
          series = this.getSeries(parseInt(seriesIndex, 10));
          markers = series.get("markers");
          len = markers && markers.length ? markers.length : 0;
          if(key === 37)
          {
              itemIndex = itemIndex > 0 ? itemIndex - 1 : len - 1;
          }
          else if(key === 39)
          {
              itemIndex = itemIndex >= len - 1 ? 0 : itemIndex + 1;
          }
          this._itemIndex = itemIndex;
          items = this.getSeriesItems(series, itemIndex);
          categoryItem = items.category;
          valueItem = items.value;
          total = series.getTotalValues();
          pct = Math.round((valueItem.value / total) * 10000)/100;
          if(categoryItem && valueItem)
          {
              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")]) + ", ";
              msg += "Percent of total " + valueItem.displayName + ": " + pct + "%,";
          }
          else
          {
              msg += "No data available,";
          }
          msg += (itemIndex + 1) + " of " + len + ". ";
          return msg;
      }
  }, {
      ATTRS: {
          /**
           * Sets the aria description for the chart.
           *
           * @attribute ariaDescription
           * @type String
           */
          ariaDescription: {
              value: "Use the left and right keys to navigate through items.",
  
              setter: function(val)
              {
                  if(this._description)
                  {
                      this._description.setContent("");
                      this._description.appendChild(DOCUMENT.createTextNode(val));
                  }
                  return val;
              }
          },
  
          /**
           * Axes to appear in the chart.
           *
           * @attribute axes
           * @type Object
           */
          axes: {
              getter: function()
              {
                  return this._axes;
              },
  
              setter: function(val)
              {
                  this._parseAxes(val);
              }
          },
  
          /**
           * Collection of series to appear on the chart. This can be an array of Series instances or object literals
           * used to describe a Series instance.
           *
           * @attribute seriesCollection
           * @type Array
           */
          seriesCollection: {
              getter: function()
              {
                  return this._getSeriesCollection();
              },
  
              setter: function(val)
              {
                  return this._setSeriesCollection(val);
              }
          },
  
          /**
           * Type of chart when there is no series collection specified.
           *
           * @attribute type
           * @type String
           */
          type: {
              value: "pie"
          }
      }
  });
  /**
   * The Chart class is the basic application used to create a chart.
   *
   * @class Chart
   * @constructor
   * @submodule charts-base
   */
  function Chart(cfg)
  {
      if(cfg.type != "pie")
      {
          return new Y.CartesianChart(cfg);
      }
      else
      {
          return new Y.PieChart(cfg);
      }
  }
  Y.Chart = Chart;
  
  
  }, '@VERSION@', {
      "requires": [
          "dom",
          "event-mouseenter",
          "event-touch",
          "graphics-group",
          "axes",
          "series-cartesian",
          "series-pie",
          "series-cartesian-stacked"
      ]
  });
  