diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/build/charts-base/charts-base.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/media/js/lib/yui/yui_3.10.3/build/charts-base/charts-base.js Tue Jul 16 14:29:46 2013 +0200
@@ -0,0 +1,5119 @@
+/*
+YUI 3.10.3 (build 2fb5187)
+Copyright 2013 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+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,
+ _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"),
+ length,
+ lineFunction;
+ 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
+ });
+ if(direction === "vertical")
+ {
+ lineFunction = this._verticalLine;
+ length = h;
+ }
+ else
+ {
+ lineFunction = this._horizontalLine;
+ length = w;
+ }
+ for(i = 0; i < l; i = i + 1)
+ {
+ lineFunction(path, points[i], length);
+ }
+ 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
+ * @private
+ */
+ _horizontalLine: function(path, pt, w)
+ {
+ 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} h Height of the Graph
+ * @private
+ */
+ _verticalLine: function(path, pt, 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.SeriesBase)
+ {
+ 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,
+ SeriesClass,
+ series;
+ seriesData.graph = this;
+ if(!seriesTypes.hasOwnProperty(type))
+ {
+ seriesTypes[type] = [];
+ }
+ typeSeriesCollection = seriesTypes[type];
+ seriesData.graph = this;
+ seriesData.order = typeSeriesCollection.length;
+ seriesData.graphOrder = seriesCollection.length;
+ SeriesClass = this._getSeries(seriesData.type);
+ series = new SeriesClass(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:
+ *
+ *
+ * | Key Value | Class |
+ * | line | Y.LineSeries |
+ * | column | Y.ColumnSeries |
+ * | bar | Y.BarSeries |
+ * | area | Y.AreaSeries |
+ * | 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 |
+ *
+ *
+ * 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()
+ {
+ 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:
+ *
+ * - background
- An object containing the following values:
+ *
+ * - fill
- Defines the style properties for the fill. Contains the following values:
+ *
+ * - color
- Color of the fill. The default value is #faf9f2.
+ * - alpha
- Number from 0 to 1 indicating the opacity of the background fill.
+ * The default value is 1.
+ *
+ *
+ * - border
- Defines the style properties for the border. Contains the following values:
+ *
+ * - color
- Color of the border. The default value is #dad8c9.
+ * - alpha
- Number from 0 to 1 indicating the opacity of the background border.
+ * The default value is 1.
+ * - weight
- Number indicating the width of the border. The default value is 1.
+ *
+ *
+ *
+ *
+ *
+ *
+ * @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.set("seriesKeys", this._buildSeriesKeys(defDataProvider), {src: "internal"});
+ }
+ return defDataProvider;
+ },
+
+ setter: function(val)
+ {
+ var dataProvider = this._setDataValues(val);
+ if(!this._seriesKeysExplicitlySet)
+ {
+ this.set("seriesKeys", this._buildSeriesKeys(dataProvider), {src: "internal"});
+ }
+ 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: {
+ lazyAdd: false,
+
+ setter: function(val)
+ {
+ var opts = arguments[2];
+ if(!val || (opts && opts.src && opts.src === "internal"))
+ {
+ this._seriesKeysExplicitlySet = false;
+ }
+ else
+ {
+ this._seriesKeysExplicitlySet = true;
+ }
+ 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.
+ * Contains the following properties:
+ *
+ * - node
- Reference to the actual dom node
+ * - showEvent
- Event that should trigger the tooltip
+ * - hideEvent
- Event that should trigger the removal of a tooltip (can be an event or an array of events)
+ * - styles
- A hash of style properties that will be applied to the tooltip node
+ * - show
- Indicates whether or not to show the tooltip
+ * - markerEventHandler
- Displays and hides tooltip based on marker events
+ * - planarEventHandler
- Displays and hides tooltip based on planar events
+ * - markerLabelFunction
- Reference to the function used to format a marker event triggered tooltip's text.
+ * The method contains the following arguments:
+ *
+ * - categoryItem
- An object containing the following:
+ *
+ * - axis
- The axis to which the category is bound.
+ * - displayName
- The display name set to the category (defaults to key if not provided).
+ * - key
- The key of the category.
+ * - value
- The value of the category.
+ *
+ *
+ * - valueItem
- An object containing the following:
+ *
+ * - axis
- The axis to which the item's series is bound.
+ * - displayName
- The display name of the series. (defaults to key if not provided)
+ * - key
- The key for the series.
+ * - value
- The value for the series item.
+ *
+ *
+ * - itemIndex
- The index of the item within the series.
+ * - series
- The `CartesianSeries` instance of the item.
+ * - seriesIndex
- The index of the series in the `seriesCollection`.
+ *
+ * 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.
+ *
+ * - planarLabelFunction
- Reference to the function used to format a planar event triggered tooltip's text
+ *
+ * - categoryAxis
- `CategoryAxis` Reference to the categoryAxis of the chart.
+ *
- 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:
+ *
+ * - axis
- The value axis of the series.
+ * - key
- The key for the series.
+ * - value
- The value for the series item.
+ * - displayName
- The display name of the series. (defaults to key if not provided)
+ *
+ *
+ * - index
- The index of the item within its series.
+ * - seriesArray
- Array of series instances for each value item.
+ * - seriesIndex
- The index of the series in the `seriesCollection`.
+ *
+ *
+ *
+ * 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.
+ *
+ * setTextFunctionMethod that writes content returned from `planarLabelFunction` or `markerLabelFunction` into the
+ * the tooltip node. Has the following signature:
+ *
+ * - label
- The `HTMLElement` that the content is to be added.
+ * - val
- 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.
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ * - category
- Specifies a `CategoryAxis`.
+ * - time
- Specifies a `TimeAxis
+ *
+ *
+ * @attribute categoryType
+ * @type String
+ * @default category
+ */
+ categoryType:{
+ value:"category"
+ },
+
+ /**
+ * Indicates the the type of interactions that will fire events.
+ *
+ *
+ * - marker
- Events will be broadcasted when the mouse interacts with individual markers.
+ * - planar
- Events will be broadcasted when the mouse intersects the plane of any markers on the chart.
+ * - none
- No events will be broadcasted.
+ *
+ *
+ * @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() {
+ 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(""),
+ 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:
+ *
+ * - categoryItem
- Hash containing information about the category `Axis`.
+ * - valueItem
- Hash containing information about the value `Axis`.
+ * - node
- The dom node of the marker.
+ * - x
- The x-coordinate of the mouse in relation to the Chart.
+ * - y
- The y-coordinate of the mouse in relation to the Chart.
+ * - series
- Reference to the series of the marker.
+ * - index
- Index of the marker in the series.
+ * - seriesIndex
- The `order` of the marker's series.
+ *
+ */
+ /**
+ * 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:
+ *
+ * - categoryItem
- Hash containing information about the category `Axis`.
+ * - valueItem
- Hash containing information about the value `Axis`.
+ * - node
- The dom node of the marker.
+ * - x
- The x-coordinate of the mouse in relation to the Chart.
+ * - y
- The y-coordinate of the mouse in relation to the Chart.
+ * - series
- Reference to the series of the marker.
+ * - index
- Index of the marker in the series.
+ * - seriesIndex
- The `order` of the marker's series.
+ *
+ */
+ /**
+ * 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:
+ *
+ * - categoryItem
- Hash containing information about the category `Axis`.
+ * - valueItem
- Hash containing information about the value `Axis`.
+ * - node
- The dom node of the marker.
+ * - x
- The x-coordinate of the mouse in relation to the Chart.
+ * - y
- The y-coordinate of the mouse in relation to the Chart.
+ * - series
- Reference to the series of the marker.
+ * - index
- Index of the marker in the series.
+ * - seriesIndex
- The `order` of the marker's series.
+ *
+ */
+ /**
+ * 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:
+ *
+ * - categoryItem
- Hash containing information about the category `Axis`.
+ * - valueItem
- Hash containing information about the value `Axis`.
+ * - node
- The dom node of the marker.
+ * - x
- The x-coordinate of the mouse in relation to the Chart.
+ * - y
- The y-coordinate of the mouse in relation to the Chart.
+ * - series
- Reference to the series of the marker.
+ * - index
- Index of the marker in the series.
+ * - seriesIndex
- The `order` of the marker's series.
+ *
+ */
+ /**
+ * 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:
+ *
+ * - categoryItem
- Hash containing information about the category `Axis`.
+ * - valueItem
- Hash containing information about the value `Axis`.
+ * - node
- The dom node of the marker.
+ * - x
- The x-coordinate of the mouse in relation to the Chart.
+ * - y
- The y-coordinate of the mouse in relation to the Chart.
+ * - pageX
- The x location of the event on the page (including scroll)
+ * - pageY
- The y location of the event on the page (including scroll)
+ * - series
- Reference to the series of the marker.
+ * - index
- Index of the marker in the series.
+ * - seriesIndex
- The `order` of the marker's series.
+ * - originEvent
- Underlying dom event.
+ *
+ */
+ 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:
+ *
+ * - axis
- The value axis of the series.
+ * - key
- The key for the series.
+ * - value
- The value for the series item.
+ * - displayName
- The display name of the series. (defaults to key if not provided)
+ *
+ * @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)
+ {
+ 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:
+ *
+ * - axis
- The axis to which the category is bound.
+ * - displayName
- The display name set to the category (defaults to key if not provided)
+ * - key
- The key of the category.
+ * - value
- The value of the category
+ *
+ * @param {Object} valueItem An object containing the following:
+ *
+ * - axis
- The axis to which the item's series is bound.
+ * - displayName
- The display name of the series. (defaults to key if not provided)
+ * - key
- The key for the series.
+ * - value
- The value for the series item.
+ *
+ * @return {String | HTML}
+ * @private
+ */
+ _tooltipLabelFunction: function(categoryItem, valueItem)
+ {
+ 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()
+ {
+ 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, Y.Renderer], {
+ /**
+ * @method renderUI
+ * @private
+ */
+ renderUI: function()
+ {
+ var bb = this.get("boundingBox"),
+ cb = this.get("contentBox"),
+ tt = this.get("tooltip"),
+ overlay,
+ overlayClass = _getClassName("overlay");
+ //move the position = absolute logic to a class file
+ bb.setStyle("position", "absolute");
+ cb.setStyle("position", "absolute");
+ this._addAxes();
+ this._addGridlines();
+ this._addSeries();
+ if(tt && tt.show)
+ {
+ this._addTooltip();
+ }
+ if(this.get("interactionType") === "planar")
+ {
+ overlay = DOCUMENT.createElement("div");
+ this.get("contentBox").appendChild(overlay);
+ this._overlay = Y.one(overlay);
+ this._overlay.set("id", this.get("id") + "_overlay");
+ this._overlay.setStyle("position", "absolute");
+ this._overlay.setStyle("background", "#fff");
+ this._overlay.setStyle("opacity", 0);
+ this._overlay.addClass(overlayClass);
+ this._overlay.setStyle("zIndex", 4);
+ }
+ this._setAriaElements(bb, cb);
+ this._redraw();
+ },
+
+ /**
+ * When `interactionType` is set to `planar`, listens for mouse move events and fires `planarEvent:mouseover` or `planarEvent:mouseout`
+ * depending on the position of the mouse in relation to data points on the `Chart`.
+ *
+ * @method _planarEventDispatcher
+ * @param {Object} e Event object.
+ * @private
+ */
+ _planarEventDispatcher: function(e)
+ {
+ var graph = this.get("graph"),
+ bb = this.get("boundingBox"),
+ cb = graph.get("contentBox"),
+ isTouch = e && e.hasOwnProperty("changedTouches"),
+ pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
+ pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
+ posX = pageX - bb.getX(),
+ posY = pageY - bb.getY(),
+ offset = {
+ x: pageX - cb.getX(),
+ y: pageY - cb.getY()
+ },
+ sc = graph.get("seriesCollection"),
+ series,
+ i = 0,
+ index,
+ oldIndex = this._selectedIndex,
+ item,
+ items = [],
+ categoryItems = [],
+ valueItems = [],
+ direction = this.get("direction"),
+ hasMarkers,
+ catAxis,
+ valAxis,
+ coord,
+ //data columns and area data could be created on a graph level
+ markerPlane,
+ len,
+ coords;
+ e.halt(true);
+ if(direction === "horizontal")
+ {
+ catAxis = "x";
+ valAxis = "y";
+ }
+ else
+ {
+ valAxis = "x";
+ catAxis = "y";
+ }
+ coord = offset[catAxis];
+ if(sc)
+ {
+ len = sc.length;
+ while(i < len && !markerPlane)
+ {
+ if(sc[i])
+ {
+ markerPlane = sc[i].get(catAxis + "MarkerPlane");
+ }
+ i++;
+ }
+ }
+ if(markerPlane)
+ {
+ len = markerPlane.length;
+ for(i = 0; i < len; ++i)
+ {
+ if(coord <= markerPlane[i].end && coord >= markerPlane[i].start)
+ {
+ index = i;
+ break;
+ }
+ }
+ len = sc.length;
+ for(i = 0; i < len; ++i)
+ {
+ series = sc[i];
+ coords = series.get(valAxis + "coords");
+ hasMarkers = series.get("markers");
+ if(hasMarkers && !isNaN(oldIndex) && oldIndex > -1)
+ {
+ series.updateMarkerState("mouseout", oldIndex);
+ }
+ if(coords && coords[index] > -1)
+ {
+ if(hasMarkers && !isNaN(index) && index > -1)
+ {
+ series.updateMarkerState("mouseover", index);
+ }
+ item = this.getSeriesItems(series, index);
+ categoryItems.push(item.category);
+ valueItems.push(item.value);
+ items.push(series);
+ }
+
+ }
+ this._selectedIndex = index;
+
+ /**
+ * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseover event.
+ *
+ *
+ * @event planarEvent:mouseover
+ * @preventable false
+ * @param {EventFacade} e Event facade with the following additional
+ * properties:
+ *
+ * - categoryItem
- An array of hashes, each containing information about the category `Axis` of each marker
+ * whose plane has been intersected.
+ * - valueItem
- An array of hashes, each containing information about the value `Axis` of each marker whose
+ * plane has been intersected.
+ * - x
- The x-coordinate of the mouse in relation to the Chart.
+ * - y
- The y-coordinate of the mouse in relation to the Chart.
+ * - pageX
- The x location of the event on the page (including scroll)
+ * - pageY
- The y location of the event on the page (including scroll)
+ * - items
- An array including all the series which contain a marker whose plane has been intersected.
+ * - index
- Index of the markers in their respective series.
+ * - originEvent
- Underlying dom event.
+ *
+ */
+ /**
+ * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseout event.
+ *
+ * @event planarEvent:mouseout
+ * @preventable false
+ * @param {EventFacade} e
+ */
+ if(index > -1)
+ {
+ this.fire("planarEvent:mouseover", {
+ categoryItem:categoryItems,
+ valueItem:valueItems,
+ x:posX,
+ y:posY,
+ pageX:pageX,
+ pageY:pageY,
+ items:items,
+ index:index,
+ originEvent:e
+ });
+ }
+ else
+ {
+ this.fire("planarEvent:mouseout");
+ }
+ }
+ },
+
+ /**
+ * Indicates the default series type for the chart.
+ *
+ * @property _type
+ * @type {String}
+ * @private
+ */
+ _type: "combo",
+
+ /**
+ * Queue of axes instances that will be updated. This method is used internally to determine when all axes have been updated.
+ *
+ * @property _itemRenderQueue
+ * @type Array
+ * @private
+ */
+ _itemRenderQueue: null,
+
+ /**
+ * Adds an `Axis` instance to the `_itemRenderQueue`.
+ *
+ * @method _addToAxesRenderQueue
+ * @param {Axis} axis An `Axis` instance.
+ * @private
+ */
+ _addToAxesRenderQueue: function(axis)
+ {
+ if(!this._itemRenderQueue)
+ {
+ this._itemRenderQueue = [];
+ }
+ if(Y.Array.indexOf(this._itemRenderQueue, axis) < 0)
+ {
+ this._itemRenderQueue.push(axis);
+ }
+ },
+
+ /**
+ * Adds axis instance to the appropriate array based on position
+ *
+ * @method _addToAxesCollection
+ * @param {String} position The position of the axis
+ * @param {Axis} axis The `Axis` instance
+ */
+ _addToAxesCollection: function(position, axis)
+ {
+ var axesCollection = this.get(position + "AxesCollection");
+ if(!axesCollection)
+ {
+ axesCollection = [];
+ this.set(position + "AxesCollection", axesCollection);
+ }
+ axesCollection.push(axis);
+ },
+
+ /**
+ * Returns the default value for the `seriesCollection` attribute.
+ *
+ * @method _getDefaultSeriesCollection
+ * @param {Array} val Array containing either `CartesianSeries` instances or objects containing data to construct series instances.
+ * @return Array
+ * @private
+ */
+ _getDefaultSeriesCollection: function()
+ {
+ var seriesCollection,
+ dataProvider = this.get("dataProvider");
+ if(dataProvider)
+ {
+ seriesCollection = this._parseSeriesCollection();
+ }
+ return seriesCollection;
+ },
+
+ /**
+ * Parses and returns a series collection from an object and default properties.
+ *
+ * @method _parseSeriesCollection
+ * @param {Object} val Object contain properties for series being set.
+ * @return Object
+ * @private
+ */
+ _parseSeriesCollection: function(val)
+ {
+ var dir = this.get("direction"),
+ seriesStyles = this.get("styles").series,
+ stylesAreArray = seriesStyles && Y_Lang.isArray(seriesStyles),
+ stylesIndex,
+ setStyles,
+ globalStyles,
+ sc = [],
+ catAxis,
+ valAxis,
+ tempKeys = [],
+ series,
+ seriesKeys = this.get("seriesKeys").concat(),
+ i,
+ index,
+ l,
+ type = this.get("type"),
+ key,
+ catKey,
+ seriesKey,
+ graph,
+ orphans = [],
+ categoryKey = this.get("categoryKey"),
+ showMarkers = this.get("showMarkers"),
+ showAreaFill = this.get("showAreaFill"),
+ showLines = this.get("showLines");
+ val = val ? val.concat() : [];
+ if(dir === "vertical")
+ {
+ catAxis = "yAxis";
+ catKey = "yKey";
+ valAxis = "xAxis";
+ seriesKey = "xKey";
+ }
+ else
+ {
+ catAxis = "xAxis";
+ catKey = "xKey";
+ valAxis = "yAxis";
+ seriesKey = "yKey";
+ }
+ l = val.length;
+ while(val && val.length > 0)
+ {
+ series = val.shift();
+ key = this._getBaseAttribute(series, seriesKey);
+ if(key)
+ {
+ index = Y.Array.indexOf(seriesKeys, key);
+ if(index > -1)
+ {
+ seriesKeys.splice(index, 1);
+ tempKeys.push(key);
+ sc.push(series);
+ }
+ else
+ {
+ orphans.push(series);
+ }
+ }
+ else
+ {
+ orphans.push(series);
+ }
+ }
+ while(orphans.length > 0)
+ {
+ series = orphans.shift();
+ if(seriesKeys.length > 0)
+ {
+ key = seriesKeys.shift();
+ this._setBaseAttribute(series, seriesKey, key);
+ tempKeys.push(key);
+ sc.push(series);
+ }
+ else if(series instanceof Y.CartesianSeries)
+ {
+ series.destroy(true);
+ }
+ }
+ if(seriesKeys.length > 0)
+ {
+ tempKeys = tempKeys.concat(seriesKeys);
+ }
+ l = tempKeys.length;
+ for(i = 0; i < l; ++i)
+ {
+ series = sc[i] || {type:type};
+ if(series instanceof Y.CartesianSeries)
+ {
+ this._parseSeriesAxes(series);
+ }
+ else
+ {
+ series[catKey] = series[catKey] || categoryKey;
+ series[seriesKey] = series[seriesKey] || seriesKeys.shift();
+ series[catAxis] = this._getCategoryAxis();
+ series[valAxis] = this._getSeriesAxis(series[seriesKey]);
+
+ series.type = series.type || type;
+ series.direction = series.direction || dir;
+
+ if(series.type === "combo" ||
+ series.type === "stackedcombo" ||
+ series.type === "combospline" ||
+ series.type === "stackedcombospline")
+ {
+ if(showAreaFill !== null)
+ {
+ series.showAreaFill = (series.showAreaFill !== null && series.showAreaFill !== undefined) ?
+ series.showAreaFill : showAreaFill;
+ }
+ if(showMarkers !== null)
+ {
+ series.showMarkers = (series.showMarkers !== null && series.showMarkers !== undefined) ? series.showMarkers : showMarkers;
+ }
+ if(showLines !== null)
+ {
+ series.showLines = (series.showLines !== null && series.showLines !== undefined) ? series.showLines : showLines;
+ }
+ }
+ if(seriesStyles)
+ {
+ stylesIndex = stylesAreArray ? i : series[seriesKey];
+ globalStyles = seriesStyles[stylesIndex];
+ if(globalStyles)
+ {
+ setStyles = series.styles;
+ if(setStyles)
+ {
+ series.styles = this._mergeStyles(setStyles, globalStyles);
+ }
+ else
+ {
+ series.styles = globalStyles;
+ }
+ }
+ }
+ sc[i] = series;
+ }
+ }
+ if(sc)
+ {
+ graph = this.get("graph");
+ graph.set("seriesCollection", sc);
+ sc = graph.get("seriesCollection");
+ }
+ return sc;
+ },
+
+ /**
+ * Parse and sets the axes for a series instance.
+ *
+ * @method _parseSeriesAxes
+ * @param {CartesianSeries} series A `CartesianSeries` instance.
+ * @private
+ */
+ _parseSeriesAxes: function(series)
+ {
+ var axes = this.get("axes"),
+ xAxis = series.get("xAxis"),
+ yAxis = series.get("yAxis"),
+ YAxis = Y.Axis,
+ axis;
+ if(xAxis && !(xAxis instanceof YAxis) && Y_Lang.isString(xAxis) && axes.hasOwnProperty(xAxis))
+ {
+ axis = axes[xAxis];
+ if(axis instanceof YAxis)
+ {
+ series.set("xAxis", axis);
+ }
+ }
+ if(yAxis && !(yAxis instanceof YAxis) && Y_Lang.isString(yAxis) && axes.hasOwnProperty(yAxis))
+ {
+ axis = axes[yAxis];
+ if(axis instanceof YAxis)
+ {
+ series.set("yAxis", axis);
+ }
+ }
+
+ },
+
+ /**
+ * Returns the category axis instance for the chart.
+ *
+ * @method _getCategoryAxis
+ * @return Axis
+ * @private
+ */
+ _getCategoryAxis: function()
+ {
+ var axis,
+ axes = this.get("axes"),
+ categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey");
+ axis = axes[categoryAxisName];
+ return axis;
+ },
+
+ /**
+ * Returns the value axis for a series.
+ *
+ * @method _getSeriesAxis
+ * @param {String} key The key value used to determine the axis instance.
+ * @return Axis
+ * @private
+ */
+ _getSeriesAxis:function(key, axisName)
+ {
+ var axes = this.get("axes"),
+ i,
+ keys,
+ axis;
+ if(axes)
+ {
+ if(axisName && axes.hasOwnProperty(axisName))
+ {
+ axis = axes[axisName];
+ }
+ else
+ {
+ for(i in axes)
+ {
+ if(axes.hasOwnProperty(i))
+ {
+ keys = axes[i].get("keys");
+ if(keys && keys.hasOwnProperty(key))
+ {
+ axis = axes[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+ return axis;
+ },
+
+ /**
+ * Gets an attribute from an object, using a getter for Base objects and a property for object
+ * literals. Used for determining attributes from series/axis references which can be an actual class instance
+ * or a hash of properties that will be used to create a class instance.
+ *
+ * @method _getBaseAttribute
+ * @param {Object} item Object or instance in which the attribute resides.
+ * @param {String} key Attribute whose value will be returned.
+ * @return Object
+ * @private
+ */
+ _getBaseAttribute: function(item, key)
+ {
+ if(item instanceof Y.Base)
+ {
+ return item.get(key);
+ }
+ if(item.hasOwnProperty(key))
+ {
+ return item[key];
+ }
+ return null;
+ },
+
+ /**
+ * Sets an attribute on an object, using a setter of Base objects and a property for object
+ * literals. Used for setting attributes on a Base class, either directly or to be stored in an object literal
+ * for use at instantiation.
+ *
+ * @method _setBaseAttribute
+ * @param {Object} item Object or instance in which the attribute resides.
+ * @param {String} key Attribute whose value will be assigned.
+ * @param {Object} value Value to be assigned to the attribute.
+ * @private
+ */
+ _setBaseAttribute: function(item, key, value)
+ {
+ if(item instanceof Y.Base)
+ {
+ item.set(key, value);
+ }
+ else
+ {
+ item[key] = value;
+ }
+ },
+
+ /**
+ * Creates `Axis` instances.
+ *
+ * @method _setAxes
+ * @param {Object} val Object containing `Axis` instances or objects in which to construct `Axis` instances.
+ * @return Object
+ * @private
+ */
+ _setAxes: function(val)
+ {
+ var hash = this._parseAxes(val),
+ axes = {},
+ axesAttrs = {
+ edgeOffset: "edgeOffset",
+ calculateEdgeOffset: "calculateEdgeOffset",
+ position: "position",
+ overlapGraph:"overlapGraph",
+ labelValues: "labelValues",
+ hideFirstMajorUnit: "hideFirstMajorUnit",
+ hideLastMajorUnit: "hideLastMajorUnit",
+ labelFunction:"labelFunction",
+ labelFunctionScope:"labelFunctionScope",
+ labelFormat:"labelFormat",
+ appendLabelFunction: "appendLabelFunction",
+ appendTitleFunction: "appendTitleFunction",
+ maximum:"maximum",
+ minimum:"minimum",
+ roundingMethod:"roundingMethod",
+ alwaysShowZero:"alwaysShowZero",
+ title:"title",
+ width:"width",
+ height:"height"
+ },
+ dp = this.get("dataProvider"),
+ ai,
+ i,
+ pos,
+ axis,
+ axisPosition,
+ dh,
+ AxisClass,
+ config,
+ axesCollection;
+ for(i in hash)
+ {
+ if(hash.hasOwnProperty(i))
+ {
+ dh = hash[i];
+ if(dh instanceof Y.Axis)
+ {
+ axis = dh;
+ }
+ else
+ {
+ axis = null;
+ config = {};
+ config.dataProvider = dh.dataProvider || dp;
+ config.keys = dh.keys;
+
+ if(dh.hasOwnProperty("roundingUnit"))
+ {
+ config.roundingUnit = dh.roundingUnit;
+ }
+ pos = dh.position;
+ if(dh.styles)
+ {
+ config.styles = dh.styles;
+ }
+ config.position = dh.position;
+ for(ai in axesAttrs)
+ {
+ if(axesAttrs.hasOwnProperty(ai) && dh.hasOwnProperty(ai))
+ {
+ config[ai] = dh[ai];
+ }
+ }
+
+ //only check for existing axis if we constructed the default axes already
+ if(val)
+ {
+ axis = this.getAxisByKey(i);
+ }
+
+ if(axis && axis instanceof Y.Axis)
+ {
+ axisPosition = axis.get("position");
+ if(pos !== axisPosition)
+ {
+ if(axisPosition !== "none")
+ {
+ axesCollection = this.get(axisPosition + "AxesCollection");
+ axesCollection.splice(Y.Array.indexOf(axesCollection, axis), 1);
+ }
+ if(pos !== "none")
+ {
+ this._addToAxesCollection(pos, axis);
+ }
+ }
+ axis.setAttrs(config);
+ }
+ else
+ {
+ AxisClass = this._getAxisClass(dh.type);
+ axis = new AxisClass(config);
+ axis.after("axisRendered", Y.bind(this._itemRendered, this));
+ }
+ }
+
+ if(axis)
+ {
+ axesCollection = this.get(pos + "AxesCollection");
+ if(axesCollection && Y.Array.indexOf(axesCollection, axis) > 0)
+ {
+ axis.set("overlapGraph", false);
+ }
+ axes[i] = axis;
+ }
+ }
+ }
+ return axes;
+ },
+
+ /**
+ * Adds axes to the chart.
+ *
+ * @method _addAxes
+ * @private
+ */
+ _addAxes: function()
+ {
+ var axes = this.get("axes"),
+ i,
+ axis,
+ pos,
+ w = this.get("width"),
+ h = this.get("height"),
+ node = Y.Node.one(this._parentNode);
+ if(!this._axesCollection)
+ {
+ this._axesCollection = [];
+ }
+ for(i in axes)
+ {
+ if(axes.hasOwnProperty(i))
+ {
+ axis = axes[i];
+ if(axis instanceof Y.Axis)
+ {
+ if(!w)
+ {
+ this.set("width", node.get("offsetWidth"));
+ w = this.get("width");
+ }
+ if(!h)
+ {
+ this.set("height", node.get("offsetHeight"));
+ h = this.get("height");
+ }
+ this._addToAxesRenderQueue(axis);
+ pos = axis.get("position");
+ if(!this.get(pos + "AxesCollection"))
+ {
+ this.set(pos + "AxesCollection", [axis]);
+ }
+ else
+ {
+ this.get(pos + "AxesCollection").push(axis);
+ }
+ this._axesCollection.push(axis);
+ if(axis.get("keys").hasOwnProperty(this.get("categoryKey")))
+ {
+ this.set("categoryAxis", axis);
+ }
+ axis.render(this.get("contentBox"));
+ }
+ }
+ }
+ },
+
+ /**
+ * Renders the Graph.
+ *
+ * @method _addSeries
+ * @private
+ */
+ _addSeries: function()
+ {
+ var graph = this.get("graph");
+ graph.render(this.get("contentBox"));
+
+ },
+
+ /**
+ * Adds gridlines to the chart.
+ *
+ * @method _addGridlines
+ * @private
+ */
+ _addGridlines: function()
+ {
+ var graph = this.get("graph"),
+ hgl = this.get("horizontalGridlines"),
+ vgl = this.get("verticalGridlines"),
+ direction = this.get("direction"),
+ leftAxesCollection = this.get("leftAxesCollection"),
+ rightAxesCollection = this.get("rightAxesCollection"),
+ bottomAxesCollection = this.get("bottomAxesCollection"),
+ topAxesCollection = this.get("topAxesCollection"),
+ seriesAxesCollection,
+ catAxis = this.get("categoryAxis"),
+ hAxis,
+ vAxis;
+ if(this._axesCollection)
+ {
+ seriesAxesCollection = this._axesCollection.concat();
+ seriesAxesCollection.splice(Y.Array.indexOf(seriesAxesCollection, catAxis), 1);
+ }
+ if(hgl)
+ {
+ if(leftAxesCollection && leftAxesCollection[0])
+ {
+ hAxis = leftAxesCollection[0];
+ }
+ else if(rightAxesCollection && rightAxesCollection[0])
+ {
+ hAxis = rightAxesCollection[0];
+ }
+ else
+ {
+ hAxis = direction === "horizontal" ? catAxis : seriesAxesCollection[0];
+ }
+ if(!this._getBaseAttribute(hgl, "axis") && hAxis)
+ {
+ this._setBaseAttribute(hgl, "axis", hAxis);
+ }
+ if(this._getBaseAttribute(hgl, "axis"))
+ {
+ graph.set("horizontalGridlines", hgl);
+ }
+ }
+ if(vgl)
+ {
+ if(bottomAxesCollection && bottomAxesCollection[0])
+ {
+ vAxis = bottomAxesCollection[0];
+ }
+ else if (topAxesCollection && topAxesCollection[0])
+ {
+ vAxis = topAxesCollection[0];
+ }
+ else
+ {
+ vAxis = direction === "vertical" ? catAxis : seriesAxesCollection[0];
+ }
+ if(!this._getBaseAttribute(vgl, "axis") && vAxis)
+ {
+ this._setBaseAttribute(vgl, "axis", vAxis);
+ }
+ if(this._getBaseAttribute(vgl, "axis"))
+ {
+ graph.set("verticalGridlines", vgl);
+ }
+ }
+ },
+
+ /**
+ * Default Function for the axes attribute.
+ *
+ * @method _getDefaultAxes
+ * @return Object
+ * @private
+ */
+ _getDefaultAxes: function()
+ {
+ var axes;
+ if(this.get("dataProvider"))
+ {
+ axes = this._parseAxes();
+ }
+ return axes;
+ },
+
+ /**
+ * Generates and returns a key-indexed object containing `Axis` instances or objects used to create `Axis` instances.
+ *
+ * @method _parseAxes
+ * @param {Object} axes Object containing `Axis` instances or `Axis` attributes.
+ * @return Object
+ * @private
+ */
+ _parseAxes: function(axes)
+ {
+ var catKey = this.get("categoryKey"),
+ axis,
+ attr,
+ keys,
+ newAxes = {},
+ claimedKeys = [],
+ newKeys = [],
+ categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"),
+ valueAxisName = this.get("valueAxisName"),
+ seriesKeys = this.get("seriesKeys").concat(),
+ i,
+ l,
+ ii,
+ ll,
+ cIndex,
+ direction = this.get("direction"),
+ seriesPosition,
+ categoryPosition,
+ valueAxes = [],
+ seriesAxis = this.get("stacked") ? "stacked" : "numeric";
+ if(direction === "vertical")
+ {
+ seriesPosition = "bottom";
+ categoryPosition = "left";
+ }
+ else
+ {
+ seriesPosition = "left";
+ categoryPosition = "bottom";
+ }
+ if(axes)
+ {
+ for(i in axes)
+ {
+ if(axes.hasOwnProperty(i))
+ {
+ axis = axes[i];
+ keys = this._getBaseAttribute(axis, "keys");
+ attr = this._getBaseAttribute(axis, "type");
+ if(attr === "time" || attr === "category")
+ {
+ categoryAxisName = i;
+ this.set("categoryAxisName", i);
+ if(Y_Lang.isArray(keys) && keys.length > 0)
+ {
+ catKey = keys[0];
+ this.set("categoryKey", catKey);
+ }
+ newAxes[i] = axis;
+ }
+ else if(i === categoryAxisName)
+ {
+ newAxes[i] = axis;
+ }
+ else
+ {
+ newAxes[i] = axis;
+ if(i !== valueAxisName && keys && Y_Lang.isArray(keys))
+ {
+ ll = keys.length;
+ for(ii = 0; ii < ll; ++ii)
+ {
+ claimedKeys.push(keys[ii]);
+ }
+ valueAxes.push(newAxes[i]);
+ }
+ if(!(this._getBaseAttribute(newAxes[i], "type")))
+ {
+ this._setBaseAttribute(newAxes[i], "type", seriesAxis);
+ }
+ if(!(this._getBaseAttribute(newAxes[i], "position")))
+ {
+ this._setBaseAttribute(
+ newAxes[i],
+ "position",
+ this._getDefaultAxisPosition(newAxes[i], valueAxes, seriesPosition)
+ );
+ }
+ }
+ }
+ }
+ }
+ cIndex = Y.Array.indexOf(seriesKeys, catKey);
+ if(cIndex > -1)
+ {
+ seriesKeys.splice(cIndex, 1);
+ }
+ l = seriesKeys.length;
+ for(i = 0; i < l; ++i)
+ {
+ cIndex = Y.Array.indexOf(claimedKeys, seriesKeys[i]);
+ if(cIndex > -1)
+ {
+ newKeys = newKeys.concat(claimedKeys.splice(cIndex, 1));
+ }
+ }
+ claimedKeys = newKeys.concat(claimedKeys);
+ l = claimedKeys.length;
+ for(i = 0; i < l; i = i + 1)
+ {
+ cIndex = Y.Array.indexOf(seriesKeys, claimedKeys[i]);
+ if(cIndex > -1)
+ {
+ seriesKeys.splice(cIndex, 1);
+ }
+ }
+ if(!newAxes.hasOwnProperty(categoryAxisName))
+ {
+ newAxes[categoryAxisName] = {};
+ }
+ if(!(this._getBaseAttribute(newAxes[categoryAxisName], "keys")))
+ {
+ this._setBaseAttribute(newAxes[categoryAxisName], "keys", [catKey]);
+ }
+
+ if(!(this._getBaseAttribute(newAxes[categoryAxisName], "position")))
+ {
+ this._setBaseAttribute(newAxes[categoryAxisName], "position", categoryPosition);
+ }
+
+ if(!(this._getBaseAttribute(newAxes[categoryAxisName], "type")))
+ {
+ this._setBaseAttribute(newAxes[categoryAxisName], "type", this.get("categoryType"));
+ }
+ if(!newAxes.hasOwnProperty(valueAxisName) && seriesKeys && seriesKeys.length > 0)
+ {
+ newAxes[valueAxisName] = {keys:seriesKeys};
+ valueAxes.push(newAxes[valueAxisName]);
+ }
+ if(claimedKeys.length > 0)
+ {
+ if(seriesKeys.length > 0)
+ {
+ seriesKeys = claimedKeys.concat(seriesKeys);
+ }
+ else
+ {
+ seriesKeys = claimedKeys;
+ }
+ }
+ if(newAxes.hasOwnProperty(valueAxisName))
+ {
+ if(!(this._getBaseAttribute(newAxes[valueAxisName], "position")))
+ {
+ this._setBaseAttribute(
+ newAxes[valueAxisName],
+ "position",
+ this._getDefaultAxisPosition(newAxes[valueAxisName], valueAxes, seriesPosition)
+ );
+ }
+ this._setBaseAttribute(newAxes[valueAxisName], "type", seriesAxis);
+ this._setBaseAttribute(newAxes[valueAxisName], "keys", seriesKeys);
+ }
+ if(!this._seriesKeysExplicitlySet)
+ {
+ this.set("seriesKeys", seriesKeys, {src: "internal"});
+ }
+ return newAxes;
+ },
+
+ /**
+ * Determines the position of an axis when one is not specified.
+ *
+ * @method _getDefaultAxisPosition
+ * @param {Axis} axis `Axis` instance.
+ * @param {Array} valueAxes Array of `Axis` instances.
+ * @param {String} position Default position depending on the direction of the chart and type of axis.
+ * @return String
+ * @private
+ */
+ _getDefaultAxisPosition: function(axis, valueAxes, position)
+ {
+ var direction = this.get("direction"),
+ i = Y.Array.indexOf(valueAxes, axis);
+
+ if(valueAxes[i - 1] && valueAxes[i - 1].position)
+ {
+ if(direction === "horizontal")
+ {
+ if(valueAxes[i - 1].position === "left")
+ {
+ position = "right";
+ }
+ else if(valueAxes[i - 1].position === "right")
+ {
+ position = "left";
+ }
+ }
+ else
+ {
+ if (valueAxes[i -1].position === "bottom")
+ {
+ position = "top";
+ }
+ else
+ {
+ position = "bottom";
+ }
+ }
+ }
+ return position;
+ },
+
+
+ /**
+ * Returns an object literal containing a categoryItem and a valueItem for a given series index. Below is the structure of each:
+ *
+ * @method getSeriesItems
+ * @param {CartesianSeries} series Reference to a series.
+ * @param {Number} index Index of the specified item within a series.
+ * @return Object An object literal containing the following:
+ *
+ *
+ * - categoryItem
- Object containing the following data related to the category axis of the series.
+ *
+ * - axis
- Reference to the category axis of the series.
+ * - key
- Category key for the series.
+ * - value
- Value on the axis corresponding to the series index.
+ *
+ *
+ * - valueItem
- Object containing the following data related to the category axis of the series.
+ *
+ * - axis
- Reference to the value axis of the series.
+ * - key
- Value key for the series.
+ * - value
- Value on the axis corresponding to the series index.
+ *
+ *
+ *
+ */
+ getSeriesItems: function(series, index)
+ {
+ var xAxis = series.get("xAxis"),
+ yAxis = series.get("yAxis"),
+ xKey = series.get("xKey"),
+ yKey = series.get("yKey"),
+ categoryItem,
+ valueItem;
+ if(this.get("direction") === "vertical")
+ {
+ categoryItem = {
+ axis:yAxis,
+ key:yKey,
+ value:yAxis.getKeyValueAt(yKey, index)
+ };
+ valueItem = {
+ axis:xAxis,
+ key:xKey,
+ value: xAxis.getKeyValueAt(xKey, index)
+ };
+ }
+ else
+ {
+ valueItem = {
+ axis:yAxis,
+ key:yKey,
+ value:yAxis.getKeyValueAt(yKey, index)
+ };
+ categoryItem = {
+ axis:xAxis,
+ key:xKey,
+ value: xAxis.getKeyValueAt(xKey, index)
+ };
+ }
+ categoryItem.displayName = series.get("categoryDisplayName");
+ valueItem.displayName = series.get("valueDisplayName");
+ categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index);
+ valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index);
+ return {category:categoryItem, value:valueItem};
+ },
+
+ /**
+ * Handler for sizeChanged event.
+ *
+ * @method _sizeChanged
+ * @param {Object} e Event object.
+ * @private
+ */
+ _sizeChanged: function()
+ {
+ if(this._axesCollection)
+ {
+ var ac = this._axesCollection,
+ i = 0,
+ l = ac.length;
+ for(; i < l; ++i)
+ {
+ this._addToAxesRenderQueue(ac[i]);
+ }
+ this._redraw();
+ }
+ },
+
+ /**
+ * Returns the maximum distance in pixels that the extends outside the top bounds of all vertical axes.
+ *
+ * @method _getTopOverflow
+ * @param {Array} set1 Collection of axes to check.
+ * @param {Array} set2 Seconf collection of axes to check.
+ * @param {Number} width Width of the axes
+ * @return Number
+ * @private
+ */
+ _getTopOverflow: function(set1, set2, height)
+ {
+ var i = 0,
+ len,
+ overflow = 0,
+ axis;
+ if(set1)
+ {
+ len = set1.length;
+ for(; i < len; ++i)
+ {
+ axis = set1[i];
+ overflow = Math.max(
+ overflow,
+ Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
+ );
+ }
+ }
+ if(set2)
+ {
+ i = 0;
+ len = set2.length;
+ for(; i < len; ++i)
+ {
+ axis = set2[i];
+ overflow = Math.max(
+ overflow,
+ Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
+ );
+ }
+ }
+ return overflow;
+ },
+
+ /**
+ * Returns the maximum distance in pixels that the extends outside the right bounds of all horizontal axes.
+ *
+ * @method _getRightOverflow
+ * @param {Array} set1 Collection of axes to check.
+ * @param {Array} set2 Seconf collection of axes to check.
+ * @param {Number} width Width of the axes
+ * @return Number
+ * @private
+ */
+ _getRightOverflow: function(set1, set2, width)
+ {
+ var i = 0,
+ len,
+ overflow = 0,
+ axis;
+ if(set1)
+ {
+ len = set1.length;
+ for(; i < len; ++i)
+ {
+ axis = set1[i];
+ overflow = Math.max(
+ overflow,
+ axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
+ );
+ }
+ }
+ if(set2)
+ {
+ i = 0;
+ len = set2.length;
+ for(; i < len; ++i)
+ {
+ axis = set2[i];
+ overflow = Math.max(
+ overflow,
+ axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
+ );
+ }
+ }
+ return overflow;
+ },
+
+ /**
+ * Returns the maximum distance in pixels that the extends outside the left bounds of all horizontal axes.
+ *
+ * @method _getLeftOverflow
+ * @param {Array} set1 Collection of axes to check.
+ * @param {Array} set2 Seconf collection of axes to check.
+ * @param {Number} width Width of the axes
+ * @return Number
+ * @private
+ */
+ _getLeftOverflow: function(set1, set2, width)
+ {
+ var i = 0,
+ len,
+ overflow = 0,
+ axis;
+ if(set1)
+ {
+ len = set1.length;
+ for(; i < len; ++i)
+ {
+ axis = set1[i];
+ overflow = Math.max(
+ overflow,
+ Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
+ );
+ }
+ }
+ if(set2)
+ {
+ i = 0;
+ len = set2.length;
+ for(; i < len; ++i)
+ {
+ axis = set2[i];
+ overflow = Math.max(
+ overflow,
+ Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
+ );
+ }
+ }
+ return overflow;
+ },
+
+ /**
+ * Returns the maximum distance in pixels that the extends outside the bottom bounds of all vertical axes.
+ *
+ * @method _getBottomOverflow
+ * @param {Array} set1 Collection of axes to check.
+ * @param {Array} set2 Seconf collection of axes to check.
+ * @param {Number} height Height of the axes
+ * @return Number
+ * @private
+ */
+ _getBottomOverflow: function(set1, set2, height)
+ {
+ var i = 0,
+ len,
+ overflow = 0,
+ axis;
+ if(set1)
+ {
+ len = set1.length;
+ for(; i < len; ++i)
+ {
+ axis = set1[i];
+ overflow = Math.max(
+ overflow,
+ axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
+ );
+ }
+ }
+ if(set2)
+ {
+ i = 0;
+ len = set2.length;
+ for(; i < len; ++i)
+ {
+ axis = set2[i];
+ overflow = Math.max(
+ overflow,
+ axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
+ );
+ }
+ }
+ return overflow;
+ },
+
+ /**
+ * Redraws and position all the components of the chart instance.
+ *
+ * @method _redraw
+ * @private
+ */
+ _redraw: function()
+ {
+ if(this._drawing)
+ {
+ this._callLater = true;
+ return;
+ }
+ this._drawing = true;
+ this._callLater = false;
+ var w = this.get("width"),
+ h = this.get("height"),
+ leftPaneWidth = 0,
+ rightPaneWidth = 0,
+ topPaneHeight = 0,
+ bottomPaneHeight = 0,
+ leftAxesCollection = this.get("leftAxesCollection"),
+ rightAxesCollection = this.get("rightAxesCollection"),
+ topAxesCollection = this.get("topAxesCollection"),
+ bottomAxesCollection = this.get("bottomAxesCollection"),
+ i = 0,
+ l,
+ axis,
+ graphOverflow = "visible",
+ graph = this.get("graph"),
+ topOverflow,
+ bottomOverflow,
+ leftOverflow,
+ rightOverflow,
+ graphWidth,
+ graphHeight,
+ graphX,
+ graphY,
+ allowContentOverflow = this.get("allowContentOverflow"),
+ diff,
+ rightAxesXCoords,
+ leftAxesXCoords,
+ topAxesYCoords,
+ bottomAxesYCoords,
+ graphRect = {};
+ if(leftAxesCollection)
+ {
+ leftAxesXCoords = [];
+ l = leftAxesCollection.length;
+ for(i = l - 1; i > -1; --i)
+ {
+ leftAxesXCoords.unshift(leftPaneWidth);
+ leftPaneWidth += leftAxesCollection[i].get("width");
+ }
+ }
+ if(rightAxesCollection)
+ {
+ rightAxesXCoords = [];
+ l = rightAxesCollection.length;
+ i = 0;
+ for(i = l - 1; i > -1; --i)
+ {
+ rightPaneWidth += rightAxesCollection[i].get("width");
+ rightAxesXCoords.unshift(w - rightPaneWidth);
+ }
+ }
+ if(topAxesCollection)
+ {
+ topAxesYCoords = [];
+ l = topAxesCollection.length;
+ for(i = l - 1; i > -1; --i)
+ {
+ topAxesYCoords.unshift(topPaneHeight);
+ topPaneHeight += topAxesCollection[i].get("height");
+ }
+ }
+ if(bottomAxesCollection)
+ {
+ bottomAxesYCoords = [];
+ l = bottomAxesCollection.length;
+ for(i = l - 1; i > -1; --i)
+ {
+ bottomPaneHeight += bottomAxesCollection[i].get("height");
+ bottomAxesYCoords.unshift(h - bottomPaneHeight);
+ }
+ }
+
+ graphWidth = w - (leftPaneWidth + rightPaneWidth);
+ graphHeight = h - (bottomPaneHeight + topPaneHeight);
+ graphRect.left = leftPaneWidth;
+ graphRect.top = topPaneHeight;
+ graphRect.bottom = h - bottomPaneHeight;
+ graphRect.right = w - rightPaneWidth;
+ if(!allowContentOverflow)
+ {
+ topOverflow = this._getTopOverflow(leftAxesCollection, rightAxesCollection);
+ bottomOverflow = this._getBottomOverflow(leftAxesCollection, rightAxesCollection);
+ leftOverflow = this._getLeftOverflow(bottomAxesCollection, topAxesCollection);
+ rightOverflow = this._getRightOverflow(bottomAxesCollection, topAxesCollection);
+
+ diff = topOverflow - topPaneHeight;
+ if(diff > 0)
+ {
+ graphRect.top = topOverflow;
+ if(topAxesYCoords)
+ {
+ i = 0;
+ l = topAxesYCoords.length;
+ for(; i < l; ++i)
+ {
+ topAxesYCoords[i] += diff;
+ }
+ }
+ }
+
+ diff = bottomOverflow - bottomPaneHeight;
+ if(diff > 0)
+ {
+ graphRect.bottom = h - bottomOverflow;
+ if(bottomAxesYCoords)
+ {
+ i = 0;
+ l = bottomAxesYCoords.length;
+ for(; i < l; ++i)
+ {
+ bottomAxesYCoords[i] -= diff;
+ }
+ }
+ }
+
+ diff = leftOverflow - leftPaneWidth;
+ if(diff > 0)
+ {
+ graphRect.left = leftOverflow;
+ if(leftAxesXCoords)
+ {
+ i = 0;
+ l = leftAxesXCoords.length;
+ for(; i < l; ++i)
+ {
+ leftAxesXCoords[i] += diff;
+ }
+ }
+ }
+
+ diff = rightOverflow - rightPaneWidth;
+ if(diff > 0)
+ {
+ graphRect.right = w - rightOverflow;
+ if(rightAxesXCoords)
+ {
+ i = 0;
+ l = rightAxesXCoords.length;
+ for(; i < l; ++i)
+ {
+ rightAxesXCoords[i] -= diff;
+ }
+ }
+ }
+ }
+ graphWidth = graphRect.right - graphRect.left;
+ graphHeight = graphRect.bottom - graphRect.top;
+ graphX = graphRect.left;
+ graphY = graphRect.top;
+ if(topAxesCollection)
+ {
+ l = topAxesCollection.length;
+ i = 0;
+ for(; i < l; i++)
+ {
+ axis = topAxesCollection[i];
+ if(axis.get("width") !== graphWidth)
+ {
+ axis.set("width", graphWidth);
+ }
+ axis.get("boundingBox").setStyle("left", graphX + "px");
+ axis.get("boundingBox").setStyle("top", topAxesYCoords[i] + "px");
+ }
+ if(axis._hasDataOverflow())
+ {
+ graphOverflow = "hidden";
+ }
+ }
+ if(bottomAxesCollection)
+ {
+ l = bottomAxesCollection.length;
+ i = 0;
+ for(; i < l; i++)
+ {
+ axis = bottomAxesCollection[i];
+ if(axis.get("width") !== graphWidth)
+ {
+ axis.set("width", graphWidth);
+ }
+ axis.get("boundingBox").setStyle("left", graphX + "px");
+ axis.get("boundingBox").setStyle("top", bottomAxesYCoords[i] + "px");
+ }
+ if(axis._hasDataOverflow())
+ {
+ graphOverflow = "hidden";
+ }
+ }
+ if(leftAxesCollection)
+ {
+ l = leftAxesCollection.length;
+ i = 0;
+ for(; i < l; ++i)
+ {
+ axis = leftAxesCollection[i];
+ axis.get("boundingBox").setStyle("top", graphY + "px");
+ axis.get("boundingBox").setStyle("left", leftAxesXCoords[i] + "px");
+ if(axis.get("height") !== graphHeight)
+ {
+ axis.set("height", graphHeight);
+ }
+ }
+ if(axis._hasDataOverflow())
+ {
+ graphOverflow = "hidden";
+ }
+ }
+ if(rightAxesCollection)
+ {
+ l = rightAxesCollection.length;
+ i = 0;
+ for(; i < l; ++i)
+ {
+ axis = rightAxesCollection[i];
+ axis.get("boundingBox").setStyle("top", graphY + "px");
+ axis.get("boundingBox").setStyle("left", rightAxesXCoords[i] + "px");
+ if(axis.get("height") !== graphHeight)
+ {
+ axis.set("height", graphHeight);
+ }
+ }
+ if(axis._hasDataOverflow())
+ {
+ graphOverflow = "hidden";
+ }
+ }
+ this._drawing = false;
+ if(this._callLater)
+ {
+ this._redraw();
+ return;
+ }
+ if(graph)
+ {
+ graph.get("boundingBox").setStyle("left", graphX + "px");
+ graph.get("boundingBox").setStyle("top", graphY + "px");
+ graph.set("width", graphWidth);
+ graph.set("height", graphHeight);
+ graph.get("boundingBox").setStyle("overflow", graphOverflow);
+ }
+
+ if(this._overlay)
+ {
+ this._overlay.setStyle("left", graphX + "px");
+ this._overlay.setStyle("top", graphY + "px");
+ this._overlay.setStyle("width", graphWidth + "px");
+ this._overlay.setStyle("height", graphHeight + "px");
+ }
+ },
+
+ /**
+ * Destructor implementation for the CartesianChart class. Calls destroy on all axes, series and the Graph instance.
+ * Removes the tooltip and overlay HTML elements.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor: function()
+ {
+ var graph = this.get("graph"),
+ i = 0,
+ len,
+ seriesCollection = this.get("seriesCollection"),
+ axesCollection = this._axesCollection,
+ tooltip = this.get("tooltip").node;
+ if(this._description)
+ {
+ this._description.empty();
+ this._description.remove(true);
+ }
+ if(this._liveRegion)
+ {
+ this._liveRegion.empty();
+ this._liveRegion.remove(true);
+ }
+ len = seriesCollection ? seriesCollection.length : 0;
+ for(; i < len; ++i)
+ {
+ if(seriesCollection[i] instanceof Y.CartesianSeries)
+ {
+ seriesCollection[i].destroy(true);
+ }
+ }
+ len = axesCollection ? axesCollection.length : 0;
+ for(i = 0; i < len; ++i)
+ {
+ if(axesCollection[i] instanceof Y.Axis)
+ {
+ axesCollection[i].destroy(true);
+ }
+ }
+ if(graph)
+ {
+ graph.destroy(true);
+ }
+ if(tooltip)
+ {
+ tooltip.empty();
+ tooltip.remove(true);
+ }
+ if(this._overlay)
+ {
+ this._overlay.empty();
+ this._overlay.remove(true);
+ }
+ },
+
+ /**
+ * Returns the appropriate message based on the key press.
+ *
+ * @method _getAriaMessage
+ * @param {Number} key The keycode that was pressed.
+ * @return String
+ */
+ _getAriaMessage: function(key)
+ {
+ var msg = "",
+ series,
+ items,
+ categoryItem,
+ valueItem,
+ seriesIndex = this._seriesIndex,
+ itemIndex = this._itemIndex,
+ seriesCollection = this.get("seriesCollection"),
+ len = seriesCollection.length,
+ dataLength;
+ if(key % 2 === 0)
+ {
+ if(len > 1)
+ {
+ if(key === 38)
+ {
+ seriesIndex = seriesIndex < 1 ? len - 1 : seriesIndex - 1;
+ }
+ else if(key === 40)
+ {
+ seriesIndex = seriesIndex >= len - 1 ? 0 : seriesIndex + 1;
+ }
+ this._itemIndex = -1;
+ }
+ else
+ {
+ seriesIndex = 0;
+ }
+ this._seriesIndex = seriesIndex;
+ series = this.getSeries(parseInt(seriesIndex, 10));
+ msg = series.get("valueDisplayName") + " series.";
+ }
+ else
+ {
+ if(seriesIndex > -1)
+ {
+ msg = "";
+ series = this.getSeries(parseInt(seriesIndex, 10));
+ }
+ else
+ {
+ seriesIndex = 0;
+ this._seriesIndex = seriesIndex;
+ series = this.getSeries(parseInt(seriesIndex, 10));
+ msg = series.get("valueDisplayName") + " series.";
+ }
+ dataLength = series._dataLength ? series._dataLength : 0;
+ if(key === 37)
+ {
+ itemIndex = itemIndex > 0 ? itemIndex - 1 : dataLength - 1;
+ }
+ else if(key === 39)
+ {
+ itemIndex = itemIndex >= dataLength - 1 ? 0 : itemIndex + 1;
+ }
+ this._itemIndex = itemIndex;
+ items = this.getSeriesItems(series, itemIndex);
+ categoryItem = items.category;
+ valueItem = items.value;
+ if(categoryItem && valueItem && categoryItem.value && valueItem.value)
+ {
+ msg += categoryItem.displayName +
+ ": " +
+ categoryItem.axis.formatLabel.apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) +
+ ", ";
+ msg += valueItem.displayName +
+ ": " +
+ valueItem.axis.formatLabel.apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]) +
+ ", ";
+ }
+ else
+ {
+ msg += "No data available.";
+ }
+ msg += (itemIndex + 1) + " of " + dataLength + ". ";
+ }
+ return msg;
+ }
+}, {
+ ATTRS: {
+ /**
+ * Indicates whether axis labels are allowed to overflow beyond the bounds of the chart's content box.
+ *
+ * @attribute allowContentOverflow
+ * @type Boolean
+ */
+ allowContentOverflow: {
+ value: false
+ },
+
+ /**
+ * Style object for the axes.
+ *
+ * @attribute axesStyles
+ * @type Object
+ * @private
+ */
+ axesStyles: {
+ lazyAdd: false,
+
+ getter: function()
+ {
+ var axes = this.get("axes"),
+ i,
+ styles = this._axesStyles;
+ if(axes)
+ {
+ for(i in axes)
+ {
+ if(axes.hasOwnProperty(i) && axes[i] instanceof Y.Axis)
+ {
+ if(!styles)
+ {
+ styles = {};
+ }
+ styles[i] = axes[i].get("styles");
+ }
+ }
+ }
+ return styles;
+ },
+
+ setter: function(val)
+ {
+ var axes = this.get("axes"),
+ i;
+ for(i in val)
+ {
+ if(val.hasOwnProperty(i) && axes.hasOwnProperty(i))
+ {
+ this._setBaseAttribute(axes[i], "styles", val[i]);
+ }
+ }
+ return val;
+ }
+ },
+
+ /**
+ * Style object for the series
+ *
+ * @attribute seriesStyles
+ * @type Object
+ * @private
+ */
+ seriesStyles: {
+ lazyAdd: false,
+
+ getter: function()
+ {
+ var styles = this._seriesStyles,
+ graph = this.get("graph"),
+ dict,
+ i;
+ if(graph)
+ {
+ dict = graph.get("seriesDictionary");
+ if(dict)
+ {
+ styles = {};
+ for(i in dict)
+ {
+ if(dict.hasOwnProperty(i))
+ {
+ styles[i] = dict[i].get("styles");
+ }
+ }
+ }
+ }
+ return styles;
+ },
+
+ setter: function(val)
+ {
+ var i,
+ l,
+ s;
+
+ if(Y_Lang.isArray(val))
+ {
+ s = this.get("seriesCollection");
+ i = 0;
+ l = val.length;
+
+ for(; i < l; ++i)
+ {
+ this._setBaseAttribute(s[i], "styles", val[i]);
+ }
+ }
+ else
+ {
+ for(i in val)
+ {
+ if(val.hasOwnProperty(i))
+ {
+ s = this.getSeries(i);
+ this._setBaseAttribute(s, "styles", val[i]);
+ }
+ }
+ }
+ return val;
+ }
+ },
+
+ /**
+ * Styles for the graph.
+ *
+ * @attribute graphStyles
+ * @type Object
+ * @private
+ */
+ graphStyles: {
+ lazyAdd: false,
+
+ getter: function()
+ {
+ var graph = this.get("graph");
+ if(graph)
+ {
+ return(graph.get("styles"));
+ }
+ return this._graphStyles;
+ },
+
+ setter: function(val)
+ {
+ var graph = this.get("graph");
+ this._setBaseAttribute(graph, "styles", val);
+ return val;
+ }
+
+ },
+
+ /**
+ * Style properties for the chart. Contains a key indexed hash of the following:
+ *
+ * - series
- A key indexed hash containing references to the `styles` attribute for each series in the chart.
+ * Specific style attributes vary depending on the series:
+ *
+ *
+ * - axes
- A key indexed hash containing references to the `styles` attribute for each axes in the chart. Specific
+ * style attributes can be found in the Axis class.
+ * - graph
- A reference to the `styles` attribute in the chart. Specific style attributes can be found in the
+ * Graph class.
+ *
+ *
+ * @attribute styles
+ * @type Object
+ */
+ styles: {
+ lazyAdd: false,
+
+ getter: function()
+ {
+ var styles = {
+ axes: this.get("axesStyles"),
+ series: this.get("seriesStyles"),
+ graph: this.get("graphStyles")
+ };
+ return styles;
+ },
+ setter: function(val)
+ {
+ if(val.hasOwnProperty("axes"))
+ {
+ if(this.get("axesStyles"))
+ {
+ this.set("axesStyles", val.axes);
+ }
+ else
+ {
+ this._axesStyles = val.axes;
+ }
+ }
+ if(val.hasOwnProperty("series"))
+ {
+ if(this.get("seriesStyles"))
+ {
+ this.set("seriesStyles", val.series);
+ }
+ else
+ {
+ this._seriesStyles = val.series;
+ }
+ }
+ if(val.hasOwnProperty("graph"))
+ {
+ this.set("graphStyles", val.graph);
+ }
+ }
+ },
+
+ /**
+ * Axes to appear in the chart. This can be a key indexed hash of axis instances or object literals
+ * used to construct the appropriate axes.
+ *
+ * @attribute axes
+ * @type Object
+ */
+ axes: {
+ lazyAdd: false,
+
+ valueFn: "_getDefaultAxes",
+
+ setter: function(val)
+ {
+ if(this.get("dataProvider"))
+ {
+ val = this._setAxes(val);
+ }
+ return val;
+ }
+ },
+
+ /**
+ * Collection of series to appear on the chart. This can be an array of Series instances or object literals
+ * used to construct the appropriate series.
+ *
+ * @attribute seriesCollection
+ * @type Array
+ */
+ seriesCollection: {
+ lazyAdd: false,
+
+ valueFn: "_getDefaultSeriesCollection",
+
+ setter: function(val)
+ {
+ if(this.get("dataProvider"))
+ {
+ return this._parseSeriesCollection(val);
+ }
+ return val;
+ }
+ },
+
+ /**
+ * Reference to the left-aligned axes for the chart.
+ *
+ * @attribute leftAxesCollection
+ * @type Array
+ * @private
+ */
+ leftAxesCollection: {},
+
+ /**
+ * Reference to the bottom-aligned axes for the chart.
+ *
+ * @attribute bottomAxesCollection
+ * @type Array
+ * @private
+ */
+ bottomAxesCollection: {},
+
+ /**
+ * Reference to the right-aligned axes for the chart.
+ *
+ * @attribute rightAxesCollection
+ * @type Array
+ * @private
+ */
+ rightAxesCollection: {},
+
+ /**
+ * Reference to the top-aligned axes for the chart.
+ *
+ * @attribute topAxesCollection
+ * @type Array
+ * @private
+ */
+ topAxesCollection: {},
+
+ /**
+ * Indicates whether or not the chart is stacked.
+ *
+ * @attribute stacked
+ * @type Boolean
+ */
+ stacked: {
+ value: false
+ },
+
+ /**
+ * Direction of chart's category axis when there is no series collection specified. Charts can
+ * be horizontal or vertical. When the chart type is column, the chart is horizontal.
+ * When the chart type is bar, the chart is vertical.
+ *
+ * @attribute direction
+ * @type String
+ */
+ direction: {
+ getter: function()
+ {
+ var type = this.get("type");
+ if(type === "bar")
+ {
+ return "vertical";
+ }
+ else if(type === "column")
+ {
+ return "horizontal";
+ }
+ return this._direction;
+ },
+
+ setter: function(val)
+ {
+ this._direction = val;
+ return this._direction;
+ }
+ },
+
+ /**
+ * Indicates whether or not an area is filled in a combo chart.
+ *
+ * @attribute showAreaFill
+ * @type Boolean
+ */
+ showAreaFill: {},
+
+ /**
+ * Indicates whether to display markers in a combo chart.
+ *
+ * @attribute showMarkers
+ * @type Boolean
+ */
+ showMarkers:{},
+
+ /**
+ * Indicates whether to display lines in a combo chart.
+ *
+ * @attribute showLines
+ * @type Boolean
+ */
+ showLines:{},
+
+ /**
+ * Indicates the key value used to identify a category axis in the `axes` hash. If
+ * not specified, the categoryKey attribute value will be used.
+ *
+ * @attribute categoryAxisName
+ * @type String
+ */
+ categoryAxisName: {
+ },
+
+ /**
+ * Indicates the key value used to identify a the series axis when an axis not generated.
+ *
+ * @attribute valueAxisName
+ * @type String
+ */
+ valueAxisName: {
+ value: "values"
+ },
+
+ /**
+ * Reference to the horizontalGridlines for the chart.
+ *
+ * @attribute horizontalGridlines
+ * @type Gridlines
+ */
+ horizontalGridlines: {
+ getter: function()
+ {
+ var graph = this.get("graph");
+ if(graph)
+ {
+ return graph.get("horizontalGridlines");
+ }
+ return this._horizontalGridlines;
+ },
+ setter: function(val)
+ {
+ var graph = this.get("graph");
+ if(val && !Y_Lang.isObject(val))
+ {
+ val = {};
+ }
+ if(graph)
+ {
+ graph.set("horizontalGridlines", val);
+ }
+ else
+ {
+ this._horizontalGridlines = val;
+ }
+ }
+ },
+
+ /**
+ * Reference to the verticalGridlines for the chart.
+ *
+ * @attribute verticalGridlines
+ * @type Gridlines
+ */
+ verticalGridlines: {
+ getter: function()
+ {
+ var graph = this.get("graph");
+ if(graph)
+ {
+ return graph.get("verticalGridlines");
+ }
+ return this._verticalGridlines;
+ },
+ setter: function(val)
+ {
+ var graph = this.get("graph");
+ if(val && !Y_Lang.isObject(val))
+ {
+ val = {};
+ }
+ if(graph)
+ {
+ graph.set("verticalGridlines", val);
+ }
+ else
+ {
+ this._verticalGridlines = val;
+ }
+ }
+ },
+
+ /**
+ * Type of chart when there is no series collection specified.
+ *
+ * @attribute type
+ * @type String
+ */
+ type: {
+ getter: function()
+ {
+ if(this.get("stacked"))
+ {
+ return "stacked" + this._type;
+ }
+ return this._type;
+ },
+
+ setter: function(val)
+ {
+ if(this._type === "bar")
+ {
+ if(val !== "bar")
+ {
+ this.set("direction", "horizontal");
+ }
+ }
+ else
+ {
+ if(val === "bar")
+ {
+ this.set("direction", "vertical");
+ }
+ }
+ this._type = val;
+ return this._type;
+ }
+ },
+
+ /**
+ * Reference to the category axis used by the chart.
+ *
+ * @attribute categoryAxis
+ * @type Axis
+ */
+ categoryAxis:{}
+ }
+});
+/**
+ * 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()
+ {
+ 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:
+ *
+ * - axis
- The axis to which the category is bound.
+ * - displayName
- The display name set to the category (defaults to key if not provided)
+ * - key
- The key of the category.
+ * - value
- The value of the category
+ *
+ * @param {Object} valueItem An object containing the following:
+ *
+ * - axis
- The axis to which the item's series is bound.
+ * - displayName
- The display name of the series. (defaults to key if not provided)
+ * - key
- The key for the series.
+ * - value
- The value for the series item.
+ *
+ * @param {Number} itemIndex The index of the item within the series.
+ * @param {CartesianSeries} series The `PieSeries` instance of the item.
+ * @return {HTML}
+ * @private
+ */
+ _tooltipLabelFunction: function(categoryItem, valueItem, itemIndex, series)
+ {
+ 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,
+ 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: {
+ lazyAdd: false,
+
+ 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;
+
+
+}, '3.10.3', {
+ "requires": [
+ "dom",
+ "event-mouseenter",
+ "event-touch",
+ "graphics-group",
+ "axes",
+ "series-pie",
+ "series-line",
+ "series-marker",
+ "series-area",
+ "series-spline",
+ "series-column",
+ "series-bar",
+ "series-areaspline",
+ "series-combo",
+ "series-combospline",
+ "series-line-stacked",
+ "series-marker-stacked",
+ "series-area-stacked",
+ "series-spline-stacked",
+ "series-column-stacked",
+ "series-bar-stacked",
+ "series-areaspline-stacked",
+ "series-combo-stacked",
+ "series-combospline-stacked"
+ ]
+});